OSDN Git Service

add preprocessor of birthmarking
[stigmata/stigmata.git] / src / main / java / jp / sourceforge / stigmata / BirthmarkEngine.java
1 package jp.sourceforge.stigmata;
2
3 /*
4  * $Id$
5  */
6
7 import java.io.ByteArrayInputStream;
8 import java.io.ByteArrayOutputStream;
9 import java.io.File;
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;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.Stack;
20
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;
40
41 /**
42  * core engine for extracting/comparing/filtering birthmarks.
43  * 
44  * This class is not thread safe.
45  * 
46  * @author Haruaki Tamada
47  * @version $Revision$ 
48  */
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;
57
58     /**
59      * constructor.
60      */
61     public BirthmarkEngine(BirthmarkEnvironment env){
62         this.environment = env;
63         factory = new BirthmarkExtractorFactory(env);
64     }
65
66     /**
67      * returns an environment of this object.
68      */
69     public BirthmarkEnvironment getEnvironment(){
70         return environment;
71     }
72
73     public void addBirthmarkEngineListener(BirthmarkEngineListener listener){
74         listeners.add(listener);
75     }
76
77     public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){
78         listeners.remove(listener);
79     }
80
81     /**
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()
87      */
88     public synchronized ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
89         operationStart(OperationType.FILTER_BIRTHMARKS);
90
91         ComparisonResultSet crs = compare(target, context);
92         crs = filter(crs);
93
94         operationDone(OperationType.FILTER_BIRTHMARKS);
95
96         return crs;
97     }
98
99     /**
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()
105      */
106     public synchronized ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
107         operationStart(OperationType.FILTER_BIRTHMARKS);
108
109         ComparisonResultSet crs = compare(targetX, targetY, context);
110         crs = filter(crs);
111
112         operationDone(OperationType.FILTER_BIRTHMARKS);
113
114         return crs;
115     }
116
117     /**
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()
123      */
124     public synchronized ComparisonResultSet filter(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
125         operationStart(OperationType.FILTER_BIRTHMARKS);
126
127         ComparisonResultSet crs = compare(er);
128         crs = filter(crs);
129
130         operationDone(OperationType.FILTER_BIRTHMARKS);
131
132         return crs;
133     }
134
135     /**
136      * filters comparison of birthmarks.
137      * @see ExtractionResultSet#getContext()
138      * @see BirthmarkContext#getFilterTypes()
139      */
140     public synchronized ComparisonResultSet filter(ComparisonResultSet crs) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
141         operationStart(OperationType.FILTER_BIRTHMARKS);
142         StigmataHookManager.getInstance().runHook(Phase.BEFORE_FILTERING, crs.getContext());
143
144         String[] filterTypes = crs.getContext().getFilterTypes();
145
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]);
151                 if(fset != null){
152                     filterList.add(fset);
153                 }
154                 else{
155                     warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);
156                 }
157             }
158             ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);
159
160             crs = new FilteredComparisonResultSet(crs, cpfs);
161         }
162
163         StigmataHookManager.getInstance().runHook(Phase.AFTER_FILTERING, crs.getContext());
164         operationDone(OperationType.FILTER_BIRTHMARKS);
165
166         return crs;
167     }
168
169     /**
170      * compares two given birthmarks and returns comparison pair.
171      */
172     public synchronized ComparisonPair compareDetails(BirthmarkSet bs1, BirthmarkSet bs2, BirthmarkContext context) throws BirthmarkComparisonFailedException{
173         return new ComparisonPair(bs1, bs2, context);
174     }
175
176     public synchronized ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
177         operationStart(OperationType.COMPARE_BIRTHMARKS);
178
179         ExtractionResultSet er = extract(target, context);
180         ComparisonResultSet crs = compare(er);
181
182         operationDone(OperationType.COMPARE_BIRTHMARKS);
183
184         return crs;
185     }
186
187     public synchronized ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
188         operationStart(OperationType.COMPARE_BIRTHMARKS);
189
190         ExtractionResultSet er = extract(targetX, targetY, context);
191         ComparisonResultSet crs = compare(er);
192
193         operationDone(OperationType.COMPARE_BIRTHMARKS);
194
195         return crs;
196     }
197
198     public synchronized ComparisonResultSet compare(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
199         operationStart(OperationType.COMPARE_BIRTHMARKS);
200         BirthmarkContext context = er.getContext();
201
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);
207             break;
208         case ROUND_ROBIN_WITHOUT_SAME_PAIR:
209             crs = new RoundRobinComparisonResultSet(er, false);
210             break;
211         case ROUND_ROBIN_XY:
212             crs = new RoundRobinComparisonResultSet(er, true);
213         case GUESSED_PAIR:
214             crs = new CertainPairComparisonResultSet(er);
215             break;
216         case SPECIFIED_PAIR:
217             crs = new CertainPairComparisonResultSet(er, context.getNameMappings());
218             break;
219         }
220
221         StigmataHookManager.getInstance().runHook(Phase.AFTER_COMPARISON, context);
222         operationDone(OperationType.COMPARE_BIRTHMARKS);
223
224         return crs;
225     }
226
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);
231         return er;
232     }
233
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);
237
238         try{
239             prepare(targetX, targetY, context);
240
241             StigmataHookManager.getInstance().runHook(Phase.BEFORE_EXTRACTION, context);
242
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);
249                 break;
250             case GUESSED_PAIR:
251             case SPECIFIED_PAIR:
252             case ROUND_ROBIN_XY:
253             default:
254                 if(targetX == null || targetY == null){
255                     throw new BirthmarkExtractionFailedException("targetX or targetY is null");
256                 }
257                 er.setTableType(true);
258                 extractImpl(targetX, er, ExtractionTarget.TARGET_X);
259                 extractImpl(targetY, er, ExtractionTarget.TARGET_Y);
260                 break;
261             }
262             return er;
263         } catch(IOException e){
264             throw new BirthmarkExtractionFailedException(e);
265         } finally{
266             StigmataHookManager.getInstance().runHook(Phase.AFTER_EXTRACTION, context);
267             operationDone(OperationType.EXTRACT_BIRTHMARKS);
268         }
269     }
270
271     public BirthmarkContext prepare(String[] targetX, String[] targetY, BirthmarkContext context) throws MalformedURLException, IOException{
272         StigmataHookManager.getInstance().runHook(Phase.BEFORE_PREPROCESS, context);
273
274         Set<String> set = new HashSet<String>();
275         if(targetX != null){
276             for(String t: targetX) set.add(t);
277         }
278         if(targetY != null){
279             for(String t: targetY) set.add(t);
280         }
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);
288             }
289         }
290         StigmataHookManager.getInstance().runHook(Phase.AFTER_PREPROCESS, context);
291
292         return context;
293     }
294
295     private String[] mergeTarget(String[] t1, String[] t2){
296         List<String> list = new ArrayList<String>();
297         addToList(list, t1);
298         addToList(list, t2);
299
300         return list.toArray(new String[list.size()]);
301     }
302
303     private void addToList(List<String> list, String[] target){
304         if(target != null){
305             for(String s: target){
306                 list.add(s);
307             }
308         }
309     }
310
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();
315
316         BirthmarkSet[] extractResult = null;
317         if(unit == ExtractionUnit.CLASS){
318             extractFromClass(archives, er, et);
319         }
320         else if(unit == ExtractionUnit.PACKAGE){
321             extractFromPackage(archives, er, et);
322         }
323         else if(unit == ExtractionUnit.ARCHIVE){
324             extractFromProduct(archives, er, et);
325         }
326
327         return extractResult;
328     }
329
330     private byte[] inputStreamToByteArray(InputStream in) throws IOException{
331         ByteArrayOutputStream bout = new ByteArrayOutputStream();
332         int read;
333         byte[] dataBuffer = new byte[512];
334         while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
335             bout.write(dataBuffer, 0, read);
336         }
337         byte[] data = bout.toByteArray();
338
339         bout.close();
340         return data;
341     }
342
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();
346
347         for(ClassFileArchive archive: archives){
348             for(ClassFileEntry entry: archive){
349                 try{
350                     String name = entry.getClassName();
351                     String packageName = parsePackageName(name);
352                     BirthmarkSet bs = map.get(packageName);
353                     if(bs == null){
354                         bs = new BirthmarkSet(packageName, archive.getLocation());
355                         map.put(packageName, bs);
356                     }
357
358                     byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
359                     for(String birthmarkType: context.getBirthmarkTypes()){
360                         try{
361                             BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
362                             if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){
363                                 Birthmark b = bs.getBirthmark(extractor.getProvider().getType());
364                                 if(b == null){
365                                     b = extractor.createBirthmark();
366                                     bs.addBirthmark(b);
367                                 }
368                                 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
369                             }
370                         } catch(ExtractorNotFoundException e){
371                             warnings.addMessage(e, birthmarkType);
372                         }
373                     }
374                 } catch(IOException e){
375                     warnings.addMessage(e, archive.getName());
376                 }
377             }
378         }
379         try{
380             for(BirthmarkSet bs: map.values()){
381                 er.addBirthmarkSet(et, bs);
382             }
383         }catch(BirthmarkStoreException e){
384         }
385     }
386
387     private String parsePackageName(String name){
388         String n = name.replace('/', '.');
389         int index = n.lastIndexOf('.');
390         if(index > 0){
391             n = n.substring(0, index - 1);
392         }
393
394         return n;
395     }
396
397     private void extractFromClass(ClassFileArchive[] archives, ExtractionResultSet er,
398             ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
399         BirthmarkContext context = er.getContext();
400
401         for(ClassFileArchive archive: archives){
402             for(ClassFileEntry entry: archive){
403                 try{
404                     BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
405                     byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
406
407                     for(String birthmarkType: context.getBirthmarkTypes()){
408                         try{
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);
413                             }
414                         } catch(ExtractorNotFoundException e){
415                             warnings.addMessage(e, birthmarkType);
416                         }
417                     }
418                     er.addBirthmarkSet(et, birthmarkset);
419                 } catch(IOException e){
420                     warnings.addMessage(e, entry.getClassName());
421                 }
422             }
423         }
424     }
425
426     private void extractFromProduct(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
427         BirthmarkContext context = er.getContext();
428
429         for(ClassFileArchive archive: archives){
430             BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
431
432             for(ClassFileEntry entry: archive){
433                 try{
434                     byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
435                     for(String birthmarkType: context.getBirthmarkTypes()){
436                         try{
437                             BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
438                             if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){
439                                 Birthmark b = birthmarkset.getBirthmark(birthmarkType);
440                                 if(b == null){
441                                     b = extractor.createBirthmark();
442                                     birthmarkset.addBirthmark(b);
443                                 }
444                                 extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
445                             }
446                         } catch(ExtractorNotFoundException e){
447                             warnings.addMessage(e, birthmarkType);
448                         } 
449                     }
450                 } catch(IOException e){
451                     warnings.addMessage(e, entry.getClassName());
452                 }
453             }
454             if(birthmarkset.getBirthmarksCount() != 0){
455                 er.addBirthmarkSet(et, birthmarkset);
456             }
457         }
458     }
459
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++){
464             try{
465                 if(files[i].endsWith(".class")){
466                     archives.add(new DefaultClassFileArchive(files[i]));
467                 }
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());
471                 }
472                 else if(files[i].endsWith(".war")){
473                     archives.add(new WarClassFileArchive(files[i]));
474                 }
475             } catch(IOException e){
476                 warnings.addMessage(e, files[i]);
477             }
478         }
479         return archives.toArray(new ClassFileArchive[archives.size()]);
480     }
481
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;
487             targetType = type;
488         }
489         stack.push(warnings);
490         /*
491          * call subOperationStart method only once when some operation is occured.
492          * Ex. extraction, comparison, filtering
493          */
494         if(latestOperationType != type){
495             fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));
496             latestOperationType = type;
497         }
498     }
499
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;
504         }
505         stack.pop();
506         if(stack.size() == 0){
507             fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));
508             warnings = null;
509             latestOperationType = null;
510         }
511     }
512
513     private void fireEvent(BirthmarkEngineEvent e){
514         for(BirthmarkEngineListener listener: listeners){
515             switch(e.getStage()){
516             case OPERATION_START:
517                 listener.operationStart(e);
518                 break;
519             case SUB_OPERATION_START:
520                 listener.subOperationStart(e);
521                 break;
522             case SUB_OPERATION_DONE:
523                 listener.subOperationDone(e);
524                 break;
525             case OPERATION_DONE:
526                 listener.operationDone(e);
527                 break;
528             default:
529                 throw new InternalError("unknown stage: " + e.getStage());
530             }
531         }
532     }
533 }