001/* 002 Copyright 2010-2016 Boxfuse GmbH 003 <p/> 004 Licensed under the Apache License, Version 2.0 (the "License"); 005 you may not use this file except in compliance with the License. 006 You may obtain a copy of the License at 007 <p/> 008 http://www.apache.org/licenses/LICENSE-2.0 009 <p/> 010 Unless required by applicable law or agreed to in writing, software 011 distributed under the License is distributed on an "AS IS" BASIS, 012 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 See the License for the specific language governing permissions and 014 limitations under the License. 015 */ 016package io.avaje.classpath.scanner.internal.scanner.classpath.android; 017 018import android.content.Context; 019import dalvik.system.DexFile; 020import dalvik.system.PathClassLoader; 021import io.avaje.classpath.scanner.ClassFilter; 022import io.avaje.classpath.scanner.Resource; 023import io.avaje.classpath.scanner.ResourceFilter; 024import io.avaje.classpath.scanner.core.ClassPathScanException; 025import io.avaje.classpath.scanner.core.Location; 026import io.avaje.classpath.scanner.andriod.ContextHolder; 027import io.avaje.classpath.scanner.internal.ResourceAndClassScanner; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031import java.io.IOException; 032import java.util.ArrayList; 033import java.util.Enumeration; 034import java.util.List; 035 036/** 037 * Class & resource scanner for Android. 038 */ 039public class AndroidScanner implements ResourceAndClassScanner { 040 041 private static final Logger LOG = LoggerFactory.getLogger(AndroidScanner.class); 042 043 private final Context context; 044 045 private final PathClassLoader classLoader; 046 047 public AndroidScanner(ClassLoader classLoader) { 048 this.classLoader = (PathClassLoader) classLoader; 049 context = ContextHolder.getContext(); 050 if (context == null) { 051 throw new ClassPathScanException("Unable to create scanner. " + 052 "Within an activity you can fix this with org.avaje.classpath.scanner.android.ContextHolder.setContext(this);"); 053 } 054 } 055 056 public List<Resource> scanForResources(Location location, ResourceFilter predicate) { 057 058 try { 059 List<Resource> resources = new ArrayList<>(); 060 String path = location.getPath(); 061 for (String asset : context.getAssets().list(path)) { 062 if (predicate.isMatch(asset)) { 063 resources.add(new AndroidResource(context.getAssets(), path, asset)); 064 } 065 } 066 067 return resources; 068 069 } catch (IOException e) { 070 throw new ClassPathScanException(e); 071 } 072 } 073 074 public List<Class<?>> scanForClasses(Location location, ClassFilter predicate) { 075 076 try { 077 078 String pkg = location.getPath().replace("/", "."); 079 080 List<Class<?>> classes = new ArrayList<>(); 081 082 DexFile dex = new DexFile(context.getApplicationInfo().sourceDir); 083 Enumeration<String> entries = dex.entries(); 084 while (entries.hasMoreElements()) { 085 String className = entries.nextElement(); 086 if (className.startsWith(pkg)) { 087 Class<?> clazz = classLoader.loadClass(className); 088 if (predicate.isMatch(clazz)) { 089 classes.add(clazz); 090 LOG.trace("... found class: {}", className); 091 } 092 } 093 } 094 return classes; 095 096 } catch (IOException | ClassNotFoundException e) { 097 throw new ClassPathScanException(e); 098 099 } 100 } 101}