/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.tools.jxc; import com.sun.tools.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.jxc.gen.config.Config; import com.sun.tools.jxc.gen.config.Schema; import com.sun.tools.xjc.SchemaCache; import com.sun.tools.xjc.api.Reference; import com.sun.tools.xjc.util.ForkContentHandler; import com.sun.xml.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<Reference> classesToBeIncluded = new HashSet<Reference>(); /** * 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<? extends TypeElement> 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<Reference> getClassesToBeIncluded() { return classesToBeIncluded; } private void checkAllClasses(Config config, Collection<? extends TypeElement> rootClasses) { List<Pattern> includeRegexList = config.getClasses().getIncludes(); List<Pattern> 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<Schema>)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<String,File> schemas = new HashMap<String,File>(); /** * 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); } } }