View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software 
12   * distributed under the License is distributed on an "AS IS" BASIS, 
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
14   * See the License for the specific language governing permissions and 
15   * limitations under the License.
16   */
17  
18  package org.apache.jdo.impl.enhancer.core;
19  
20  import java.io.PrintStream;
21  import java.io.ByteArrayOutputStream;
22  
23  import org.apache.jdo.impl.enhancer.classfile.ClassAttribute;
24  import org.apache.jdo.impl.enhancer.classfile.ClassFile;
25  import org.apache.jdo.impl.enhancer.classfile.GenericAttribute;
26  import org.apache.jdo.impl.enhancer.util.Support;
27  
28  
29  
30  
31  /***
32   * Controls the enhancement of a class.
33   */
34  public final class Controller
35      extends Support
36      implements EnhancerConstants
37  {
38      /***
39       * Repository for enhancer options.
40       */
41      private final Environment env;
42  
43      /***
44       * The classfile to be enhanced.
45       */
46      private final ClassFile classFile;
47  
48      /***
49       * The class name in user ('.' delimited) form.
50       */
51      private final String userClassName;
52  
53      /***
54       * The analyzer for this class.
55       */
56      private final Analyzer analyzer;
57  
58      /***
59       * The augmentation controller for this class.
60       */
61      private final Augmenter augmenter;
62  
63      /***
64       * The method annotation controller for this class.
65       */
66      private final Annotater annotater;
67  
68      /***
69       * If true, this class is believed to have been modified in some way.
70       */
71      private boolean classUpdated = false;
72  
73      /***
74       * Constructor.
75       */
76      public Controller(ClassFile classFile,
77                        Environment env)
78      {
79          affirm(classFile != null);
80          affirm(env != null);
81  
82          this.classFile = classFile;
83          this.userClassName = classFile.userClassName();
84          this.env = env;
85          this.analyzer = new Analyzer(this, env);
86          this.augmenter = new Augmenter(this, analyzer, env);
87          this.annotater = new Annotater(this, analyzer, env);
88  
89          affirm(userClassName != null);
90          affirm(analyzer != null);
91          affirm(augmenter != null);
92          affirm(annotater != null);
93      }
94  
95      // ------------------------------------------------------------
96  
97      /***
98       * Returns the class file which we are operating on.
99       */
100     public ClassFile getClassFile()
101     {
102         return classFile;
103     }
104 
105     /***
106      * Returns true if the classfile has been updated.
107      */
108     public boolean updated()
109     {
110         return classUpdated;
111     }
112 
113     /***
114      * Records a modification of the class.
115      */
116     void noteUpdate()
117     {
118         classUpdated = true;
119     }
120 
121     // ------------------------------------------------------------
122 
123     /***
124      * Determines what modifications are needed and perform them.
125      */
126     public void enhanceClass()
127     {
128         try{
129             if (env.doTimingStatistics()) {
130                 Support.timer.push("Controller.enhanceClass()");
131             }
132 
133             // examine classes
134             scan();
135 
136             if (env.errorCount() > 0)
137                 return;
138 
139             // augment class
140             augment();
141 
142             if (env.errorCount() > 0)
143                 return;
144 
145             // annotate class
146             annotate();                
147 
148             if (env.errorCount() > 0)
149                 return;
150 
151             update();
152         } finally {
153             if (env.doTimingStatistics()) {
154                 Support.timer.pop();
155             }
156         }
157     }
158 
159     // ------------------------------------------------------------
160 
161     /***
162      * Notes the class characteristics.
163      */
164     private void scan()
165     {
166         if (analyzer.isAnalyzed()) {
167             return;
168         }
169 
170         try {
171             if (env.doTimingStatistics()) {
172                 Support.timer.push("Controller.scan()");
173             }
174 
175             if (env.dumpClass()) {
176                 dumpClass();
177             }
178 
179             analyzer.scan();
180         } finally {
181             if (env.doTimingStatistics()) {
182                 Support.timer.pop();
183             }
184         }
185     }
186 
187     /***
188      * Performs necessary augmentation actions on the class.
189      */
190     private void augment()
191     {
192         if (!analyzer.isAugmentable() || env.noAugment()) {
193             return;
194         }
195 
196         try{
197             if (env.doTimingStatistics()) {
198                 Support.timer.push("Controller.augment()");
199             }
200             augmenter.augment();
201 
202             if (env.dumpClass()) {
203                 dumpClass();
204             }
205         } finally {
206             if (env.doTimingStatistics()) {
207                 Support.timer.pop();
208             }
209         }
210     }
211 
212     /***
213      * Performs necessary annotation actions on the class.
214      */
215     private void annotate()
216     {
217         if (!analyzer.isAnnotateable() || env.noAnnotate()) {
218             return;
219         }
220 
221         try{
222             if (env.doTimingStatistics()) {
223                 Support.timer.push("Controller.annotate()");
224             }
225             annotater.annotate();
226 
227             if (env.dumpClass()) {
228                 dumpClass();
229             }
230         } finally {
231             if (env.doTimingStatistics()) {
232                 Support.timer.pop();
233             }
234         }
235     }
236 
237     /***
238      * Marks the class being enhanced.
239      */
240     private void update()
241     {
242         if (!classUpdated) {
243             return;
244         }
245     
246         affirm((analyzer.isAugmentable() && !env.noAugment())
247                || (analyzer.isAnnotateable() && !env.noAnnotate()));
248 
249         //^olsen: move non-modifying code to Analyzer
250         final byte[] data = new byte[2];
251         data[0] = (byte)(SUNJDO_PC_EnhancedVersion >>> 8);
252         data[1] = (byte)(SUNJDO_PC_EnhancedVersion & 0xff);
253         final ClassAttribute annotatedAttr
254             = new GenericAttribute(
255                 classFile.pool().addUtf8(SUNJDO_PC_EnhancedAttribute),
256                 data);
257         classFile.attributes().addElement(annotatedAttr);
258     }
259     
260     /***
261      * Dumps a class' signature and byte-code (for debugging).
262      */
263     private void dumpClass()
264     {
265         final ByteArrayOutputStream bs = new ByteArrayOutputStream();
266         final PrintStream ps = new PrintStream(bs);
267         env.messageNL("dumping class " + userClassName + " {");
268         classFile.print(ps);
269         env.getOutputWriter().println(bs.toString());
270         env.messageNL("} // end of class " + userClassName);
271     }
272 }