1 package jp.sourceforge.stigmata;
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.net.MalformedURLException;
9 import java.util.ArrayList;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.List;
15 import java.util.Stack;
17 import jp.sourceforge.stigmata.birthmarks.extractors.BirthmarkExtractorFactory;
18 import jp.sourceforge.stigmata.digger.ClassFileArchive;
19 import jp.sourceforge.stigmata.digger.ClassFileEntry;
20 import jp.sourceforge.stigmata.digger.ClasspathContext;
21 import jp.sourceforge.stigmata.digger.DefaultClassFileArchive;
22 import jp.sourceforge.stigmata.digger.JarClassFileArchive;
23 import jp.sourceforge.stigmata.digger.WarClassFileArchive;
24 import jp.sourceforge.stigmata.event.BirthmarkEngineEvent;
25 import jp.sourceforge.stigmata.event.BirthmarkEngineListener;
26 import jp.sourceforge.stigmata.event.OperationStage;
27 import jp.sourceforge.stigmata.event.OperationType;
28 import jp.sourceforge.stigmata.event.WarningMessages;
29 import jp.sourceforge.stigmata.filter.ComparisonPairFilterManager;
30 import jp.sourceforge.stigmata.filter.FilteredComparisonResultSet;
31 import jp.sourceforge.stigmata.hook.Phase;
32 import jp.sourceforge.stigmata.hook.StigmataHookManager;
33 import jp.sourceforge.stigmata.result.CertainPairComparisonResultSet;
34 import jp.sourceforge.stigmata.result.RoundRobinComparisonResultSet;
35 import jp.sourceforge.stigmata.spi.BirthmarkService;
38 * core engine for extracting/comparing/filtering birthmarks.
40 * This class is not thread safe.
42 * @author Haruaki Tamada
44 public class BirthmarkEngine{
45 private BirthmarkEnvironment environment;
46 private List<BirthmarkEngineListener> listeners = new ArrayList<BirthmarkEngineListener>();
47 private Stack<WarningMessages> stack = new Stack<WarningMessages>();
48 private WarningMessages warnings;
49 private OperationType latestOperationType;
50 private OperationType targetType;
51 private BirthmarkExtractorFactory factory;
56 public BirthmarkEngine(BirthmarkEnvironment env){
57 this.environment = env;
58 factory = new BirthmarkExtractorFactory(env);
62 * returns an environment of this object.
64 public BirthmarkEnvironment getEnvironment(){
68 public void addBirthmarkEngineListener(BirthmarkEngineListener listener){
69 listeners.add(listener);
72 public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){
73 listeners.remove(listener);
77 * filters comparison of birthmarks from target files.
78 * @see #extract(String[], BirthmarkContext)
79 * @see #compare(String[], BirthmarkContext)
80 * @see #filter(ComparisonResultSet)
81 * @see BirthmarkContext#getFilterTypes()
83 public synchronized ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
84 operationStart(OperationType.FILTER_BIRTHMARKS);
86 ComparisonResultSet crs = compare(target, context);
89 operationDone(OperationType.FILTER_BIRTHMARKS);
95 * filters comparison of birthmarks from given two targets targetx, and targetY
96 * @see #extract(String[], String[], BirthmarkContext)
97 * @see #compare(String[], String[], BirthmarkContext)
98 * @see #filter(ComparisonResultSet)
99 * @see BirthmarkContext#getFilterTypes()
101 public synchronized ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
102 operationStart(OperationType.FILTER_BIRTHMARKS);
104 ComparisonResultSet crs = compare(targetX, targetY, context);
107 operationDone(OperationType.FILTER_BIRTHMARKS);
113 * filters comparison of birthmarks from given extraction result set.
114 * @see #compare(ExtractionResultSet)
115 * @see #filter(ComparisonResultSet)
116 * @see ExtractionResultSet#getContext()
117 * @see BirthmarkContext#getFilterTypes()
119 public synchronized ComparisonResultSet filter(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
120 operationStart(OperationType.FILTER_BIRTHMARKS);
122 ComparisonResultSet crs = compare(er);
125 operationDone(OperationType.FILTER_BIRTHMARKS);
131 * filters comparison of birthmarks.
132 * @see ExtractionResultSet#getContext()
133 * @see BirthmarkContext#getFilterTypes()
135 public synchronized ComparisonResultSet filter(ComparisonResultSet crs) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
136 operationStart(OperationType.FILTER_BIRTHMARKS);
137 StigmataHookManager.getInstance().runHook(Phase.BEFORE_FILTERING, crs.getContext());
139 String[] filterTypes = crs.getContext().getFilterTypes();
141 if(filterTypes != null){
142 List<ComparisonPairFilterSet> filterList = new ArrayList<ComparisonPairFilterSet>();
143 ComparisonPairFilterManager manager = environment.getFilterManager();
144 for(int i = 0; i < filterTypes.length; i++){
145 ComparisonPairFilterSet fset = manager.getFilterSet(filterTypes[i]);
147 filterList.add(fset);
150 warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);
153 ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);
155 crs = new FilteredComparisonResultSet(crs, cpfs);
158 StigmataHookManager.getInstance().runHook(Phase.AFTER_FILTERING, crs.getContext());
159 operationDone(OperationType.FILTER_BIRTHMARKS);
165 * compares two given birthmarks and returns comparison pair.
167 public synchronized ComparisonPair compareDetails(BirthmarkSet bs1, BirthmarkSet bs2, BirthmarkContext context) throws BirthmarkComparisonFailedException{
168 return new ComparisonPair(bs1, bs2, context);
171 public synchronized ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
172 operationStart(OperationType.COMPARE_BIRTHMARKS);
174 ExtractionResultSet er = extract(target, context);
175 ComparisonResultSet crs = compare(er);
177 operationDone(OperationType.COMPARE_BIRTHMARKS);
182 public synchronized ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
183 operationStart(OperationType.COMPARE_BIRTHMARKS);
185 ExtractionResultSet er = extract(targetX, targetY, context);
186 ComparisonResultSet crs = compare(er);
188 operationDone(OperationType.COMPARE_BIRTHMARKS);
193 public synchronized ComparisonResultSet compare(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
194 operationStart(OperationType.COMPARE_BIRTHMARKS);
195 BirthmarkContext context = er.getContext();
197 StigmataHookManager.getInstance().runHook(Phase.BEFORE_COMPARISON, context);
198 ComparisonResultSet crs = null;
199 switch(context.getComparisonMethod()){
200 case ROUND_ROBIN_SAME_PAIR:
201 crs = new RoundRobinComparisonResultSet(er, true);
203 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
204 crs = new RoundRobinComparisonResultSet(er, false);
207 crs = new RoundRobinComparisonResultSet(er, true);
209 crs = new CertainPairComparisonResultSet(er);
212 crs = new CertainPairComparisonResultSet(er, context.getNameMappings());
216 StigmataHookManager.getInstance().runHook(Phase.AFTER_COMPARISON, context);
217 operationDone(OperationType.COMPARE_BIRTHMARKS);
222 public synchronized ExtractionResultSet extract(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
223 operationStart(OperationType.EXTRACT_BIRTHMARKS);
224 ExtractionResultSet er = extract(target, null, context);
225 operationDone(OperationType.EXTRACT_BIRTHMARKS);
229 public synchronized ExtractionResultSet extract(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
230 operationStart(OperationType.EXTRACT_BIRTHMARKS);
231 ExtractionResultSet er = context.getEnvironment().getHistoryManager().createDefaultResultSet(context);
234 prepare(targetX, targetY, context);
236 StigmataHookManager.getInstance().runHook(Phase.BEFORE_EXTRACTION, context);
238 switch(context.getComparisonMethod()){
239 case ROUND_ROBIN_SAME_PAIR:
240 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
241 er.setTableType(false);
242 String[] targetXY = mergeTarget(targetX, targetY);
243 extractImpl(targetXY, er, ExtractionTarget.TARGET_XY);
249 if(targetX == null || targetY == null){
250 throw new BirthmarkExtractionFailedException("targetX or targetY is null");
252 er.setTableType(true);
253 extractImpl(targetX, er, ExtractionTarget.TARGET_X);
254 extractImpl(targetY, er, ExtractionTarget.TARGET_Y);
258 } catch(IOException e){
259 throw new BirthmarkExtractionFailedException(e);
261 StigmataHookManager.getInstance().runHook(Phase.AFTER_EXTRACTION, context);
262 operationDone(OperationType.EXTRACT_BIRTHMARKS);
266 public BirthmarkContext prepare(String[] targetX, String[] targetY, BirthmarkContext context) throws MalformedURLException, IOException{
267 StigmataHookManager.getInstance().runHook(Phase.BEFORE_PREPROCESS, context);
269 Set<String> set = new HashSet<String>();
271 for(String t: targetX) set.add(t);
274 for(String t: targetY) set.add(t);
276 String[] target = set.toArray(new String[set.size()]);
277 ClassFileArchive[] archives = createArchives(target, environment);
278 for(String type: context.getBirthmarkTypes()){
279 BirthmarkService service = context.getEnvironment().getService(type);
280 if(service != null && service.getPreprocessor() != null){
281 BirthmarkPreprocessor preprocessor = service.getPreprocessor();
282 preprocessor.preprocess(archives, context);
285 StigmataHookManager.getInstance().runHook(Phase.AFTER_PREPROCESS, context);
290 private String[] mergeTarget(String[] t1, String[] t2){
291 List<String> list = new ArrayList<String>();
295 return list.toArray(new String[list.size()]);
298 private void addToList(List<String> list, String[] target){
300 for(String s: target){
306 private BirthmarkSet[] extractImpl(String[] target, ExtractionResultSet er, ExtractionTarget et) throws BirthmarkExtractionFailedException, IOException, BirthmarkStoreException{
307 ClassFileArchive[] archives = createArchives(target, environment);
308 BirthmarkContext context = er.getContext();
309 ExtractionUnit unit = context.getExtractionUnit();
311 BirthmarkSet[] extractResult = null;
312 if(unit == ExtractionUnit.CLASS){
313 extractFromClass(archives, er, et);
315 else if(unit == ExtractionUnit.PACKAGE){
316 extractFromPackage(archives, er, et);
318 else if(unit == ExtractionUnit.ARCHIVE){
319 extractFromProduct(archives, er, et);
322 return extractResult;
325 private byte[] inputStreamToByteArray(InputStream in) throws IOException{
326 ByteArrayOutputStream bout = new ByteArrayOutputStream();
328 byte[] dataBuffer = new byte[512];
329 while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
330 bout.write(dataBuffer, 0, read);
332 byte[] data = bout.toByteArray();
338 private void extractFromPackage(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException{
339 Map<String, BirthmarkSet> map = new HashMap<String, BirthmarkSet>();
340 BirthmarkContext context = er.getContext();
342 for(ClassFileArchive archive: archives){
343 for(ClassFileEntry entry: archive){
345 String name = entry.getClassName();
346 String packageName = parsePackageName(name);
347 BirthmarkSet bs = map.get(packageName);
349 bs = new BirthmarkSet(packageName, archive.getLocation());
350 map.put(packageName, bs);
353 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
354 for(String birthmarkType: context.getBirthmarkTypes()){
356 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
357 if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){
358 Birthmark b = bs.getBirthmark(extractor.getProvider().getType());
360 b = extractor.createBirthmark();
363 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
365 } catch(ExtractorNotFoundException e){
366 warnings.addMessage(e, birthmarkType);
369 } catch(IOException e){
370 warnings.addMessage(e, archive.getName());
375 for(BirthmarkSet bs: map.values()){
376 er.addBirthmarkSet(et, bs);
378 }catch(BirthmarkStoreException e){
382 private String parsePackageName(String name){
383 String n = name.replace('/', '.');
384 int index = n.lastIndexOf('.');
386 n = n.substring(0, index - 1);
392 private void extractFromClass(ClassFileArchive[] archives, ExtractionResultSet er,
393 ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
394 BirthmarkContext context = er.getContext();
396 for(ClassFileArchive archive: archives){
397 for(ClassFileEntry entry: archive){
399 BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
400 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
402 for(String birthmarkType: context.getBirthmarkTypes()){
404 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
405 if(extractor.isAcceptable(ExtractionUnit.CLASS)){
406 Birthmark b = extractor.extract(new ByteArrayInputStream(data), er.getContext());
407 birthmarkset.addBirthmark(b);
409 } catch(ExtractorNotFoundException e){
410 warnings.addMessage(e, birthmarkType);
413 er.addBirthmarkSet(et, birthmarkset);
414 } catch(IOException e){
415 warnings.addMessage(e, entry.getClassName());
421 private void extractFromProduct(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
422 BirthmarkContext context = er.getContext();
424 for(ClassFileArchive archive: archives){
425 BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
427 for(ClassFileEntry entry: archive){
429 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
430 for(String birthmarkType: context.getBirthmarkTypes()){
432 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
433 if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){
434 Birthmark b = birthmarkset.getBirthmark(birthmarkType);
436 b = extractor.createBirthmark();
437 birthmarkset.addBirthmark(b);
439 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
441 } catch(ExtractorNotFoundException e){
442 warnings.addMessage(e, birthmarkType);
445 } catch(IOException e){
446 warnings.addMessage(e, entry.getClassName());
449 if(birthmarkset.getBirthmarksCount() != 0){
450 er.addBirthmarkSet(et, birthmarkset);
455 private ClassFileArchive[] createArchives(String[] files, BirthmarkEnvironment environment) throws IOException, MalformedURLException{
456 ClasspathContext bytecode = environment.getClasspathContext();
457 List<ClassFileArchive> archives = new ArrayList<ClassFileArchive>();
458 for(int i = 0; i < files.length; i++){
460 if(files[i].endsWith(".class")){
461 archives.add(new DefaultClassFileArchive(files[i]));
463 else if(files[i].endsWith(".jar") || files[i].endsWith(".zip")){
464 archives.add(new JarClassFileArchive(files[i]));
465 bytecode.addClasspath(new File(files[i]).toURI().toURL());
467 else if(files[i].endsWith(".war")){
468 archives.add(new WarClassFileArchive(files[i]));
470 } catch(IOException e){
471 warnings.addMessage(e, files[i]);
474 return archives.toArray(new ClassFileArchive[archives.size()]);
477 private void operationStart(OperationType type){
478 if(warnings == null){
479 warnings = new WarningMessages(type);
480 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_START, type, warnings));
481 latestOperationType = type;
484 stack.push(warnings);
486 * call subOperationStart method only once when some operation is occured.
487 * Ex. extraction, comparison, filtering
489 if(latestOperationType != type){
490 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));
491 latestOperationType = type;
495 private void operationDone(OperationType type){
496 if(latestOperationType != type && targetType != type){
497 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_DONE, type, warnings));
498 latestOperationType = type;
501 if(stack.size() == 0){
502 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));
504 latestOperationType = null;
508 private void fireEvent(BirthmarkEngineEvent e){
509 for(BirthmarkEngineListener listener: listeners){
510 switch(e.getStage()){
511 case OPERATION_START:
512 listener.operationStart(e);
514 case SUB_OPERATION_START:
515 listener.subOperationStart(e);
517 case SUB_OPERATION_DONE:
518 listener.subOperationDone(e);
521 listener.operationDone(e);
524 throw new InternalError("unknown stage: " + e.getStage());