OSDN Git Service

必要のないクラスを削除し,クラスの整理を行った.
[stigmata/stigmata.git] / src / main / java / jp / sourceforge / stigmata / BirthmarkEngine.java
1 package jp.sourceforge.stigmata;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.File;
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;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.Stack;
16
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;
35
36 /**
37  * core engine for extracting/comparing/filtering birthmarks.
38  * 
39  * This class is not thread safe.
40  * 
41  * @author Haruaki Tamada
42  */
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;
50
51     /**
52      * constructor.
53      */
54     public BirthmarkEngine(BirthmarkEnvironment env){
55         this.environment = env;
56     }
57
58     /**
59      * returns an environment of this object.
60      */
61     public BirthmarkEnvironment getEnvironment(){
62         return environment;
63     }
64
65     public void addBirthmarkEngineListener(BirthmarkEngineListener listener){
66         listeners.add(listener);
67     }
68
69     public void removeBirthmarkEngineListener(BirthmarkEngineListener listener){
70         listeners.remove(listener);
71     }
72
73     /**
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()
79      */
80     public synchronized ComparisonResultSet filter(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
81         operationStart(OperationType.FILTER_BIRTHMARKS);
82
83         ComparisonResultSet crs = compare(target, context);
84         crs = filter(crs);
85
86         operationDone(OperationType.FILTER_BIRTHMARKS);
87
88         return crs;
89     }
90
91     /**
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()
97      */
98     public synchronized ComparisonResultSet filter(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
99         operationStart(OperationType.FILTER_BIRTHMARKS);
100
101         ComparisonResultSet crs = compare(targetX, targetY, context);
102         crs = filter(crs);
103
104         operationDone(OperationType.FILTER_BIRTHMARKS);
105
106         return crs;
107     }
108
109     /**
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()
115      */
116     public synchronized ComparisonResultSet filter(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
117         operationStart(OperationType.FILTER_BIRTHMARKS);
118
119         ComparisonResultSet crs = compare(er);
120         crs = filter(crs);
121
122         operationDone(OperationType.FILTER_BIRTHMARKS);
123
124         return crs;
125     }
126
127     /**
128      * filters comparison of birthmarks.
129      * @see ExtractionResultSet#getContext()
130      * @see BirthmarkContext#getFilterTypes()
131      */
132     public synchronized ComparisonResultSet filter(ComparisonResultSet crs) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
133         operationStart(OperationType.FILTER_BIRTHMARKS);
134         StigmataHookManager.getInstance().runHook(Phase.BEFORE_FILTERING, crs.getContext());
135
136         String[] filterTypes = crs.getContext().getFilterTypes();
137
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]);
143                 if(fset != null){
144                     filterList.add(fset);
145                 }
146                 else{
147                     warnings.addMessage(new FilterNotFoundException("filter not found"), filterTypes[i]);
148                 }
149             }
150             ComparisonPairFilterSet[] cpfs = filterList.toArray(new ComparisonPairFilterSet[filterList.size()]);
151
152             crs = new FilteredComparisonResultSet(crs, cpfs);
153         }
154
155         StigmataHookManager.getInstance().runHook(Phase.AFTER_FILTERING, crs.getContext());
156         operationDone(OperationType.FILTER_BIRTHMARKS);
157
158         return crs;
159     }
160
161     /**
162      * compares two given birthmarks and returns comparison pair.
163      */
164     public synchronized ComparisonPair compareDetails(BirthmarkSet bs1, BirthmarkSet bs2, BirthmarkContext context) throws BirthmarkComparisonFailedException{
165         return new ComparisonPair(bs1, bs2, context);
166     }
167
168     public synchronized ComparisonResultSet compare(String[] target, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
169         operationStart(OperationType.COMPARE_BIRTHMARKS);
170
171         ExtractionResultSet er = extract(target, context);
172         ComparisonResultSet crs = compare(er);
173
174         operationDone(OperationType.COMPARE_BIRTHMARKS);
175
176         return crs;
177     }
178
179     public synchronized ComparisonResultSet compare(String[] targetX, String[] targetY, BirthmarkContext context) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException, BirthmarkStoreException{
180         operationStart(OperationType.COMPARE_BIRTHMARKS);
181
182         ExtractionResultSet er = extract(targetX, targetY, context);
183         ComparisonResultSet crs = compare(er);
184
185         operationDone(OperationType.COMPARE_BIRTHMARKS);
186
187         return crs;
188     }
189
190     public synchronized ComparisonResultSet compare(ExtractionResultSet er) throws BirthmarkExtractionFailedException, BirthmarkComparisonFailedException{
191         operationStart(OperationType.COMPARE_BIRTHMARKS);
192         BirthmarkContext context = er.getContext();
193
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);
199             break;
200         case ROUND_ROBIN_WITHOUT_SAME_PAIR:
201             crs = new RoundRobinComparisonResultSet(er, false);
202             break;
203         case ROUND_ROBIN_XY:
204             crs = new RoundRobinComparisonResultSet(er, true);
205         case GUESSED_PAIR:
206             crs = new CertainPairComparisonResultSet(er);
207             break;
208         case SPECIFIED_PAIR:
209             crs = new CertainPairComparisonResultSet(er, context.getNameMappings());
210             break;
211         }
212
213         StigmataHookManager.getInstance().runHook(Phase.AFTER_COMPARISON, context);
214         operationDone(OperationType.COMPARE_BIRTHMARKS);
215
216         return crs;
217     }
218
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);
223         return er;
224     }
225
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);
229
230         try{
231             prepare(targetX, targetY, context);
232
233             StigmataHookManager.getInstance().runHook(Phase.BEFORE_EXTRACTION, context);
234
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);
241                 break;
242             case GUESSED_PAIR:
243             case SPECIFIED_PAIR:
244             case ROUND_ROBIN_XY:
245             default:
246                 if(targetX == null || targetY == null){
247                     throw new BirthmarkExtractionFailedException("targetX or targetY is null");
248                 }
249                 er.setTableType(true);
250                 extractImpl(targetX, er, ExtractionTarget.TARGET_X);
251                 extractImpl(targetY, er, ExtractionTarget.TARGET_Y);
252                 break;
253             }
254             return er;
255         } catch(IOException e){
256             throw new BirthmarkExtractionFailedException(e);
257         } finally{
258             StigmataHookManager.getInstance().runHook(Phase.AFTER_EXTRACTION, context);
259             operationDone(OperationType.EXTRACT_BIRTHMARKS);
260         }
261     }
262
263     public BirthmarkContext prepare(String[] targetX, String[] targetY, BirthmarkContext context) throws MalformedURLException, IOException{
264         StigmataHookManager.getInstance().runHook(Phase.BEFORE_PREPROCESS, context);
265
266         Set<String> set = new HashSet<String>();
267         if(targetX != null){
268             for(String t: targetX) set.add(t);
269         }
270         if(targetY != null){
271             for(String t: targetY) set.add(t);
272         }
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);
280             }
281         }
282         StigmataHookManager.getInstance().runHook(Phase.AFTER_PREPROCESS, context);
283
284         return context;
285     }
286
287     private String[] mergeTarget(String[] t1, String[] t2){
288         List<String> list = new ArrayList<String>();
289         addToList(list, t1);
290         addToList(list, t2);
291
292         return list.toArray(new String[list.size()]);
293     }
294
295     private void addToList(List<String> list, String[] target){
296         if(target != null){
297             for(String s: target){
298                 list.add(s);
299             }
300         }
301     }
302
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();
307
308         BirthmarkSet[] extractResult = null;
309         if(unit == ExtractionUnit.CLASS){
310             extractFromClass(archives, er, et);
311         }
312         else if(unit == ExtractionUnit.PACKAGE){
313             extractFromPackage(archives, er, et);
314         }
315         else if(unit == ExtractionUnit.ARCHIVE){
316             extractFromProduct(archives, er, et);
317         }
318
319         return extractResult;
320     }
321
322     private byte[] inputStreamToByteArray(InputStream in) throws IOException{
323         ByteArrayOutputStream bout = new ByteArrayOutputStream();
324         int read;
325         byte[] dataBuffer = new byte[512];
326         while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
327             bout.write(dataBuffer, 0, read);
328         }
329         byte[] data = bout.toByteArray();
330
331         bout.close();
332         return data;
333     }
334
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();
338
339         for(ClassFileArchive archive: archives){
340             for(ClassFileEntry entry: archive){
341                 try{
342                     String name = entry.getClassName();
343                     String packageName = parsePackageName(name);
344                     BirthmarkSet bs = map.get(packageName);
345                     if(bs == null){
346                         bs = new BirthmarkSet(packageName, archive.getLocation());
347                         map.put(packageName, bs);
348                     }
349
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());
356                             if(b == null){
357                                 b = extractor.createBirthmark();
358                                 bs.addBirthmark(b);
359                             }
360                             extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
361                         }
362                     }
363                 } catch(IOException e){
364                     warnings.addMessage(e, archive.getName());
365                 }
366             }
367         }
368         try{
369             for(BirthmarkSet bs: map.values()){
370                 er.addBirthmarkSet(et, bs);
371             }
372         }catch(BirthmarkStoreException e){
373         }
374     }
375
376     private String parsePackageName(String name){
377         String n = name.replace('/', '.');
378         int index = n.lastIndexOf('.');
379         if(index > 0){
380             n = n.substring(0, index - 1);
381         }
382
383         return n;
384     }
385
386     private void extractFromClass(ClassFileArchive[] archives, ExtractionResultSet er,
387             ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
388         BirthmarkContext context = er.getContext();
389
390         for(ClassFileArchive archive: archives){
391             for(ClassFileEntry entry: archive){
392                 try{
393                     BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
394                     byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
395
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);
402                         }
403                     }
404                     er.addBirthmarkSet(et, birthmarkset);
405                 } catch(IOException e){
406                     warnings.addMessage(e, entry.getClassName());
407                 }
408             }
409         }
410     }
411
412     private void extractFromProduct(ClassFileArchive[] archives, ExtractionResultSet er, ExtractionTarget et) throws IOException, BirthmarkExtractionFailedException, BirthmarkStoreException{
413         BirthmarkContext context = er.getContext();
414
415         for(ClassFileArchive archive: archives){
416             BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
417
418             for(ClassFileEntry entry: archive){
419                 try{
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);
426                             if(b == null){
427                                 b = extractor.createBirthmark();
428                                 birthmarkset.addBirthmark(b);
429                             }
430                             extractor.extract(b, new ByteArrayInputStream(data), er.getContext());
431                         }
432                     }
433                 } catch(IOException e){
434                     warnings.addMessage(e, entry.getClassName());
435                 }
436             }
437             if(birthmarkset.getBirthmarksCount() != 0){
438                 er.addBirthmarkSet(et, birthmarkset);
439             }
440         }
441     }
442
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++){
447             try{
448                 if(files[i].endsWith(".class")){
449                     archives.add(new DefaultClassFileArchive(files[i]));
450                 }
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());
454                 }
455                 else if(files[i].endsWith(".war")){
456                     archives.add(new WarClassFileArchive(files[i]));
457                 }
458             } catch(IOException e){
459                 warnings.addMessage(e, files[i]);
460             }
461         }
462         return archives.toArray(new ClassFileArchive[archives.size()]);
463     }
464
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;
470             targetType = type;
471         }
472         stack.push(warnings);
473         /*
474          * call subOperationStart method only once when some operation is occured.
475          * Ex. extraction, comparison, filtering
476          */
477         if(latestOperationType != type){
478             fireEvent(new BirthmarkEngineEvent(OperationStage.SUB_OPERATION_START, type, warnings));
479             latestOperationType = type;
480         }
481     }
482
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;
487         }
488         stack.pop();
489         if(stack.size() == 0){
490             fireEvent(new BirthmarkEngineEvent(OperationStage.OPERATION_DONE, type, warnings));
491             warnings = null;
492             latestOperationType = null;
493         }
494     }
495
496     private void fireEvent(BirthmarkEngineEvent e){
497         for(BirthmarkEngineListener listener: listeners){
498             switch(e.getStage()){
499             case OPERATION_START:
500                 listener.operationStart(e);
501                 break;
502             case SUB_OPERATION_START:
503                 listener.subOperationStart(e);
504                 break;
505             case SUB_OPERATION_DONE:
506                 listener.subOperationDone(e);
507                 break;
508             case OPERATION_DONE:
509                 listener.operationDone(e);
510                 break;
511             default:
512                 throw new InternalError("unknown stage: " + e.getStage());
513             }
514         }
515     }
516 }