ohair@286: /* alanb@368: * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.tools.internal.jxc; ohair@286: alanb@368: import com.sun.tools.internal.jxc.ap.Options; ohair@286: import java.io.File; ohair@286: import java.io.IOException; ohair@286: import java.util.Collection; ohair@286: import java.util.HashMap; ohair@286: import java.util.HashSet; ohair@286: import java.util.List; ohair@286: import java.util.Map; ohair@286: import java.util.Set; ohair@286: import java.util.regex.Matcher; ohair@286: import java.util.regex.Pattern; ohair@286: ohair@286: import javax.xml.bind.SchemaOutputResolver; ohair@286: import javax.xml.parsers.ParserConfigurationException; ohair@286: import javax.xml.parsers.SAXParserFactory; ohair@286: import javax.xml.transform.Result; ohair@286: import javax.xml.transform.stream.StreamResult; ohair@286: import javax.xml.validation.ValidatorHandler; ohair@286: ohair@286: import javax.annotation.processing.ProcessingEnvironment; ohair@286: import javax.lang.model.element.TypeElement; ohair@286: import com.sun.tools.internal.jxc.gen.config.Config; ohair@286: import com.sun.tools.internal.jxc.gen.config.Schema; ohair@286: import com.sun.tools.internal.xjc.SchemaCache; ohair@286: import com.sun.tools.internal.xjc.api.Reference; ohair@286: import com.sun.tools.internal.xjc.util.ForkContentHandler; ohair@286: alanb@368: import com.sun.xml.internal.bind.v2.util.XmlFactory; ohair@286: import org.xml.sax.ErrorHandler; ohair@286: import org.xml.sax.InputSource; ohair@286: import org.xml.sax.SAXException; ohair@286: import org.xml.sax.XMLReader; ohair@286: ohair@286: ohair@286: /** ohair@286: * This reads the config files passed by the user to annotation processing ohair@286: * and obtains a list of classes that need to be included ohair@286: * for a particular config from the set of classes passed ohair@286: * by the user to annotation processing. ohair@286: * ohair@286: * @author Bhakti Mehta (bhakti.mehta@sun.com) ohair@286: */ ohair@286: public final class ConfigReader { ohair@286: ohair@286: /** ohair@286: * The set of classes to be passed to XJC ohair@286: * ohair@286: */ ohair@286: private final Set classesToBeIncluded = new HashSet(); ohair@286: ohair@286: ohair@286: /** ohair@286: * The SchemaOutputResolver used to generate the schemas ohair@286: */ ohair@286: private final SchemaOutputResolver schemaOutputResolver; ohair@286: ohair@286: private final ProcessingEnvironment env; ohair@286: ohair@286: /** ohair@286: * ohair@286: * @param classes ohair@286: * The set of classes passed to the AnnotationProcessor ohair@286: * @param xmlFile ohair@286: * The configuration file. ohair@286: * @throws SAXException ohair@286: * If this is thrown, the error has already been reported. ohair@286: * @throws IOException ohair@286: * If any IO errors occur. ohair@286: */ ohair@286: public ConfigReader(ProcessingEnvironment env, Collection classes, File xmlFile, ErrorHandler errorHandler) throws SAXException, IOException { ohair@286: this.env = env; alanb@368: Config config = parseAndGetConfig(xmlFile, errorHandler, env.getOptions().containsKey(Options.DISABLE_XML_SECURITY)); ohair@286: checkAllClasses(config,classes); ohair@286: String path = xmlFile.getAbsolutePath(); ohair@286: String xmlPath = path.substring(0,path.lastIndexOf(File.separatorChar)); ohair@286: schemaOutputResolver = createSchemaOutputResolver(config,xmlPath); ohair@286: ohair@286: } ohair@286: ohair@286: ohair@286: /** ohair@286: * This creates a regular expression ohair@286: * for the user pattern , matches the input classes ohair@286: * passed by the user and returns the final ohair@286: * list of classes that need to be included for a config file ohair@286: * after applying those patterns ohair@286: * ohair@286: */ ohair@286: public Collection getClassesToBeIncluded() { ohair@286: return classesToBeIncluded; ohair@286: } ohair@286: ohair@286: private void checkAllClasses(Config config, Collection rootClasses) { ohair@286: ohair@286: List includeRegexList = config.getClasses().getIncludes(); ohair@286: List excludeRegexList = config.getClasses().getExcludes(); ohair@286: ohair@286: OUTER: ohair@286: for (TypeElement typeDecl : rootClasses) { ohair@286: ohair@286: String qualifiedName = typeDecl.getQualifiedName().toString(); ohair@286: ohair@286: for (Pattern pattern : excludeRegexList) { ohair@286: boolean match = checkPatternMatch(qualifiedName, pattern); ohair@286: if (match) ohair@286: continue OUTER; // excluded ohair@286: } ohair@286: ohair@286: for (Pattern pattern : includeRegexList) { ohair@286: boolean match = checkPatternMatch(qualifiedName, pattern); ohair@286: if (match) { ohair@286: classesToBeIncluded.add(new Reference(typeDecl,env)); ohair@286: break; ohair@286: } ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * This returns the SchemaOutputResolver to generate the schemas ohair@286: */ ohair@286: public SchemaOutputResolver getSchemaOutputResolver(){ ohair@286: return schemaOutputResolver; ohair@286: } ohair@286: ohair@286: private SchemaOutputResolver createSchemaOutputResolver(Config config, String xmlpath) { ohair@286: File baseDir = new File(xmlpath, config.getBaseDir().getPath()); alanb@368: SchemaOutputResolverImpl outResolver = new SchemaOutputResolverImpl (baseDir); ohair@286: ohair@286: for( Schema schema : (List)config.getSchema() ) { ohair@286: String namespace = schema.getNamespace(); ohair@286: File location = schema.getLocation(); alanb@368: outResolver.addSchemaInfo(namespace,location); ohair@286: } alanb@368: return outResolver; ohair@286: } ohair@286: ohair@286: /** ohair@286: * This will check if the qualified name matches the pattern ohair@286: * ohair@286: * @param qualifiedName ohair@286: * The qualified name of the TypeDeclaration ohair@286: * @param pattern ohair@286: * The pattern obtained from the users input ohair@286: * ohair@286: */ ohair@286: private boolean checkPatternMatch(String qualifiedName, Pattern pattern) { ohair@286: Matcher matcher = pattern.matcher(qualifiedName); ohair@286: return matcher.matches(); ohair@286: } ohair@286: ohair@286: ohair@286: ohair@286: /** ohair@286: * Lazily parsed schema for the binding file. ohair@286: */ ohair@286: private static SchemaCache configSchema = new SchemaCache(Config.class.getResource("config.xsd")); ohair@286: ohair@286: ohair@286: /** ohair@286: * Parses an xml config file and returns a Config object. ohair@286: * ohair@286: * @param xmlFile ohair@286: * The xml config file which is passed by the user to annotation processing ohair@286: * @return ohair@286: * A non null Config object ohair@286: */ alanb@368: private Config parseAndGetConfig (File xmlFile, ErrorHandler errorHandler, boolean disableSecureProcessing) throws SAXException, IOException { ohair@286: XMLReader reader; ohair@286: try { alanb@368: SAXParserFactory factory = XmlFactory.createParserFactory(disableSecureProcessing); ohair@286: reader = factory.newSAXParser().getXMLReader(); ohair@286: } catch (ParserConfigurationException e) { ohair@286: // in practice this will never happen ohair@286: throw new Error(e); ohair@286: } ohair@286: NGCCRuntimeEx runtime = new NGCCRuntimeEx(errorHandler); ohair@286: ohair@286: // set up validator ohair@286: ValidatorHandler validator = configSchema.newValidator(); ohair@286: validator.setErrorHandler(errorHandler); ohair@286: ohair@286: // the validator will receive events first, then the parser. ohair@286: reader.setContentHandler(new ForkContentHandler(validator,runtime)); ohair@286: ohair@286: reader.setErrorHandler(errorHandler); ohair@286: Config config = new Config(runtime); ohair@286: runtime.setRootHandler(config); ohair@286: reader.parse(new InputSource(xmlFile.toURL().toExternalForm())); ohair@286: runtime.reset(); ohair@286: ohair@286: return config; ohair@286: } ohair@286: /** ohair@286: * Controls where the JAXB RI puts the generates ohair@286: * schema files. ohair@286: * @author ohair@286: * Bhakti Mehta (bhakti.mehta@sun.com) ohair@286: */ ohair@286: private static final class SchemaOutputResolverImpl extends SchemaOutputResolver{ ohair@286: ohair@286: /** ohair@286: * Directory to which we put the rest of the files. ohair@286: * Never be null. ohair@286: */ ohair@286: private final File baseDir; ohair@286: ohair@286: /** ohair@286: * Namespace URI to the location of the schema. ohair@286: * This captures what the user specifies. ohair@286: */ ohair@286: private final Map schemas = new HashMap(); ohair@286: ohair@286: ohair@286: /** ohair@286: * Decides where the schema file (of the given namespace URI) ohair@286: * will be written, and return it as a {@link Result} object. ohair@286: * ohair@286: */ ohair@286: public Result createOutput( String namespaceUri, String suggestedFileName ) { ohair@286: ohair@286: // the user's preference takes a precedence ohair@286: if(schemas.containsKey(namespaceUri)) { ohair@286: File loc = schemas.get(namespaceUri); ohair@286: if(loc==null) return null; // specifically not to generate a schema ohair@286: ohair@286: // create directories if necessary. we've already checked that the baseDir ohair@286: // exists, so this should be no surprise to users. ohair@286: loc.getParentFile().mkdirs(); ohair@286: ohair@286: return new StreamResult(loc); // generate into a file the user specified. ohair@286: } ohair@286: ohair@286: // if the user didn't say anything about this namespace, ohair@286: // generate it into the default directory with a default name. ohair@286: ohair@286: File schemaFile = new File (baseDir, suggestedFileName); ohair@286: // The systemId for the result will be schemaFile ohair@286: return new StreamResult(schemaFile); ohair@286: } ohair@286: ohair@286: ohair@286: public SchemaOutputResolverImpl(File baseDir) { ohair@286: assert baseDir!=null; ohair@286: this.baseDir = baseDir; ohair@286: } ohair@286: ohair@286: public void addSchemaInfo(String namespaceUri, File location) { ohair@286: if (namespaceUri == null ) ohair@286: //generate elements in no namespace ohair@286: namespaceUri = ""; ohair@286: schemas.put(namespaceUri, location); ohair@286: ohair@286: } ohair@286: ohair@286: } ohair@286: }