1 /* 2 * Copyright (c) 1997, 2015, 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.io.InputStream; 32 import java.util.Collection; 33 import java.util.HashMap; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Set; 38 import java.util.regex.Matcher; 39 import java.util.regex.Pattern; 40 41 import javax.xml.bind.SchemaOutputResolver; 42 import javax.xml.parsers.ParserConfigurationException; 43 import javax.xml.parsers.SAXParserFactory; 44 import javax.xml.transform.Result; 45 import javax.xml.transform.stream.StreamResult; 46 import javax.xml.transform.stream.StreamSource; 47 import javax.xml.validation.ValidatorHandler; 48 49 import javax.annotation.processing.ProcessingEnvironment; 50 import javax.lang.model.element.TypeElement; 51 import com.sun.tools.internal.jxc.gen.config.Config; 52 import com.sun.tools.internal.jxc.gen.config.Schema; 53 import com.sun.tools.internal.xjc.SchemaCache; 54 import com.sun.tools.internal.xjc.api.Reference; 55 import com.sun.tools.internal.xjc.util.ForkContentHandler; 56 57 import com.sun.xml.internal.bind.v2.util.XmlFactory; 58 import org.xml.sax.ErrorHandler; 59 import org.xml.sax.InputSource; 60 import org.xml.sax.SAXException; 61 import org.xml.sax.XMLReader; 62 63 64 /** 65 * This reads the config files passed by the user to annotation processing 66 * and obtains a list of classes that need to be included 67 * for a particular config from the set of classes passed 68 * by the user to annotation processing. 69 * 70 * @author Bhakti Mehta (bhakti.mehta@sun.com) 71 */ 72 public final class ConfigReader { 73 74 /** 75 * The set of classes to be passed to XJC 76 * 77 */ 78 private final Set<Reference> classesToBeIncluded = new HashSet<Reference>(); 79 80 81 /** 82 * The SchemaOutputResolver used to generate the schemas 83 */ 84 private final SchemaOutputResolver schemaOutputResolver; 85 86 private final ProcessingEnvironment env; 87 88 /** 89 * 90 * @param classes 91 * The set of classes passed to the AnnotationProcessor 92 * @param xmlFile 93 * The configuration file. 94 * @throws SAXException 95 * If this is thrown, the error has already been reported. 96 * @throws IOException 97 * If any IO errors occur. 98 */ 99 public ConfigReader(ProcessingEnvironment env, Collection<? extends TypeElement> classes, File xmlFile, ErrorHandler errorHandler) throws SAXException, IOException { 100 this.env = env; 101 Config config = parseAndGetConfig(xmlFile, errorHandler, env.getOptions().containsKey(Options.DISABLE_XML_SECURITY)); 102 checkAllClasses(config,classes); 103 String path = xmlFile.getAbsolutePath(); 104 String xmlPath = path.substring(0,path.lastIndexOf(File.separatorChar)); 105 schemaOutputResolver = createSchemaOutputResolver(config,xmlPath); 106 107 } 108 109 110 /** 111 * This creates a regular expression 112 * for the user pattern , matches the input classes 113 * passed by the user and returns the final 114 * list of classes that need to be included for a config file 115 * after applying those patterns 116 * 117 */ 118 public Collection<Reference> getClassesToBeIncluded() { 119 return classesToBeIncluded; 120 } 121 122 private void checkAllClasses(Config config, Collection<? extends TypeElement> rootClasses) { 123 124 List<Pattern> includeRegexList = config.getClasses().getIncludes(); 125 List<Pattern> excludeRegexList = config.getClasses().getExcludes(); 126 127 OUTER: 128 for (TypeElement typeDecl : rootClasses) { 129 130 String qualifiedName = typeDecl.getQualifiedName().toString(); 131 132 for (Pattern pattern : excludeRegexList) { 133 boolean match = checkPatternMatch(qualifiedName, pattern); 134 if (match) 135 continue OUTER; // excluded 136 } 137 138 for (Pattern pattern : includeRegexList) { 139 boolean match = checkPatternMatch(qualifiedName, pattern); 140 if (match) { 141 classesToBeIncluded.add(new Reference(typeDecl,env)); 142 break; 143 } 144 } 145 } 146 } 147 148 /** 149 * This returns the SchemaOutputResolver to generate the schemas 150 */ 151 public SchemaOutputResolver getSchemaOutputResolver(){ 152 return schemaOutputResolver; 153 } 154 155 private SchemaOutputResolver createSchemaOutputResolver(Config config, String xmlpath) { 156 File baseDir = new File(xmlpath, config.getBaseDir().getPath()); 157 SchemaOutputResolverImpl outResolver = new SchemaOutputResolverImpl (baseDir); 158 159 for( Schema schema : (List<Schema>)config.getSchema() ) { 160 String namespace = schema.getNamespace(); 161 File location = schema.getLocation(); 162 outResolver.addSchemaInfo(namespace,location); 163 } 164 return outResolver; 165 } 166 167 /** 168 * This will check if the qualified name matches the pattern 169 * 170 * @param qualifiedName 171 * The qualified name of the TypeDeclaration 172 * @param pattern 173 * The pattern obtained from the users input 174 * 175 */ 176 private boolean checkPatternMatch(String qualifiedName, Pattern pattern) { 177 Matcher matcher = pattern.matcher(qualifiedName); 178 return matcher.matches(); 179 } 180 181 182 183 /** 184 * Lazily parsed schema for the binding file. 185 */ 186 private static SchemaCache configSchema = new SchemaCache(newStreamSource("config.xsd")); 187 188 private static StreamSource newStreamSource(String systemId) { 189 InputStream is = Config.class.getResourceAsStream(systemId); 190 StreamSource schema = new StreamSource(is); 191 schema.setSystemId(systemId); 192 return schema; 193 } 194 195 /** 196 * Parses an xml config file and returns a Config object. 197 * 198 * @param xmlFile 199 * The xml config file which is passed by the user to annotation processing 200 * @return 201 * A non null Config object 202 */ 203 private Config parseAndGetConfig (File xmlFile, ErrorHandler errorHandler, boolean disableSecureProcessing) throws SAXException, IOException { 204 XMLReader reader; 205 try { 206 SAXParserFactory factory = XmlFactory.createParserFactory(disableSecureProcessing); 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,runtime)); 220 221 reader.setErrorHandler(errorHandler); 222 Config config = new Config(runtime); 223 runtime.setRootHandler(config); 224 reader.parse(new InputSource(xmlFile.toURL().toExternalForm())); 225 runtime.reset(); 226 227 return config; 228 } 229 /** 230 * Controls where the JAXB RI puts the generates 231 * schema files. 232 * @author 233 * Bhakti Mehta (bhakti.mehta@sun.com) 234 */ 235 private static final class SchemaOutputResolverImpl extends SchemaOutputResolver{ 236 237 /** 238 * Directory to which we put the rest of the files. 239 * Never be null. 240 */ 241 private final File baseDir; 242 243 /** 244 * Namespace URI to the location of the schema. 245 * This captures what the user specifies. 246 */ 247 private final Map<String,File> schemas = new HashMap<String,File>(); 248 249 250 /** 251 * Decides where the schema file (of the given namespace URI) 252 * will be written, and return it as a {@link Result} object. 253 * 254 */ 255 public Result createOutput( String namespaceUri, String suggestedFileName ) { 256 257 // the user's preference takes a precedence 258 if(schemas.containsKey(namespaceUri)) { 259 File loc = schemas.get(namespaceUri); 260 if(loc==null) return null; // specifically not to generate a schema 261 262 // create directories if necessary. we've already checked that the baseDir 263 // exists, so this should be no surprise to users. 264 loc.getParentFile().mkdirs(); 265 266 return new StreamResult(loc); // generate into a file the user specified. 267 } 268 269 // if the user didn't say anything about this namespace, 270 // generate it into the default directory with a default name. 271 272 File schemaFile = new File (baseDir, suggestedFileName); 273 // The systemId for the result will be schemaFile 274 return new StreamResult(schemaFile); 275 } 276 277 278 public SchemaOutputResolverImpl(File baseDir) { 279 assert baseDir!=null; 280 this.baseDir = baseDir; 281 } 282 283 public void addSchemaInfo(String namespaceUri, File location) { 284 if (namespaceUri == null ) 285 //generate elements in no namespace 286 namespaceUri = ""; 287 schemas.put(namespaceUri, location); 288 289 } 290 291 } 292 }