KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > spoon > support > template > SubstitutionVisitor


1 package spoon.support.template;
2
3 import java.util.ArrayList JavaDoc;
4 import java.util.Collection JavaDoc;
5 import java.util.List JavaDoc;
6 import java.util.TreeSet JavaDoc;
7
8 import spoon.reflect.Factory;
9 import spoon.reflect.code.CtAbstractInvocation;
10 import spoon.reflect.code.CtArrayAccess;
11 import spoon.reflect.code.CtBlock;
12 import spoon.reflect.code.CtCodeElement;
13 import spoon.reflect.code.CtExpression;
14 import spoon.reflect.code.CtFieldAccess;
15 import spoon.reflect.code.CtInvocation;
16 import spoon.reflect.code.CtLiteral;
17 import spoon.reflect.code.CtReturn;
18 import spoon.reflect.code.CtStatement;
19 import spoon.reflect.code.CtVariableAccess;
20 import spoon.reflect.declaration.CtAnnotation;
21 import spoon.reflect.declaration.CtClass;
22 import spoon.reflect.declaration.CtConstructor;
23 import spoon.reflect.declaration.CtElement;
24 import spoon.reflect.declaration.CtExecutable;
25 import spoon.reflect.declaration.CtField;
26 import spoon.reflect.declaration.CtMethod;
27 import spoon.reflect.declaration.CtNamedElement;
28 import spoon.reflect.declaration.CtParameter;
29 import spoon.reflect.declaration.CtSimpleType;
30 import spoon.reflect.declaration.CtTypedElement;
31 import spoon.reflect.reference.CtArrayTypeReference;
32 import spoon.reflect.reference.CtExecutableReference;
33 import spoon.reflect.reference.CtFieldReference;
34 import spoon.reflect.reference.CtReference;
35 import spoon.reflect.reference.CtTypeReference;
36 import spoon.reflect.visitor.CtInheritanceScanner;
37 import spoon.reflect.visitor.CtScanner;
38 import spoon.template.Local;
39 import spoon.template.Parameter;
40 import spoon.template.Template;
41 import spoon.template.TemplateParameter;
42
43 class SkipException extends RuntimeException JavaDoc {
44     private static final long serialVersionUID = 1L;
45
46     Object JavaDoc skipped;
47
48     public SkipException(Object JavaDoc e) {
49         super("skipping " + e.toString());
50         skipped = e;
51     }
52
53 }
54
55 /**
56  * This visitor implements the substitution engine of Spoon templates.
57  */

58 public class SubstitutionVisitor extends CtScanner {
59
60     public class InheritanceSustitutionScanner extends CtInheritanceScanner {
61
62         SubstitutionVisitor parent = null;
63
64         public InheritanceSustitutionScanner(SubstitutionVisitor parent) {
65             this.parent = parent;
66         }
67
68         /**
69          * Replaces method parameters when defined as a list of
70          * {@link CtParameter}.
71          */

72         @Override JavaDoc
73         public <R> void scanCtExecutable(CtExecutable<R> e) {
74             // replace method parameters
75
for (CtParameter<?> parameter : new ArrayList JavaDoc<CtParameter>(e
76                     .getParameters())) {
77                 String JavaDoc name = parameter.getSimpleName();
78                 for (String JavaDoc pname : parameterNames) {
79                     if (name.equals(pname)) {
80                         Object JavaDoc value = Parameters.getValue(template, pname,
81                                 null);
82                         int i = parameter.getParent().getParameters().indexOf(
83                                 parameter);
84                         if (value instanceof List JavaDoc) {
85                             List JavaDoc l = (List JavaDoc) value;
86                             for (Object JavaDoc p : l) {
87                                 CtParameter<?> p2 = e.getFactory().Core()
88                                         .clone((CtParameter) p);
89                                 p2.setParent(parameter.getParent());
90                                 parameter.getParent().getParameters().add(i++,
91                                         p2);
92                             }
93                             parameter.getParent().getParameters().remove(
94                                     parameter);
95                         }
96                     }
97                 }
98             }
99             super.scanCtExecutable(e);
100         }
101
102         /**
103          * Remove template-specific {@link Local} annotations.
104          */

105         @Override JavaDoc
106         public void scanCtElement(CtElement e) {
107             CtAnnotation a = e.getAnnotation(e.getFactory().Type()
108                     .createReference(Local.class));
109             if (a != null)
110                 e.getAnnotations().remove(a);
111             super.scanCtElement(e);
112         }
113
114         /**
115          * Replaces parameters in element names (even if detected as a
116          * substring).
117          */

118         @Override JavaDoc
119         public void scanCtNamedElement(CtNamedElement element) {
120             if (element.getDocComment() != null) {
121                 element.setDocComment(substituteInDocComment(element
122                         .getDocComment()));
123             }
124             // replace parameters in names
125
String JavaDoc name = element.getSimpleName();
126             for (String JavaDoc pname : parameterNames) {
127                 if (name.contains(pname)) {
128                     Object JavaDoc value = Parameters.getValue(template, pname, null);
129                     if (value instanceof String JavaDoc) {
130                         // replace with the string value
131
name = name.replace(pname, (String JavaDoc) value);
132                         element.setSimpleName(name);
133                     } else if ((value instanceof CtTypeReference)
134                             && (element instanceof CtSimpleType)) {
135                         // replace with the type reference's name
136
name = name.replace(pname, ((CtTypeReference) value)
137                                 .getSimpleName());
138                         element.setSimpleName(name);
139                     }
140                 }
141             }
142             super.scanCtNamedElement(element);
143         }
144
145         private String JavaDoc substituteInDocComment(String JavaDoc docComment) {
146             String JavaDoc result = docComment;
147             for (String JavaDoc pname : parameterNames) {
148                 Object JavaDoc value = Parameters.getValue(template, pname, null);
149                 if (value instanceof String JavaDoc) {
150                     result = result.replace(pname, (String JavaDoc) value);
151                 }
152             }
153             return result;
154         }
155
156         /**
157          * Removes all the elements that are part of the template definitions.
158          *
159          * @see Template
160          * @see TemplateParameter
161          * @see Local
162          * @see Parameter
163          */

164         @Override JavaDoc
165         public <T> void visitCtClass(CtClass<T> ctClass) {
166             ctClass.getSuperInterfaces().remove(
167                     f.Type().createReference(Template.class));
168             for (CtMethod m : new TreeSet JavaDoc<CtMethod>(ctClass.getMethods())) {
169                 if (m.getAnnotation(Local.class) != null) {
170                     ctClass.getMethods().remove(m);
171                 }
172             }
173             for (CtConstructor c : new TreeSet JavaDoc<CtConstructor>(ctClass
174                     .getConstructors())) {
175                 if (c.getAnnotation(Local.class) != null) {
176                     ctClass.getConstructors().remove(c);
177                 }
178             }
179             for (CtField<?> field : new TreeSet JavaDoc<CtField>(ctClass.getFields())) {
180                 if (field.getAnnotation(Local.class) != null
181                         || Parameters.isParameterSource(field.getReference())) {
182                     ctClass.getFields().remove(field);
183                     continue;
184                 }
185                 // replace fields parameters
186
String JavaDoc name = field.getSimpleName();
187                 for (String JavaDoc pname : parameterNames) {
188                     if (name.equals(pname)) {
189                         Object JavaDoc value = Parameters.getValue(template, pname,
190                                 null);
191                         int i = ctClass.getFields().indexOf(field);
192                         if (value instanceof List JavaDoc) {
193                             List JavaDoc l = (List JavaDoc) value;
194                             for (Object JavaDoc f : l) {
195                                 CtField<?> f2 = ctClass.getFactory().Core()
196                                         .clone((CtField) f);
197                                 f2.setParent(ctClass);
198                                 ctClass.getFields().add(i++, f2);
199                             }
200                             ctClass.getFields().remove(field);
201                         }
202                     }
203                 }
204             }
205             super.visitCtClass(ctClass);
206         }
207
208         /**
209          * Replaces direct field parameter accesses.
210          */

211         @SuppressWarnings JavaDoc("unchecked")
212         @Override JavaDoc
213         public <T> void visitCtFieldAccess(CtFieldAccess<T> fieldAccess) {
214             CtFieldReference<?> ref = fieldAccess.getVariable();
215             if (Parameters.isParameterSource(ref)) {
216                 // replace direct field parameter accesses
217
Object JavaDoc value = Parameters.getValue(template, ref
218                         .getSimpleName(), Parameters.getIndex(fieldAccess));
219                 CtElement toReplace = fieldAccess;
220                 if (fieldAccess.getParent() instanceof CtArrayAccess) {
221                     toReplace = fieldAccess.getParent();
222                 }
223                 if (!(value instanceof TemplateParameter)) {
224                     if (value instanceof Class JavaDoc) {
225                         toReplace.replace(f.Code().createClassAccess(
226                                 f.Type().createReference(
227                                         ((Class JavaDoc) value).getName())));
228                     } else if (value instanceof Enum JavaDoc) {
229                         CtTypeReference<?> enumType = f.Type().createReference(
230                                 value.getClass());
231                         toReplace.replace(f.Code().createVariableAccess(
232                                 f.Field().createReference(enumType, enumType,
233                                         ((Enum JavaDoc) value).name()), true));
234                     } else if (value instanceof List JavaDoc) {
235                         // replace list of CtParameter for generic access to the
236
// parameters
237
List JavaDoc<CtParameter<?>> l = (List JavaDoc<CtParameter<?>>) value;
238                         List JavaDoc<CtExpression<?>> vas = f.Code()
239                                 .createVariableAccesses(l);
240                         CtAbstractInvocation<?> inv = (CtAbstractInvocation<?>) fieldAccess
241                                 .getParent();
242                         int i = inv.getArguments().indexOf(fieldAccess);
243                         inv.getArguments().remove(i);
244                         inv.getExecutable().getParameterTypes().remove(i);
245                         for (CtExpression<?> va : vas) {
246                             va.setParent(fieldAccess.getParent());
247                             inv.getArguments().add(i, va);
248                             inv.getExecutable().getParameterTypes().add(i,
249                                     va.getType());
250                             i++;
251                         }
252                     } else if (value!=null && value.getClass().isArray()) {
253                         toReplace.replace(f.Code().createLiteralArray((Object JavaDoc[])value));
254                     } else {
255                         toReplace.replace(f.Code().createLiteral(value));
256                     }
257                 } else {
258                     toReplace.replace(((TemplateParameter<?>) value)
259                             .getSubstitution(targetType));
260                 }
261                 // do not visit if replaced
262
throw new SkipException(fieldAccess);
263             } else {
264                 super.visitCtFieldAccess(fieldAccess);
265             }
266
267         }
268
269         /**
270          * Replaces _xx_.S().
271          */

272         @Override JavaDoc
273         public <T> void visitCtInvocation(CtInvocation<T> invocation) {
274             if (invocation.getExecutable().isOverloading(S)) {
275                 CtFieldAccess<?> fa = null;
276                 if ((invocation.getTarget() instanceof CtFieldAccess)) {
277                     fa = (CtFieldAccess<?>) invocation.getTarget();
278                 }
279                 if ((invocation.getTarget() instanceof CtArrayAccess && ((CtArrayAccess<?, CtExpression>) invocation
280                         .getTarget()).getTarget() instanceof CtFieldAccess)) {
281                     fa = (CtFieldAccess<?>) ((CtArrayAccess<?, CtExpression>) invocation
282                             .getTarget()).getTarget();
283                 }
284                 if (fa != null && fa.getTarget() == null) {
285                     TemplateParameter<?> tparamValue = (TemplateParameter) Parameters
286                             .getValue(template, fa.getVariable()
287                                     .getSimpleName(), Parameters.getIndex(fa));
288                     CtCodeElement r = null;
289                     if (tparamValue != null) {
290                         r = tparamValue.getSubstitution(targetType);
291                         // substitute in the replacement (for fixing type
292
// references
293
// and
294
// for recursive substitution)
295
r.accept(parent);
296                     }
297                     if ((invocation.getParent() instanceof CtReturn)
298                             && (r instanceof CtBlock)) {
299                         // block template parameters in returns should
300
// replace
301
// the return
302
invocation.getParent().replace(r);
303                     } else {
304                         invocation.replace(r);
305                     }
306                 }
307                 // do not visit the invocation if replaced
308
throw new SkipException(invocation);
309             } else {
310                 super.visitCtInvocation(invocation);
311             }
312
313         }
314
315         @SuppressWarnings JavaDoc("unchecked")
316         @Override JavaDoc
317         public <T> void scanCtExpression(CtExpression<T> expression) {
318             for (int i = 0; i < expression.getTypeCasts().size(); i++) {
319                 CtTypeReference<T> t = (CtTypeReference<T>) expression
320                         .getTypeCasts().get(i);
321                 if (parameterNames.contains(t.getSimpleName())) {
322                     // replace type parameters
323
// TODO: this would probably not work with inner classes!!!
324
Object JavaDoc o = Parameters.getValue(template, t.getSimpleName(),
325                             null);
326                     if (o instanceof Class JavaDoc) {
327                         t = f.Type().createReference(((Class JavaDoc<T>) o));
328                     } else if (o instanceof CtTypeReference) {
329                         t = (CtTypeReference<T>) o;
330                         expression.getTypeCasts().set(i, t);
331                     } else {
332                         throw new RuntimeException JavaDoc(
333                                 "unsupported reference substitution");
334                     }
335                 }
336             }
337             if (expression instanceof CtLiteral) {
338                 CtLiteral lit = (CtLiteral) expression;
339                 if (lit.getValue() instanceof CtTypeReference) {
340                     CtTypeReference t = (CtTypeReference) lit.getValue();
341                     if (parameterNames.contains(t.getSimpleName())) {
342                         // replace type parameters
343
// TODO: this would probably not work with inner
344
// classes!!!
345
Object JavaDoc o = Parameters.getValue(template, t
346                                 .getSimpleName(), null);
347                         if (o instanceof Class JavaDoc) {
348                             t = f.Type().createReference(((Class JavaDoc<T>) o));
349                         } else if (o instanceof CtTypeReference) {
350                             t = (CtTypeReference<T>) o;
351                             lit.setValue(t);
352                         } else {
353                             throw new RuntimeException JavaDoc(
354                                     "unsupported reference substitution");
355                         }
356                     }
357                 }
358             }
359             super.scanCtExpression(expression);
360         }
361
362         @SuppressWarnings JavaDoc("unchecked")
363         @Override JavaDoc
364         public <T> void scanCtTypedElement(CtTypedElement<T> e) {
365             if (e.getType() != null
366                     && (!(e.getType() instanceof CtArrayTypeReference))
367                     && parameterNames.contains(e.getType().getSimpleName())) {
368                 // replace type parameters
369
// TODO: this would probably not work with inner classes!!!
370
CtTypeReference<T> t;
371                 Object JavaDoc o = Parameters.getValue(template, e.getType()
372                         .getSimpleName(), null);
373                 if (o instanceof Class JavaDoc) {
374                     t = f.Type().createReference(((Class JavaDoc<T>) o));
375                 } else if (o instanceof CtTypeReference) {
376                     t = (CtTypeReference<T>) o;
377                     e.setType(t);
378                 } else {
379                     throw new RuntimeException JavaDoc(
380                             "unsupported reference substitution");
381                 }
382             }
383             super.scanCtTypedElement(e);
384         }
385
386         // fixes the references to executables in templates
387
public <T> void visitCtExecutableReference(
388                 CtExecutableReference<T> reference) {
389             scanCtReference(reference);
390             visitCtTypeReference(reference.getDeclaringType());
391             scanCtGenericElementReference(reference);
392         }
393
394         /**
395          * Replaces type parameters and references to the template type with
396          * references to the target type (only if the referenced element exists
397          * in the target).
398          */

399         @Override JavaDoc
400         public <T> void visitCtTypeReference(CtTypeReference<T> reference) {
401             // if (reference.equals(templateRef) || (!reference.isPrimitif() &&
402
// f.Type().createReference(Template.class).isAssignableFrom(reference)
403
// && reference.isAssignableFrom(templateRef))) {
404
if (reference.equals(templateRef)) {
405                 // replace references to the template type with references
406
// to the targetType (only if the referenced element exists
407
// in the target)
408
reference.setDeclaringType(targetRef.getDeclaringType());
409                 reference.setPackage(targetRef.getPackage());
410                 reference.setSimpleName(targetRef.getSimpleName());
411             }
412             if (parameterNames.contains(reference.getSimpleName())) {
413                 // replace type parameters
414
// TODO: this would probably not work with inner classes!!!
415
CtTypeReference<?> t;
416                 Object JavaDoc o = Parameters.getValue(template, reference
417                         .getSimpleName(), null);
418                 if (o instanceof Class JavaDoc) {
419                     t = f.Type().createReference(((Class JavaDoc<?>) o));
420                 } else if (o instanceof CtTypeReference) {
421                     t = (CtTypeReference<?>) o;
422                     reference
423                             .setActualTypeArguments(t.getActualTypeArguments());
424                 } else {
425                     throw new RuntimeException JavaDoc(
426                             "unsupported reference substitution");
427                 }
428                 reference.setPackage(t.getPackage());
429                 reference.setSimpleName(t.getSimpleName());
430                 reference.setDeclaringType(t.getDeclaringType());
431             }
432             super.visitCtTypeReference(reference);
433         }
434
435         @SuppressWarnings JavaDoc("unchecked")
436         @Override JavaDoc
437         public <T> void visitCtVariableAccess(CtVariableAccess<T> variableAccess) {
438             String JavaDoc name = variableAccess.getVariable().getSimpleName();
439             for (String JavaDoc pname : parameterNames) {
440                 if (name.contains(pname)) {
441                     Object JavaDoc value = Parameters.getValue(template, pname, null);
442                     if (value instanceof List JavaDoc && name.equals(pname)) {
443                         // replace list of CtParameter for generic access to the
444
// parameters
445
List JavaDoc<CtParameter<?>> l = (List JavaDoc<CtParameter<?>>) value;
446                         List JavaDoc<CtExpression<?>> vas = f.Code()
447                                 .createVariableAccesses(l);
448                         CtAbstractInvocation<?> inv = (CtAbstractInvocation<?>) variableAccess
449                                 .getParent();
450                         int i = inv.getArguments().indexOf(variableAccess);
451                         inv.getArguments().remove(i);
452                         inv.getExecutable().getParameterTypes().remove(i);
453                         for (CtExpression<?> va : vas) {
454                             va.setParent(variableAccess.getParent());
455                             inv.getArguments().add(i, va);
456                             inv.getExecutable().getParameterTypes().add(i,
457                                     va.getType());
458                             i++;
459                         }
460                         // inv.getArguments().remove(variableAccess);
461
throw new SkipException(variableAccess);
462                     } else {
463                         // replace variable accesses names
464
if (value instanceof String JavaDoc) {
465                             name = name.replace(pname, (String JavaDoc) value);
466                             variableAccess.getVariable().setSimpleName(name);
467                         }
468                     }
469                 }
470             }
471             CtTypeReference<T> reference = variableAccess.getType();
472             if (parameterNames != null && reference != null
473                     && parameterNames.contains(reference.getSimpleName())) {
474                 CtTypeReference<T> t;
475                 Object JavaDoc o = Parameters.getValue(template, reference
476                         .getSimpleName(), null);
477                 if (o instanceof Class JavaDoc) {
478                     t = f.Type().createReference(((Class JavaDoc<T>) o));
479                 } else if (o instanceof CtTypeReference) {
480                     t = (CtTypeReference<T>) o;
481                     reference
482                             .setActualTypeArguments(t.getActualTypeArguments());
483                 } else {
484                     throw new RuntimeException JavaDoc(
485                             "unsupported reference substitution");
486                 }
487                 variableAccess.setType(t);
488             }
489             super.visitCtVariableAccess(variableAccess);
490         }
491
492     }
493
494     Factory f;
495
496     InheritanceSustitutionScanner inheritanceScanner;
497
498     CtExecutableReference S;
499
500     CtTypeReference<?> targetRef;
501
502     CtSimpleType<?> targetType;
503
504     Template template;
505
506     CtTypeReference<? extends Template> templateRef;
507
508     CtClass<? extends Template> templateType;
509
510     Collection JavaDoc<String JavaDoc> parameterNames;
511
512     /**
513      * Creates a new substitution visitor.
514      *
515      * @param f
516      * the factory
517      * @param targetType
518      * the target type of the substitution
519      * @param template
520      * the template that holds the parameter values
521      */

522     public SubstitutionVisitor(Factory f, CtSimpleType<?> targetType,
523             Template template) {
524         inheritanceScanner = new InheritanceSustitutionScanner(this);
525         this.f = f;
526         this.template = template;
527         this.targetType = targetType;
528         this.S = f.Executable().createReference(
529                 f.Type().createReference(TemplateParameter.class),
530                 f.Type().createTypeParameterReference("T"), "S");
531         this.templateRef = f.Type().createReference(template.getClass());
532         this.templateType = f.Template().get(
533                 templateRef.getQualifiedName());
534         this.parameterNames = Parameters.getNames(templateType);
535         this.targetRef = f.Type().createReference(targetType);
536         // substitute target ref
537
this.targetRef.accept(this);
538     }
539
540     /**
541      * Override to scan on collection copies and avoid potential concurrent
542      * modification exceptions.
543      */

544     @Override JavaDoc
545     public void scan(Collection JavaDoc<? extends CtElement> elements) {
546         super.scan(new ArrayList JavaDoc<CtElement>(elements));
547     }
548
549     @Override JavaDoc
550     public void scan(CtElement element) {
551         try {
552             inheritanceScanner.scan(element);
553             super.scan(element);
554         } catch (SkipException e) {
555             // System.out.println(e.getMessage());
556
} catch (UndefinedParameterException upe) {
557             if (element instanceof CtStatement) {
558                 element.replace(null);
559             } else {
560                 throw upe;
561             }
562         }
563     }
564
565     /**
566      * Replaces parameters in reference names (even if detected as a substring).
567      */

568     @Override JavaDoc
569     public void scan(CtReference reference) {
570         if (reference == null)
571             return;
572         inheritanceScanner.scan(reference);
573         if (!(reference instanceof CtTypeReference)) {
574             // replace parameters in reference names
575
String JavaDoc name = reference.getSimpleName();
576             for (String JavaDoc pname : parameterNames) {
577                 if (name.contains(pname)) {
578                     name = name.replace(pname, (String JavaDoc) Parameters.getValue(
579                             template, pname, null));
580                     reference.setSimpleName(name);
581                 }
582             }
583             super.scan(reference);
584         } else {
585             if (!(parameterNames.contains(reference.getSimpleName())
586                     && ((CtTypeReference<?>) reference).getDeclaringType() != null && ((CtTypeReference<?>) reference)
587                     .getDeclaringType().equals(templateRef))) {
588                 super.scan(reference);
589             }
590         }
591     }
592 }
Popular Tags