KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > spoon > template > TemplateMatcher


1 package spoon.template;
2
3 import java.lang.reflect.Field JavaDoc;
4 import java.util.ArrayList JavaDoc;
5 import java.util.Collection JavaDoc;
6 import java.util.HashMap JavaDoc;
7 import java.util.Iterator JavaDoc;
8 import java.util.List JavaDoc;
9 import java.util.Map JavaDoc;
10 import java.util.regex.Matcher JavaDoc;
11 import java.util.regex.Pattern JavaDoc;
12
13 import spoon.reflect.code.CtFieldAccess;
14 import spoon.reflect.code.CtInvocation;
15 import spoon.reflect.code.CtStatementList;
16 import spoon.reflect.declaration.CtClass;
17 import spoon.reflect.declaration.CtElement;
18 import spoon.reflect.declaration.CtField;
19 import spoon.reflect.declaration.CtNamedElement;
20 import spoon.reflect.declaration.CtParameter;
21 import spoon.reflect.declaration.CtVariable;
22 import spoon.reflect.reference.CtExecutableReference;
23 import spoon.reflect.reference.CtFieldReference;
24 import spoon.reflect.reference.CtPackageReference;
25 import spoon.reflect.reference.CtReference;
26 import spoon.reflect.reference.CtTypeParameterReference;
27 import spoon.reflect.reference.CtTypeReference;
28 import spoon.reflect.visitor.CtScanner;
29 import spoon.reflect.visitor.Query;
30 import spoon.support.query.InvocationFilter;
31 import spoon.support.template.DefaultParameterMatcher;
32 import spoon.support.template.ParameterMatcher;
33 import spoon.support.template.Parameters;
34 import spoon.support.util.RtHelper;
35
36 /**
37  * This class defines an engine for matching a template to pieces of code.
38  */

39 public class TemplateMatcher {
40
41     private static List JavaDoc<CtInvocation<?>> getMethods(
42             CtClass<? extends Template> root) {
43         CtExecutableReference methodRef = root.getFactory().Executable()
44                 .createReference(
45                         root.getFactory().Type().createReference(
46                                 TemplateParameter.class),
47                         root.getFactory().Type().createTypeParameterReference(
48                                 "T"), "S");
49         List JavaDoc<CtInvocation<?>> meths = Query.getElements(root,
50                 new InvocationFilter(methodRef));
51
52         return meths;
53     }
54
55     private static List JavaDoc<String JavaDoc> getTemplateNameParameters(
56             CtClass<? extends Template> templateType) {
57         final List JavaDoc<String JavaDoc> ts = new ArrayList JavaDoc<String JavaDoc>();
58         final Collection JavaDoc<String JavaDoc> c = Parameters.getNames(templateType);
59         ts.addAll(c);
60         return ts;
61     }
62
63     private static List JavaDoc<CtTypeReference<?>> getTemplateTypeParameters(
64             final CtClass<? extends Template> templateType) {
65
66         final List JavaDoc<CtTypeReference<?>> ts = new ArrayList JavaDoc<CtTypeReference<?>>();
67         final Collection JavaDoc<String JavaDoc> c = Parameters.getNames(templateType);
68         new CtScanner() {
69             @Override JavaDoc
70             public void visitCtTypeParameterReference(
71                     CtTypeParameterReference reference) {
72                 if (c.contains(reference.getSimpleName())) {
73                     ts.add(reference);
74                 }
75             };
76
77             public <T> void visitCtTypeReference(CtTypeReference<T> reference) {
78                 if (c.contains(reference.getSimpleName())) {
79                     ts.add(reference);
80                 }
81             }
82
83         }.scan(templateType);
84         return ts;
85     }
86
87     @SuppressWarnings JavaDoc("unchecked")
88     private static List JavaDoc<CtFieldReference> getVarargs(
89             CtClass<? extends Template> root, List JavaDoc<CtInvocation<?>> variables) {
90         List JavaDoc<CtFieldReference> fields = new ArrayList JavaDoc<CtFieldReference>();
91         for (CtFieldReference field : root.getReference().getAllFields()) {
92             if (field.getType().getActualClass() == CtStatementList.class) {
93                 boolean alreadyAdded = false;
94                 for (CtInvocation<?> invocation : variables) {
95                     if (invocation instanceof CtInvocation<?>) {
96                         alreadyAdded |= ((CtFieldAccess) invocation.getTarget())
97                                 .getVariable().getDeclaration().equals(field);
98                     }
99                 }
100                 if (!alreadyAdded) {
101                     fields.add(field);
102                 }
103             }
104         }
105         return fields;
106     }
107
108     private List JavaDoc<CtElement> finds = new ArrayList JavaDoc<CtElement>();
109
110     private boolean found;
111
112     private Map JavaDoc<Object JavaDoc, Object JavaDoc> matches = new HashMap JavaDoc<Object JavaDoc, Object JavaDoc>();
113
114     private List JavaDoc<String JavaDoc> names;
115
116     private CtClass<? extends Template> templateType;
117
118     private List JavaDoc<CtTypeReference<?>> typeVariables;
119
120     private List JavaDoc<CtFieldReference> varArgs;
121
122     private List JavaDoc<CtInvocation<?>> variables;
123
124     /**
125      * Constructs a matcher for a given template.
126      *
127      * @param templateType
128      * the type of the template
129      */

130     public TemplateMatcher(CtClass<? extends Template> templateType) {
131         variables = TemplateMatcher.getMethods(templateType);
132         typeVariables = TemplateMatcher.getTemplateTypeParameters(templateType);
133         names = TemplateMatcher.getTemplateNameParameters(templateType);
134         varArgs = TemplateMatcher.getVarargs(templateType, variables);
135         this.templateType = templateType;
136     }
137
138     private boolean addMatch(Object JavaDoc template, Object JavaDoc target) {
139         Object JavaDoc inv = matches.get(template);
140         Object JavaDoc o = matches.put(template, target);
141         return null == inv || inv.equals(o);
142     }
143
144     private CtElement checkListStatements(List JavaDoc teList) {
145         for (Object JavaDoc tem : teList) {
146             if (variables.contains(tem) && tem instanceof CtInvocation) {
147                 CtInvocation<?> listCand = (CtInvocation<?>) tem;
148                 boolean ok = listCand.getFactory().Type().createReference(
149                         TemplateParameterList.class).isAssignableFrom(
150                         listCand.getTarget().getType());
151                 return ok ? listCand : null;
152             }
153             if (tem instanceof CtVariable) {
154                 CtVariable var = (CtVariable) tem;
155                 String JavaDoc name = var.getSimpleName();
156                 for (CtFieldReference f : varArgs) {
157                     if (f.getSimpleName().equals(name)) {
158                         return f.getDeclaration();
159                     }
160                 }
161             }
162         }
163
164         return null;
165     }
166
167     /**
168      * Finds all target program sub-trees that correspond to a template. Once
169      * this method has been called, {@link #getFinds()} will give the mathing
170      * CtElements if any.
171      *
172      * @param targetRoot
173      * the target to be tested for match
174      * @param templateRoot
175      * the template to match against
176      * @return true if there is one or more matches
177      *
178      * @see #getFinds()
179      */

180     public boolean find(CtElement targetRoot, final CtElement templateRoot) {
181         found = false;
182         new CtScanner() {
183             @Override JavaDoc
184             public void scan(CtElement element) {
185                 if (match(element, templateRoot)) {
186                     finds.add(element);
187                     found = true;
188                     // matches.clear();
189
}
190                 super.scan(element);
191             }
192         }.scan(targetRoot);
193
194         return found;
195     }
196
197     private ParameterMatcher findParameterMatcher(CtElement declaration,
198             String JavaDoc name) throws InstantiationException JavaDoc, IllegalAccessException JavaDoc {
199         if (declaration == null) {
200             return new DefaultParameterMatcher();
201         }
202         CtClass<?> clazz = declaration.getParent(CtClass.class);
203         if (clazz == null) {
204             return new DefaultParameterMatcher();
205         }
206
207         Collection JavaDoc<CtFieldReference> fields = clazz.getReference()
208                 .getAllFields();
209
210         CtFieldReference param = null;
211         for (CtFieldReference field : fields) {
212             Parameter p = field.getAnnotation(Parameter.class);
213             if (p == null)
214                 continue; // not a parameter.
215
String JavaDoc proxy = p.value();
216             if (proxy != "") {
217                 if (name.contains(proxy)) {
218                     param = field;
219                     break;
220                 }
221             }
222
223             if (name.contains(field.getSimpleName()) && p != null) {
224                 param = field;
225                 break;
226             }
227             // todo: check for field hack.
228
}
229
230         if (param == null) {
231             throw new IllegalStateException JavaDoc("Parameter not defined " + name
232                     + "at " + declaration.getPosition());
233         }
234         return getParameterInstance(param);
235     }
236
237     @SuppressWarnings JavaDoc("unused")
238     private String JavaDoc getBindedParameter(String JavaDoc pname) {
239         final String JavaDoc[] x = new String JavaDoc[1]; // HACK! jeje
240
x[0] = pname;
241         new CtScanner() {
242             @Override JavaDoc
243             public <T> void visitCtField(CtField<T> f) {
244                 Parameter p = f.getAnnotation(Parameter.class);
245                 if (p != null && p.value().equals(x[0])) {
246                     x[0] = f.getSimpleName();
247                     return;
248                 }
249                 super.visitCtField(f);
250             }
251         }.scan(templateType);
252
253         return x[0];
254     }
255
256     /**
257      * Returns all the elements that correspond to a given template. The
258      * {@link #find(CtElement, CtElement)} method must have been called before
259      */

260     public List JavaDoc<CtElement> getFinds() {
261         return finds;
262     }
263
264     /**
265      * Returns all the matches in a map where the keys are the corresponding
266      * template parameters. The {@link #match(CtElement, CtElement)} method must
267      * have been called before.
268      */

269     public Map JavaDoc<Object JavaDoc, Object JavaDoc> getMatches() {
270         return matches;
271     }
272
273     private ParameterMatcher getParameterInstance(CtFieldReference param)
274             throws InstantiationException JavaDoc, IllegalAccessException JavaDoc {
275         Parameter anParam = param.getAnnotation(Parameter.class);
276         if (anParam == null) {
277             // Parameter not annotated. Probably is a TemplateParameter. Just
278
// return a default impl
279
return new DefaultParameterMatcher();
280         }
281         Class JavaDoc<? extends ParameterMatcher> pm = anParam.match();
282         ParameterMatcher instance = (ParameterMatcher) pm.newInstance();
283         return instance;
284     }
285
286     /*
287      * Made private to hide the Objects.
288      */

289     @SuppressWarnings JavaDoc("unchecked")
290     private boolean helperMatch(Object JavaDoc target, Object JavaDoc template) {
291         if (target == null && template == null)
292             return true;
293         if (target == null || template == null)
294             return false;
295         if (variables.contains(template) || typeVariables.contains(template)) {
296             // TODO: upcall the parameter matcher if defined
297
boolean add = invokeCallBack(target, template);
298             if (add)
299                 return addMatch(template, target);
300             else
301                 return false;
302         }
303         if (target.getClass() != template.getClass()) {
304             return false;
305         }
306         if (template instanceof CtTypeReference
307                 && template.equals(templateType.getReference()))
308             return true;
309         if (template instanceof CtPackageReference
310                 && template.equals(templateType.getPackage()))
311             return true;
312         if (template instanceof CtReference) {
313             CtReference tRef = (CtReference) template;
314             boolean ok = matchNames(tRef.getSimpleName(),
315                     ((CtReference) target).getSimpleName());
316             if (ok && !template.equals(target)) {
317                 boolean remove = !invokeCallBack(target, template);
318                 if (remove) {
319                     matches.remove(tRef.getSimpleName());
320                     return false;
321                 }
322                 return true;
323             }
324         }
325
326         if (template instanceof CtNamedElement) {
327             CtNamedElement named = (CtNamedElement) template;
328             boolean ok = matchNames(named.getSimpleName(),
329                     ((CtNamedElement) target).getSimpleName());
330             if (ok && !template.equals(target)) {
331                 boolean remove = !invokeCallBack(target, template);
332                 if (remove) {
333                     matches.remove(named.getSimpleName());
334                     return false;
335                 }
336             }
337         }
338
339         if (template instanceof Collection JavaDoc) {
340             return matchCollections((Collection JavaDoc) target, (Collection JavaDoc) template);
341         }
342
343         if (target instanceof CtElement || target instanceof CtReference) {
344             for (Field JavaDoc f : RtHelper.getAllFields(target.getClass())) {
345                 f.setAccessible(true);
346                 if (f.getName().equals("parent"))
347                     continue;
348                 if (f.getName().equals("position"))
349                     continue;
350                 if (f.getName().equals("docComment"))
351                     continue;
352                 if (f.getName().equals("factory"))
353                     continue;
354                 try {
355                     if (!helperMatch(f.get(target), f.get(template)))
356                         return false;
357                 } catch (Exception JavaDoc e) {
358                     e.printStackTrace();
359                 }
360             }
361             return true;
362         } else if (target instanceof String JavaDoc) {
363             return matchNames((String JavaDoc) template, (String JavaDoc) target);
364         } else {
365             return target.equals(template);
366         }
367     }
368
369     @SuppressWarnings JavaDoc("unchecked")
370     private boolean invokeCallBack(Object JavaDoc target, Object JavaDoc template) {
371         try {
372             if (template instanceof CtInvocation) {
373                 CtFieldAccess<?> param = (CtFieldAccess) ((CtInvocation<?>) template)
374                         .getTarget();
375                 ParameterMatcher instance = getParameterInstance(param
376                         .getVariable());
377                 return instance.match(this, (CtInvocation) template,
378                         (CtElement) target);
379             } else if (template instanceof CtReference) {
380                 // Get parameter
381
CtReference ref = (CtReference) template;
382                 Parameter param = ref.getAnnotation(Parameter.class);
383                 ParameterMatcher instance;
384                 if (param == null)
385                     instance = new DefaultParameterMatcher();
386                 else
387                     instance = param.match().newInstance();
388                 return instance.match(this, (CtReference) template,
389                         (CtReference) target);
390             } else if (template instanceof CtNamedElement) {
391                 CtNamedElement named = (CtNamedElement) template;
392                 ParameterMatcher instance = findParameterMatcher(named, named
393                         .getSimpleName());
394                 return instance.match(this, (CtElement) template,
395                         (CtElement) target);
396             }
397
398             else {
399                 // Should not happen
400
throw new RuntimeException JavaDoc();
401             }
402         } catch (InstantiationException JavaDoc e) {
403             e.printStackTrace();
404             return true;
405         } catch (IllegalAccessException JavaDoc e) {
406             // TODO Auto-generated catch block
407
e.printStackTrace();
408             return true;
409         }
410     }
411
412     private boolean isCurrentTemplate(Object JavaDoc object, CtElement inMulti) {
413         if (object instanceof CtInvocation<?>) {
414             return object.equals(inMulti);
415         }
416         if (object instanceof CtParameter) {
417             CtParameter param = (CtParameter) object;
418             for (CtFieldReference varArg : varArgs) {
419                 if (param.getSimpleName().equals(varArg.getSimpleName())) {
420                     return varArg.equals(inMulti);
421                 }
422             }
423         }
424         return false;
425     }
426
427     /**
428      * Matches a target program sub-tree against a template. Once this method
429      * has been called, {@link #getMatches()} will give the mathing parts if
430      * any.
431      *
432      * @param targetRoot
433      * the target to be tested for match
434      * @param templateRoot
435      * the template to match against
436      * @return true if matches
437      *
438      * @see #getMatches()
439      */

440     public boolean match(CtElement targetRoot, CtElement templateRoot) {
441         return helperMatch(targetRoot, templateRoot);
442     }
443
444     @SuppressWarnings JavaDoc("unchecked")
445     private boolean matchCollections(Collection JavaDoc target, Collection JavaDoc template) {
446         List JavaDoc teList = new ArrayList JavaDoc(template);
447         List JavaDoc taList = new ArrayList JavaDoc(target);
448
449         int numOfNonParamsinTeList = teList.size();
450
451         // inMulti keeps the multiElement templateVariable we are at
452
CtElement inMulti = nextListStatement(teList, null);
453
454         // multi keeps the values to assign to inMulti
455
List JavaDoc<CtElement> multi = new ArrayList JavaDoc();
456
457         if (null == inMulti) {
458             // If we are not looking at template with multiElements
459
// the sizes should then be the same
460
if (teList.size() != taList.size())
461                 return false;
462
463             for (int te = 0, ta = 0; te < teList.size() && ta < taList.size(); te++, ta++) {
464                 if (!helperMatch(taList.get(ta), teList.get(te))) {
465                     return false;
466                 }
467             }
468             return true;
469         } else {
470
471             for (int te = 0, ta = 0; te < teList.size() && ta < taList.size(); te++, ta++) {
472
473                 if (isCurrentTemplate(teList.get(te), inMulti)) {
474                     numOfNonParamsinTeList--;
475                     if (te + 1 >= teList.size()) {
476                         multi.addAll(taList.subList(te, taList.size()));
477                         CtStatementList tpl = templateType.getFactory().Core()
478                                 .createStatementList();
479                         tpl.setStatements(multi);
480                         if (!invokeCallBack(tpl, inMulti)) {
481                             return false;
482                         }
483                         boolean ret = addMatch(inMulti, multi);
484                         return ret;
485                     }
486                     te++;
487                     while (te < teList.size() && ta < taList.size()
488                             && !helperMatch(taList.get(ta), teList.get(te))) {
489                         multi.add((CtElement) taList.get(ta));
490                         ta++;
491                     }
492                     CtStatementList tpl = templateType.getFactory().Core()
493                             .createStatementList();
494                     tpl.setStatements(multi);
495                     if (!invokeCallBack(tpl, inMulti)) {
496                         return false;
497                     }
498                     addMatch(inMulti, tpl);
499                     // update inMulti
500
inMulti = nextListStatement(teList, inMulti);
501                     multi = new ArrayList JavaDoc();
502                     numOfNonParamsinTeList--;
503                 } else {
504                     if (!helperMatch(taList.get(ta), teList.get(te))) {
505                         return false;
506                     }
507                     if (!(ta + 1 < taList.size()) && inMulti != null) {
508                         CtStatementList tpl = templateType.getFactory().Core()
509                                 .createStatementList();
510                         tpl.setStatements(multi);
511                         if (!invokeCallBack(tpl, inMulti)) {
512                             return false;
513                         }
514                         addMatch(inMulti, tpl);
515                         // update inMulti
516
inMulti = nextListStatement(teList, inMulti);
517                         multi = new ArrayList JavaDoc();
518                         numOfNonParamsinTeList--;
519                     }
520                 }
521             }
522             return true;
523         }
524
525     }
526
527     private boolean matchNames(String JavaDoc name, String JavaDoc tname) {
528
529         try {
530             for (String JavaDoc pname : names) {
531                 // pname = pname.replace("_FIELD_", "");
532
if (name.contains(pname)) {
533                     String JavaDoc newName = name.replace(pname, "(.*)");
534                     Pattern JavaDoc p = Pattern.compile(newName);
535                     Matcher JavaDoc m = p.matcher(tname);
536                     if (!m.matches()) {
537                         return false;
538                     }
539                     // TODO: fix with parameter from @Parameter
540
// boolean ok = addMatch(getBindedParameter(pname),
541
// m.group(1));
542
boolean ok = addMatch(pname, m.group(1));
543                     if (!ok) {
544                         System.out.println("incongruent match");
545                         return false;
546                     } else {
547                         return true;
548                     }
549                 }
550             }
551         } catch (RuntimeException JavaDoc e) {
552             // //fall back on dumb way to do it.
553
// if
554
}
555         return name.equals(tname);
556     }
557
558     private CtElement nextListStatement(List JavaDoc<?> teList, CtElement inMulti) {
559         if (inMulti == null)
560             return checkListStatements(teList);
561         else {
562             List JavaDoc<?> teList2 = new ArrayList JavaDoc<Object JavaDoc>(teList);
563             if (inMulti instanceof CtInvocation) {
564                 teList2.remove(inMulti);
565             } else if (inMulti instanceof CtVariable) {
566                 CtVariable var = (CtVariable) inMulti;
567                 for (Iterator JavaDoc iter = teList2.iterator(); iter.hasNext();) {
568                     CtVariable teVar = (CtVariable) iter.next();
569                     if (teVar.getSimpleName().equals(var.getSimpleName())) {
570                         iter.remove();
571                     }
572                 }
573             }
574             return checkListStatements(teList2);
575         }
576     }
577
578 }
579
Popular Tags