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
48 public class BirthmarkEngine{
49 private BirthmarkEnvironment environment;
50 private List<BirthmarkEngineListener> listeners = new ArrayList<BirthmarkEngineListener>();
51 private Stack<WarningMessages> stack = new Stack<WarningMessages>();
52 private WarningMessages warnings;
53 private OperationType latestOperationType;
54 private OperationType targetType;
55 private BirthmarkExtractorFactory factory;
60 public BirthmarkEngine(BirthmarkEnvironment env){
61 this.environment = env;
62 factory = new BirthmarkExtractorFactory(env);
66 * returns an environment of this object.
68 public BirthmarkEnvironment getEnvironment(){
72 public void addBirthmarkEngineListener(BirthmarkEngineListener listener){
73 listeners.add(listener);
76 public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){
77 listeners.remove(listener);
81 * filters comparison of birthmarks from target files.
82 * @see #extract(String[], BirthmarkContext)
83 * @see #compare(String[], BirthmarkContext)
84 * @see #filter(ComparisonResultSet)
85 * @see BirthmarkContext#getFilterTypes()
87 public synchronized ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
88 operationStart(OperationType.FILTER_BIRTHMARKS);
90 ComparisonResultSet crs = compare(target, context);
93 operationDone(OperationType.FILTER_BIRTHMARKS);
99 * filters comparison of birthmarks from given two targets targetx, and targetY
100 * @see #extract(String[], String[], BirthmarkContext)
101 * @see #compare(String[], String[], BirthmarkContext)
102 * @see #filter(ComparisonResultSet)
103 * @see BirthmarkContext#getFilterTypes()
105 public synchronized ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
106 operationStart(OperationType.FILTER_BIRTHMARKS);
108 ComparisonResultSet crs = compare(targetX, targetY, context);
111 operationDone(OperationType.FILTER_BIRTHMARKS);
117 * filters comparison of birthmarks from given extraction result set.
118 * @see #compare(ExtractionResultSet)
119 * @see #filter(ComparisonResultSet)
120 * @see ExtractionResultSet#getContext()
121 * @see BirthmarkContext#getFilterTypes()
123 public synchronized ComparisonResultSet filter(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
124 operationStart(OperationType.FILTER_BIRTHMARKS);
126 ComparisonResultSet crs = compare(er);
129 operationDone(OperationType.FILTER_BIRTHMARKS);
135 * filters comparison of birthmarks.
136 * @see ExtractionResultSet#getContext()
137 * @see BirthmarkContext#getFilterTypes()
139 public synchronized ComparisonResultSet filter(ComparisonResultSet crs) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
140 operationStart(OperationType.FILTER_BIRTHMARKS);
141 StigmataHookManager.getInstance().runHook(Phase.BEFORE_FILTERING, crs.getContext());
143 String[] filterTypes = crs.getContext().getFilterTypes();
145 if(filterTypes != null){
146 List<ComparisonPairFilterSet> filterList = new ArrayList<ComparisonPairFilterSet>();
147 ComparisonPairFilterManager manager = environment.getFilterManager();
148 for(int i = 0; i < filterTypes.length; i++){
149 ComparisonPairFilterSet fset = manager.getFilterSet(filterTypes[i]);
151 filterList.add(fset);
154 warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);
157 ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);
159 crs = new FilteredComparisonResultSet(crs, cpfs);
162 StigmataHookManager.getInstance().runHook(Phase.AFTER_FILTERING, crs.getContext());
163 operationDone(OperationType.FILTER_BIRTHMARKS);
169 * compares two given birthmarks and returns comparison pair.
171 public synchronized ComparisonPair compareDetails(BirthmarkSet bs1, BirthmarkSet bs2, BirthmarkContext context) throws BirthmarkComparisonFailedException{
172 return new ComparisonPair(bs1, bs2, context);
175 public synchronized ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
176 operationStart(OperationType.COMPARE_BIRTHMARKS);
178 ExtractionResultSet er = extract(target, context);
179 ComparisonResultSet crs = compare(er);
181 operationDone(OperationType.COMPARE_BIRTHMARKS);
186 public synchronized ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
187 operationStart(OperationType.COMPARE_BIRTHMARKS);
189 ExtractionResultSet er = extract(targetX, targetY, context);
190 ComparisonResultSet crs = compare(er);
192 operationDone(OperationType.COMPARE_BIRTHMARKS);
197 public synchronized ComparisonResultSet compare(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
198 operationStart(OperationType.COMPARE_BIRTHMARKS);
199 BirthmarkContext context = er.getContext();
201 StigmataHookManager.getInstance().runHook(Phase.BEFORE_COMPARISON, context);
202 ComparisonResultSet crs = null;
203 switch(context.getComparisonMethod()){
204 case ROUND_ROBIN_SAME_PAIR:
205 crs = new RoundRobinComparisonResultSet(er, true);
207 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
208 crs = new RoundRobinComparisonResultSet(er, false);
211 crs = new RoundRobinComparisonResultSet(er, true);
213 crs = new CertainPairComparisonResultSet(er);
216 crs = new CertainPairComparisonResultSet(er, context.getNameMappings());
220 StigmataHookManager.getInstance().runHook(Phase.AFTER_COMPARISON, context);
221 operationDone(OperationType.COMPARE_BIRTHMARKS);
226 public synchronized ExtractionResultSet extract(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
227 operationStart(OperationType.EXTRACT_BIRTHMARKS);
228 ExtractionResultSet er = extract(target, null, context);
229 operationDone(OperationType.EXTRACT_BIRTHMARKS);
233 public synchronized ExtractionResultSet extract(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
234 operationStart(OperationType.EXTRACT_BIRTHMARKS);
235 ExtractionResultSet er = context.getEnvironment().getHistoryManager().createDefaultResultSet(context);
238 prepare(targetX, targetY, context);
240 StigmataHookManager.getInstance().runHook(Phase.BEFORE_EXTRACTION, context);
242 switch(context.getComparisonMethod()){
243 case ROUND_ROBIN_SAME_PAIR:
244 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
245 er.setTableType(false);
246 String[] targetXY = mergeTarget(targetX, targetY);
247 extractImpl(targetXY, er, ExtractionTarget.TARGET_XY);
253 if(targetX == null || targetY == null){
254 throw new BirthmarkExtractionFailedException("targetX or targetY is null");
256 er.setTableType(true);
257 extractImpl(targetX, er, ExtractionTarget.TARGET_X);
258 extractImpl(targetY, er, ExtractionTarget.TARGET_Y);
262 } catch(IOException e){
263 throw new BirthmarkExtractionFailedException(e);
265 StigmataHookManager.getInstance().runHook(Phase.AFTER_EXTRACTION, context);
266 operationDone(OperationType.EXTRACT_BIRTHMARKS);
270 public BirthmarkContext prepare(String[] targetX, String[] targetY, BirthmarkContext context) throws MalformedURLException, IOException{
271 StigmataHookManager.getInstance().runHook(Phase.BEFORE_PREPROCESS, context);
273 Set<String> set = new HashSet<String>();
275 for(String t: targetX) set.add(t);
278 for(String t: targetY) set.add(t);
280 String[] target = set.toArray(new String[set.size()]);
281 ClassFileArchive[] archives = createArchives(target, environment);
282 for(String type: context.getBirthmarkTypes()){
283 BirthmarkSpi service = context.getEnvironment().getService(type);
284 if(service != null && service.getPreprocessor() != null){
285 BirthmarkPreprocessor preprocessor = service.getPreprocessor();
286 preprocessor.preprocess(archives, context);
289 StigmataHookManager.getInstance().runHook(Phase.AFTER_PREPROCESS, context);
294 private String[] mergeTarget(String[] t1, String[] t2){
295 List<String> list = new ArrayList<String>();
299 return list.toArray(new String[list.size()]);
302 private void addToList(List<String> list, String[] target){
304 for(String s: target){
310 private BirthmarkSet[] extractImpl(String[] target, ExtractionResultSet er, ExtractionTarget et) throws BirthmarkExtractionFailedException, IOException, BirthmarkStoreException{
311 ClassFileArchive[] archives = createArchives(target, environment);
312 BirthmarkContext context = er.getContext();
313 ExtractionUnit unit = context.getExtractionUnit();
315 BirthmarkSet[] extractResult = null;
316 if(unit == ExtractionUnit.CLASS){
317 extractFromClass(archives, er, et);
319 else if(unit == ExtractionUnit.PACKAGE){
320 extractFromPackage(archives, er, et);
322 else if(unit == ExtractionUnit.ARCHIVE){
323 extractFromProduct(archives, er, et);
326 return extractResult;
329 private byte[] inputStreamToByteArray(InputStream in) throws IOException{
330 ByteArrayOutputStream bout = new ByteArrayOutputStream();
332 byte[] dataBuffer = new byte[512];
333 while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
334 bout.write(dataBuffer, 0, read);
336 byte[] data = bout.toByteArray();
342 private void extractFromPackage(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException{
343 Map<String, BirthmarkSet> map = new HashMap<String, BirthmarkSet>();
344 BirthmarkContext context = er.getContext();
346 for(ClassFileArchive archive: archives){
347 for(ClassFileEntry entry: archive){
349 String name = entry.getClassName();
350 String packageName = parsePackageName(name);
351 BirthmarkSet bs = map.get(packageName);
353 bs = new BirthmarkSet(packageName, archive.getLocation());
354 map.put(packageName, bs);
357 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
358 for(String birthmarkType: context.getBirthmarkTypes()){
360 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
361 if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){
362 Birthmark b = bs.getBirthmark(extractor.getProvider().getType());
364 b = extractor.createBirthmark();
367 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
369 } catch(ExtractorNotFoundException e){
370 warnings.addMessage(e, birthmarkType);
373 } catch(IOException e){
374 warnings.addMessage(e, archive.getName());
379 for(BirthmarkSet bs: map.values()){
380 er.addBirthmarkSet(et, bs);
382 }catch(BirthmarkStoreException e){
386 private String parsePackageName(String name){
387 String n = name.replace('/', '.');
388 int index = n.lastIndexOf('.');
390 n = n.substring(0, index - 1);
396 private void extractFromClass(ClassFileArchive[] archives, ExtractionResultSet er,
397 ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
398 BirthmarkContext context = er.getContext();
400 for(ClassFileArchive archive: archives){
401 for(ClassFileEntry entry: archive){
403 BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
404 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
406 for(String birthmarkType: context.getBirthmarkTypes()){
408 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
409 if(extractor.isAcceptable(ExtractionUnit.CLASS)){
410 Birthmark b = extractor.extract(new ByteArrayInputStream(data), er.getContext());
411 birthmarkset.addBirthmark(b);
413 } catch(ExtractorNotFoundException e){
414 warnings.addMessage(e, birthmarkType);
417 er.addBirthmarkSet(et, birthmarkset);
418 } catch(IOException e){
419 warnings.addMessage(e, entry.getClassName());
425 private void extractFromProduct(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
426 BirthmarkContext context = er.getContext();
428 for(ClassFileArchive archive: archives){
429 BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
431 for(ClassFileEntry entry: archive){
433 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
434 for(String birthmarkType: context.getBirthmarkTypes()){
436 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
437 if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){
438 Birthmark b = birthmarkset.getBirthmark(birthmarkType);
440 b = extractor.createBirthmark();
441 birthmarkset.addBirthmark(b);
443 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
445 } catch(ExtractorNotFoundException e){
446 warnings.addMessage(e, birthmarkType);
449 } catch(IOException e){
450 warnings.addMessage(e, entry.getClassName());
453 if(birthmarkset.getBirthmarksCount() != 0){
454 er.addBirthmarkSet(et, birthmarkset);
459 private ClassFileArchive[] createArchives(String[] files, BirthmarkEnvironment environment) throws IOException, MalformedURLException{
460 ClasspathContext bytecode = environment.getClasspathContext();
461 List<ClassFileArchive> archives = new ArrayList<ClassFileArchive>();
462 for(int i = 0; i < files.length; i++){
464 if(files[i].endsWith(".class")){
465 archives.add(new DefaultClassFileArchive(files[i]));
467 else if(files[i].endsWith(".jar") || files[i].endsWith(".zip")){
468 archives.add(new JarClassFileArchive(files[i]));
469 bytecode.addClasspath(new File(files[i]).toURI().toURL());
471 else if(files[i].endsWith(".war")){
472 archives.add(new WarClassFileArchive(files[i]));
474 } catch(IOException e){
475 warnings.addMessage(e, files[i]);
478 return archives.toArray(new ClassFileArchive[archives.size()]);
481 private void operationStart(OperationType type){
482 if(warnings == null){
483 warnings = new WarningMessages(type);
484 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_START, type, warnings));
485 latestOperationType = type;
488 stack.push(warnings);
490 * call subOperationStart method only once when some operation is occured.
491 * Ex. extraction, comparison, filtering
493 if(latestOperationType != type){
494 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));
495 latestOperationType = type;
499 private void operationDone(OperationType type){
500 if(latestOperationType != type && targetType != type){
501 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_DONE, type, warnings));
502 latestOperationType = type;
505 if(stack.size() == 0){
506 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));
508 latestOperationType = null;
512 private void fireEvent(BirthmarkEngineEvent e){
513 for(BirthmarkEngineListener listener: listeners){
514 switch(e.getStage()){
515 case OPERATION_START:
516 listener.operationStart(e);
518 case SUB_OPERATION_START:
519 listener.subOperationStart(e);
521 case SUB_OPERATION_DONE:
522 listener.subOperationDone(e);
525 listener.operationDone(e);
528 throw new InternalError("unknown stage: " + e.getStage());