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.digger.ClassFileArchive;
18 import jp.sourceforge.stigmata.digger.ClassFileEntry;
19 import jp.sourceforge.stigmata.digger.ClasspathContext;
20 import jp.sourceforge.stigmata.digger.DefaultClassFileArchive;
21 import jp.sourceforge.stigmata.digger.JarClassFileArchive;
22 import jp.sourceforge.stigmata.digger.WarClassFileArchive;
23 import jp.sourceforge.stigmata.event.BirthmarkEngineEvent;
24 import jp.sourceforge.stigmata.event.BirthmarkEngineListener;
25 import jp.sourceforge.stigmata.event.OperationStage;
26 import jp.sourceforge.stigmata.event.OperationType;
27 import jp.sourceforge.stigmata.event.WarningMessages;
28 import jp.sourceforge.stigmata.filter.ComparisonPairFilterManager;
29 import jp.sourceforge.stigmata.filter.FilteredComparisonResultSet;
30 import jp.sourceforge.stigmata.hook.Phase;
31 import jp.sourceforge.stigmata.hook.StigmataHookManager;
32 import jp.sourceforge.stigmata.result.CertainPairComparisonResultSet;
33 import jp.sourceforge.stigmata.result.RoundRobinComparisonResultSet;
34 import jp.sourceforge.stigmata.spi.BirthmarkService;
37 * core engine for extracting/comparing/filtering birthmarks.
39 * This class is not thread safe.
41 * @author Haruaki Tamada
43 public class BirthmarkEngine{
44 private BirthmarkEnvironment environment;
45 private List<BirthmarkEngineListener> listeners = new ArrayList<BirthmarkEngineListener>();
46 private Stack<WarningMessages> stack = new Stack<WarningMessages>();
47 private WarningMessages warnings;
48 private OperationType latestOperationType;
49 private OperationType targetType;
54 public BirthmarkEngine(BirthmarkEnvironment env){
55 this.environment = env;
59 * returns an environment of this object.
61 public BirthmarkEnvironment getEnvironment(){
65 public void addBirthmarkEngineListener(BirthmarkEngineListener listener){
66 listeners.add(listener);
69 public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){
70 listeners.remove(listener);
74 * filters comparison of birthmarks from target files.
75 * @see #extract(String[], BirthmarkContext)
76 * @see #compare(String[], BirthmarkContext)
77 * @see #filter(ComparisonResultSet)
78 * @see BirthmarkContext#getFilterTypes()
80 public synchronized ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
81 operationStart(OperationType.FILTER_BIRTHMARKS);
83 ComparisonResultSet crs = compare(target, context);
86 operationDone(OperationType.FILTER_BIRTHMARKS);
92 * filters comparison of birthmarks from given two targets targetx, and targetY
93 * @see #extract(String[], String[], BirthmarkContext)
94 * @see #compare(String[], String[], BirthmarkContext)
95 * @see #filter(ComparisonResultSet)
96 * @see BirthmarkContext#getFilterTypes()
98 public synchronized ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
99 operationStart(OperationType.FILTER_BIRTHMARKS);
101 ComparisonResultSet crs = compare(targetX, targetY, context);
104 operationDone(OperationType.FILTER_BIRTHMARKS);
110 * filters comparison of birthmarks from given extraction result set.
111 * @see #compare(ExtractionResultSet)
112 * @see #filter(ComparisonResultSet)
113 * @see ExtractionResultSet#getContext()
114 * @see BirthmarkContext#getFilterTypes()
116 public synchronized ComparisonResultSet filter(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
117 operationStart(OperationType.FILTER_BIRTHMARKS);
119 ComparisonResultSet crs = compare(er);
122 operationDone(OperationType.FILTER_BIRTHMARKS);
128 * filters comparison of birthmarks.
129 * @see ExtractionResultSet#getContext()
130 * @see BirthmarkContext#getFilterTypes()
132 public synchronized ComparisonResultSet filter(ComparisonResultSet crs) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
133 operationStart(OperationType.FILTER_BIRTHMARKS);
134 StigmataHookManager.getInstance().runHook(Phase.BEFORE_FILTERING, crs.getContext());
136 String[] filterTypes = crs.getContext().getFilterTypes();
138 if(filterTypes != null){
139 List<ComparisonPairFilterSet> filterList = new ArrayList<ComparisonPairFilterSet>();
140 ComparisonPairFilterManager manager = environment.getFilterManager();
141 for(int i = 0; i < filterTypes.length; i++){
142 ComparisonPairFilterSet fset = manager.getFilterSet(filterTypes[i]);
144 filterList.add(fset);
147 warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);
150 ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);
152 crs = new FilteredComparisonResultSet(crs, cpfs);
155 StigmataHookManager.getInstance().runHook(Phase.AFTER_FILTERING, crs.getContext());
156 operationDone(OperationType.FILTER_BIRTHMARKS);
162 * compares two given birthmarks and returns comparison pair.
164 public synchronized ComparisonPair compareDetails(BirthmarkSet bs1, BirthmarkSet bs2, BirthmarkContext context) throws BirthmarkComparisonFailedException{
165 return new ComparisonPair(bs1, bs2, context);
168 public synchronized ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
169 operationStart(OperationType.COMPARE_BIRTHMARKS);
171 ExtractionResultSet er = extract(target, context);
172 ComparisonResultSet crs = compare(er);
174 operationDone(OperationType.COMPARE_BIRTHMARKS);
179 public synchronized ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
180 operationStart(OperationType.COMPARE_BIRTHMARKS);
182 ExtractionResultSet er = extract(targetX, targetY, context);
183 ComparisonResultSet crs = compare(er);
185 operationDone(OperationType.COMPARE_BIRTHMARKS);
190 public synchronized ComparisonResultSet compare(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
191 operationStart(OperationType.COMPARE_BIRTHMARKS);
192 BirthmarkContext context = er.getContext();
194 StigmataHookManager.getInstance().runHook(Phase.BEFORE_COMPARISON, context);
195 ComparisonResultSet crs = null;
196 switch(context.getComparisonMethod()){
197 case ROUND_ROBIN_SAME_PAIR:
198 crs = new RoundRobinComparisonResultSet(er, true);
200 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
201 crs = new RoundRobinComparisonResultSet(er, false);
204 crs = new RoundRobinComparisonResultSet(er, true);
206 crs = new CertainPairComparisonResultSet(er);
209 crs = new CertainPairComparisonResultSet(er, context.getNameMappings());
213 StigmataHookManager.getInstance().runHook(Phase.AFTER_COMPARISON, context);
214 operationDone(OperationType.COMPARE_BIRTHMARKS);
219 public synchronized ExtractionResultSet extract(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
220 operationStart(OperationType.EXTRACT_BIRTHMARKS);
221 ExtractionResultSet er = extract(target, null, context);
222 operationDone(OperationType.EXTRACT_BIRTHMARKS);
226 public synchronized ExtractionResultSet extract(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkStoreException{
227 operationStart(OperationType.EXTRACT_BIRTHMARKS);
228 ExtractionResultSet er = context.getEnvironment().getHistoryManager().createDefaultResultSet(context);
231 prepare(targetX, targetY, context);
233 StigmataHookManager.getInstance().runHook(Phase.BEFORE_EXTRACTION, context);
235 switch(context.getComparisonMethod()){
236 case ROUND_ROBIN_SAME_PAIR:
237 case ROUND_ROBIN_WITHOUT_SAME_PAIR:
238 er.setTableType(false);
239 String[] targetXY = mergeTarget(targetX, targetY);
240 extractImpl(targetXY, er, ExtractionTarget.TARGET_XY);
246 if(targetX == null || targetY == null){
247 throw new BirthmarkExtractionFailedException("targetX or targetY is null");
249 er.setTableType(true);
250 extractImpl(targetX, er, ExtractionTarget.TARGET_X);
251 extractImpl(targetY, er, ExtractionTarget.TARGET_Y);
255 } catch(IOException e){
256 throw new BirthmarkExtractionFailedException(e);
258 StigmataHookManager.getInstance().runHook(Phase.AFTER_EXTRACTION, context);
259 operationDone(OperationType.EXTRACT_BIRTHMARKS);
263 public BirthmarkContext prepare(String[] targetX, String[] targetY, BirthmarkContext context) throws MalformedURLException, IOException{
264 StigmataHookManager.getInstance().runHook(Phase.BEFORE_PREPROCESS, context);
266 Set<String> set = new HashSet<String>();
268 for(String t: targetX) set.add(t);
271 for(String t: targetY) set.add(t);
273 String[] target = set.toArray(new String[set.size()]);
274 ClassFileArchive[] archives = createArchives(target, environment);
275 for(String type: context.getBirthmarkTypes()){
276 BirthmarkService service = context.getEnvironment().getService(type);
277 if(service != null && service.getPreprocessor() != null){
278 BirthmarkPreprocessor preprocessor = service.getPreprocessor();
279 preprocessor.preprocess(archives, context);
282 StigmataHookManager.getInstance().runHook(Phase.AFTER_PREPROCESS, context);
287 private String[] mergeTarget(String[] t1, String[] t2){
288 List<String> list = new ArrayList<String>();
292 return list.toArray(new String[list.size()]);
295 private void addToList(List<String> list, String[] target){
297 for(String s: target){
303 private BirthmarkSet[] extractImpl(String[] target, ExtractionResultSet er, ExtractionTarget et) throws BirthmarkExtractionFailedException, IOException, BirthmarkStoreException{
304 ClassFileArchive[] archives = createArchives(target, environment);
305 BirthmarkContext context = er.getContext();
306 ExtractionUnit unit = context.getExtractionUnit();
308 BirthmarkSet[] extractResult = null;
309 if(unit == ExtractionUnit.CLASS){
310 extractFromClass(archives, er, et);
312 else if(unit == ExtractionUnit.PACKAGE){
313 extractFromPackage(archives, er, et);
315 else if(unit == ExtractionUnit.ARCHIVE){
316 extractFromProduct(archives, er, et);
319 return extractResult;
322 private byte[] inputStreamToByteArray(InputStream in) throws IOException{
323 ByteArrayOutputStream bout = new ByteArrayOutputStream();
325 byte[] dataBuffer = new byte[512];
326 while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
327 bout.write(dataBuffer, 0, read);
329 byte[] data = bout.toByteArray();
335 private void extractFromPackage(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException{
336 Map<String, BirthmarkSet> map = new HashMap<String, BirthmarkSet>();
337 BirthmarkContext context = er.getContext();
339 for(ClassFileArchive archive: archives){
340 for(ClassFileEntry entry: archive){
342 String name = entry.getClassName();
343 String packageName = parsePackageName(name);
344 BirthmarkSet bs = map.get(packageName);
346 bs = new BirthmarkSet(packageName, archive.getLocation());
347 map.put(packageName, bs);
350 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
351 for(String birthmarkType: context.getBirthmarkTypes()){
352 BirthmarkService service = getEnvironment().getService(birthmarkType);
353 BirthmarkExtractor extractor = service.getExtractor();
354 if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){
355 Birthmark b = bs.getBirthmark(extractor.getProvider().getType());
357 b = extractor.createBirthmark();
360 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
363 } catch(IOException e){
364 warnings.addMessage(e, archive.getName());
369 for(BirthmarkSet bs: map.values()){
370 er.addBirthmarkSet(et, bs);
372 }catch(BirthmarkStoreException e){
376 private String parsePackageName(String name){
377 String n = name.replace('/', '.');
378 int index = n.lastIndexOf('.');
380 n = n.substring(0, index - 1);
386 private void extractFromClass(ClassFileArchive[] archives, ExtractionResultSet er,
387 ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
388 BirthmarkContext context = er.getContext();
390 for(ClassFileArchive archive: archives){
391 for(ClassFileEntry entry: archive){
393 BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
394 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
396 for(String birthmarkType: context.getBirthmarkTypes()){
397 BirthmarkService service = getEnvironment().getService(birthmarkType);
398 BirthmarkExtractor extractor = service.getExtractor();
399 if(extractor.isAcceptable(ExtractionUnit.CLASS)){
400 Birthmark b = extractor.extract(new ByteArrayInputStream(data), er.getContext());
401 birthmarkset.addBirthmark(b);
404 er.addBirthmarkSet(et, birthmarkset);
405 } catch(IOException e){
406 warnings.addMessage(e, entry.getClassName());
412 private void extractFromProduct(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
413 BirthmarkContext context = er.getContext();
415 for(ClassFileArchive archive: archives){
416 BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
418 for(ClassFileEntry entry: archive){
420 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
421 for(String birthmarkType: context.getBirthmarkTypes()){
422 BirthmarkService service = getEnvironment().getService(birthmarkType);
423 BirthmarkExtractor extractor = service.getExtractor();
424 if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){
425 Birthmark b = birthmarkset.getBirthmark(birthmarkType);
427 b = extractor.createBirthmark();
428 birthmarkset.addBirthmark(b);
430 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
433 } catch(IOException e){
434 warnings.addMessage(e, entry.getClassName());
437 if(birthmarkset.getBirthmarksCount() != 0){
438 er.addBirthmarkSet(et, birthmarkset);
443 private ClassFileArchive[] createArchives(String[] files, BirthmarkEnvironment environment) throws IOException, MalformedURLException{
444 ClasspathContext bytecode = environment.getClasspathContext();
445 List<ClassFileArchive> archives = new ArrayList<ClassFileArchive>();
446 for(int i = 0; i < files.length; i++){
448 if(files[i].endsWith(".class")){
449 archives.add(new DefaultClassFileArchive(files[i]));
451 else if(files[i].endsWith(".jar") || files[i].endsWith(".zip")){
452 archives.add(new JarClassFileArchive(files[i]));
453 bytecode.addClasspath(new File(files[i]).toURI().toURL());
455 else if(files[i].endsWith(".war")){
456 archives.add(new WarClassFileArchive(files[i]));
458 } catch(IOException e){
459 warnings.addMessage(e, files[i]);
462 return archives.toArray(new ClassFileArchive[archives.size()]);
465 private void operationStart(OperationType type){
466 if(warnings == null){
467 warnings = new WarningMessages(type);
468 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_START, type, warnings));
469 latestOperationType = type;
472 stack.push(warnings);
474 * call subOperationStart method only once when some operation is occured.
475 * Ex. extraction, comparison, filtering
477 if(latestOperationType != type){
478 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));
479 latestOperationType = type;
483 private void operationDone(OperationType type){
484 if(latestOperationType != type && targetType != type){
485 fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_DONE, type, warnings));
486 latestOperationType = type;
489 if(stack.size() == 0){
490 fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));
492 latestOperationType = null;
496 private void fireEvent(BirthmarkEngineEvent e){
497 for(BirthmarkEngineListener listener: listeners){
498 switch(e.getStage()){
499 case OPERATION_START:
500 listener.operationStart(e);
502 case SUB_OPERATION_START:
503 listener.subOperationStart(e);
505 case SUB_OPERATION_DONE:
506 listener.subOperationDone(e);
509 listener.operationDone(e);
512 throw new InternalError("unknown stage: " + e.getStage());