001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.tools.jxc;
038:
039: import java.io.File;
040: import java.io.IOException;
041: import java.util.Collection;
042: import java.util.HashMap;
043: import java.util.HashSet;
044: import java.util.List;
045: import java.util.Map;
046: import java.util.Set;
047: import java.util.regex.Matcher;
048: import java.util.regex.Pattern;
049:
050: import javax.xml.bind.SchemaOutputResolver;
051: import javax.xml.parsers.ParserConfigurationException;
052: import javax.xml.parsers.SAXParserFactory;
053: import javax.xml.transform.Result;
054: import javax.xml.transform.stream.StreamResult;
055: import javax.xml.validation.ValidatorHandler;
056:
057: import com.sun.mirror.apt.AnnotationProcessorEnvironment;
058: import com.sun.mirror.declaration.TypeDeclaration;
059: import com.sun.tools.jxc.gen.config.Config;
060: import com.sun.tools.jxc.gen.config.Schema;
061: import com.sun.tools.xjc.SchemaCache;
062: import com.sun.tools.xjc.api.Reference;
063: import com.sun.tools.xjc.util.ForkContentHandler;
064:
065: import org.xml.sax.ErrorHandler;
066: import org.xml.sax.InputSource;
067: import org.xml.sax.SAXException;
068: import org.xml.sax.XMLReader;
069:
070: /**
071: * This reads the config files passed by the user to apt
072: * and obtains a list of classes that need to be included
073: * for a particular config from the set of classes passed
074: * by the user to apt.
075: *
076: * @author Bhakti Mehta (bhakti.mehta@sun.com)
077: */
078: public final class ConfigReader {
079:
080: /**
081: * The set of classes to be passed to XJC
082: *
083: */
084: private final Set<Reference> classesToBeIncluded = new HashSet<Reference>();;
085:
086: /**
087: * The SchemaOutputResolver used to generate the schemas
088: */
089: private final SchemaOutputResolver schemaOutputResolver;
090:
091: private final AnnotationProcessorEnvironment env;
092:
093: /**
094: *
095: * @param classes
096: * The set of classes passed to the AnnotationProcessor
097: * @param xmlFile
098: * The configuration file.
099: * @throws SAXException
100: * If this is thrown, the error has already been reported.
101: * @throws IOException
102: * If any IO errors occur.
103: */
104: public ConfigReader(AnnotationProcessorEnvironment env,
105: Collection<? extends TypeDeclaration> classes,
106: File xmlFile, ErrorHandler errorHandler)
107: throws SAXException, IOException {
108: this .env = env;
109: Config config = parseAndGetConfig(xmlFile, errorHandler);
110: checkAllClasses(config, classes);
111: String path = xmlFile.getAbsolutePath();
112: String xmlPath = path.substring(0, path
113: .lastIndexOf(File.separatorChar));
114: schemaOutputResolver = createSchemaOutputResolver(config,
115: xmlPath);
116:
117: }
118:
119: /**
120: * This creates creates a regular expression
121: * for the user pattern , matches the input classes
122: * passed by the user and returns the final
123: * list of classes that need to be included for a config file
124: * after applying those patterns
125: *
126: */
127: public Collection<Reference> getClassesToBeIncluded() {
128: return classesToBeIncluded;
129: }
130:
131: private void checkAllClasses(Config config,
132: Collection<? extends TypeDeclaration> rootClasses) {
133:
134: List<Pattern> includeRegexList = config.getClasses()
135: .getIncludes();
136: List<Pattern> excludeRegexList = config.getClasses()
137: .getExcludes();
138:
139: OUTER: for (TypeDeclaration typeDecl : rootClasses) {
140:
141: String qualifiedName = typeDecl.getQualifiedName();
142:
143: for (Pattern pattern : excludeRegexList) {
144: boolean match = checkPatternMatch(qualifiedName,
145: pattern);
146: if (match)
147: continue OUTER; // excluded
148: }
149:
150: for (Pattern pattern : includeRegexList) {
151: boolean match = checkPatternMatch(qualifiedName,
152: pattern);
153: if (match) {
154: classesToBeIncluded
155: .add(new Reference(typeDecl, env));
156: break;
157: }
158: }
159: }
160: }
161:
162: /**
163: * This returns the SchemaOutputResolver to generate the schemas
164: */
165: public SchemaOutputResolver getSchemaOutputResolver() {
166: return schemaOutputResolver;
167: }
168:
169: private SchemaOutputResolver createSchemaOutputResolver(
170: Config config, String xmlpath) {
171: File baseDir = new File(xmlpath, config.getBaseDir().getPath());
172: SchemaOutputResolverImpl schemaOutputResolver = new SchemaOutputResolverImpl(
173: baseDir);
174:
175: for (Schema schema : (List<Schema>) config.getSchema()) {
176: String namespace = schema.getNamespace();
177: File location = schema.getLocation();
178: schemaOutputResolver.addSchemaInfo(namespace, location);
179: }
180: return schemaOutputResolver;
181: }
182:
183: /**
184: * This will check if the qualified name matches the pattern
185: *
186: * @param qualifiedName
187: * The qualified name of the TypeDeclaration
188: * @param pattern
189: * The pattern obtained from the users input
190: *
191: */
192: private boolean checkPatternMatch(String qualifiedName,
193: Pattern pattern) {
194: Matcher matcher = pattern.matcher(qualifiedName);
195: return matcher.matches();
196: }
197:
198: /**
199: * Lazily parsed schema for the binding file.
200: */
201: private static SchemaCache configSchema = new SchemaCache(
202: Config.class.getResource("config.xsd"));
203:
204: /**
205: * Parses an xml config file and returns a Config object.
206: *
207: * @param xmlFile
208: * The xml config file which is passed by the user to apt
209: * @return
210: * A non null Config object
211: */
212: private Config parseAndGetConfig(File xmlFile,
213: ErrorHandler errorHandler) throws SAXException, IOException {
214: XMLReader reader;
215: try {
216: SAXParserFactory factory = SAXParserFactory.newInstance();
217: factory.setNamespaceAware(true);
218: reader = factory.newSAXParser().getXMLReader();
219: } catch (ParserConfigurationException e) {
220: // in practice this will never happen
221: throw new Error(e);
222: }
223: NGCCRuntimeEx runtime = new NGCCRuntimeEx(errorHandler);
224:
225: // set up validator
226: ValidatorHandler validator = configSchema.newValidator();
227: validator.setErrorHandler(errorHandler);
228:
229: // the validator will receive events first, then the parser.
230: reader.setContentHandler(new ForkContentHandler(validator,
231: runtime));
232:
233: reader.setErrorHandler(errorHandler);
234: Config config = new Config(runtime);
235: runtime.setRootHandler(config);
236: reader.parse(new InputSource(xmlFile.toURL().toExternalForm()));
237: runtime.reset();
238:
239: return config;
240: }
241:
242: /**
243: * Controls where the JAXB RI puts the generates
244: * schema files.
245: * @author
246: * Bhakti Mehta (bhakti.mehta@sun.com)
247: */
248: private static final class SchemaOutputResolverImpl extends
249: SchemaOutputResolver {
250:
251: /**
252: * Directory to which we put the rest of the files.
253: * Never be null.
254: */
255: private final File baseDir;
256:
257: /**
258: * Namespace URI to the location of the schema.
259: * This captures what the user specifies.
260: */
261: private final Map<String, File> schemas = new HashMap<String, File>();
262:
263: /**
264: * Decides where the schema file (of the given namespace URI)
265: * will be written, and return it as a {@link Result} object.
266: *
267: */
268: public Result createOutput(String namespaceUri,
269: String suggestedFileName) {
270:
271: // the user's preference takes a precedence
272: if (schemas.containsKey(namespaceUri)) {
273: File loc = schemas.get(namespaceUri);
274: if (loc == null)
275: return null; // specifically not to generate a schema
276:
277: // create directories if necessary. we've already checked that the baseDir
278: // exists, so this should be no surprise to users.
279: loc.getParentFile().mkdirs();
280:
281: return new StreamResult(loc); // generate into a file the user specified.
282: }
283:
284: // if the user didn't say anything about this namespace,
285: // generate it into the default directory with a default name.
286:
287: File schemaFile = new File(baseDir, suggestedFileName);
288: // The systemId for the result will be schemaFile
289: return new StreamResult(schemaFile);
290: }
291:
292: public SchemaOutputResolverImpl(File baseDir) {
293: assert baseDir != null;
294: this .baseDir = baseDir;
295: }
296:
297: public void addSchemaInfo(String namespaceUri, File location) {
298: if (namespaceUri == null)
299: //generate elements in no namespace
300: namespaceUri = "";
301: schemas.put(namespaceUri, location);
302:
303: }
304:
305: }
306: }
|