1 package jp.sourceforge.stigmata;
7 import java.io.ByteArrayInputStream;
8 import java.io.ByteArrayOutputStream;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.net.MalformedURLException;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
19 import java.util.Stack;
21 import jp.sourceforge.stigmata.birthmarks.extractors.BirthmarkExtractorFactory;
22 import jp.sourceforge.stigmata.digger.ClassFileArchive;
23 import jp.sourceforge.stigmata.digger.ClassFileEntry;
24 import jp.sourceforge.stigmata.digger.ClasspathContext;
25 import jp.sourceforge.stigmata.digger.DefaultClassFileArchive;
26 import jp.sourceforge.stigmata.digger.JarClassFileArchive;
27 import jp.sourceforge.stigmata.digger.WarClassFileArchive;
28 import jp.sourceforge.stigmata.event.BirthmarkEngineEvent;
29 import jp.sourceforge.stigmata.event.BirthmarkEngineListener;
30 import jp.sourceforge.stigmata.event.OperationStage;
31 import jp.sourceforge.stigmata.event.OperationType;
32 import jp.sourceforge.stigmata.event.WarningMessages;
33 import jp.sourceforge.stigmata.filter.ComparisonPairFilterManager;
34 import jp.sourceforge.stigmata.filter.FilteredComparisonResultSet;
35 import jp.sourceforge.stigmata.hook.Phase;
36 import jp.sourceforge.stigmata.hook.StigmataHookManager;
37 import jp.sourceforge.stigmata.result.CertainPairComparisonResultSet;
38 import jp.sourceforge.stigmata.result.RoundRobinComparisonResultSet;
39 import jp.sourceforge.stigmata.spi.BirthmarkSpi;
42 * core engine for extracting/comparing/filtering birthmarks.
44 * This class is not thread safe.
46 * @author Haruaki Tamada
49 public class BirthmarkEngine{
50 private BirthmarkEnvironment environment;
51 private List<BirthmarkEngineListener> listeners = new ArrayList<BirthmarkEngineListener>();
52 private Stack<WarningMessages> stack = new Stack<WarningMessages>();
53 private WarningMessages warnings;
54 private OperationType latestOperationType;
55 private OperationType targetType;
56 private BirthmarkExtractorFactory factory;
61 public BirthmarkEngine(BirthmarkEnvironment env){
62 this.environment = env;
63 factory = new BirthmarkExtractorFactory(env);
67 * returns an environment of this object.
69 public BirthmarkEnvironment getEnvironment(){
73 public void addBirthmarkEngineListener(BirthmarkEngineListener listener){
74 listeners.add(listener);
77 public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){
78 listeners.remove(listener);
82 * filters comparison of birthmarks from target files.
83 * @see #extract(String[], BirthmarkContext)
84 * @see #compare(String[], BirthmarkContext)
85 * @see #filter(ComparisonResultSet)
86 * @see BirthmarkContext#getFilterTypes()
88 public synchronized ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
89 operationStart(OperationType.FILTER_BIRTHMARKS);
91 ComparisonResultSet crs = compare(target, context);
94 operationDone(OperationType.FILTER_BIRTHMARKS);
100 * filters comparison of birthmarks from given two targets targetx, and targetY
101 * @see #extract(String[], String[], BirthmarkContext)
102 * @see #compare(String[], String[], BirthmarkContext)
103 * @see #filter(ComparisonResultSet)
104 * @see BirthmarkContext#getFilterTypes()
106 public synchronized ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
107 operationStart(OperationType.FILTER_BIRTHMARKS);
109 ComparisonResultSet crs = compare(targetX, targetY, context);
112 operationDone(OperationType.FILTER_BIRTHMARKS);
118 * filters comparison of birthmarks from given extraction result set.
119 * @see #compare(ExtractionResultSet)
120 * @see #filter(ComparisonResultSet)
121 * @see ExtractionResultSet#getContext()
122 * @see BirthmarkContext#getFilterTypes()
124 public synchronized ComparisonResultSet filter(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
125 operationStart(OperationType.FILTER_BIRTHMARKS);
127 ComparisonResultSet crs = compare(er);
130 operationDone(OperationType.FILTER_BIRTHMARKS);
136 * filters comparison of birthmarks.
137 * @see ExtractionResultSet#getContext()
138 * @see BirthmarkContext#getFilterTypes()
140 public synchronized ComparisonResultSet filter(ComparisonResultSet crs) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
141 operationStart(OperationType.FILTER_BIRTHMARKS);
142 StigmataHookManager.getInstance().runHook(Phase.BEFORE_FILTERING, crs.getContext());
144 String[] filterTypes = crs.getContext().getFilterTypes();
146 if(filterTypes != null){
147 List<ComparisonPairFilterSet> filterList = new ArrayList<ComparisonPairFilterSet>();
148 ComparisonPairFilterManager manager = environment.getFilterManager();
149 for(int i = 0; i < filterTypes.length; i++){
150 ComparisonPairFilterSet fset = manager.getFilterSet(filterTypes[i]);
152 filterList.add(fset);
155 warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);
158 ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);
160 crs = new FilteredComparisonResultSet(crs, cpfs);
163 StigmataHookManager.getInstance().runHook(Phase.AFTER_FILTERING, crs.getContext());
164 operationDone(OperationType.FILTER_BIRTHMARKS);
170 * compares two given birthmarks and returns comparison pair.
172 public synchronized ComparisonPair compareDetails(BirthmarkSet bs1, BirthmarkSet bs2, BirthmarkContext context) throws BirthmarkComparisonFailedException{
173 return new ComparisonPair(bs1, bs2, context);
176 public synchronized ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
177 operationStart(OperationType.COMPARE_BIRTHMARKS);
179 ExtractionResultSet er = extract(target, context);
180 ComparisonResultSet crs = compare(er);
182 operationDone(OperationType.COMPARE_BIRTHMARKS);
187 public synchronized ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
188 operationStart(OperationType.COMPARE_BIRTHMARKS);
190 ExtractionResultSet er = extract(targetX, targetY, context);
191 ComparisonResultSet crs = compare(er);
193 operationDone(OperationType.COMPARE_BIRTHMARKS);
198 public synchronized ComparisonResultSet compare(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
199 operationStart(OperationType.COMPARE_BIRTHMARKS);
200 BirthmarkContext context = er.getContext();
202 StigmataHookManager.getInstance().runHook(Phase.BEFORE_COMPARISON, context);
203 ComparisonResultSet crs = null;
204 switch(context.getComparisonMethod()){
205 case ROUND_ROBIN_SAME_PAIR:
206 crs = new RoundRobinComparisonResultSet(er, true);
208 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
209 crs = new RoundRobinComparisonResultSet(er, false);
212 crs = new RoundRobinComparisonResultSet(er, true);
214 crs = new CertainPairComparisonResultSet(er);
217 crs = new CertainPairComparisonResultSet(er, context.getNameMappings());
221 StigmataHookManager.getInstance().runHook(Phase.AFTER_COMPARISON, context);
222 operationDone(OperationType.COMPARE_BIRTHMARKS);
227 public synchronized ExtractionResultSet extract(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
228 operationStart(OperationType.EXTRACT_BIRTHMARKS);
229 ExtractionResultSet er = extract(target, null, context);
230 operationDone(OperationType.EXTRACT_BIRTHMARKS);
234 public synchronized ExtractionResultSet extract(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
235 operationStart(OperationType.EXTRACT_BIRTHMARKS);
236 ExtractionResultSet er = context.getEnvironment().getHistoryManager().createDefaultResultSet(context);
239 prepare(targetX, targetY, context);
241 StigmataHookManager.getInstance().runHook(Phase.BEFORE_EXTRACTION, context);
243 switch(context.getComparisonMethod()){
244 case ROUND_ROBIN_SAME_PAIR:
245 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
246 er.setTableType(false);
247 String[] targetXY = mergeTarget(targetX, targetY);
248 extractImpl(targetXY, er, ExtractionTarget.TARGET_XY);
254 if(targetX == null || targetY == null){
255 throw new BirthmarkExtractionFailedException("targetX or targetY is null");
257 er.setTableType(true);
258 extractImpl(targetX, er, ExtractionTarget.TARGET_X);
259 extractImpl(targetY, er, ExtractionTarget.TARGET_Y);
263 } catch(IOException e){
264 throw new BirthmarkExtractionFailedException(e);
266 StigmataHookManager.getInstance().runHook(Phase.AFTER_EXTRACTION, context);
267 operationDone(OperationType.EXTRACT_BIRTHMARKS);
271 public BirthmarkContext prepare(String[] targetX, String[] targetY, BirthmarkContext context) throws MalformedURLException, IOException{
272 StigmataHookManager.getInstance().runHook(Phase.BEFORE_PREPROCESS, context);
274 Set<String> set = new HashSet<String>();
276 for(String t: targetX) set.add(t);
279 for(String t: targetY) set.add(t);
281 String[] target = set.toArray(new String[set.size()]);
282 ClassFileArchive[] archives = createArchives(target, environment);
283 for(String type: context.getBirthmarkTypes()){
284 BirthmarkSpi service = context.getEnvironment().getService(type);
285 if(service != null && service.getPreprocessor() != null){
286 BirthmarkPreprocessor preprocessor = service.getPreprocessor();
287 preprocessor.preprocess(archives, context);
290 StigmataHookManager.getInstance().runHook(Phase.AFTER_PREPROCESS, context);
295 private String[] mergeTarget(String[] t1, String[] t2){
296 List<String> list = new ArrayList<String>();
300 return list.toArray(new String[list.size()]);
303 private void addToList(List<String> list, String[] target){
305 for(String s: target){
311 private BirthmarkSet[] extractImpl(String[] target, ExtractionResultSet er, ExtractionTarget et) throws BirthmarkExtractionFailedException, IOException, BirthmarkStoreException{
312 ClassFileArchive[] archives = createArchives(target, environment);
313 BirthmarkContext context = er.getContext();
314 ExtractionUnit unit = context.getExtractionUnit();
316 BirthmarkSet[] extractResult = null;
317 if(unit == ExtractionUnit.CLASS){
318 extractFromClass(archives, er, et);
320 else if(unit == ExtractionUnit.PACKAGE){
321 extractFromPackage(archives, er, et);
323 else if(unit == ExtractionUnit.ARCHIVE){
324 extractFromProduct(archives, er, et);
327 return extractResult;
330 private byte[] inputStreamToByteArray(InputStream in) throws IOException{
331 ByteArrayOutputStream bout = new ByteArrayOutputStream();
333 byte[] dataBuffer = new byte[512];
334 while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
335 bout.write(dataBuffer, 0, read);
337 byte[] data = bout.toByteArray();
343 private void extractFromPackage(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException{
344 Map<String, BirthmarkSet> map = new HashMap<String, BirthmarkSet>();
345 BirthmarkContext context = er.getContext();
347 for(ClassFileArchive archive: archives){
348 for(ClassFileEntry entry: archive){
350 String name = entry.getClassName();
351 String packageName = parsePackageName(name);
352 BirthmarkSet bs = map.get(packageName);
354 bs = new BirthmarkSet(packageName, archive.getLocation());
355 map.put(packageName, bs);
358 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
359 for(String birthmarkType: context.getBirthmarkTypes()){
361 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
362 if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){
363 Birthmark b = bs.getBirthmark(extractor.getProvider().getType());
365 b = extractor.createBirthmark();
368 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
370 } catch(ExtractorNotFoundException e){
371 warnings.addMessage(e, birthmarkType);
374 } catch(IOException e){
375 warnings.addMessage(e, archive.getName());
380 for(BirthmarkSet bs: map.values()){
381 er.addBirthmarkSet(et, bs);
383 }catch(BirthmarkStoreException e){
387 private String parsePackageName(String name){
388 String n = name.replace('/', '.');
389 int index = n.lastIndexOf('.');
391 n = n.substring(0, index - 1);
397 private void extractFromClass(ClassFileArchive[] archives, ExtractionResultSet er,
398 ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
399 BirthmarkContext context = er.getContext();
401 for(ClassFileArchive archive: archives){
402 for(ClassFileEntry entry: archive){
404 BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
405 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
407 for(String birthmarkType: context.getBirthmarkTypes()){
409 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
410 if(extractor.isAcceptable(ExtractionUnit.CLASS)){
411 Birthmark b = extractor.extract(new ByteArrayInputStream(data), er.getContext());
412 birthmarkset.addBirthmark(b);
414 } catch(ExtractorNotFoundException e){
415 warnings.addMessage(e, birthmarkType);
418 er.addBirthmarkSet(et, birthmarkset);
419 } catch(IOException e){
420 warnings.addMessage(e, entry.getClassName());
426 private void extractFromProduct(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
427 BirthmarkContext context = er.getContext();
429 for(ClassFileArchive archive: archives){
430 BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
432 for(ClassFileEntry entry: archive){
434 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
435 for(String birthmarkType: context.getBirthmarkTypes()){
437 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
438 if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){
439 Birthmark b = birthmarkset.getBirthmark(birthmarkType);
441 b = extractor.createBirthmark();
442 birthmarkset.addBirthmark(b);
444 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
446 } catch(ExtractorNotFoundException e){
447 warnings.addMessage(e, birthmarkType);
450 } catch(IOException e){
451 warnings.addMessage(e, entry.getClassName());
454 if(birthmarkset.getBirthmarksCount() != 0){
455 er.addBirthmarkSet(et, birthmarkset);
460 private ClassFileArchive[] createArchives(String[] files, BirthmarkEnvironment environment) throws IOException, MalformedURLException{
461 ClasspathContext bytecode = environment.getClasspathContext();
462 List<ClassFileArchive> archives = new ArrayList<ClassFileArchive>();
463 for(int i = 0; i < files.length; i++){
465 if(files[i].endsWith(".class")){
466 archives.add(new DefaultClassFileArchive(files[i]));
468 else if(files[i].endsWith(".jar") || files[i].endsWith(".zip")){
469 archives.add(new JarClassFileArchive(files[i]));
470 bytecode.addClasspath(new File(files[i]).toURI().toURL());
472 else if(files[i].endsWith(".war")){
473 archives.add(new WarClassFileArchive(files[i]));
475 } catch(IOException e){
476 warnings.addMessage(e, files[i]);
479 return archives.toArray(new ClassFileArchive[archives.size()]);
482 private void operationStart(OperationType type){
483 if(warnings == null){
484 warnings = new WarningMessages(type);
485 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_START, type, warnings));
486 latestOperationType = type;
489 stack.push(warnings);
491 * call subOperationStart method only once when some operation is occured.
492 * Ex. extraction, comparison, filtering
494 if(latestOperationType != type){
495 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));
496 latestOperationType = type;
500 private void operationDone(OperationType type){
501 if(latestOperationType != type && targetType != type){
502 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_DONE, type, warnings));
503 latestOperationType = type;
506 if(stack.size() == 0){
507 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));
509 latestOperationType = null;
513 private void fireEvent(BirthmarkEngineEvent e){
514 for(BirthmarkEngineListener listener: listeners){
515 switch(e.getStage()){
516 case OPERATION_START:
517 listener.operationStart(e);
519 case SUB_OPERATION_START:
520 listener.subOperationStart(e);
522 case SUB_OPERATION_DONE:
523 listener.subOperationDone(e);
526 listener.operationDone(e);
529 throw new InternalError("unknown stage: " + e.getStage());