blob: 06a421b0f0ececcebb54126d2bf7f7a14abf6a59 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.giraph.utils;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
/**
* Helper class to deal with annotations in runtime.
*/
public class AnnotationUtils {
/** Do not instantiate. */
private AnnotationUtils() {
}
/**
* Finds all classes within a package which are annotated with certain
* annotation.
*
* @param annotation Annotation which we are looking for
* @param <T> Annotation class
* @param packageName Package in which to search
* @return The list of annotated classes
*/
public static <T extends Annotation> List<Class<?>> getAnnotatedClasses(
Class<T> annotation, String packageName) {
ArrayList<Class<?>> ret = new ArrayList<Class<?>>();
for (Iterator<Class<?>> it = getClassesIterator(packageName);
it.hasNext();) {
Class<?> clazz = it.next();
if (clazz.getAnnotation(annotation) != null) {
ret.add(clazz);
}
}
return ret;
}
/**
* @param packageName Package through which to iterate
* @return Iterator through the classes of this jar file (if executed from
* jar) or through the classes of org package (if .class file is
* executed)
*/
public static Iterator<Class<?>> getClassesIterator(String packageName) {
if (isExecutedFromJar()) {
try {
return new JarClassesIterator(packageName);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
return new GeneralClassesIterator(packageName);
}
}
/**
* @return Whether or not the code is executed from jar file
*/
private static boolean isExecutedFromJar() {
return AnnotationUtils.class.getResource("AnnotationUtils.class")
.getProtocol().equals("jar");
}
/**
* To be used when {@link #isExecutedFromJar()} is true.
*
* @return If executed from jar file returns the path to the jar
* (otherwise it will return the folder in which this .class file is
* located)
*/
private static String getCurrentJar() {
return AnnotationUtils.class.getProtectionDomain().getCodeSource()
.getLocation().getFile();
}
/**
* To be used when {@link #isExecutedFromJar()} is true.
* <p/>
* Iterator through classes of this jar file.
*/
private static class JarClassesIterator implements Iterator<Class<?>> {
/** Used to go through classes in current jar */
private final JarInputStream jarIn;
/** Are we positioned on the next class entry */
private boolean entryLoaded;
/** Next class entry */
private JarEntry currentEntry;
/** Folder in which to look */
private final String path;
/**
* @param packageName Package through which to iterate
* @throws IOException
*/
public JarClassesIterator(String packageName) throws IOException {
jarIn = new JarInputStream(new FileInputStream(new File(
getCurrentJar())));
entryLoaded = false;
currentEntry = null;
path = packageName.replace(".", File.separator);
}
@Override
public boolean hasNext() {
loadNextEntry();
return currentEntry != null;
}
@Override
public Class<?> next() {
loadNextEntry();
if (currentEntry == null) {
throw new NoSuchElementException();
}
entryLoaded = false;
String className = currentEntry.getName().replace(".class",
"").replace(File.separator, ".");
return loadClass(className);
}
/**
* Sets position to next class entry
*/
private void loadNextEntry() {
while (!entryLoaded) {
try {
currentEntry = jarIn.getNextJarEntry();
if (currentEntry == null || (currentEntry.getName().endsWith(
".class") && (currentEntry.getName().startsWith(path)))) {
entryLoaded = true;
} else {
currentEntry = null;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (currentEntry == null) {
try {
jarIn.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void remove() {
throw new UnsupportedOperationException(
"Can't remove from classes iterator");
}
}
/**
* To be used when {@link #isExecutedFromJar()} is false.
* <p/>
* Iterator through classes of some package.
*/
private static class GeneralClassesIterator implements Iterator<Class<?>> {
/** From which position in path the package name starts */
private final int stringPosition;
/** Recursive directory iterator */
private final Iterator<File> iterator;
/**
* @param packageName Package through which to iterate
*/
public GeneralClassesIterator(String packageName) {
String mainPath = AnnotationUtils.class.getProtectionDomain()
.getCodeSource().getLocation().getFile();
String subPath = packageName.replace(".", File.separator);
File directory = new File(mainPath + subPath);
stringPosition = directory.getPath().length() - packageName.length();
List<File> files = new ArrayList<File>();
addAllClassFiles(directory, files);
iterator = files.iterator();
}
/**
* Recursively add all .class files from the directory to the list.
*
* @param directory Directory from which we are adding files
* @param files List we add files to
*/
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
private void addAllClassFiles(File directory, List<File> files) {
for (File file : directory.listFiles()) {
if (file.isDirectory()) {
addAllClassFiles(file, files);
} else if (file.getName().endsWith(".class")) {
files.add(file);
}
}
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Class<?> next() {
String className = iterator.next().getPath().substring(stringPosition)
.replace(".class", "").replace(File.separator, ".");
return loadClass(className);
}
@Override
public void remove() {
throw new UnsupportedOperationException(
"Can't remove from classes iterator");
}
}
/**
* Loads the class with the specified name
*
* @param className Name of the class we are loading
* @return Class with the specified name
*/
private static Class<?> loadClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Error loading class " + className, e);
} catch (NoClassDefFoundError e) {
throw new RuntimeException("Error loading class " + className, e);
}
}
}