OSDN Git Service

Remove Date tag of Subversion
[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.List;
16 import java.util.Map;
17 import java.util.Stack;
18
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;
37
38 /**
39  * core engine for extracting/comparing/filtering birthmarks.
40  * 
41  * This class is not thread safe.
42  * 
43  * @author Haruaki Tamada
44  * @version $Revision$ 
45  */
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;
54
55     /**
56      * constructor.
57      */
58     public BirthmarkEngine(BirthmarkEnvironment env){
59         this.environment = env;
60         factory = new BirthmarkExtractorFactory(env);
61     }
62
63     /**
64      * returns an environment of this object.
65      */
66     public BirthmarkEnvironment getEnvironment(){
67         return environment;
68     }
69
70     public void addBirthmarkEngineListener(BirthmarkEngineListener listener){
71         listeners.add(listener);
72     }
73
74     public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){
75         listeners.remove(listener);
76     }
77
78     /**
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()
84      */
85     public synchronized ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
86         operationStart(OperationType.FILTER_BIRTHMARKS);
87
88         ComparisonResultSet crs = compare(target, context);
89         crs = filter(crs);
90
91         operationDone(OperationType.FILTER_BIRTHMARKS);
92
93         return crs;
94     }
95
96     /**
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()
102      */
103     public synchronized ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
104         operationStart(OperationType.FILTER_BIRTHMARKS);
105
106         ComparisonResultSet crs = compare(targetX, targetY, context);
107         crs = filter(crs);
108
109         operationDone(OperationType.FILTER_BIRTHMARKS);
110
111         return crs;
112     }
113
114     /**
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()
120      */
121     public synchronized ComparisonResultSet filter(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
122         operationStart(OperationType.FILTER_BIRTHMARKS);
123
124         ComparisonResultSet crs = compare(er);
125         crs = filter(crs);
126
127         operationDone(OperationType.FILTER_BIRTHMARKS);
128
129         return crs;
130     }
131
132     /**
133      * filters comparison of birthmarks.
134      * @see ExtractionResultSet#getContext()
135      * @see BirthmarkContext#getFilterTypes()
136      */
137     public synchronized ComparisonResultSet filter(ComparisonResultSet crs) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
138         operationStart(OperationType.FILTER_BIRTHMARKS);
139         StigmataHookManager.getInstance().runHook(Phase.BEFORE_FILTERING, crs.getContext());
140
141         String[] filterTypes = crs.getContext().getFilterTypes();
142
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]);
148                 if(fset != null){
149                     filterList.add(fset);
150                 }
151                 else{
152                     warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);
153                 }
154             }
155             ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);
156
157             crs = new FilteredComparisonResultSet(crs, cpfs);
158         }
159
160         StigmataHookManager.getInstance().runHook(Phase.AFTER_FILTERING, crs.getContext());
161         operationDone(OperationType.FILTER_BIRTHMARKS);
162
163         return crs;
164     }
165
166     /**
167      * compares two given birthmarks and returns comparison pair.
168      */
169     public synchronized ComparisonPair compareDetails(BirthmarkSet bs1, BirthmarkSet bs2, BirthmarkContext context) throws BirthmarkComparisonFailedException{
170         return new ComparisonPair(bs1, bs2, context);
171     }
172
173     public synchronized ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
174         operationStart(OperationType.COMPARE_BIRTHMARKS);
175
176         ExtractionResultSet er = extract(target, context);
177         ComparisonResultSet crs = compare(er);
178
179         operationDone(OperationType.COMPARE_BIRTHMARKS);
180
181         return crs;
182     }
183
184     public synchronized ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
185         operationStart(OperationType.COMPARE_BIRTHMARKS);
186
187         ExtractionResultSet er = extract(targetX, targetY, context);
188         ComparisonResultSet crs = compare(er);
189
190         operationDone(OperationType.COMPARE_BIRTHMARKS);
191
192         return crs;
193     }
194
195     public synchronized ComparisonResultSet compare(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
196         operationStart(OperationType.COMPARE_BIRTHMARKS);
197         BirthmarkContext context = er.getContext();
198
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);
204             break;
205         case ROUND_ROBIN_WITHOUT_SAME_PAIR:
206             crs = new RoundRobinComparisonResultSet(er, false);
207             break;
208         case ROUND_ROBIN_XY:
209             crs = new RoundRobinComparisonResultSet(er, true);
210         case GUESSED_PAIR:
211             crs = new CertainPairComparisonResultSet(er);
212             break;
213         case SPECIFIED_PAIR:
214             crs = new CertainPairComparisonResultSet(er, context.getNameMappings());
215             break;
216         }
217
218         StigmataHookManager.getInstance().runHook(Phase.AFTER_COMPARISON, context);
219         operationDone(OperationType.COMPARE_BIRTHMARKS);
220
221         return crs;
222     }
223
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);
228         return er;
229     }
230
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);
234
235         try{
236             StigmataHookManager.getInstance().runHook(Phase.BEFORE_EXTRACTION, context);
237
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);
244                 break;
245             case GUESSED_PAIR:
246             case SPECIFIED_PAIR:
247             case ROUND_ROBIN_XY:
248             default:
249                 if(targetX == null || targetY == null){
250                     throw new BirthmarkExtractionFailedException("targetX or targetY is null");
251                 }
252                 er.setTableType(true);
253                 extractImpl(targetX, er, ExtractionTarget.TARGET_X);
254                 extractImpl(targetY, er, ExtractionTarget.TARGET_Y);
255                 break;
256             }
257             return er;
258         } catch(IOException e){
259             throw new BirthmarkExtractionFailedException(e);
260         } finally{
261             StigmataHookManager.getInstance().runHook(Phase.AFTER_EXTRACTION, context);
262             operationDone(OperationType.EXTRACT_BIRTHMARKS);
263         }
264     }
265
266     private String[] mergeTarget(String[] t1, String[] t2){
267         List<String> list = new ArrayList<String>();
268         addToList(list, t1);
269         addToList(list, t2);
270
271         return list.toArray(new String[list.size()]);
272     }
273
274     private void addToList(List<String> list, String[] target){
275         if(target != null){
276             for(String s: target){
277                 list.add(s);
278             }
279         }
280     }
281
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();
286
287         BirthmarkSet[] extractResult = null;
288         if(unit == ExtractionUnit.CLASS){
289             extractFromClass(archives, er, et);
290         }
291         else if(unit == ExtractionUnit.PACKAGE){
292             extractFromPackage(archives, er, et);
293         }
294         else if(unit == ExtractionUnit.ARCHIVE){
295             extractFromProduct(archives, er, et);
296         }
297
298         return extractResult;
299     }
300
301     private byte[] inputStreamToByteArray(InputStream in) throws IOException{
302         ByteArrayOutputStream bout = new ByteArrayOutputStream();
303         int read;
304         byte[] dataBuffer = new byte[512];
305         while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
306             bout.write(dataBuffer, 0, read);
307         }
308         byte[] data = bout.toByteArray();
309
310         bout.close();
311         return data;
312     }
313
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();
317
318         for(ClassFileArchive archive: archives){
319             for(ClassFileEntry entry: archive){
320                 try{
321                     String name = entry.getClassName();
322                     String packageName = parsePackageName(name);
323                     BirthmarkSet bs = map.get(packageName);
324                     if(bs == null){
325                         bs = new BirthmarkSet(packageName, archive.getLocation());
326                         map.put(packageName, bs);
327                     }
328
329                     byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
330                     for(String birthmarkType: context.getBirthmarkTypes()){
331                         try{
332                             BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
333                             if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){
334                                 Birthmark b = bs.getBirthmark(extractor.getProvider().getType());
335                                 if(b == null){
336                                     b = extractor.createBirthmark();
337                                     bs.addBirthmark(b);
338                                 }
339                                 extractor.extract(b, new ByteArrayInputStream(data), er.getEnvironment());
340                             }
341                         } catch(ExtractorNotFoundException e){
342                             warnings.addMessage(e, birthmarkType);
343                         }
344                     }
345                 } catch(IOException e){
346                     warnings.addMessage(e, archive.getName());
347                 }
348             }
349         }
350         try{
351             for(BirthmarkSet bs: map.values()){
352                 er.addBirthmarkSet(et, bs);
353             }
354         }catch(BirthmarkStoreException e){
355         }
356     }
357
358     private String parsePackageName(String name){
359         String n = name.replace('/', '.');
360         int index = n.lastIndexOf('.');
361         if(index > 0){
362             n = n.substring(0, index - 1);
363         }
364
365         return n;
366     }
367
368     private void extractFromClass(ClassFileArchive[] archives, ExtractionResultSet er,
369             ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
370         BirthmarkContext context = er.getContext();
371
372         for(ClassFileArchive archive: archives){
373             for(ClassFileEntry entry: archive){
374                 try{
375                     BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
376                     byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
377
378                     for(String birthmarkType: context.getBirthmarkTypes()){
379                         try{
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);
384                             }
385                         } catch(ExtractorNotFoundException e){
386                             warnings.addMessage(e, birthmarkType);
387                         }
388                     }
389                     er.addBirthmarkSet(et, birthmarkset);
390                 } catch(IOException e){
391                     warnings.addMessage(e, entry.getClassName());
392                 }
393             }
394         }
395     }
396
397     private void extractFromProduct(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
398         BirthmarkContext context = er.getContext();
399
400         for(ClassFileArchive archive: archives){
401             BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
402
403             for(ClassFileEntry entry: archive){
404                 try{
405                     byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
406                     for(String birthmarkType: context.getBirthmarkTypes()){
407                         try{
408                             BirthmarkExtractor extractor = factory.getExtractor(birthmarkType);
409                             if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){
410                                 Birthmark b = birthmarkset.getBirthmark(birthmarkType);
411                                 if(b == null){
412                                     b = extractor.createBirthmark();
413                                     birthmarkset.addBirthmark(b);
414                                 }
415                                 extractor.extract(b, new ByteArrayInputStream(data), er.getEnvironment());
416                             }
417                         } catch(ExtractorNotFoundException e){
418                             warnings.addMessage(e, birthmarkType);
419                         } 
420                     }
421                 } catch(IOException e){
422                     warnings.addMessage(e, entry.getClassName());
423                 }
424             }
425             if(birthmarkset.getBirthmarksCount() != 0){
426                 er.addBirthmarkSet(et, birthmarkset);
427             }
428         }
429     }
430
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++){
435             try{
436                 if(files[i].endsWith(".class")){
437                     archives.add(new DefaultClassFileArchive(files[i]));
438                 }
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());
442                 }
443                 else if(files[i].endsWith(".war")){
444                     archives.add(new WarClassFileArchive(files[i]));
445                 }
446             } catch(IOException e){
447                 warnings.addMessage(e, files[i]);
448             }
449         }
450         return archives.toArray(new ClassFileArchive[archives.size()]);
451     }
452
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;
458             targetType = type;
459         }
460         stack.push(warnings);
461         /*
462          * call subOperationStart method only once when some operation is occured.
463          * Ex. extraction, comparison, filtering
464          */
465         if(latestOperationType != type){
466             fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));
467             latestOperationType = type;
468         }
469     }
470
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;
475         }
476         stack.pop();
477         if(stack.size() == 0){
478             fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));
479             warnings = null;
480             latestOperationType = null;
481         }
482     }
483
484     private void fireEvent(BirthmarkEngineEvent e){
485         for(BirthmarkEngineListener listener: listeners){
486             switch(e.getStage()){
487             case OPERATION_START:
488                 listener.operationStart(e);
489                 break;
490             case SUB_OPERATION_START:
491                 listener.subOperationStart(e);
492                 break;
493             case SUB_OPERATION_DONE:
494                 listener.subOperationDone(e);
495                 break;
496             case OPERATION_DONE:
497                 listener.operationDone(e);
498                 break;
499             default:
500                 throw new InternalError("unknown stage: " + e.getStage());
501             }
502         }
503     }
504 }