--- /dev/null
+package jp.naist.se.stigmata;\r
+\r
+/*\r
+ * $Id$\r
+ */\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.net.MalformedURLException;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Stack;\r
+\r
+import jp.naist.se.stigmata.event.BirthmarkEngineEvent;\r
+import jp.naist.se.stigmata.event.BirthmarkEngineListener;\r
+import jp.naist.se.stigmata.event.OperationStage;\r
+import jp.naist.se.stigmata.event.OperationType;\r
+import jp.naist.se.stigmata.event.WarningMessages;\r
+import jp.naist.se.stigmata.filter.ComparisonPairFilterManager;\r
+import jp.naist.se.stigmata.filter.FilteredComparisonResultSet;\r
+import jp.naist.se.stigmata.reader.ClassFileArchive;\r
+import jp.naist.se.stigmata.reader.ClassFileEntry;\r
+import jp.naist.se.stigmata.reader.ClasspathContext;\r
+import jp.naist.se.stigmata.reader.DefaultClassFileArchive;\r
+import jp.naist.se.stigmata.reader.JarClassFileArchive;\r
+import jp.naist.se.stigmata.reader.WarClassFileArchive;\r
+import jp.naist.se.stigmata.spi.BirthmarkSpi;\r
+\r
+import org.apache.commons.beanutils.BeanUtils;\r
+\r
+/**\r
+ * \r
+ * @author Haruaki Tamada\r
+ * @version $Revision$ $Date$\r
+ */\r
+public class BirthmarkEngine{\r
+ private BirthmarkEnvironment environment;\r
+ private List<BirthmarkEngineListener> listeners = new ArrayList<BirthmarkEngineListener>();\r
+ private Stack<WarningMessages> stack = new Stack<WarningMessages>();\r
+ private WarningMessages warnings;\r
+ private OperationType latestOperationType;\r
+ private OperationType targetType;\r
+\r
+ public BirthmarkEngine(){\r
+ this(BirthmarkEnvironment.getDefaultEnvironment());\r
+ }\r
+\r
+ public BirthmarkEngine(BirthmarkEnvironment env){\r
+ this.environment = env;\r
+ }\r
+\r
+ public void addBirthmarkEngineListener(BirthmarkEngineListener listener){\r
+ listeners.add(listener);\r
+ }\r
+\r
+ public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){\r
+ listeners.remove(listener);\r
+ }\r
+\r
+ public ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{\r
+ operationStart(OperationType.FILTER_BIRTHMARKS);\r
+\r
+ ComparisonResultSet crs = compare(target, context);\r
+ crs = filter(crs, context);\r
+ \r
+ operationDone(OperationType.FILTER_BIRTHMARKS);\r
+\r
+ return crs;\r
+ }\r
+\r
+ public ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{\r
+ operationStart(OperationType.FILTER_BIRTHMARKS);\r
+\r
+ ComparisonResultSet crs = compare(targetX, targetY, context);\r
+ crs = filter(crs, context);\r
+ \r
+ operationDone(OperationType.FILTER_BIRTHMARKS);\r
+\r
+ return crs;\r
+ }\r
+\r
+ public ComparisonResultSet filter(ExtractionResult er, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{\r
+ operationStart(OperationType.FILTER_BIRTHMARKS);\r
+\r
+ ComparisonResultSet crs = compare(er, context);\r
+ crs = filter(crs, context);\r
+ \r
+ operationDone(OperationType.FILTER_BIRTHMARKS);\r
+\r
+ return crs;\r
+ }\r
+\r
+ public ComparisonResultSet filter(ComparisonResultSet crs, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{\r
+ operationStart(OperationType.FILTER_BIRTHMARKS);\r
+\r
+ String[] filterTypes = context.getFilterTypes();\r
+\r
+ if(filterTypes != null){\r
+ List<ComparisonPairFilterSet> filterList = new ArrayList<ComparisonPairFilterSet>();\r
+ ComparisonPairFilterManager manager = environment.getFilterManager();\r
+ for(int i = 0; i < filterTypes.length; i++){\r
+ ComparisonPairFilterSet fset = manager.getFilterSet(filterTypes[i]);\r
+ if(fset != null){\r
+ filterList.add(fset);\r
+ }\r
+ else{\r
+ warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);\r
+ }\r
+ }\r
+ ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);\r
+\r
+ crs = new FilteredComparisonResultSet(crs, cpfs);\r
+ }\r
+ \r
+ operationDone(OperationType.FILTER_BIRTHMARKS);\r
+ return crs;\r
+ }\r
+\r
+ public ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{\r
+ operationStart(OperationType.COMPARE_BIRTHMARKS);\r
+\r
+ ExtractionResult er = extract(target, context);\r
+ ComparisonResultSet crs = compare(er, context);\r
+\r
+ operationDone(OperationType.COMPARE_BIRTHMARKS);\r
+\r
+ return crs;\r
+ }\r
+\r
+ public ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{\r
+ operationStart(OperationType.COMPARE_BIRTHMARKS);\r
+\r
+ ExtractionResult er = extract(targetX, targetY, context);\r
+ ComparisonResultSet crs = compare(er, context);\r
+\r
+ operationDone(OperationType.COMPARE_BIRTHMARKS);\r
+\r
+ return crs;\r
+ }\r
+\r
+ public ComparisonResultSet compare(ExtractionResult er, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{\r
+ operationStart(OperationType.COMPARE_BIRTHMARKS);\r
+\r
+ ComparisonResultSet crs = null;\r
+ switch(context.getComparisonMethod()){\r
+ case ROUND_ROBIN:\r
+ crs = new RoundRobinComparisonResultSet(\r
+ er.getBirthmarkSetXY(), environment, true\r
+ );\r
+ break;\r
+ case ROUND_ROBIN_XY:\r
+ crs = new RoundRobinComparisonResultSet(\r
+ er.getBirthmarkSetX(), er.getBirthmarkSetY(), environment\r
+ );\r
+ case GUESSED_PAIR:\r
+ crs = new CertainPairComparisonResultSet(\r
+ er.getBirthmarkSetX(), er.getBirthmarkSetY(), environment\r
+ );\r
+ break;\r
+ case SPECIFIED_PAIR:\r
+ crs = new CertainPairComparisonResultSet(\r
+ er.getBirthmarkSetX(), er.getBirthmarkSetY(),\r
+ context.getNameMappings(), environment\r
+ );\r
+ break;\r
+ }\r
+\r
+ operationDone(OperationType.COMPARE_BIRTHMARKS);\r
+ return crs;\r
+ }\r
+\r
+ public ExtractionResult extract(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException{\r
+ operationStart(OperationType.EXTRACT_BIRTHMARKS);\r
+ ExtractionResult er = extract(target, null, context);\r
+ operationDone(OperationType.EXTRACT_BIRTHMARKS);\r
+ return er;\r
+ }\r
+\r
+ public ExtractionResult extract(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException{\r
+ operationStart(OperationType.EXTRACT_BIRTHMARKS);\r
+ ExtractionResult er = new ExtractionResult();\r
+\r
+ try{\r
+ switch(context.getComparisonMethod()){\r
+ case ROUND_ROBIN:\r
+ String[] targetXY = mergeTarget(targetX, targetY);\r
+ BirthmarkSet[] s = extractImpl(targetXY, context);\r
+ er.setBirthmarkSetXY(s);\r
+ break;\r
+ case GUESSED_PAIR:\r
+ case SPECIFIED_PAIR:\r
+ case ROUND_ROBIN_XY:\r
+ default:\r
+ if(targetX == null || targetY == null){\r
+ throw new BirthmarkExtractionFailedException("targetX or targetY is null");\r
+ }\r
+ BirthmarkSet[] extractResultX = extractImpl(targetX, context);\r
+ BirthmarkSet[] extractResultY = extractImpl(targetY, context);\r
+\r
+ er.setBirthmarkSetX(extractResultX);\r
+ er.setBirthmarkSetY(extractResultY);\r
+ break;\r
+ }\r
+ return er;\r
+ } catch(IOException e){\r
+ throw new BirthmarkExtractionFailedException(e);\r
+ } finally{\r
+ operationDone(OperationType.EXTRACT_BIRTHMARKS);\r
+ }\r
+ }\r
+\r
+ private String[] mergeTarget(String[] t1, String[] t2){\r
+ List<String> list = new ArrayList<String>();\r
+ addToList(list, t1);\r
+ addToList(list, t2);\r
+\r
+ return list.toArray(new String[list.size()]);\r
+ }\r
+\r
+ private void addToList(List<String> list, String[] target){\r
+ if(target != null){\r
+ for(String s: target){\r
+ list.add(s);\r
+ }\r
+ }\r
+ }\r
+\r
+ private BirthmarkSet[] extractImpl(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, IOException{\r
+ ClassFileArchive[] archives = createArchives(target, environment);\r
+ BirthmarkExtractor[] extractors = createExtractors(context.getExtractionTypes(), environment);\r
+ ExtractionUnit unit = context.getExtractionUnit();\r
+\r
+ BirthmarkSet[] extractResult = null;\r
+ if(unit == ExtractionUnit.CLASS){\r
+ extractResult = extractFromClass(archives, extractors, environment);\r
+ }\r
+ else if(unit == ExtractionUnit.PACKAGE){\r
+ extractResult = extractFromPackage(archives, extractors, environment);\r
+ }\r
+ else if(unit == ExtractionUnit.ARCHIVE){\r
+ extractResult = extractFromProduct(archives, extractors, environment);\r
+ }\r
+\r
+ return extractResult;\r
+ }\r
+\r
+ private BirthmarkExtractor[] createExtractors(String[] birthmarkTypes, BirthmarkEnvironment environment){\r
+ List<BirthmarkExtractor> list = new ArrayList<BirthmarkExtractor>();\r
+ for(String type: birthmarkTypes){\r
+ BirthmarkExtractor extractor = createExtractor(type, environment);\r
+ list.add(extractor);\r
+ }\r
+ return list.toArray(new BirthmarkExtractor[list.size()]);\r
+ }\r
+\r
+ private BirthmarkExtractor createExtractor(String birthmarkType, BirthmarkEnvironment environment){\r
+ BirthmarkSpi spi = environment.getService(birthmarkType);\r
+ BirthmarkExtractor extractor = null;\r
+ if(spi != null){\r
+ extractor = spi.getExtractor();\r
+ try{\r
+ if(extractor != null){\r
+ Map props = BeanUtils.describe(extractor);\r
+ props.remove("class");\r
+ props.remove("provider");\r
+ for(Object keyObject: props.keySet()){\r
+ String key = "extractor." + spi.getType() + "." + String.valueOf(keyObject);\r
+ if(environment.getProperty(key) != null){\r
+ BeanUtils.setProperty(\r
+ extractor, (String)keyObject, environment.getProperty(key)\r
+ );\r
+ }\r
+ }\r
+ }\r
+ } catch(InvocationTargetException e){\r
+ throw new InternalError(e.getMessage());\r
+ } catch(NoSuchMethodException e){\r
+ throw new InternalError(e.getMessage());\r
+ } catch(IllegalAccessException e){\r
+ throw new InternalError(e.getMessage());\r
+ }\r
+ }\r
+ if(extractor == null){\r
+ warnings.addMessage(new ExtractorNotFoundException("extractor not found"), birthmarkType);\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ private byte[] inputStreamToByteArray(InputStream in) throws IOException{\r
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();\r
+ int read;\r
+ byte[] dataBuffer = new byte[512];\r
+ while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){\r
+ bout.write(dataBuffer, 0, read);\r
+ }\r
+ byte[] data = bout.toByteArray();\r
+\r
+ bout.close();\r
+ return data;\r
+ }\r
+\r
+ private BirthmarkSet[] extractFromPackage(ClassFileArchive[] archives, BirthmarkExtractor[] extractors, BirthmarkEnvironment context) throws IOException, BirthmarkExtractionFailedException{\r
+ Map<String, BirthmarkSet> list = new HashMap<String, BirthmarkSet>();\r
+\r
+ for(ClassFileArchive archive: archives){\r
+ for(ClassFileEntry entry: archive){\r
+ try{\r
+ String name = entry.getClassName();\r
+ String packageName = parsePackageName(name);\r
+ BirthmarkSet bs = list.get(packageName);\r
+ if(bs == null){\r
+ bs = new BirthmarkSet(packageName, archive.getLocation());\r
+ list.put(packageName, bs);\r
+ }\r
+\r
+ byte[] data = inputStreamToByteArray(entry.getLocation().openStream());\r
+ for(BirthmarkExtractor extractor: extractors){\r
+ if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){\r
+ Birthmark b = bs.getBirthmark(extractor.getProvider().getType());\r
+ if(b == null){\r
+ b = extractor.createBirthmark();\r
+ bs.addBirthmark(b);\r
+ }\r
+ extractor.extract(b, new ByteArrayInputStream(data), context);\r
+ }\r
+ }\r
+ } catch(IOException e){\r
+ warnings.addMessage(e, archive.getName());\r
+ }\r
+ }\r
+ }\r
+\r
+ return list.values().toArray(new BirthmarkSet[list.size()]);\r
+ }\r
+\r
+ private String parsePackageName(String name){\r
+ String n = name.replace('/', '.');\r
+ int index = n.lastIndexOf('.');\r
+ if(index > 0){\r
+ n = n.substring(0, index - 1);\r
+ }\r
+\r
+ return n;\r
+ }\r
+\r
+ private BirthmarkSet[] extractFromClass(ClassFileArchive[] archives, BirthmarkExtractor[] extractors, BirthmarkEnvironment context) throws IOException, BirthmarkExtractionFailedException{\r
+ List<BirthmarkSet> list = new ArrayList<BirthmarkSet>();\r
+\r
+ for(ClassFileArchive archive: archives){\r
+ for(ClassFileEntry entry: archive){\r
+ try{\r
+ BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());\r
+ list.add(birthmarkset);\r
+ byte[] data = inputStreamToByteArray(entry.getLocation().openStream());\r
+ for(BirthmarkExtractor extractor: extractors){\r
+ if(extractor.isAcceptable(ExtractionUnit.CLASS)){\r
+ Birthmark b = extractor.extract(new ByteArrayInputStream(data), context);\r
+ birthmarkset.addBirthmark(b);\r
+ }\r
+ }\r
+ } catch(IOException e){\r
+ warnings.addMessage(e, entry.getClassName());\r
+ }\r
+ }\r
+ }\r
+ return list.toArray(new BirthmarkSet[list.size()]);\r
+ }\r
+\r
+ private BirthmarkSet[] extractFromProduct(ClassFileArchive[] archives, BirthmarkExtractor[] extractors, BirthmarkEnvironment context) throws IOException, BirthmarkExtractionFailedException{\r
+ List<BirthmarkSet> list = new ArrayList<BirthmarkSet>();\r
+\r
+ for(ClassFileArchive archive: archives){\r
+ BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());\r
+ list.add(birthmarkset);\r
+\r
+ for(ClassFileEntry entry: archive){\r
+ try{\r
+ byte[] data = inputStreamToByteArray(entry.getLocation().openStream());\r
+ for(BirthmarkExtractor extractor: extractors){\r
+ if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){\r
+ Birthmark b = birthmarkset.getBirthmark(extractor.getProvider().getType());\r
+ if(b == null){\r
+ b = extractor.createBirthmark();\r
+ birthmarkset.addBirthmark(b);\r
+ }\r
+ extractor.extract(b, new ByteArrayInputStream(data), context);\r
+ }\r
+ }\r
+ } catch(IOException e){\r
+ warnings.addMessage(e, entry.getClassName());\r
+ }\r
+ }\r
+ }\r
+ for(Iterator<BirthmarkSet> i = list.iterator(); i.hasNext(); ){\r
+ BirthmarkSet set = i.next();\r
+ if(set.getBirthmarksCount() == 0){\r
+ i.remove();\r
+ }\r
+ }\r
+\r
+ return list.toArray(new BirthmarkSet[list.size()]);\r
+ }\r
+\r
+ private ClassFileArchive[] createArchives(String[] files, BirthmarkEnvironment environment) throws IOException, MalformedURLException{\r
+ ClasspathContext bytecode = environment.getClasspathContext();\r
+ List<ClassFileArchive> archives = new ArrayList<ClassFileArchive>();\r
+ for(int i = 0; i < files.length; i++){\r
+ try{\r
+ if(files[i].endsWith(".class")){\r
+ archives.add(new DefaultClassFileArchive(files[i]));\r
+ }\r
+ else if(files[i].endsWith(".jar") || files[i].endsWith(".zip")){\r
+ archives.add(new JarClassFileArchive(files[i]));\r
+ bytecode.addClasspath(new File(files[i]).toURI().toURL());\r
+ }\r
+ else if(files[i].endsWith(".war")){\r
+ archives.add(new WarClassFileArchive(files[i]));\r
+ }\r
+ } catch(IOException e){\r
+ warnings.addMessage(e, files[i]);\r
+ }\r
+ }\r
+ return archives.toArray(new ClassFileArchive[archives.size()]);\r
+ }\r
+\r
+ private void operationStart(OperationType type){\r
+ if(warnings == null){\r
+ warnings = new WarningMessages(type);\r
+ fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_START, type, warnings));\r
+ latestOperationType = type;\r
+ targetType = type;\r
+ }\r
+ stack.push(warnings);\r
+ /*\r
+ * call subOperationStart method only once when some operation is occured.\r
+ * Ex. extraction, comparison, filtering\r
+ */\r
+ if(latestOperationType != type){\r
+ fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));\r
+ latestOperationType = type;\r
+ }\r
+ }\r
+\r
+ private void operationDone(OperationType type){\r
+ if(latestOperationType != type && targetType != type){\r
+ fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_DONE, type, warnings));\r
+ latestOperationType = type;\r
+ }\r
+ stack.pop();\r
+ if(stack.size() == 0){\r
+ fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));\r
+ warnings = null;\r
+ latestOperationType = null;\r
+ }\r
+ }\r
+\r
+ private void fireEvent(BirthmarkEngineEvent e){\r
+ for(BirthmarkEngineListener listener: listeners){\r
+ switch(e.getStage()){\r
+ case OPERATION_START:\r
+ listener.operationStart(e);\r
+ break;\r
+ case SUB_OPERATION_START:\r
+ listener.subOperationStart(e);\r
+ break;\r
+ case SUB_OPERATION_DONE:\r
+ listener.subOperationDone(e);\r
+ break;\r
+ case OPERATION_DONE:\r
+ listener.operationDone(e);\r
+ break;\r
+ default:\r
+ throw new InternalError("unknown stage: " + e.getStage());\r
+ }\r
+ }\r
+ }\r
+}\r