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.xjc.api.impl.s2j; 27 28 import java.io.IOException; 29 import java.net.MalformedURLException; 30 import java.net.URI; 31 import java.net.URISyntaxException; 32 import java.net.URL; 33 34 import javax.xml.XMLConstants; 35 import javax.xml.stream.XMLStreamException; 36 import javax.xml.stream.XMLStreamReader; 37 import javax.xml.validation.SchemaFactory; 38 39 import com.sun.codemodel.internal.JCodeModel; 40 import com.sun.istack.internal.NotNull; 41 import com.sun.istack.internal.SAXParseException2; 42 import com.sun.tools.internal.xjc.ErrorReceiver; 43 import com.sun.tools.internal.xjc.ModelLoader; 44 import com.sun.tools.internal.xjc.Options; 45 import com.sun.tools.internal.xjc.api.ClassNameAllocator; 46 import com.sun.tools.internal.xjc.api.ErrorListener; 47 import com.sun.tools.internal.xjc.api.SchemaCompiler; 48 import com.sun.tools.internal.xjc.api.SpecVersion; 49 import com.sun.tools.internal.xjc.model.Model; 50 import com.sun.tools.internal.xjc.outline.Outline; 51 import com.sun.tools.internal.xjc.reader.internalizer.DOMForest; 52 import com.sun.tools.internal.xjc.reader.internalizer.SCDBasedBindingSet; 53 import com.sun.tools.internal.xjc.reader.xmlschema.parser.LSInputSAXWrapper; 54 import com.sun.tools.internal.xjc.reader.xmlschema.parser.XMLSchemaInternalizationLogic; 55 import com.sun.xml.internal.bind.unmarshaller.DOMScanner; 56 import com.sun.xml.internal.bind.v2.util.XmlFactory; 57 import com.sun.xml.internal.xsom.XSSchemaSet; 58 59 import org.w3c.dom.Element; 60 import org.w3c.dom.ls.LSInput; 61 import org.w3c.dom.ls.LSResourceResolver; 62 import org.xml.sax.ContentHandler; 63 import org.xml.sax.EntityResolver; 64 import org.xml.sax.InputSource; 65 import org.xml.sax.SAXException; 66 import org.xml.sax.SAXParseException; 67 import org.xml.sax.helpers.LocatorImpl; 68 69 /** 70 * {@link SchemaCompiler} implementation. 71 * 72 * This class builds a {@link DOMForest} until the {@link #bind()} method, 73 * then this method does the rest of the hard work. 74 * 75 * @see ModelLoader 76 * 77 * @author 78 * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) 79 */ 80 public final class SchemaCompilerImpl extends ErrorReceiver implements SchemaCompiler { 81 82 /** 83 * User-specified error receiver. 84 * This field can be null, in which case errors need to be discarded. 85 */ 86 private ErrorListener errorListener; 87 88 protected final Options opts = new Options(); 89 90 protected @NotNull DOMForest forest; 91 92 /** 93 * Set to true once an error is found. 94 */ 95 private boolean hadError; 96 97 public SchemaCompilerImpl() { 98 opts.compatibilityMode = Options.EXTENSION; 99 resetSchema(); 100 101 if(System.getProperty("xjc-api.test")!=null) { 102 opts.debugMode = true; 103 opts.verbose = true; 104 } 105 } 106 107 @NotNull 108 public Options getOptions() { 109 return opts; 110 } 111 112 public ContentHandler getParserHandler( String systemId ) { 113 return forest.getParserHandler(systemId,true); 114 } 115 116 public void parseSchema( String systemId, Element element ) { 117 checkAbsoluteness(systemId); 118 try { 119 DOMScanner scanner = new DOMScanner(); 120 121 // use a locator that sets the system ID correctly 122 // so that we can resolve relative URLs in most of the case. 123 // it still doesn't handle xml:base and XInclude and all those things 124 // correctly. There's just no way to make all those things work with DOM! 125 LocatorImpl loc = new LocatorImpl(); 126 loc.setSystemId(systemId); 127 scanner.setLocator(loc); 128 129 scanner.setContentHandler(getParserHandler(systemId)); 130 scanner.scan(element); 131 } catch (SAXException e) { 132 // since parsing DOM shouldn't cause a SAX exception 133 // and our handler will never throw it, it's not clear 134 // if this will ever happen. 135 fatalError(new SAXParseException2( 136 e.getMessage(), null, systemId,-1,-1, e)); 137 } 138 } 139 140 public void parseSchema(InputSource source) { 141 checkAbsoluteness(source.getSystemId()); 142 try { 143 forest.parse(source,true); 144 } catch (SAXException e) { 145 // parsers are required to report an error to ErrorHandler, 146 // so we should never see this error. 147 e.printStackTrace(); 148 } 149 } 150 151 public void setTargetVersion(SpecVersion version) { 152 if(version==null) 153 version = SpecVersion.LATEST; 154 opts.target = version; 155 } 156 157 public void parseSchema(String systemId, XMLStreamReader reader) throws XMLStreamException { 158 checkAbsoluteness(systemId); 159 forest.parse(systemId,reader,true); 160 } 161 162 /** 163 * Checks if the system ID is absolute. 164 */ 165 @SuppressWarnings("ResultOfObjectAllocationIgnored") 166 private void checkAbsoluteness(String systemId) { 167 // we need to be able to handle system IDs like "urn:foo", which java.net.URL can't process, 168 // but OTOH we also need to be able to process system IDs like "file://a b c/def.xsd", 169 // which java.net.URI can't process. So for now, let's fail only if both of them fail. 170 // eventually we need a proper URI class that works for us. 171 try { 172 new URL(systemId); 173 } catch( MalformedURLException mue) { 174 try { 175 new URI(systemId); 176 } catch (URISyntaxException e ) { 177 throw new IllegalArgumentException("system ID '"+systemId+"' isn't absolute",e); 178 } 179 } 180 } 181 182 public void setEntityResolver(EntityResolver entityResolver) { 183 forest.setEntityResolver(entityResolver); 184 opts.entityResolver = entityResolver; 185 } 186 187 public void setDefaultPackageName(String packageName) { 188 opts.defaultPackage2 = packageName; 189 } 190 191 public void forcePackageName(String packageName) { 192 opts.defaultPackage = packageName; 193 } 194 195 public void setClassNameAllocator(ClassNameAllocator allocator) { 196 opts.classNameAllocator = allocator; 197 } 198 199 public void resetSchema() { 200 forest = new DOMForest(new XMLSchemaInternalizationLogic(), opts); 201 forest.setErrorHandler(this); 202 forest.setEntityResolver(opts.entityResolver); 203 } 204 205 public JAXBModelImpl bind() { 206 // this has been problematic. turn it off. 207 // if(!forest.checkSchemaCorrectness(this)) 208 // return null; 209 210 // parse all the binding files given via XJC -b options. 211 // this also takes care of the binding files given in the -episode option. 212 for (InputSource is : opts.getBindFiles()) 213 parseSchema(is); 214 215 // internalization 216 SCDBasedBindingSet scdBasedBindingSet = forest.transform(opts.isExtensionMode()); 217 218 if (!NO_CORRECTNESS_CHECK) { 219 // correctness check 220 SchemaFactory sf = XmlFactory.createSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI, opts.disableXmlSecurity); 221 222 // fix for https://jaxb.dev.java.net/issues/show_bug.cgi?id=795 223 // taken from SchemaConstraintChecker, TODO XXX FIXME UGLY 224 if (opts.entityResolver != null) { 225 sf.setResourceResolver(new LSResourceResolver() { 226 public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) { 227 try { 228 // XSOM passes the namespace URI to the publicID parameter. 229 // we do the same here . 230 InputSource is = opts.entityResolver.resolveEntity(namespaceURI, systemId); 231 if (is == null) return null; 232 return new LSInputSAXWrapper(is); 233 } catch (SAXException e) { 234 // TODO: is this sufficient? 235 return null; 236 } catch (IOException e) { 237 // TODO: is this sufficient? 238 return null; 239 } 240 } 241 }); 242 } 243 244 sf.setErrorHandler(new DowngradingErrorHandler(this)); 245 forest.weakSchemaCorrectnessCheck(sf); 246 if (hadError) 247 return null; // error in the correctness check. abort now 248 } 249 250 JCodeModel codeModel = new JCodeModel(); 251 252 ModelLoader gl = new ModelLoader(opts,codeModel,this); 253 try { 254 XSSchemaSet result = gl.createXSOM(forest, scdBasedBindingSet); 255 if(result==null) 256 return null; 257 258 // we need info about each field, so we go ahead and generate the 259 // skeleton at this point. 260 // REVISIT: we should separate FieldRenderer and FieldAccessor 261 // so that accessors can be used before we build the code. 262 Model model = gl.annotateXMLSchema(result); 263 if(model==null) return null; 264 265 if(hadError) return null; // if we have any error by now, abort 266 267 model.setPackageLevelAnnotations(opts.packageLevelAnnotations); 268 269 Outline context = model.generateCode(opts,this); 270 if(context==null) return null; 271 272 if(hadError) return null; 273 274 return new JAXBModelImpl(context); 275 } catch( SAXException e ) { 276 // since XSOM uses our parser that scans DOM, 277 // no parser error is possible. 278 // all the other errors will be directed to ErrorReceiver 279 // before it's thrown, so when the exception is thrown 280 // the error should have already been reported. 281 282 // thus ignore. 283 return null; 284 } 285 } 286 287 public void setErrorListener(ErrorListener errorListener) { 288 this.errorListener = errorListener; 289 } 290 291 public void info(SAXParseException exception) { 292 if(errorListener!=null) 293 errorListener.info(exception); 294 } 295 public void warning(SAXParseException exception) { 296 if(errorListener!=null) 297 errorListener.warning(exception); 298 } 299 public void error(SAXParseException exception) { 300 hadError = true; 301 if(errorListener!=null) 302 errorListener.error(exception); 303 } 304 public void fatalError(SAXParseException exception) { 305 hadError = true; 306 if(errorListener!=null) 307 errorListener.fatalError(exception); 308 } 309 310 /** 311 * We use JAXP 1.3 to do a schema correctness check, but we know 312 * it doesn't always work. So in case some people hit the problem, 313 * this switch is here so that they can turn it off as a workaround. 314 */ 315 private static boolean NO_CORRECTNESS_CHECK = false; 316 317 static { 318 try { 319 NO_CORRECTNESS_CHECK = Boolean.getBoolean(SchemaCompilerImpl.class.getName()+".noCorrectnessCheck"); 320 } catch( Throwable t) { 321 // ignore 322 } 323 } 324 }