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