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.Resource;
022import io.avaje.classpath.scanner.core.AndriodContextHolder;
023import io.avaje.classpath.scanner.core.Location;
024import io.avaje.classpath.scanner.internal.ScanLog;
025import io.avaje.classpath.scanner.internal.ResourceAndClassScanner;
026
027import java.io.IOException;
028import java.io.UncheckedIOException;
029import java.util.ArrayList;
030import java.util.Enumeration;
031import java.util.List;
032import java.util.function.Predicate;
033
034/**
035 * Class & resource scanner for Android.
036 */
037public class AndroidScanner implements ResourceAndClassScanner {
038
039  private final Context context;
040
041  private final PathClassLoader classLoader;
042
043  public AndroidScanner(ClassLoader classLoader) {
044    this.classLoader = (PathClassLoader) classLoader;
045    context = AndriodContextHolder.getContext();
046    if (context == null) {
047      throw new IllegalStateException("Unable to create scanner. Within an activity fix this with io.avaje.classpath.scanner.android.ContextHolder.setContext(this);");
048    }
049  }
050
051  public List<Resource> scanForResources(Location location, Predicate<String> predicate) {
052    try {
053      List<Resource> resources = new ArrayList<>();
054      String path = location.path();
055      for (String asset : context.getAssets().list(path)) {
056        if (predicate.test(asset)) {
057          resources.add(new AndroidResource(context.getAssets(), path, asset));
058        }
059      }
060      return resources;
061    } catch (IOException e) {
062      throw new UncheckedIOException(e);
063    }
064  }
065
066  public List<Class<?>> scanForClasses(Location location, Predicate<Class<?>> predicate) {
067    try {
068      String pkg = location.path().replace("/", ".");
069      List<Class<?>> classes = new ArrayList<>();
070      DexFile dex = new DexFile(context.getApplicationInfo().sourceDir);
071      Enumeration<String> entries = dex.entries();
072      while (entries.hasMoreElements()) {
073        String className = entries.nextElement();
074        if (className.startsWith(pkg)) {
075          Class<?> clazz = classLoader.loadClass(className);
076          if (predicate.test(clazz)) {
077            classes.add(clazz);
078            ScanLog.log.trace("found {}", className);
079          }
080        }
081      }
082      return classes;
083
084    } catch (IOException | ClassNotFoundException e) {
085      throw new RuntimeException(e);
086    }
087  }
088}