/*
* Copyright 2012 William Bernardet
*
* Licensed 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 com.googlecode.japi.checker;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.objectweb.asm.ClassReader;
import com.googlecode.japi.checker.model.ClassData;
import com.googlecode.japi.checker.utils.AntPatternMatcher;
/**
* This abstract class provide basic behavior for the BCChecker to extract ClassData
* out of a container e.g: a directory, a zip file, a jar file...
* It must be extended to implement such functionalities.
*
*/
public abstract class AbstractClassReader {
private Map<String, List<ClassData>> classes = new Hashtable<String, List<ClassData>>();
/**
* This method should implement the extraction of the classes out of its container.
*
* @throws IOException is thrown in case of reading error.
*/
abstract void read() throws IOException;
/**
*
* @param visitor
* @param name
* @param data
* @throws ReadClassException
*/
protected void readClass(ClassDumper visitor, String name, byte[] data) throws ReadClassException {
try {
ClassReader cr = new ClassReader(data);
cr.accept(visitor, 0);
this.put(name, visitor.getClasses());
} catch (RuntimeException exc) {
throw new ReadClassException("Error occurred while loading class " + name + ": " + exc.toString(), exc);
}
}
/**
* Get all the discovered classes.
* @return a list of all the discovered classes.
*/
public List<ClassData> getClasses() {
return getClasses(Collections.<AntPatternMatcher> emptyList(), Collections.<AntPatternMatcher> emptyList());
}
/**
* Get a filtered list of classes. The patterns are filesystem based.
* @param includes the include patterns.
* @param excludes the exclude patterns.
* @return a filtered list.
*/
public List<ClassData> getClasses(List<AntPatternMatcher> includes, List<AntPatternMatcher> excludes) {
List<ClassData> result = new ArrayList<ClassData>();
for (Entry<String, List<ClassData>> entry : this.classes.entrySet()) {
if (shouldInclude(entry.getKey(), includes, excludes)) {
result.addAll(entry.getValue());
}
}
return result;
}
/**
* Empty this class data cache.
*/
protected void clear() {
classes.clear();
}
/**
* Add a new class definition to the cache.
* @param name the filename
* @param classes the classes related to this filename.
*/
protected void put(String name, List<ClassData> classes) {
if (this.classes.containsKey(name)) {
this.classes.get(name).addAll(classes);
} else {
this.classes.put(name, new ArrayList<ClassData>(classes));
}
}
/**
* Should a path be included based on the patterns.
* @param subpath the path to check
* @param includes the include patterns
* @param excludes the exclude patterns.
* @return Returns true if the if should be included into the list, false otherwise.
*/
protected boolean shouldInclude(String subpath, List<AntPatternMatcher> includes, List<AntPatternMatcher> excludes) {
boolean included = includes.size() == 0 ? true : false;
for (AntPatternMatcher inc : includes) {
if (inc.matches(subpath)) {
included = true;
break;
}
}
for (AntPatternMatcher exc : excludes) {
if (exc.matches(subpath)) {
return false;
}
}
return included;
}
}