KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > aop > instrument > FieldAccessTransformer


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.aop.instrument;
23
24 import java.util.Collection JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import org.jboss.aop.AspectManager;
28 import org.jboss.aop.ClassAdvisor;
29 import org.jboss.aop.classpool.AOPClassPool;
30 import org.jboss.aop.util.Advisable;
31
32 import javassist.CannotCompileException;
33 import javassist.ClassPool;
34 import javassist.CodeConverter;
35 import javassist.CtClass;
36 import javassist.CtField;
37 import javassist.CtMethod;
38 import javassist.CtNewMethod;
39 import javassist.Modifier;
40 import javassist.NotFoundException;
41 import javassist.expr.ExprEditor;
42 import javassist.expr.FieldAccess;
43
44 /**
45  * @author <a HREF="mailto:kabirkhan@bigfoot.com">Kabir Khan</a>
46  */

47 public abstract class FieldAccessTransformer implements CodeConversionObserver
48 {
49    // Constants -----------------------------------------------------
50

51    final static String JavaDoc FIELD_INFO_CLASS_NAME = "org.jboss.aop.FieldInfo";
52    
53    // Attributes ----------------------------------------------------
54
Instrumentor instrumentor;
55    boolean optimize;
56    private Codifier codifier;
57    private JoinpointClassifier classifier;
58    
59    // Static --------------------------------------------------------
60
// there are two transformations types available to a field: get and set
61
protected static final String JavaDoc[] transformations = new String JavaDoc[] {"get", "set"};
62    protected static final int GET_INDEX = 0;
63    protected static final int SET_INDEX = 1;
64    protected static final WrapperTransformer wrapper = new WrapperTransformer(transformations);
65    
66    // Constructors --------------------------------------------------
67
protected FieldAccessTransformer(Instrumentor instrumentor)
68    {
69       this.instrumentor = instrumentor;
70       this.optimize = AspectManager.optimize;
71       this.codifier = new Codifier();
72       this.classifier = instrumentor.joinpointClassifier;
73    }
74
75    // Public --------------------------------------------------------
76

77    protected void buildFieldWrappers(CtClass clazz, ClassAdvisor advisor) throws NotFoundException, CannotCompileException
78    {
79       List JavaDoc fields = Instrumentor.getAdvisableFields(clazz);
80
81       int fieldIndex = fieldOffset(clazz.getSuperclass());
82       JoinpointClassification[] classificationGet = classifyFieldGet(clazz, advisor);
83       JoinpointClassification[] classificationSet = classifyFieldSet(clazz, advisor);
84       Iterator JavaDoc it = fields.iterator();
85       boolean skipFieldInterception = true;
86       for (int index = 0; it.hasNext(); index++, fieldIndex++)
87       {
88          CtField field = (CtField) it.next();
89
90          if (!isPrepared(classificationGet[index]) && !isPrepared(classificationSet[index]))
91          {
92             continue;
93          }
94          
95          if (!javassist.Modifier.isPrivate(field.getModifiers()))
96          {
97             skipFieldInterception = false;
98          }
99          
100          doBuildFieldWrappers(clazz, field, fieldIndex, classificationGet[index], classificationSet[index]);
101       }
102       
103       if (skipFieldInterception)
104       {
105          advisor.getManager().skipFieldAccess(clazz.getName());
106       }
107       else
108       {
109          advisor.getManager().addFieldInterceptionMarker(clazz.getName());
110       }
111          
112    }
113    
114    protected boolean isPrepared(JoinpointClassification classification)
115    {
116       return classification != JoinpointClassification.NOT_INSTRUMENTED;
117    }
118    
119    protected abstract void doBuildFieldWrappers(CtClass clazz, CtField field, int index, JoinpointClassification classificationGet, JoinpointClassification classificationSet) throws NotFoundException, CannotCompileException;
120    /**
121     * replace field access for possible public/protected fields that are intercepted
122     * don't need to replace access for private fields.
123     *
124     * @param clazz
125     * @param fieldsAdvisor
126     * @return
127     * @throws NotFoundException
128     */

129    public boolean replaceFieldAccess(List JavaDoc fields, CtClass clazz, ClassAdvisor fieldsAdvisor) throws NotFoundException
130    {
131       CodeConverter converter = instrumentor.getCodeConverter();
132       boolean converted = false;
133       Iterator JavaDoc it = fields.iterator();
134       while (it.hasNext())
135       {
136          CtField field = (CtField) it.next();
137          if (!Modifier.isPrivate(field.getModifiers()) && Advisable.isAdvisable(field))
138          {
139             JoinpointClassification fieldGetClassification = classifier.classifyFieldGet(field, fieldsAdvisor);
140
141             if (fieldGetClassification.equals(JoinpointClassification.WRAPPED))
142             {
143                converted = true;
144                converter.replaceFieldRead(field, clazz, fieldRead(field.getName()));
145             }
146             JoinpointClassification fieldSetClassification = classifier.classifyFieldSet(field, fieldsAdvisor);
147             if (fieldSetClassification.equals(JoinpointClassification.WRAPPED))
148             {
149                converted = true;
150                converter.replaceFieldWrite(field, clazz, fieldWrite(field.getName()));
151             }
152          }
153       }
154       return converted;
155    }
156
157    /**
158     * Wraps the field joinpoints contained in <code>fieldsGet</code> and <code>fieldsSet</code>.
159     * @param clazz the class being instrumented.
160     * @param fieldsGet a collection of <code>java.lang.Integer</code> indentifying
161     * the field reads to be wrapped.
162     * @param fieldsSet a collection of <code>java.lang.Integer</code> indentifying
163     * the field writes to be wrapped.
164     */

165    public void wrap(CtClass clazz, Collection JavaDoc fieldsGet, Collection JavaDoc fieldsSet) throws CannotCompileException, NotFoundException
166    {
167       List JavaDoc advisableFields = Instrumentor.getAdvisableFields(clazz);
168       CtField[] fields = new CtField[advisableFields.size()];
169       fields = (CtField[] ) advisableFields.toArray(fields);
170       for (Iterator JavaDoc iterator = fieldsGet.iterator(); iterator.hasNext(); )
171       {
172          int fieldIndex = ((Integer JavaDoc) iterator.next()).intValue();
173          CtField field = fields[fieldIndex];
174          if (wrapper.isNotPrepared(field, GET_INDEX))
175          {
176             continue;
177          }
178          // wrap
179
wrapper.wrap(field, GET_INDEX);
180          // execute wrapping
181
String JavaDoc code = "{" + field.getType().getName() + " var; return var;}";
182          CtMethod method = getWrapperReadMethod(field, clazz);
183          method.setBody(code);
184          code = getWrapperBody(clazz, field, true, fieldIndex);
185          if (!Modifier.isPrivate(field.getModifiers()))
186          {
187             instrumentor.converter.replaceFieldRead(field, clazz, fieldRead(field.getName()));
188             codifier.addPendingCode(method, code);
189          }
190          else
191          {
192             replaceFieldAccessInternally(clazz, field, true, false, fieldIndex);
193             method.setBody(code);
194          }
195       }
196       for (Iterator JavaDoc iterator = fieldsSet.iterator(); iterator.hasNext(); )
197       {
198          int fieldIndex = ((Integer JavaDoc) iterator.next()).intValue();
199          CtField field = fields[fieldIndex];
200          if (wrapper.isNotPrepared(field, SET_INDEX))
201          {
202             continue;
203          }
204          // wrap
205
wrapper.wrap(field, SET_INDEX);
206          // executeWrapping
207
String JavaDoc code = "{ }";
208          CtMethod method = getWrapperWriteMethod(field, clazz);
209          method.setBody(code);
210          code = getWrapperBody(clazz, field, false, fieldIndex);
211          if (!Modifier.isPrivate(field.getModifiers()))
212          {
213             instrumentor.converter.replaceFieldWrite(field, clazz, fieldWrite(field.getName()));
214             codifier.addPendingCode(method, code);
215          }
216          else
217          {
218             replaceFieldAccessInternally(clazz, field, false, true, fieldIndex);
219             method.setBody(code);
220          }
221       }
222    }
223
224    
225    protected CtMethod getWrapperReadMethod(CtField field, CtClass clazz)throws NotFoundException
226    {
227       return clazz.getDeclaredMethod(fieldRead(field.getName()));
228    }
229    
230    protected CtMethod getWrapperWriteMethod(CtField field, CtClass clazz)throws NotFoundException
231    {
232       return clazz.getDeclaredMethod(fieldWrite(field.getName()));
233    }
234    
235    /**
236     * Unwraps the field joinpoints contained in <code>fieldsGet</code> and <code>fieldsSet</code>.
237     * @param clazz the class being instrumented.
238     * @param fieldsGet a collection of <code>java.lang.Integer</code> indentifying
239     * the field reads to be unwrapped.
240     * @param fieldsSet a collection of <code>java.lang.Integer</code> indentifying
241     * the field writes to be unwrapped.
242     */

243    public void unwrap(CtClass clazz, Collection JavaDoc fieldsGet, Collection JavaDoc fieldsSet) throws CannotCompileException, NotFoundException
244    {
245       ClassPool classPool = instrumentor.getClassPool();
246       List JavaDoc advisableFields = instrumentor.getAdvisableFields(clazz);
247       CtField[] fields = new CtField[advisableFields.size()];
248       fields = (CtField[] ) advisableFields.toArray(fields);
249       // unwrapping field gets
250
for (Iterator JavaDoc iterator = fieldsGet.iterator(); iterator.hasNext(); )
251       {
252          int fieldIndex = ((Integer JavaDoc) iterator.next()).intValue();
253          CtField field = fields[fieldIndex];
254          if (wrapper.isNotPrepared(field, GET_INDEX))
255          {
256             continue;
257          }
258          // unwrap
259
wrapper.unwrap(field, GET_INDEX);
260          // execute unwrapping
261
CtMethod method = clazz.getDeclaredMethod(fieldRead(field.getName()));
262          String JavaDoc target = Modifier.isStatic(field.getModifiers())? clazz.getName(): "((" + clazz.getName() + ")$1)";
263          method.setBody("return " + target + "." + field.getName() + ";");
264       }
265       
266       for (Iterator JavaDoc iterator = fieldsSet.iterator(); iterator.hasNext(); )
267       {
268          int fieldIndex = ((Integer JavaDoc) iterator.next()).intValue();
269          CtField field = fields[fieldIndex];
270          if (wrapper.isNotPrepared(field, SET_INDEX))
271          {
272             continue;
273          }
274          // unwrap
275
wrapper.unwrap(field, SET_INDEX);
276          // execute unwrapping
277
// create field trapWrite memthod
278
CtMethod method = clazz.getDeclaredMethod(fieldWrite(field.getName()));
279          String JavaDoc target = Modifier.isStatic(field.getModifiers())? clazz.getName(): "((" + clazz.getName() + ")$1)";
280          method.setBody(target + "." + field.getName() + "=$2" + ";");
281       }
282    }
283    
284    /**
285     * Notifies this transformer that the code conversion is done.
286     */

287    public void codeConverted() throws NotFoundException, CannotCompileException {
288       codifier.codifyPending();
289    }
290    
291    protected int fieldOffset(CtClass clazz) throws NotFoundException
292    {
293       if (clazz == null)
294          return 0;
295       if (clazz.getName().equals("java.lang.Object"))
296          return 0;
297       int offset = fieldOffset(clazz.getSuperclass());
298
299       CtField[] fields = clazz.getDeclaredFields();
300       for (int i = 0; i < fields.length; i++)
301       {
302          if (Advisable.isAdvisable(fields[i]))
303          {
304             offset++;
305          }
306       }
307       return offset;
308    }
309
310    /**
311     * Classifies the field read joinpoints.
312     * @param clazz the clazz whose field reads will be classified.
313     * @param advisor the advisor associated to <code>clazz</code>.
314     * @return a classification array.
315     */

316    private JoinpointClassification[] classifyFieldGet(CtClass clazz, ClassAdvisor advisor) throws NotFoundException
317    {
318       List JavaDoc fields = instrumentor.getAdvisableFields(clazz);
319       JoinpointClassification[] classification = new JoinpointClassification[fields.size()];
320       int index = 0;
321       for (Iterator JavaDoc iterator = fields.iterator(); iterator.hasNext(); index++)
322       {
323          CtField field = (CtField) iterator.next();
324          classification[index] = instrumentor.joinpointClassifier.classifyFieldGet(field, advisor);
325       }
326       return classification;
327    }
328
329    /**
330     * Classifies the field write joinpoints.
331     * @param clazz the clazz whose field writes will be classified.
332     * @param advisor the advisor associated to <code>clazz</code>.
333     * @return a classification array.
334     */

335    private JoinpointClassification[] classifyFieldSet(CtClass clazz, ClassAdvisor advisor) throws NotFoundException
336    {
337       List JavaDoc fields = instrumentor.getAdvisableFields(clazz);
338       JoinpointClassification[] classification = new JoinpointClassification[fields.size()];
339       int index = 0;
340       for (Iterator JavaDoc iterator = fields.iterator(); iterator.hasNext(); index++)
341       {
342          CtField field = (CtField) iterator.next();
343          classification[index] = instrumentor.joinpointClassifier.classifyFieldSet(field, advisor);
344       }
345       return classification;
346    }
347
348    protected String JavaDoc addFieldReadInfoFieldWithAccessors(int modifiers, CtClass addTo, CtField field) throws NotFoundException, CannotCompileException
349    {
350       return addFieldReadInfoFieldWithAccessors(modifiers, addTo, field, null);
351    }
352    
353    /**
354     * Adds a FieldInfo field to the passed in class
355     */

356    protected String JavaDoc addFieldReadInfoFieldWithAccessors(int modifiers, CtClass addTo, CtField field, CtField.Initializer init) throws NotFoundException, CannotCompileException
357    {
358       String JavaDoc name = getFieldReadInfoFieldName(field.getName());
359       TransformerCommon.addInfoField(instrumentor, FIELD_INFO_CLASS_NAME, name, modifiers, addTo, addInfoAsWeakReference(), init);
360       return name;
361    }
362    
363    protected String JavaDoc addFieldWriteInfoField(int modifiers, CtClass addTo, CtField field) throws NotFoundException, CannotCompileException
364    {
365       return addFieldWriteInfoField(modifiers, addTo, field, null);
366    }
367    
368    /**
369     * Adds a FieldInfo field to the passed in class
370     */

371    protected String JavaDoc addFieldWriteInfoField(int modifiers, CtClass addTo, CtField field, CtField.Initializer init) throws NotFoundException, CannotCompileException
372    {
373       String JavaDoc name = getFieldWriteInfoFieldName(field.getName());
374       TransformerCommon.addInfoField(instrumentor, FIELD_INFO_CLASS_NAME, name, modifiers, addTo, addInfoAsWeakReference(), init);
375       return name;
376    }
377    
378    protected boolean addInfoAsWeakReference()
379    {
380       return true;
381    }
382
383    public static String JavaDoc getFieldReadInfoFieldName(String JavaDoc fieldName)
384    {
385       return "aop$FieldInfo_r_" + fieldName;
386    }
387
388    public static String JavaDoc getFieldWriteInfoFieldName(String JavaDoc fieldName)
389    {
390       return "aop$FieldInfo_w_" + fieldName;
391    }
392
393    public static String JavaDoc fieldRead(String JavaDoc fieldName)
394    {
395       return fieldName + "_r_" + ClassAdvisor.NOT_TRANSFORMABLE_SUFFIX;
396    }
397
398    public static String JavaDoc fieldWrite(String JavaDoc fieldName)
399    {
400       return fieldName + "_w_" + ClassAdvisor.NOT_TRANSFORMABLE_SUFFIX;
401    }
402
403    protected static String JavaDoc fieldInfoFromWeakReference(String JavaDoc localName, String JavaDoc fieldInfoName)
404    {
405       return TransformerCommon.infoFromWeakReference(FIELD_INFO_CLASS_NAME, localName, fieldInfoName);
406    }
407    
408    protected int getStaticModifiers(CtField field)
409    {
410       int mod = Modifier.STATIC;
411       if ((field.getModifiers() & Modifier.PUBLIC) != 0)
412       {
413          mod |= Modifier.PUBLIC;
414       }
415       else if ((field.getModifiers() & Modifier.PROTECTED) != 0)
416       {
417          mod |= Modifier.PROTECTED;
418       }
419       else if ((field.getModifiers() & Modifier.PRIVATE) != 0)
420       {
421          mod |= Modifier.PRIVATE;
422       }
423       else
424       {
425          mod |= Modifier.PUBLIC;
426       }
427
428       return mod;
429    }
430
431    /**
432     * This function replaces internal field accesses with bytecode hooks into framework
433     * todo this must do it for inherited protected/public fields as well
434     *
435     * @param clazz
436     * @param field
437     * @param doGet
438     * @param doSet
439     * @param index
440     * @throws javassist.CannotCompileException
441     *
442     */

443    protected abstract void replaceFieldAccessInternally(CtClass clazz, CtField field, boolean doGet, boolean doSet, int index) throws CannotCompileException;
444    
445    /**
446     *
447     * Generate the wrapper place holders.
448     * <p> PS: Removed from inside inner classes to avoid code repetition.</p>
449     */

450    protected void buildWrapperPlaceHolders(CtClass clazz, CtField field, boolean doGet, boolean doSet, int mod)
451    throws NotFoundException, CannotCompileException
452    {
453       if (doGet)
454       {
455          buildReadWrapperPlaceHolder(clazz, field, fieldRead(field.getName()), mod);
456       }
457       if (doSet)
458       {
459          buildWriteWrapperPlaceHolder(clazz, field, fieldWrite(field.getName()), mod);
460       }
461    }
462
463    /**
464     * Builds the read wrapper place holder for preparation of <code>field</code> read
465     * joinpoint.
466     * @param clazz the <code>clazz</code> to add wrapper to
467     * @param field the <code>field</code> whose read joinpoint wrapper will be generated.
468     * @param wrapperName The name of the field wrapper to be generated
469     * @param mod the modifiers of the generated wrapper.
470     */

471    protected CtMethod buildReadWrapperPlaceHolder(CtClass clazz, CtField field, String JavaDoc wrapperName, int mod)
472    throws NotFoundException, CannotCompileException
473    {
474       AOPClassPool classPool = (AOPClassPool) instrumentor.getClassPool();
475
476       String JavaDoc name = field.getName();
477       CtClass ftype = field.getType();
478       CtClass[] readParam = new CtClass[]{classPool.get("java.lang.Object")};
479       
480       //Doesn't matter what we put in as long as it compiles,
481
//body will be replaced when the field read gets wrapped
482
String JavaDoc code = "{" + ftype.getName() + " var = ";
483       if (ftype.isPrimitive())
484       {
485          if (ftype == CtClass.booleanType)
486          {
487             code += false;
488          }
489          else if (ftype == CtClass.byteType)
490          {
491             code += "(byte)0";
492          }
493          else if (ftype == CtClass.charType)
494          {
495             code += "(char)0";
496          }
497          else if (ftype == CtClass.doubleType)
498          {
499             code += "0.0";
500          }
501          else if (ftype == CtClass.floatType)
502          {
503             code += "(float)0.0";
504          }
505          else if (ftype == CtClass.intType)
506          {
507             code += "0";
508          }
509          else if (ftype == CtClass.longType)
510          {
511             code += "(long)0";
512          }
513          else if (ftype == CtClass.shortType)
514          {
515             code += "(short)0";
516          }
517       }
518       else
519       {
520          code += "null";
521       }
522       code += "; return var;}";
523       CtMethod rmethod = CtNewMethod.make(ftype, wrapperName, readParam, null, code, clazz);
524       rmethod.setModifiers(mod);
525       clazz.addMethod(rmethod);
526       
527       return rmethod;
528    }
529
530    /**
531     * Builds the write wrapper place holder for preparation of <code>field</code> read
532     * joinpoint.
533     * @param clazz the class the wrapper will be added to
534     * @param field the <code>field</code> whose write joinpoint wrapper will be generated.
535     * @param wrapperName The name of the field wrapper to be generated
536     * @param mod the modifiers of the generated wrapper.
537     */

538    protected CtMethod buildWriteWrapperPlaceHolder(CtClass clazz, CtField field, String JavaDoc wrapperName, int mod)
539    throws NotFoundException, CannotCompileException
540    {
541       AOPClassPool classPool = (AOPClassPool) instrumentor.getClassPool();
542
543       String JavaDoc name = field.getName();
544       CtClass ftype = field.getType();
545
546       // create field trapWrite memthod
547
CtClass[] writeParam = new CtClass[2];
548       writeParam[0] = classPool.get("java.lang.Object");
549       writeParam[1] = ftype;
550
551       //Doesn't matter what we put in as long as it compiles,
552
//body will be replaced when we the field write gets wrapped
553

554       CtMethod wmethod = CtNewMethod.make(CtClass.voidType, wrapperName, writeParam, null, "{}", clazz);
555       wmethod.setModifiers(mod);
556       clazz.addMethod(wmethod);
557       
558       return wmethod;
559    }
560
561    /**
562     * Returns the wrapper body of the <code>field</code> joinpoint.
563     * @param clazz the class declaring <code>field</code>.
564     * @param field the field whose joinpoint wrapper code will be generated.
565     * @param get indicates if the wrapper is a field read wrapper or a field
566     * write wrapper.
567     * @param fieldIndex the index of <code>field</code>.
568     * @return teh wrapper body code.
569     */

570    protected abstract String JavaDoc getWrapperBody(CtClass clazz, CtField field, boolean get, int fieldIndex) throws NotFoundException, CannotCompileException;
571
572    protected abstract class FieldAccessExprEditor extends ExprEditor
573    {
574       CtClass clazz;
575       CtField field;
576       boolean doGet;
577       boolean doSet;
578       int fieldIndex;
579
580       public FieldAccessExprEditor(CtClass clazz, CtField field, boolean doGet, boolean doSet, int index)
581       {
582          this.clazz = clazz;
583          this.field = field;
584          this.doGet = doGet;
585          this.doSet = doSet;
586          this.fieldIndex = index;
587       }
588
589       public void edit(FieldAccess fieldAccess) throws CannotCompileException
590       {
591          if (!fieldAccess.getClassName().equals(clazz.getName())) return;
592          if (!fieldAccess.getFieldName().equals(field.getName())) return;
593          if (calledByInvocationClass(fieldAccess))return;
594
595          if (fieldAccess.isReader() && doGet)
596          {
597             replaceRead(fieldAccess);
598          }
599          if (fieldAccess.isWriter() && doSet)
600          {
601             replaceWrite(fieldAccess);
602          }
603       }
604
605       private boolean calledByInvocationClass(FieldAccess fieldAccess)
606       {
607          try
608          {
609             return isInvocationClass(fieldAccess.where().getDeclaringClass());
610          }
611          catch (RuntimeException JavaDoc e)
612          {
613             //This means it is one of the access$xxx methods,
614
//This occurs when the field access is from one of the invocation classes, which we want to skip
615
return true;
616          }
617       }
618       
619       private boolean isInvocationClass(CtClass superClazz)
620       {
621          try
622          {
623             if (superClazz == null)
624             {
625                return false;
626             }
627             if (superClazz.getName().equals("java.lang.Object"))
628             {
629                return false;
630             }
631             if (superClazz.getName().equals("org.jboss.aop.joinpoint.Invocation"))
632             {
633                return true;
634             }
635             return (isInvocationClass(superClazz.getSuperclass()));
636          }
637          catch (NotFoundException e)
638          {
639             throw new RuntimeException JavaDoc(e.getMessage(), e);
640          }
641       }
642       
643       protected abstract void replaceRead(FieldAccess fieldAccess) throws CannotCompileException;
644       protected abstract void replaceWrite(FieldAccess fieldAccess) throws CannotCompileException;
645    }//End Inner class FieldAccessExprEditor
646

647
648 }
649
Popular Tags