1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.internal.jxc; 27 28 import com.sun.tools.internal.jxc.ap.Options; 29 import java.io.File; 30 import java.io.IOException; 31 import java.util.Collection; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Set; 37 import java.util.regex.Matcher; 38 import java.util.regex.Pattern; 39 40 import javax.xml.bind.SchemaOutputResolver; 41 import javax.xml.parsers.ParserConfigurationException; 42 import javax.xml.parsers.SAXParserFactory; 43 import javax.xml.transform.Result; 44 import javax.xml.transform.stream.StreamResult; 45 import javax.xml.validation.ValidatorHandler; 46 47 import javax.annotation.processing.ProcessingEnvironment; 48 import javax.lang.model.element.TypeElement; 49 import com.sun.tools.internal.jxc.gen.config.Config; 50 import com.sun.tools.internal.jxc.gen.config.Schema; 51 import com.sun.tools.internal.xjc.SchemaCache; 52 import com.sun.tools.internal.xjc.api.Reference; 53 import com.sun.tools.internal.xjc.util.ForkContentHandler; 54 55 import com.sun.xml.internal.bind.v2.util.XmlFactory; 56 import org.xml.sax.ErrorHandler; 57 import org.xml.sax.InputSource; 58 import org.xml.sax.SAXException; 59 import org.xml.sax.XMLReader; 60 61 62 /** 63 * This reads the config files passed by the user to annotation processing 64 * and obtains a list of classes that need to be included 65 * for a particular config from the set of classes passed 66 * by the user to annotation processing. 67 * 68 * @author Bhakti Mehta (bhakti.mehta@sun.com) 69 */ 70 public final class ConfigReader { 71 72 /** 73 * The set of classes to be passed to XJC 74 * 75 */ 76 private final Set<Reference> classesToBeIncluded = new HashSet<Reference>(); 77 78 79 /** 80 * The SchemaOutputResolver used to generate the schemas 81 */ 82 private final SchemaOutputResolver schemaOutputResolver; 83 84 private final ProcessingEnvironment env; 85 86 /** 87 * 88 * @param classes 89 * The set of classes passed to the AnnotationProcessor 90 * @param xmlFile 91 * The configuration file. 92 * @throws SAXException 93 * If this is thrown, the error has already been reported. 94 * @throws IOException 95 * If any IO errors occur. 96 */ 97 public ConfigReader(ProcessingEnvironment env, Collection<? extends TypeElement> classes, File xmlFile, ErrorHandler errorHandler) throws SAXException, IOException { 98 this.env = env; 99 Config config = parseAndGetConfig(xmlFile, errorHandler, env.getOptions().containsKey(Options.DISABLE_XML_SECURITY)); 100 checkAllClasses(config,classes); 101 String path = xmlFile.getAbsolutePath(); 102 String xmlPath = path.substring(0,path.lastIndexOf(File.separatorChar)); 103 schemaOutputResolver = createSchemaOutputResolver(config,xmlPath); 104 105 } 106 107 108 /** 109 * This 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, Collection<? extends TypeElement> rootClasses) { 121 122 List<Pattern> includeRegexList = config.getClasses().getIncludes(); 123 List<Pattern> excludeRegexList = config.getClasses().getExcludes(); 124 125 OUTER: 126 for (TypeElement typeDecl : rootClasses) { 127 128 String qualifiedName = typeDecl.getQualifiedName().toString(); 129 130 for (Pattern pattern : excludeRegexList) { 131 boolean match = checkPatternMatch(qualifiedName, pattern); 132 if (match) 133 continue OUTER; // excluded 134 } 135 136 for (Pattern pattern : includeRegexList) { 137 boolean match = checkPatternMatch(qualifiedName, pattern); 138 if (match) { 139 classesToBeIncluded.add(new Reference(typeDecl,env)); 140 break; 141 } 142 } 143 } 144 } 145 146 /** 147 * This returns the SchemaOutputResolver to generate the schemas 148 */ 149 public SchemaOutputResolver getSchemaOutputResolver(){ 150 return schemaOutputResolver; 151 } 152 153 private SchemaOutputResolver createSchemaOutputResolver(Config config, String xmlpath) { 154 File baseDir = new File(xmlpath, config.getBaseDir().getPath()); 155 SchemaOutputResolverImpl outResolver = new SchemaOutputResolverImpl (baseDir); 156 157 for( Schema schema : (List<Schema>)config.getSchema() ) { 158 String namespace = schema.getNamespace(); 159 File location = schema.getLocation(); 160 outResolver.addSchemaInfo(namespace,location); 161 } 162 return outResolver; 163 } 164 165 /** 166 * This will check if the qualified name matches the pattern 167 * 168 * @param qualifiedName 169 * The qualified name of the TypeDeclaration 170 * @param pattern 171 * The pattern obtained from the users input 172 * 173 */ 174 private boolean checkPatternMatch(String qualifiedName, Pattern pattern) { 175 Matcher matcher = pattern.matcher(qualifiedName); 176 return matcher.matches(); 177 } 178 179 180 181 /** 182 * Lazily parsed schema for the binding file. 183 */ 184 private static SchemaCache configSchema = new SchemaCache(Config.class.getResource("config.xsd")); 185 186 187 /** 188 * Parses an xml config file and returns a Config object. 189 * 190 * @param xmlFile 191 * The xml config file which is passed by the user to annotation processing 192 * @return 193 * A non null Config object 194 */ 195 private Config parseAndGetConfig (File xmlFile, ErrorHandler errorHandler, boolean disableSecureProcessing) throws SAXException, IOException { 196 XMLReader reader; 197 try { 198 SAXParserFactory factory = XmlFactory.createParserFactory(disableSecureProcessing); 199 reader = factory.newSAXParser().getXMLReader(); 200 } catch (ParserConfigurationException e) { 201 // in practice this will never happen 202 throw new Error(e); 203 } 204 NGCCRuntimeEx runtime = new NGCCRuntimeEx(errorHandler); 205 206 // set up validator 207 ValidatorHandler validator = configSchema.newValidator(); 208 validator.setErrorHandler(errorHandler); 209 210 // the validator will receive events first, then the parser. 211 reader.setContentHandler(new ForkContentHandler(validator,runtime)); 212 213 reader.setErrorHandler(errorHandler); 214 Config config = new Config(runtime); 215 runtime.setRootHandler(config); 216 reader.parse(new InputSource(xmlFile.toURL().toExternalForm())); 217 runtime.reset(); 218 219 return config; 220 } 221 /** 222 * Controls where the JAXB RI puts the generates 223 * schema files. 224 * @author 225 * Bhakti Mehta (bhakti.mehta@sun.com) 226 */ 227 private static final class SchemaOutputResolverImpl extends SchemaOutputResolver{ 228 229 /** 230 * Directory to which we put the rest of the files. 231 * Never be null. 232 */ 233 private final File baseDir; 234 235 /** 236 * Namespace URI to the location of the schema. 237 * This captures what the user specifies. 238 */ 239 private final Map<String,File> schemas = new HashMap<String,File>(); 240 241 242 /** 243 * Decides where the schema file (of the given namespace URI) 244 * will be written, and return it as a {@link Result} object. 245 * 246 */ 247 public Result createOutput( String namespaceUri, String suggestedFileName ) { 248 249 // the user's preference takes a precedence 250 if(schemas.containsKey(namespaceUri)) { 251 File loc = schemas.get(namespaceUri); 252 if(loc==null) return null; // specifically not to generate a schema 253 254 // create directories if necessary. we've already checked that the baseDir 255 // exists, so this should be no surprise to users. 256 loc.getParentFile().mkdirs(); 257 258 return new StreamResult(loc); // generate into a file the user specified. 259 } 260 261 // if the user didn't say anything about this namespace, 262 // generate it into the default directory with a default name. 263 264 File schemaFile = new File (baseDir, suggestedFileName); 265 // The systemId for the result will be schemaFile 266 return new StreamResult(schemaFile); 267 } 268 269 270 public SchemaOutputResolverImpl(File baseDir) { 271 assert baseDir!=null; 272 this.baseDir = baseDir; 273 } 274 275 public void addSchemaInfo(String namespaceUri, File location) { 276 if (namespaceUri == null ) 277 //generate elements in no namespace 278 namespaceUri = ""; 279 schemas.put(namespaceUri, location); 280 281 } 282 283 } 284 }