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.List;
17 import java.util.Stack;
19 import jp.sourceforge.stigmata.birthmarks.extractors.BirthmarkExtractorFactory;
20 import jp.sourceforge.stigmata.event.BirthmarkEngineEvent;
21 import jp.sourceforge.stigmata.event.BirthmarkEngineListener;
22 import jp.sourceforge.stigmata.event.OperationStage;
23 import jp.sourceforge.stigmata.event.OperationType;
24 import jp.sourceforge.stigmata.event.WarningMessages;
25 import jp.sourceforge.stigmata.filter.ComparisonPairFilterManager;
26 import jp.sourceforge.stigmata.filter.FilteredComparisonResultSet;
27 import jp.sourceforge.stigmata.hook.Phase;
28 import jp.sourceforge.stigmata.hook.StigmataHookManager;
29 import jp.sourceforge.stigmata.reader.ClassFileArchive;
30 import jp.sourceforge.stigmata.reader.ClassFileEntry;
31 import jp.sourceforge.stigmata.reader.ClasspathContext;
32 import jp.sourceforge.stigmata.reader.DefaultClassFileArchive;
33 import jp.sourceforge.stigmata.reader.JarClassFileArchive;
34 import jp.sourceforge.stigmata.reader.WarClassFileArchive;
35 import jp.sourceforge.stigmata.result.CertainPairComparisonResultSet;
36 import jp.sourceforge.stigmata.result.RoundRobinComparisonResultSet;
39 * core engine for extracting/comparing/filtering birthmarks.
41 * This class is not thread safe.
43 * @author Haruaki Tamada
46 public class BirthmarkEngine{
47 private BirthmarkEnvironment environment;
48 private List<BirthmarkEngineListener> listeners = new ArrayList<BirthmarkEngineListener>();
49 private Stack<WarningMessages> stack = new Stack<WarningMessages>();
50 private WarningMessages warnings;
51 private OperationType latestOperationType;
52 private OperationType targetType;
53 private BirthmarkExtractorFactory factory;
58 public BirthmarkEngine(BirthmarkEnvironment env){
59 this.environment = env;
60 factory = new BirthmarkExtractorFactory(env);
64 * returns an environment of this object.
66 public BirthmarkEnvironment getEnvironment(){
70 public void addBirthmarkEngineListener(BirthmarkEngineListener listener){
71 listeners.add(listener);
74 public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){
75 listeners.remove(listener);
79 * filters comparison of birthmarks from target files.
80 * @see #extract(String[], BirthmarkContext)
81 * @see #compare(String[], BirthmarkContext)
82 * @see #filter(ComparisonResultSet)
83 * @see BirthmarkContext#getFilterTypes()
85 public synchronized ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
86 operationStart(OperationType.FILTER_BIRTHMARKS);
88 ComparisonResultSet crs = compare(target, context);
91 operationDone(OperationType.FILTER_BIRTHMARKS);
97 * filters comparison of birthmarks from given two targets targetx, and targetY
98 * @see #extract(String[], String[], BirthmarkContext)
99 * @see #compare(String[], String[], BirthmarkContext)
100 * @see #filter(ComparisonResultSet)
101 * @see BirthmarkContext#getFilterTypes()
103 public synchronized ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
104 operationStart(OperationType.FILTER_BIRTHMARKS);
106 ComparisonResultSet crs = compare(targetX, targetY, context);
109 operationDone(OperationType.FILTER_BIRTHMARKS);
115 * filters comparison of birthmarks from given extraction result set.
116 * @see #compare(ExtractionResultSet)
117 * @see #filter(ComparisonResultSet)
118 * @see ExtractionResultSet#getContext()
119 * @see BirthmarkContext#getFilterTypes()
121 public synchronized ComparisonResultSet filter(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
122 operationStart(OperationType.FILTER_BIRTHMARKS);
124 ComparisonResultSet crs = compare(er);
127 operationDone(OperationType.FILTER_BIRTHMARKS);
133 * filters comparison of birthmarks.
134 * @see ExtractionResultSet#getContext()
135 * @see BirthmarkContext#getFilterTypes()
137 public synchronized ComparisonResultSet filter(ComparisonResultSet crs) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
138 operationStart(OperationType.FILTER_BIRTHMARKS);
139 StigmataHookManager.getInstance().runHook(Phase.BEFORE_FILTERING, crs.getContext());
141 String[] filterTypes = crs.getContext().getFilterTypes();
143 if(filterTypes != null){
144 List<ComparisonPairFilterSet> filterList = new ArrayList<ComparisonPairFilterSet>();
145 ComparisonPairFilterManager manager = environment.getFilterManager();
146 for(int i = 0; i < filterTypes.length; i++){
147 ComparisonPairFilterSet fset = manager.getFilterSet(filterTypes[i]);
149 filterList.add(fset);
152 warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);
155 ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);
157 crs = new FilteredComparisonResultSet(crs, cpfs);
160 StigmataHookManager.getInstance().runHook(Phase.AFTER_FILTERING, crs.getContext());
161 operationDone(OperationType.FILTER_BIRTHMARKS);
167 * compares two given birthmarks and returns comparison pair.
169 public synchronized ComparisonPair compareDetails(BirthmarkSet bs1, BirthmarkSet bs2, BirthmarkContext context) throws BirthmarkComparisonFailedException{
170 return new ComparisonPair(bs1, bs2, context);
173 public synchronized ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
174 operationStart(OperationType.COMPARE_BIRTHMARKS);
176 ExtractionResultSet er = extract(target, context);
177 ComparisonResultSet crs = compare(er);
179 operationDone(OperationType.COMPARE_BIRTHMARKS);
184 public synchronized ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
185 operationStart(OperationType.COMPARE_BIRTHMARKS);
187 ExtractionResultSet er = extract(targetX, targetY, context);
188 ComparisonResultSet crs = compare(er);
190 operationDone(OperationType.COMPARE_BIRTHMARKS);
195 public synchronized ComparisonResultSet compare(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
196 operationStart(OperationType.COMPARE_BIRTHMARKS);
197 BirthmarkContext context = er.getContext();
199 StigmataHookManager.getInstance().runHook(Phase.BEFORE_COMPARISON, context);
200 ComparisonResultSet crs = null;
201 switch(context.getComparisonMethod()){
202 case ROUND_ROBIN_SAME_PAIR:
203 crs = new RoundRobinComparisonResultSet(er, true);
205 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
206 crs = new RoundRobinComparisonResultSet(er, false);
209 crs = new RoundRobinComparisonResultSet(er, true);
211 crs = new CertainPairComparisonResultSet(er);
214 crs = new CertainPairComparisonResultSet(er, context.getNameMappings());
218 StigmataHookManager.getInstance().runHook(Phase.AFTER_COMPARISON, context);
219 operationDone(OperationType.COMPARE_BIRTHMARKS);
224 public synchronized ExtractionResultSet extract(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
225 operationStart(OperationType.EXTRACT_BIRTHMARKS);
226 ExtractionResultSet er = extract(target, null, context);
227 operationDone(OperationType.EXTRACT_BIRTHMARKS);
231 public synchronized ExtractionResultSet extract(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
232 operationStart(OperationType.EXTRACT_BIRTHMARKS);
233 ExtractionResultSet er = context.getEnvironment().getHistoryManager().createDefaultResultSet(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 private String[] mergeTarget(String[] t1, String[] t2){
267 List<String> list = new ArrayList<String>();
271 return list.toArray(new String[list.size()]);
274 private void addToList(List<String> list, String[] target){
276 for(String s: target){
282 private BirthmarkSet[] extractImpl(String[] target, ExtractionResultSet er, ExtractionTarget et) throws BirthmarkExtractionFailedException, IOException, BirthmarkStoreException{
283 ClassFileArchive[] archives = createArchives(target, environment);
284 BirthmarkContext context = er.getContext();
285 ExtractionUnit unit = context.getExtractionUnit();
287 BirthmarkSet[] extractResult = null;
288 if(unit == ExtractionUnit.CLASS){
289 extractFromClass(archives, er, et);
291 else if(unit == ExtractionUnit.PACKAGE){
292 extractFromPackage(archives, er, et);
294 else if(unit == ExtractionUnit.ARCHIVE){
295 extractFromProduct(archives, er, et);
298 return extractResult;
301 private byte[] inputStreamToByteArray(InputStream in) throws IOException{
302 ByteArrayOutputStream bout = new ByteArrayOutputStream();
304 byte[] dataBuffer = new byte[512];
305 while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
306 bout.write(dataBuffer, 0, read);
308 byte[] data = bout.toByteArray();
314 private void extractFromPackage(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException{
315 Map<String, BirthmarkSet> map = new HashMap<String, BirthmarkSet>();
316 BirthmarkContext context = er.getContext();
318 for(ClassFileArchive archive: archives){
319 for(ClassFileEntry entry: archive){
321 String name = entry.getClassName();
322 String packageName = parsePackageName(name);
323 BirthmarkSet bs = map.get(packageName);
325 bs = new BirthmarkSet(packageName, archive.getLocation());
326 map.put(packageName, bs);
329 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
330 for(String birthmarkType: context.getBirthmarkTypes()){
332 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
333 if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){
334 Birthmark b = bs.getBirthmark(extractor.getProvider().getType());
336 b = extractor.createBirthmark();
339 extractor.extract(b, new ByteArrayInputStream(data), er.getEnvironment());
341 } catch(ExtractorNotFoundException e){
342 warnings.addMessage(e, birthmarkType);
345 } catch(IOException e){
346 warnings.addMessage(e, archive.getName());
351 for(BirthmarkSet bs: map.values()){
352 er.addBirthmarkSet(et, bs);
354 }catch(BirthmarkStoreException e){
358 private String parsePackageName(String name){
359 String n = name.replace('/', '.');
360 int index = n.lastIndexOf('.');
362 n = n.substring(0, index - 1);
368 private void extractFromClass(ClassFileArchive[] archives, ExtractionResultSet er,
369 ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
370 BirthmarkContext context = er.getContext();
372 for(ClassFileArchive archive: archives){
373 for(ClassFileEntry entry: archive){
375 BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
376 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
378 for(String birthmarkType: context.getBirthmarkTypes()){
380 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
381 if(extractor.isAcceptable(ExtractionUnit.CLASS)){
382 Birthmark b = extractor.extract(new ByteArrayInputStream(data), er.getEnvironment());
383 birthmarkset.addBirthmark(b);
385 } catch(ExtractorNotFoundException e){
386 warnings.addMessage(e, birthmarkType);
389 er.addBirthmarkSet(et, birthmarkset);
390 } catch(IOException e){
391 warnings.addMessage(e, entry.getClassName());
397 private void extractFromProduct(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
398 BirthmarkContext context = er.getContext();
400 for(ClassFileArchive archive: archives){
401 BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
403 for(ClassFileEntry entry: archive){
405 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
406 for(String birthmarkType: context.getBirthmarkTypes()){
408 BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
409 if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){
410 Birthmark b = birthmarkset.getBirthmark(birthmarkType);
412 b = extractor.createBirthmark();
413 birthmarkset.addBirthmark(b);
415 extractor.extract(b, new ByteArrayInputStream(data), er.getEnvironment());
417 } catch(ExtractorNotFoundException e){
418 warnings.addMessage(e, birthmarkType);
421 } catch(IOException e){
422 warnings.addMessage(e, entry.getClassName());
425 if(birthmarkset.getBirthmarksCount() != 0){
426 er.addBirthmarkSet(et, birthmarkset);
431 private ClassFileArchive[] createArchives(String[] files, BirthmarkEnvironment environment) throws IOException, MalformedURLException{
432 ClasspathContext bytecode = environment.getClasspathContext();
433 List<ClassFileArchive> archives = new ArrayList<ClassFileArchive>();
434 for(int i = 0; i < files.length; i++){
436 if(files[i].endsWith(".class")){
437 archives.add(new DefaultClassFileArchive(files[i]));
439 else if(files[i].endsWith(".jar") || files[i].endsWith(".zip")){
440 archives.add(new JarClassFileArchive(files[i]));
441 bytecode.addClasspath(new File(files[i]).toURI().toURL());
443 else if(files[i].endsWith(".war")){
444 archives.add(new WarClassFileArchive(files[i]));
446 } catch(IOException e){
447 warnings.addMessage(e, files[i]);
450 return archives.toArray(new ClassFileArchive[archives.size()]);
453 private void operationStart(OperationType type){
454 if(warnings == null){
455 warnings = new WarningMessages(type);
456 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_START, type, warnings));
457 latestOperationType = type;
460 stack.push(warnings);
462 * call subOperationStart method only once when some operation is occured.
463 * Ex. extraction, comparison, filtering
465 if(latestOperationType != type){
466 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));
467 latestOperationType = type;
471 private void operationDone(OperationType type){
472 if(latestOperationType != type && targetType != type){
473 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_DONE, type, warnings));
474 latestOperationType = type;
477 if(stack.size() == 0){
478 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));
480 latestOperationType = null;
484 private void fireEvent(BirthmarkEngineEvent e){
485 for(BirthmarkEngineListener listener: listeners){
486 switch(e.getStage()){
487 case OPERATION_START:
488 listener.operationStart(e);
490 case SUB_OPERATION_START:
491 listener.subOperationStart(e);
493 case SUB_OPERATION_DONE:
494 listener.subOperationDone(e);
497 listener.operationDone(e);
500 throw new InternalError("unknown stage: " + e.getStage());