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}