001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.compiler.processor;
020:
021: import java.util.*;
022: import java.text.MessageFormat;
023:
024: import org.apache.beehive.netui.compiler.typesystem.env.CoreAnnotationProcessor;
025: import org.apache.beehive.netui.compiler.typesystem.env.CoreAnnotationProcessorEnv;
026: import org.apache.beehive.netui.compiler.typesystem.declaration.AnnotationTypeDeclaration;
027: import org.apache.beehive.netui.compiler.typesystem.declaration.Declaration;
028: import org.apache.beehive.netui.compiler.typesystem.declaration.ClassDeclaration;
029: import org.apache.beehive.netui.compiler.typesystem.declaration.MethodDeclaration;
030: import org.apache.beehive.netui.compiler.typesystem.declaration.TypeDeclaration;
031: import org.apache.beehive.netui.compiler.Diagnostics;
032: import org.apache.beehive.netui.compiler.FatalCompileTimeException;
033: import org.apache.beehive.netui.compiler.CompilerUtils;
034:
035: /**
036: * The TwoPhaseCoreAnnotationProcessor class is an abstract class that implements the APT
037: * CoreAnnotationProcessor interface. It breaks the work of the process() method of the
038: * CoreAnnotationProcessor down into two distinct phases, represented as abstract method
039: * of TwoPhaseCoreAnnotationProcessor that are to be implemented by concrete subclasses.
040: * <p/>
041: * The two phases of processing are:
042: *
043: * The <b>check</b> phase is used to validate input Declarations that have been
044: * annotated with annotations claimed by the processor to ensure that it
045: * is semantically valid. If the presence of the input Declaration implies the need
046: * to add new files, and those files need to be visible during the check phase for
047: * other Declarations, then the CoreAnnotationProcessorEnv's Filer API should be
048: * used to add those files in this phase. The adding of such files at this point
049: * should typically not result in their emission to persistent storage (i.e. disk),
050: * but rather be kept in memory to be referenced by the check phase of other
051: * Declarations.
052: * The <b>generate</b> phase will actually emit any source, binary, or class files
053: * that are derived from the input Declaration, including files added via the Filer
054: * API during the check phase. The Filer API may also be used in this phase to add
055: * new files, however, such additions will not be visible during the check phase of
056: * any Declarations.
057: * </ul>
058: * <p/>
059: * The benefits of breaking process() down into check() and generate() phases are:
060: *
061: * Makes it possible to perform the semantic validation of Declarations without
062: * necessarily resulting in code generation.
063: * Provides a clearer association between input Declarations and generator output.
064: * </ol>
065: * TwoPhaseCoreAnnotationProcessor is intended provide a uniform mechanism for writing
066: * CoreAnnotationProcessor implementations that can be used in tooling environments more
067: * sophisticated than command-line tools (that may not do all their work on source
068: * in a single pass). Such environments will typically also provide implementations
069: * of the CoreAnnotationProcessorEnv and associated interfaces (Messager,
070: * Filer etc).
071: */
072: public abstract class TwoPhaseCoreAnnotationProcessor extends
073: Diagnostics implements CoreAnnotationProcessor {
074: protected TwoPhaseCoreAnnotationProcessor(
075: AnnotationTypeDeclaration[] atds,
076: CoreAnnotationProcessorEnv env) {
077: super (env);
078: _atds = atds;
079: _locale = Locale.getDefault();
080: }
081:
082: /**
083: * Implements CoreAnnotationProcessor.process() as two phases, "check" and "generate".
084: * "generate" will not be called if "check" emitted any errors (via printError()).
085: */
086: public void process() {
087: try {
088: check();
089:
090: boolean isReconcilePhase = false;
091:
092: // In the Eclipse IDE's integration between JDT and APT, annotation processors
093: // run in two phases -- reconcile and build. These translate into the
094: // check and generate phases for a TwoPhaseAnnotationProcessor. In order to
095: // optimize for hosting in this environment (and other IDE-centric AP environments)
096: // the generate phase can be cut out when performing only the check phase.
097: // Custom AP environments that wish to control this shoudl set the "phase" flag of the
098: // annotation processor to "RECONCILE".
099: try {
100: String phase = (String) CompilerUtils
101: .isReconcilePhase(getAnnotationProcessorEnvironment());
102: isReconcilePhase = "RECONCILE".equals(phase);
103: } catch (FatalCompileTimeException e) {
104: e.printDiagnostic(this );
105: }
106:
107: // Do not call generate if check resulted in errors of if the AP is running in
108: // a phase called "reconcile"
109: if (!isReconcilePhase && !hasErrors()) {
110: generate();
111: }
112: } catch (FatalCompileTimeException e) {
113: e.printDiagnostic(this );
114: }
115: }
116:
117: /**
118: * Performs semantic validation of input Declarations that are annotated with
119: * annotations claimed by this CoreAnnotationProcessor.
120: */
121: public void check() throws FatalCompileTimeException {
122: HashSet declsToCheck = new HashSet();
123:
124: //
125: // First, build up the Set of declarations to check. We don't want any duplicates.
126: //
127: for (int i = 0; i < _atds.length; ++i) {
128: AnnotationTypeDeclaration atd = _atds[i];
129: Declaration[] decls = getAnnotationProcessorEnvironment()
130: .getDeclarationsAnnotatedWith(atd);
131: for (int j = 0; j < decls.length; j++) {
132: declsToCheck.add(decls[j]);
133: }
134: }
135:
136: // Now, check the declarations.
137: check(declsToCheck);
138: }
139:
140: protected void check(Collection decls)
141: throws FatalCompileTimeException {
142: for (Iterator i = decls.iterator(); i.hasNext();) {
143: Declaration decl = (Declaration) i.next();
144: check(decl);
145: }
146: }
147:
148: /**
149: * Emits additional artifacts for input Declarations that are annotated with
150: * annotations claimed by this CoreAnnotationProcessor.
151: */
152: public void generate() {
153: try {
154: HashSet alreadyProcessed = new HashSet();
155:
156: for (int i = 0; i < _atds.length; i++) {
157: AnnotationTypeDeclaration atd = _atds[i];
158: Declaration[] decls = getAnnotationProcessorEnvironment()
159: .getDeclarationsAnnotatedWith(atd);
160:
161: for (int j = 0; j < decls.length; j++) {
162: Declaration decl = decls[j];
163: if (!alreadyProcessed.contains(decl))
164: generate(decl);
165: alreadyProcessed.add(decl);
166: }
167: }
168: } catch (FatalCompileTimeException e) {
169: e.printDiagnostic(this );
170: }
171: }
172:
173: /**
174: * The check method is responsible for all semantic validation of the input Declaration.
175: * <p/>
176: * All semantic errors/warnings associated with the input Declaration should
177: * be output during check via methods on {@link Diagnostics}.
178: * <p/>
179: * If the presence of the input Declaration implies the need to add new files,
180: * and those files need to be visible during the check phase for
181: * other Declarations, then the CoreAnnotationProcessorEnv's Filer API should be
182: * used to add those files in this phase. The adding of such files at this point
183: * should typically not result in their emission to persistent storage (i.e. disk),
184: * but rather be kept in memory to be referenced by the check phase of other
185: * Declarations.
186: */
187: public abstract void check(Declaration decl)
188: throws FatalCompileTimeException;
189:
190: /**
191: * The generate method is responsible for the generation of any additional artifacts
192: * (source, class, or binary) that are derived from the input Declaration.
193: */
194: public abstract void generate(Declaration decl)
195: throws FatalCompileTimeException;
196:
197: //
198: // Helper functions for handling diagnostics
199: //
200:
201: protected String getResourceString(String id, Object[] args) {
202: ResourceBundle rb = ResourceBundle.getBundle(getClass()
203: .getPackage().getName()
204: + ".strings", _locale);
205: String pattern = rb.getString(id);
206: return MessageFormat.format(pattern, args);
207: }
208:
209: private AnnotationTypeDeclaration[] _atds;
210: private Locale _locale;
211: }
|