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