1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/bind/v2/schemagen/XmlSchemaGenerator.java Wed Apr 27 01:27:09 2016 +0800 1.3 @@ -0,0 +1,1596 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.xml.internal.bind.v2.schemagen; 1.30 + 1.31 +import java.io.IOException; 1.32 +import java.io.OutputStream; 1.33 +import java.io.Writer; 1.34 +import java.io.File; 1.35 +import java.net.URI; 1.36 +import java.net.URISyntaxException; 1.37 +import java.util.Comparator; 1.38 +import java.util.HashMap; 1.39 +import java.util.LinkedHashSet; 1.40 +import java.util.Map; 1.41 +import java.util.Set; 1.42 +import java.util.TreeMap; 1.43 +import java.util.ArrayList; 1.44 +import java.util.logging.Level; 1.45 +import java.util.logging.Logger; 1.46 + 1.47 +import javax.activation.MimeType; 1.48 +import javax.xml.bind.SchemaOutputResolver; 1.49 +import javax.xml.bind.annotation.XmlElement; 1.50 +import javax.xml.namespace.QName; 1.51 +import javax.xml.transform.Result; 1.52 +import javax.xml.transform.stream.StreamResult; 1.53 + 1.54 +import com.sun.istack.internal.Nullable; 1.55 +import com.sun.istack.internal.NotNull; 1.56 +import com.sun.xml.internal.bind.Util; 1.57 +import com.sun.xml.internal.bind.api.CompositeStructure; 1.58 +import com.sun.xml.internal.bind.api.ErrorListener; 1.59 +import com.sun.xml.internal.bind.v2.TODO; 1.60 +import com.sun.xml.internal.bind.v2.WellKnownNamespace; 1.61 +import com.sun.xml.internal.bind.v2.util.CollisionCheckStack; 1.62 +import com.sun.xml.internal.bind.v2.util.StackRecorder; 1.63 +import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_SCHEMA; 1.64 +import com.sun.xml.internal.bind.v2.model.core.Adapter; 1.65 +import com.sun.xml.internal.bind.v2.model.core.ArrayInfo; 1.66 +import com.sun.xml.internal.bind.v2.model.core.AttributePropertyInfo; 1.67 +import com.sun.xml.internal.bind.v2.model.core.ClassInfo; 1.68 +import com.sun.xml.internal.bind.v2.model.core.Element; 1.69 +import com.sun.xml.internal.bind.v2.model.core.ElementInfo; 1.70 +import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo; 1.71 +import com.sun.xml.internal.bind.v2.model.core.EnumConstant; 1.72 +import com.sun.xml.internal.bind.v2.model.core.EnumLeafInfo; 1.73 +import com.sun.xml.internal.bind.v2.model.core.MapPropertyInfo; 1.74 +import com.sun.xml.internal.bind.v2.model.core.MaybeElement; 1.75 +import com.sun.xml.internal.bind.v2.model.core.NonElement; 1.76 +import com.sun.xml.internal.bind.v2.model.core.NonElementRef; 1.77 +import com.sun.xml.internal.bind.v2.model.core.PropertyInfo; 1.78 +import com.sun.xml.internal.bind.v2.model.core.ReferencePropertyInfo; 1.79 +import com.sun.xml.internal.bind.v2.model.core.TypeInfo; 1.80 +import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet; 1.81 +import com.sun.xml.internal.bind.v2.model.core.TypeRef; 1.82 +import com.sun.xml.internal.bind.v2.model.core.ValuePropertyInfo; 1.83 +import com.sun.xml.internal.bind.v2.model.core.WildcardMode; 1.84 +import com.sun.xml.internal.bind.v2.model.impl.ClassInfoImpl; 1.85 +import com.sun.xml.internal.bind.v2.model.nav.Navigator; 1.86 +import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter; 1.87 +import static com.sun.xml.internal.bind.v2.schemagen.Util.*; 1.88 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Any; 1.89 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttrDecls; 1.90 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexExtension; 1.91 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexType; 1.92 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexTypeHost; 1.93 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ExplicitGroup; 1.94 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Import; 1.95 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List; 1.96 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalAttribute; 1.97 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalElement; 1.98 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Schema; 1.99 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleExtension; 1.100 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleRestrictionModel; 1.101 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleType; 1.102 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleTypeHost; 1.103 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelAttribute; 1.104 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelElement; 1.105 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeHost; 1.106 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ContentModelContainer; 1.107 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeDefParticle; 1.108 +import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttributeType; 1.109 +import com.sun.xml.internal.bind.v2.schemagen.episode.Bindings; 1.110 +import com.sun.xml.internal.txw2.TXW; 1.111 +import com.sun.xml.internal.txw2.TxwException; 1.112 +import com.sun.xml.internal.txw2.TypedXmlWriter; 1.113 +import com.sun.xml.internal.txw2.output.ResultFactory; 1.114 +import com.sun.xml.internal.txw2.output.XmlSerializer; 1.115 +import java.util.Collection; 1.116 +import org.xml.sax.SAXParseException; 1.117 + 1.118 +/** 1.119 + * Generates a set of W3C XML Schema documents from a set of Java classes. 1.120 + * 1.121 + * <p> 1.122 + * A client must invoke methods in the following order: 1.123 + * <ol> 1.124 + * <li>Create a new {@link XmlSchemaGenerator} 1.125 + * <li>Invoke {@link #add} methods, multiple times if necessary. 1.126 + * <li>Invoke {@link #write} 1.127 + * <li>Discard the {@link XmlSchemaGenerator}. 1.128 + * </ol> 1.129 + * 1.130 + * @author Ryan Shoemaker 1.131 + * @author Kohsuke Kawaguchi (kk@kohsuke.org) 1.132 + */ 1.133 +public final class XmlSchemaGenerator<T,C,F,M> { 1.134 + 1.135 + private static final Logger logger = Util.getClassLogger(); 1.136 + 1.137 + /** 1.138 + * Java classes to be written, organized by their namespace. 1.139 + * 1.140 + * <p> 1.141 + * We use a {@link TreeMap} here so that the suggested names will 1.142 + * be consistent across JVMs. 1.143 + * 1.144 + * @see SchemaOutputResolver#createOutput(String, String) 1.145 + */ 1.146 + private final Map<String,Namespace> namespaces = new TreeMap<String,Namespace>(NAMESPACE_COMPARATOR); 1.147 + 1.148 + /** 1.149 + * {@link ErrorListener} to send errors to. 1.150 + */ 1.151 + private ErrorListener errorListener; 1.152 + 1.153 + /** model navigator **/ 1.154 + private Navigator<T,C,F,M> navigator; 1.155 + 1.156 + private final TypeInfoSet<T,C,F,M> types; 1.157 + 1.158 + /** 1.159 + * Representation for xs:string. 1.160 + */ 1.161 + private final NonElement<T,C> stringType; 1.162 + 1.163 + /** 1.164 + * Represents xs:anyType. 1.165 + */ 1.166 + private final NonElement<T,C> anyType; 1.167 + 1.168 + /** 1.169 + * Used to detect cycles in anonymous types. 1.170 + */ 1.171 + private final CollisionCheckStack<ClassInfo<T,C>> collisionChecker = new CollisionCheckStack<ClassInfo<T,C>>(); 1.172 + 1.173 + public XmlSchemaGenerator( Navigator<T,C,F,M> navigator, TypeInfoSet<T,C,F,M> types ) { 1.174 + this.navigator = navigator; 1.175 + this.types = types; 1.176 + 1.177 + this.stringType = types.getTypeInfo(navigator.ref(String.class)); 1.178 + this.anyType = types.getAnyTypeInfo(); 1.179 + 1.180 + // populate the object 1.181 + for( ClassInfo<T,C> ci : types.beans().values() ) 1.182 + add(ci); 1.183 + for( ElementInfo<T,C> ei1 : types.getElementMappings(null).values() ) 1.184 + add(ei1); 1.185 + for( EnumLeafInfo<T,C> ei : types.enums().values() ) 1.186 + add(ei); 1.187 + for( ArrayInfo<T,C> a : types.arrays().values()) 1.188 + add(a); 1.189 + } 1.190 + 1.191 + private Namespace getNamespace(String uri) { 1.192 + Namespace n = namespaces.get(uri); 1.193 + if(n==null) 1.194 + namespaces.put(uri,n=new Namespace(uri)); 1.195 + return n; 1.196 + } 1.197 + 1.198 + /** 1.199 + * Adds a new class to the list of classes to be written. 1.200 + * 1.201 + * <p> 1.202 + * A {@link ClassInfo} may have two namespaces --- one for the element name 1.203 + * and the other for the type name. If they are different, we put the same 1.204 + * {@link ClassInfo} to two {@link Namespace}s. 1.205 + */ 1.206 + public void add( ClassInfo<T,C> clazz ) { 1.207 + assert clazz!=null; 1.208 + 1.209 + String nsUri = null; 1.210 + 1.211 + if(clazz.getClazz()==navigator.asDecl(CompositeStructure.class)) 1.212 + return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema 1.213 + 1.214 + if(clazz.isElement()) { 1.215 + // put element -> type reference 1.216 + nsUri = clazz.getElementName().getNamespaceURI(); 1.217 + Namespace ns = getNamespace(nsUri); 1.218 + ns.classes.add(clazz); 1.219 + ns.addDependencyTo(clazz.getTypeName()); 1.220 + 1.221 + // schedule writing this global element 1.222 + add(clazz.getElementName(),false,clazz); 1.223 + } 1.224 + 1.225 + QName tn = clazz.getTypeName(); 1.226 + if(tn!=null) { 1.227 + nsUri = tn.getNamespaceURI(); 1.228 + } else { 1.229 + // anonymous type 1.230 + if(nsUri==null) 1.231 + return; 1.232 + } 1.233 + 1.234 + Namespace n = getNamespace(nsUri); 1.235 + n.classes.add(clazz); 1.236 + 1.237 + // search properties for foreign namespace references 1.238 + for( PropertyInfo<T,C> p : clazz.getProperties()) { 1.239 + n.processForeignNamespaces(p, 1); 1.240 + if (p instanceof AttributePropertyInfo) { 1.241 + AttributePropertyInfo<T,C> ap = (AttributePropertyInfo<T,C>) p; 1.242 + String aUri = ap.getXmlName().getNamespaceURI(); 1.243 + if(aUri.length()>0) { 1.244 + // global attribute 1.245 + getNamespace(aUri).addGlobalAttribute(ap); 1.246 + n.addDependencyTo(ap.getXmlName()); 1.247 + } 1.248 + } 1.249 + if (p instanceof ElementPropertyInfo) { 1.250 + ElementPropertyInfo<T,C> ep = (ElementPropertyInfo<T,C>) p; 1.251 + for (TypeRef<T,C> tref : ep.getTypes()) { 1.252 + String eUri = tref.getTagName().getNamespaceURI(); 1.253 + if(eUri.length()>0 && !eUri.equals(n.uri)) { 1.254 + getNamespace(eUri).addGlobalElement(tref); 1.255 + n.addDependencyTo(tref.getTagName()); 1.256 + } 1.257 + } 1.258 + } 1.259 + 1.260 + if(generateSwaRefAdapter(p)) 1.261 + n.useSwaRef = true; 1.262 + 1.263 + MimeType mimeType = p.getExpectedMimeType(); 1.264 + if( mimeType != null ) { 1.265 + n.useMimeNs = true; 1.266 + } 1.267 + 1.268 + } 1.269 + 1.270 + // recurse on baseTypes to make sure that we can refer to them in the schema 1.271 + ClassInfo<T,C> bc = clazz.getBaseClass(); 1.272 + if (bc != null) { 1.273 + add(bc); 1.274 + n.addDependencyTo(bc.getTypeName()); 1.275 + } 1.276 + } 1.277 + 1.278 + /** 1.279 + * Adds a new element to the list of elements to be written. 1.280 + */ 1.281 + public void add( ElementInfo<T,C> elem ) { 1.282 + assert elem!=null; 1.283 + 1.284 + @SuppressWarnings("UnusedAssignment") 1.285 + boolean nillable = false; // default value 1.286 + 1.287 + QName name = elem.getElementName(); 1.288 + Namespace n = getNamespace(name.getNamespaceURI()); 1.289 + ElementInfo ei; 1.290 + 1.291 + if (elem.getScope() != null) { // (probably) never happens 1.292 + ei = this.types.getElementInfo(elem.getScope().getClazz(), name); 1.293 + } else { 1.294 + ei = this.types.getElementInfo(null, name); 1.295 + } 1.296 + 1.297 + XmlElement xmlElem = ei.getProperty().readAnnotation(XmlElement.class); 1.298 + 1.299 + if (xmlElem == null) { 1.300 + nillable = false; 1.301 + } else { 1.302 + nillable = xmlElem.nillable(); 1.303 + } 1.304 + 1.305 + n.elementDecls.put(name.getLocalPart(),n.new ElementWithType(nillable, elem.getContentType())); 1.306 + 1.307 + // search for foreign namespace references 1.308 + n.processForeignNamespaces(elem.getProperty(), 1); 1.309 + } 1.310 + 1.311 + public void add( EnumLeafInfo<T,C> envm ) { 1.312 + assert envm!=null; 1.313 + 1.314 + String nsUri = null; 1.315 + 1.316 + if(envm.isElement()) { 1.317 + // put element -> type reference 1.318 + nsUri = envm.getElementName().getNamespaceURI(); 1.319 + Namespace ns = getNamespace(nsUri); 1.320 + ns.enums.add(envm); 1.321 + ns.addDependencyTo(envm.getTypeName()); 1.322 + 1.323 + // schedule writing this global element 1.324 + add(envm.getElementName(),false,envm); 1.325 + } 1.326 + 1.327 + final QName typeName = envm.getTypeName(); 1.328 + if (typeName != null) { 1.329 + nsUri = typeName.getNamespaceURI(); 1.330 + } else { 1.331 + if(nsUri==null) 1.332 + return; // anonymous type 1.333 + } 1.334 + 1.335 + Namespace n = getNamespace(nsUri); 1.336 + n.enums.add(envm); 1.337 + 1.338 + // search for foreign namespace references 1.339 + n.addDependencyTo(envm.getBaseType().getTypeName()); 1.340 + } 1.341 + 1.342 + public void add( ArrayInfo<T,C> a ) { 1.343 + assert a!=null; 1.344 + 1.345 + final String namespaceURI = a.getTypeName().getNamespaceURI(); 1.346 + Namespace n = getNamespace(namespaceURI); 1.347 + n.arrays.add(a); 1.348 + 1.349 + // search for foreign namespace references 1.350 + n.addDependencyTo(a.getItemType().getTypeName()); 1.351 + } 1.352 + 1.353 + /** 1.354 + * Adds an additional element declaration. 1.355 + * 1.356 + * @param tagName 1.357 + * The name of the element declaration to be added. 1.358 + * @param type 1.359 + * The type this element refers to. 1.360 + * Can be null, in which case the element refers to an empty anonymous complex type. 1.361 + */ 1.362 + public void add( QName tagName, boolean isNillable, NonElement<T,C> type ) { 1.363 + 1.364 + if(type!=null && type.getType()==navigator.ref(CompositeStructure.class)) 1.365 + return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema 1.366 + 1.367 + 1.368 + Namespace n = getNamespace(tagName.getNamespaceURI()); 1.369 + n.elementDecls.put(tagName.getLocalPart(), n.new ElementWithType(isNillable,type)); 1.370 + 1.371 + // search for foreign namespace references 1.372 + if(type!=null) 1.373 + n.addDependencyTo(type.getTypeName()); 1.374 + } 1.375 + 1.376 + /** 1.377 + * Writes out the episode file. 1.378 + */ 1.379 + public void writeEpisodeFile(XmlSerializer out) { 1.380 + Bindings root = TXW.create(Bindings.class, out); 1.381 + 1.382 + if(namespaces.containsKey("")) // otherwise jaxb binding NS should be the default namespace 1.383 + root._namespace(WellKnownNamespace.JAXB,"jaxb"); 1.384 + root.version("2.1"); 1.385 + // TODO: don't we want to bake in versions? 1.386 + 1.387 + // generate listing per schema 1.388 + for (Map.Entry<String,Namespace> e : namespaces.entrySet()) { 1.389 + Bindings group = root.bindings(); 1.390 + 1.391 + String prefix; 1.392 + String tns = e.getKey(); 1.393 + if(!tns.equals("")) { 1.394 + group._namespace(tns,"tns"); 1.395 + prefix = "tns:"; 1.396 + } else { 1.397 + prefix = ""; 1.398 + } 1.399 + 1.400 + group.scd("x-schema::"+(tns.equals("")?"":"tns")); 1.401 + group.schemaBindings().map(false); 1.402 + 1.403 + for (ClassInfo<T,C> ci : e.getValue().classes) { 1.404 + if(ci.getTypeName()==null) continue; // local type 1.405 + 1.406 + if(ci.getTypeName().getNamespaceURI().equals(tns)) { 1.407 + Bindings child = group.bindings(); 1.408 + child.scd('~'+prefix+ci.getTypeName().getLocalPart()); 1.409 + child.klass().ref(ci.getName()); 1.410 + } 1.411 + 1.412 + if(ci.isElement() && ci.getElementName().getNamespaceURI().equals(tns)) { 1.413 + Bindings child = group.bindings(); 1.414 + child.scd(prefix+ci.getElementName().getLocalPart()); 1.415 + child.klass().ref(ci.getName()); 1.416 + } 1.417 + } 1.418 + 1.419 + for (EnumLeafInfo<T,C> en : e.getValue().enums) { 1.420 + if(en.getTypeName()==null) continue; // local type 1.421 + 1.422 + Bindings child = group.bindings(); 1.423 + child.scd('~'+prefix+en.getTypeName().getLocalPart()); 1.424 + child.klass().ref(navigator.getClassName(en.getClazz())); 1.425 + } 1.426 + 1.427 + group.commit(true); 1.428 + } 1.429 + 1.430 + root.commit(); 1.431 + } 1.432 + 1.433 + /** 1.434 + * Write out the schema documents. 1.435 + */ 1.436 + public void write(SchemaOutputResolver resolver, ErrorListener errorListener) throws IOException { 1.437 + if(resolver==null) 1.438 + throw new IllegalArgumentException(); 1.439 + 1.440 + if(logger.isLoggable(Level.FINE)) { 1.441 + // debug logging to see what's going on. 1.442 + logger.log(Level.FINE,"Wrigin XML Schema for "+toString(),new StackRecorder()); 1.443 + } 1.444 + 1.445 + // make it fool-proof 1.446 + resolver = new FoolProofResolver(resolver); 1.447 + this.errorListener = errorListener; 1.448 + 1.449 + Map<String, String> schemaLocations = types.getSchemaLocations(); 1.450 + 1.451 + Map<Namespace,Result> out = new HashMap<Namespace,Result>(); 1.452 + Map<Namespace,String> systemIds = new HashMap<Namespace,String>(); 1.453 + 1.454 + // we create a Namespace object for the XML Schema namespace 1.455 + // as a side-effect, but we don't want to generate it. 1.456 + namespaces.remove(WellKnownNamespace.XML_SCHEMA); 1.457 + 1.458 + // first create the outputs for all so that we can resolve references among 1.459 + // schema files when we write 1.460 + for( Namespace n : namespaces.values() ) { 1.461 + String schemaLocation = schemaLocations.get(n.uri); 1.462 + if(schemaLocation!=null) { 1.463 + systemIds.put(n,schemaLocation); 1.464 + } else { 1.465 + Result output = resolver.createOutput(n.uri,"schema"+(out.size()+1)+".xsd"); 1.466 + if(output!=null) { // null result means no schema for that namespace 1.467 + out.put(n,output); 1.468 + systemIds.put(n,output.getSystemId()); 1.469 + } 1.470 + } 1.471 + } 1.472 + 1.473 + // then write'em all 1.474 + for( Map.Entry<Namespace,Result> e : out.entrySet() ) { 1.475 + Result result = e.getValue(); 1.476 + e.getKey().writeTo( result, systemIds ); 1.477 + if(result instanceof StreamResult) { 1.478 + OutputStream outputStream = ((StreamResult)result).getOutputStream(); 1.479 + if(outputStream != null) { 1.480 + outputStream.close(); // fix for bugid: 6291301 1.481 + } else { 1.482 + final Writer writer = ((StreamResult)result).getWriter(); 1.483 + if(writer != null) writer.close(); 1.484 + } 1.485 + } 1.486 + } 1.487 + } 1.488 + 1.489 + 1.490 + 1.491 + /** 1.492 + * Schema components are organized per namespace. 1.493 + */ 1.494 + private class Namespace { 1.495 + final @NotNull String uri; 1.496 + 1.497 + /** 1.498 + * Other {@link Namespace}s that this namespace depends on. 1.499 + */ 1.500 + private final Set<Namespace> depends = new LinkedHashSet<Namespace>(); 1.501 + 1.502 + /** 1.503 + * If this schema refers to components from this schema by itself. 1.504 + */ 1.505 + private boolean selfReference; 1.506 + 1.507 + /** 1.508 + * List of classes in this namespace. 1.509 + */ 1.510 + private final Set<ClassInfo<T,C>> classes = new LinkedHashSet<ClassInfo<T,C>>(); 1.511 + 1.512 + /** 1.513 + * Set of enums in this namespace 1.514 + */ 1.515 + private final Set<EnumLeafInfo<T,C>> enums = new LinkedHashSet<EnumLeafInfo<T,C>>(); 1.516 + 1.517 + /** 1.518 + * Set of arrays in this namespace 1.519 + */ 1.520 + private final Set<ArrayInfo<T,C>> arrays = new LinkedHashSet<ArrayInfo<T,C>>(); 1.521 + 1.522 + /** 1.523 + * Global attribute declarations keyed by their local names. 1.524 + */ 1.525 + private final MultiMap<String,AttributePropertyInfo<T,C>> attributeDecls = new MultiMap<String,AttributePropertyInfo<T,C>>(null); 1.526 + 1.527 + /** 1.528 + * Global element declarations to be written, keyed by their local names. 1.529 + */ 1.530 + private final MultiMap<String,ElementDeclaration> elementDecls = 1.531 + new MultiMap<String,ElementDeclaration>(new ElementWithType(true,anyType)); 1.532 + 1.533 + private Form attributeFormDefault; 1.534 + private Form elementFormDefault; 1.535 + 1.536 + /** 1.537 + * Does schema in this namespace uses swaRef? If so, we need to generate import 1.538 + * statement. 1.539 + */ 1.540 + private boolean useSwaRef; 1.541 + 1.542 + /** 1.543 + * Import for mime namespace needs to be generated. 1.544 + * See #856 1.545 + */ 1.546 + private boolean useMimeNs; 1.547 + 1.548 + public Namespace(String uri) { 1.549 + this.uri = uri; 1.550 + assert !XmlSchemaGenerator.this.namespaces.containsKey(uri); 1.551 + XmlSchemaGenerator.this.namespaces.put(uri,this); 1.552 + } 1.553 + 1.554 + /** 1.555 + * Process the given PropertyInfo looking for references to namespaces that 1.556 + * are foreign to the given namespace. Any foreign namespace references 1.557 + * found are added to the given namespaces dependency list and an <import> 1.558 + * is generated for it. 1.559 + * 1.560 + * @param p the PropertyInfo 1.561 + */ 1.562 + private void processForeignNamespaces(PropertyInfo<T, C> p, int processingDepth) { 1.563 + for (TypeInfo<T, C> t : p.ref()) { 1.564 + if ((t instanceof ClassInfo) && (processingDepth > 0)) { 1.565 + java.util.List<PropertyInfo> l = ((ClassInfo) t).getProperties(); 1.566 + for (PropertyInfo subp : l) { 1.567 + processForeignNamespaces(subp, --processingDepth); 1.568 + } 1.569 + } 1.570 + if (t instanceof Element) { 1.571 + addDependencyTo(((Element) t).getElementName()); 1.572 + } 1.573 + if (t instanceof NonElement) { 1.574 + addDependencyTo(((NonElement) t).getTypeName()); 1.575 + } 1.576 + } 1.577 + } 1.578 + 1.579 + private void addDependencyTo(@Nullable QName qname) { 1.580 + // even though the Element interface says getElementName() returns non-null, 1.581 + // ClassInfo always implements Element (even if an instance of ClassInfo might not be an Element). 1.582 + // so this check is still necessary 1.583 + if (qname==null) { 1.584 + return; 1.585 + } 1.586 + 1.587 + String nsUri = qname.getNamespaceURI(); 1.588 + 1.589 + if (nsUri.equals(XML_SCHEMA)) { 1.590 + // no need to explicitly refer to XSD namespace 1.591 + return; 1.592 + } 1.593 + 1.594 + if (nsUri.equals(uri)) { 1.595 + selfReference = true; 1.596 + return; 1.597 + } 1.598 + 1.599 + // found a type in a foreign namespace, so make sure we generate an import for it 1.600 + depends.add(getNamespace(nsUri)); 1.601 + } 1.602 + 1.603 + /** 1.604 + * Writes the schema document to the specified result. 1.605 + * 1.606 + * @param systemIds 1.607 + * System IDs of the other schema documents. "" indicates 'implied'. 1.608 + */ 1.609 + private void writeTo(Result result, Map<Namespace,String> systemIds) throws IOException { 1.610 + try { 1.611 + Schema schema = TXW.create(Schema.class,ResultFactory.createSerializer(result)); 1.612 + 1.613 + // additional namespace declarations to be made. 1.614 + Map<String, String> xmlNs = types.getXmlNs(uri); 1.615 + 1.616 + for (Map.Entry<String, String> e : xmlNs.entrySet()) { 1.617 + schema._namespace(e.getValue(),e.getKey()); 1.618 + } 1.619 + 1.620 + if(useSwaRef) 1.621 + schema._namespace(WellKnownNamespace.SWA_URI,"swaRef"); 1.622 + 1.623 + if(useMimeNs) 1.624 + schema._namespace(WellKnownNamespace.XML_MIME_URI,"xmime"); 1.625 + 1.626 + attributeFormDefault = Form.get(types.getAttributeFormDefault(uri)); 1.627 + attributeFormDefault.declare("attributeFormDefault",schema); 1.628 + 1.629 + elementFormDefault = Form.get(types.getElementFormDefault(uri)); 1.630 + // TODO: if elementFormDefault is UNSET, figure out the right default value to use 1.631 + elementFormDefault.declare("elementFormDefault",schema); 1.632 + 1.633 + 1.634 + // declare XML Schema namespace to be xs, but allow the user to override it. 1.635 + // if 'xs' is used for other things, we'll just let TXW assign a random prefix 1.636 + if(!xmlNs.containsValue(WellKnownNamespace.XML_SCHEMA) 1.637 + && !xmlNs.containsKey("xs")) 1.638 + schema._namespace(WellKnownNamespace.XML_SCHEMA,"xs"); 1.639 + schema.version("1.0"); 1.640 + 1.641 + if(uri.length()!=0) 1.642 + schema.targetNamespace(uri); 1.643 + 1.644 + // declare prefixes for them at this level, so that we can avoid redundant 1.645 + // namespace declarations 1.646 + for (Namespace ns : depends) { 1.647 + schema._namespace(ns.uri); 1.648 + } 1.649 + 1.650 + if(selfReference && uri.length()!=0) { 1.651 + // use common 'tns' prefix for the own namespace 1.652 + // if self-reference is needed 1.653 + schema._namespace(uri,"tns"); 1.654 + } 1.655 + 1.656 + schema._pcdata(newline); 1.657 + 1.658 + // refer to other schemas 1.659 + for( Namespace n : depends ) { 1.660 + Import imp = schema._import(); 1.661 + if(n.uri.length()!=0) 1.662 + imp.namespace(n.uri); 1.663 + String refSystemId = systemIds.get(n); 1.664 + if(refSystemId!=null && !refSystemId.equals("")) { 1.665 + // "" means implied. null if the SchemaOutputResolver said "don't generate!" 1.666 + imp.schemaLocation(relativize(refSystemId,result.getSystemId())); 1.667 + } 1.668 + schema._pcdata(newline); 1.669 + } 1.670 + if(useSwaRef) { 1.671 + schema._import().namespace(WellKnownNamespace.SWA_URI).schemaLocation("http://ws-i.org/profiles/basic/1.1/swaref.xsd"); 1.672 + } 1.673 + if(useMimeNs) { 1.674 + schema._import().namespace(WellKnownNamespace.XML_MIME_URI).schemaLocation("http://www.w3.org/2005/05/xmlmime"); 1.675 + } 1.676 + 1.677 + // then write each component 1.678 + for (Map.Entry<String,ElementDeclaration> e : elementDecls.entrySet()) { 1.679 + e.getValue().writeTo(e.getKey(),schema); 1.680 + schema._pcdata(newline); 1.681 + } 1.682 + for (ClassInfo<T, C> c : classes) { 1.683 + if (c.getTypeName()==null) { 1.684 + // don't generate anything if it's an anonymous type 1.685 + continue; 1.686 + } 1.687 + if(uri.equals(c.getTypeName().getNamespaceURI())) 1.688 + writeClass(c, schema); 1.689 + schema._pcdata(newline); 1.690 + } 1.691 + for (EnumLeafInfo<T, C> e : enums) { 1.692 + if (e.getTypeName()==null) { 1.693 + // don't generate anything if it's an anonymous type 1.694 + continue; 1.695 + } 1.696 + if(uri.equals(e.getTypeName().getNamespaceURI())) 1.697 + writeEnum(e,schema); 1.698 + schema._pcdata(newline); 1.699 + } 1.700 + for (ArrayInfo<T, C> a : arrays) { 1.701 + writeArray(a,schema); 1.702 + schema._pcdata(newline); 1.703 + } 1.704 + for (Map.Entry<String,AttributePropertyInfo<T,C>> e : attributeDecls.entrySet()) { 1.705 + TopLevelAttribute a = schema.attribute(); 1.706 + a.name(e.getKey()); 1.707 + if(e.getValue()==null) 1.708 + writeTypeRef(a,stringType,"type"); 1.709 + else 1.710 + writeAttributeTypeRef(e.getValue(),a); 1.711 + schema._pcdata(newline); 1.712 + } 1.713 + 1.714 + // close the schema 1.715 + schema.commit(); 1.716 + } catch( TxwException e ) { 1.717 + logger.log(Level.INFO,e.getMessage(),e); 1.718 + throw new IOException(e.getMessage()); 1.719 + } 1.720 + } 1.721 + 1.722 + /** 1.723 + * Writes a type attribute (if the referenced type is a global type) 1.724 + * or writes out the definition of the anonymous type in place (if the referenced 1.725 + * type is not a global type.) 1.726 + * 1.727 + * Also provides processing for ID/IDREF, MTOM @xmime, and swa:ref 1.728 + * 1.729 + * ComplexTypeHost and SimpleTypeHost don't share an api for creating 1.730 + * and attribute in a type-safe way, so we will compromise for now and 1.731 + * use _attribute(). 1.732 + */ 1.733 + private void writeTypeRef(TypeHost th, NonElementRef<T, C> typeRef, String refAttName) { 1.734 + // ID / IDREF handling 1.735 + switch(typeRef.getSource().id()) { 1.736 + case ID: 1.737 + th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "ID")); 1.738 + return; 1.739 + case IDREF: 1.740 + th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "IDREF")); 1.741 + return; 1.742 + case NONE: 1.743 + // no ID/IDREF, so continue on and generate the type 1.744 + break; 1.745 + default: 1.746 + throw new IllegalStateException(); 1.747 + } 1.748 + 1.749 + // MTOM handling 1.750 + MimeType mimeType = typeRef.getSource().getExpectedMimeType(); 1.751 + if( mimeType != null ) { 1.752 + th._attribute(new QName(WellKnownNamespace.XML_MIME_URI, "expectedContentTypes", "xmime"), mimeType.toString()); 1.753 + } 1.754 + 1.755 + // ref:swaRef handling 1.756 + if(generateSwaRefAdapter(typeRef)) { 1.757 + th._attribute(refAttName, new QName(WellKnownNamespace.SWA_URI, "swaRef", "ref")); 1.758 + return; 1.759 + } 1.760 + 1.761 + // type name override 1.762 + if(typeRef.getSource().getSchemaType()!=null) { 1.763 + th._attribute(refAttName,typeRef.getSource().getSchemaType()); 1.764 + return; 1.765 + } 1.766 + 1.767 + // normal type generation 1.768 + writeTypeRef(th, typeRef.getTarget(), refAttName); 1.769 + } 1.770 + 1.771 + /** 1.772 + * Writes a type attribute (if the referenced type is a global type) 1.773 + * or writes out the definition of the anonymous type in place (if the referenced 1.774 + * type is not a global type.) 1.775 + * 1.776 + * @param th 1.777 + * the TXW interface to which the attribute will be written. 1.778 + * @param type 1.779 + * type to be referenced. 1.780 + * @param refAttName 1.781 + * The name of the attribute used when referencing a type by QName. 1.782 + */ 1.783 + private void writeTypeRef(TypeHost th, NonElement<T,C> type, String refAttName) { 1.784 + Element e = null; 1.785 + if (type instanceof MaybeElement) { 1.786 + MaybeElement me = (MaybeElement)type; 1.787 + boolean isElement = me.isElement(); 1.788 + if (isElement) e = me.asElement(); 1.789 + } 1.790 + if (type instanceof Element) { 1.791 + e = (Element)type; 1.792 + } 1.793 + if (type.getTypeName()==null) { 1.794 + if ((e != null) && (e.getElementName() != null)) { 1.795 + th.block(); // so that the caller may write other attributes 1.796 + if(type instanceof ClassInfo) { 1.797 + writeClass( (ClassInfo<T,C>)type, th ); 1.798 + } else { 1.799 + writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th); 1.800 + } 1.801 + } else { 1.802 + // anonymous 1.803 + th.block(); // so that the caller may write other attributes 1.804 + if(type instanceof ClassInfo) { 1.805 + if(collisionChecker.push((ClassInfo<T,C>)type)) { 1.806 + errorListener.warning(new SAXParseException( 1.807 + Messages.ANONYMOUS_TYPE_CYCLE.format(collisionChecker.getCycleString()), 1.808 + null 1.809 + )); 1.810 + } else { 1.811 + writeClass( (ClassInfo<T,C>)type, th ); 1.812 + } 1.813 + collisionChecker.pop(); 1.814 + } else { 1.815 + writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th); 1.816 + } 1.817 + } 1.818 + } else { 1.819 + th._attribute(refAttName,type.getTypeName()); 1.820 + } 1.821 + } 1.822 + 1.823 + /** 1.824 + * writes the schema definition for the given array class 1.825 + */ 1.826 + private void writeArray(ArrayInfo<T, C> a, Schema schema) { 1.827 + ComplexType ct = schema.complexType().name(a.getTypeName().getLocalPart()); 1.828 + ct._final("#all"); 1.829 + LocalElement le = ct.sequence().element().name("item"); 1.830 + le.type(a.getItemType().getTypeName()); 1.831 + le.minOccurs(0).maxOccurs("unbounded"); 1.832 + le.nillable(true); 1.833 + ct.commit(); 1.834 + } 1.835 + 1.836 + /** 1.837 + * writes the schema definition for the specified type-safe enum in the given TypeHost 1.838 + */ 1.839 + private void writeEnum(EnumLeafInfo<T, C> e, SimpleTypeHost th) { 1.840 + SimpleType st = th.simpleType(); 1.841 + writeName(e,st); 1.842 + 1.843 + SimpleRestrictionModel base = st.restriction(); 1.844 + writeTypeRef(base, e.getBaseType(), "base"); 1.845 + 1.846 + for (EnumConstant c : e.getConstants()) { 1.847 + base.enumeration().value(c.getLexicalValue()); 1.848 + } 1.849 + st.commit(); 1.850 + } 1.851 + 1.852 + /** 1.853 + * Writes the schema definition for the specified class to the schema writer. 1.854 + * 1.855 + * @param c the class info 1.856 + * @param parent the writer of the parent element into which the type will be defined 1.857 + */ 1.858 + private void writeClass(ClassInfo<T,C> c, TypeHost parent) { 1.859 + // special handling for value properties 1.860 + if (containsValueProp(c)) { 1.861 + if (c.getProperties().size() == 1) { 1.862 + // [RESULT 2 - simpleType if the value prop is the only prop] 1.863 + // 1.864 + // <simpleType name="foo"> 1.865 + // <xs:restriction base="xs:int"/> 1.866 + // </> 1.867 + ValuePropertyInfo<T,C> vp = (ValuePropertyInfo<T,C>)c.getProperties().get(0); 1.868 + SimpleType st = ((SimpleTypeHost)parent).simpleType(); 1.869 + writeName(c, st); 1.870 + if(vp.isCollection()) { 1.871 + writeTypeRef(st.list(),vp.getTarget(),"itemType"); 1.872 + } else { 1.873 + writeTypeRef(st.restriction(),vp.getTarget(),"base"); 1.874 + } 1.875 + return; 1.876 + } else { 1.877 + // [RESULT 1 - complexType with simpleContent] 1.878 + // 1.879 + // <complexType name="foo"> 1.880 + // <simpleContent> 1.881 + // <extension base="xs:int"/> 1.882 + // <attribute name="b" type="xs:boolean"/> 1.883 + // </> 1.884 + // </> 1.885 + // </> 1.886 + // ... 1.887 + // <element name="f" type="foo"/> 1.888 + // ... 1.889 + ComplexType ct = ((ComplexTypeHost)parent).complexType(); 1.890 + writeName(c,ct); 1.891 + if(c.isFinal()) 1.892 + ct._final("extension restriction"); 1.893 + 1.894 + SimpleExtension se = ct.simpleContent().extension(); 1.895 + se.block(); // because we might have attribute before value 1.896 + for (PropertyInfo<T,C> p : c.getProperties()) { 1.897 + switch (p.kind()) { 1.898 + case ATTRIBUTE: 1.899 + handleAttributeProp((AttributePropertyInfo<T,C>)p,se); 1.900 + break; 1.901 + case VALUE: 1.902 + TODO.checkSpec("what if vp.isCollection() == true?"); 1.903 + ValuePropertyInfo vp = (ValuePropertyInfo) p; 1.904 + se.base(vp.getTarget().getTypeName()); 1.905 + break; 1.906 + case ELEMENT: // error 1.907 + case REFERENCE: // error 1.908 + default: 1.909 + assert false; 1.910 + throw new IllegalStateException(); 1.911 + } 1.912 + } 1.913 + se.commit(); 1.914 + } 1.915 + TODO.schemaGenerator("figure out what to do if bc != null"); 1.916 + TODO.checkSpec("handle sec 8.9.5.2, bullet #4"); 1.917 + // Java types containing value props can only contain properties of type 1.918 + // ValuePropertyinfo and AttributePropertyInfo which have just been handled, 1.919 + // so return. 1.920 + return; 1.921 + } 1.922 + 1.923 + // we didn't fall into the special case for value props, so we 1.924 + // need to initialize the ct. 1.925 + // generate the complexType 1.926 + ComplexType ct = ((ComplexTypeHost)parent).complexType(); 1.927 + writeName(c,ct); 1.928 + if(c.isFinal()) 1.929 + ct._final("extension restriction"); 1.930 + if(c.isAbstract()) 1.931 + ct._abstract(true); 1.932 + 1.933 + // these are where we write content model and attributes 1.934 + AttrDecls contentModel = ct; 1.935 + TypeDefParticle contentModelOwner = ct; 1.936 + 1.937 + // if there is a base class, we need to generate an extension in the schema 1.938 + final ClassInfo<T,C> bc = c.getBaseClass(); 1.939 + if (bc != null) { 1.940 + if(bc.hasValueProperty()) { 1.941 + // extending complex type with simple content 1.942 + SimpleExtension se = ct.simpleContent().extension(); 1.943 + contentModel = se; 1.944 + contentModelOwner = null; 1.945 + se.base(bc.getTypeName()); 1.946 + } else { 1.947 + ComplexExtension ce = ct.complexContent().extension(); 1.948 + contentModel = ce; 1.949 + contentModelOwner = ce; 1.950 + 1.951 + ce.base(bc.getTypeName()); 1.952 + // TODO: what if the base type is anonymous? 1.953 + } 1.954 + } 1.955 + 1.956 + if(contentModelOwner!=null) { 1.957 + // build the tree that represents the explicit content model from iterate over the properties 1.958 + ArrayList<Tree> children = new ArrayList<Tree>(); 1.959 + for (PropertyInfo<T,C> p : c.getProperties()) { 1.960 + // handling for <complexType @mixed='true' ...> 1.961 + if(p instanceof ReferencePropertyInfo && ((ReferencePropertyInfo)p).isMixed()) { 1.962 + ct.mixed(true); 1.963 + } 1.964 + Tree t = buildPropertyContentModel(p); 1.965 + if(t!=null) 1.966 + children.add(t); 1.967 + } 1.968 + 1.969 + Tree top = Tree.makeGroup( c.isOrdered() ? GroupKind.SEQUENCE : GroupKind.ALL, children); 1.970 + 1.971 + // write the content model 1.972 + top.write(contentModelOwner); 1.973 + } 1.974 + 1.975 + // then attributes 1.976 + for (PropertyInfo<T,C> p : c.getProperties()) { 1.977 + if (p instanceof AttributePropertyInfo) { 1.978 + handleAttributeProp((AttributePropertyInfo<T,C>)p, contentModel); 1.979 + } 1.980 + } 1.981 + if( c.hasAttributeWildcard()) { 1.982 + contentModel.anyAttribute().namespace("##other").processContents("skip"); 1.983 + } 1.984 + ct.commit(); 1.985 + } 1.986 + 1.987 + /** 1.988 + * Writes the name attribute if it's named. 1.989 + */ 1.990 + private void writeName(NonElement<T,C> c, TypedXmlWriter xw) { 1.991 + QName tn = c.getTypeName(); 1.992 + if(tn!=null) 1.993 + xw._attribute("name",tn.getLocalPart()); // named 1.994 + } 1.995 + 1.996 + private boolean containsValueProp(ClassInfo<T, C> c) { 1.997 + for (PropertyInfo p : c.getProperties()) { 1.998 + if (p instanceof ValuePropertyInfo) return true; 1.999 + } 1.1000 + return false; 1.1001 + } 1.1002 + 1.1003 + /** 1.1004 + * Builds content model writer for the specified property. 1.1005 + */ 1.1006 + private Tree buildPropertyContentModel(PropertyInfo<T,C> p) { 1.1007 + switch(p.kind()) { 1.1008 + case ELEMENT: 1.1009 + return handleElementProp((ElementPropertyInfo<T,C>)p); 1.1010 + case ATTRIBUTE: 1.1011 + // attribuets are handled later 1.1012 + return null; 1.1013 + case REFERENCE: 1.1014 + return handleReferenceProp((ReferencePropertyInfo<T,C>)p); 1.1015 + case MAP: 1.1016 + return handleMapProp((MapPropertyInfo<T,C>)p); 1.1017 + case VALUE: 1.1018 + // value props handled above in writeClass() 1.1019 + assert false; 1.1020 + throw new IllegalStateException(); 1.1021 + default: 1.1022 + assert false; 1.1023 + throw new IllegalStateException(); 1.1024 + } 1.1025 + } 1.1026 + 1.1027 + /** 1.1028 + * Generate the proper schema fragment for the given element property into the 1.1029 + * specified schema compositor. 1.1030 + * 1.1031 + * The element property may or may not represent a collection and it may or may 1.1032 + * not be wrapped. 1.1033 + * 1.1034 + * @param ep the element property 1.1035 + */ 1.1036 + private Tree handleElementProp(final ElementPropertyInfo<T,C> ep) { 1.1037 + if (ep.isValueList()) { 1.1038 + return new Tree.Term() { 1.1039 + protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { 1.1040 + TypeRef<T,C> t = ep.getTypes().get(0); 1.1041 + LocalElement e = parent.element(); 1.1042 + e.block(); // we will write occurs later 1.1043 + QName tn = t.getTagName(); 1.1044 + e.name(tn.getLocalPart()); 1.1045 + List lst = e.simpleType().list(); 1.1046 + writeTypeRef(lst,t, "itemType"); 1.1047 + elementFormDefault.writeForm(e,tn); 1.1048 + writeOccurs(e,isOptional||!ep.isRequired(),repeated); 1.1049 + } 1.1050 + }; 1.1051 + } 1.1052 + 1.1053 + ArrayList<Tree> children = new ArrayList<Tree>(); 1.1054 + for (final TypeRef<T,C> t : ep.getTypes()) { 1.1055 + children.add(new Tree.Term() { 1.1056 + protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { 1.1057 + LocalElement e = parent.element(); 1.1058 + 1.1059 + QName tn = t.getTagName(); 1.1060 + 1.1061 + PropertyInfo propInfo = t.getSource(); 1.1062 + TypeInfo parentInfo = (propInfo == null) ? null : propInfo.parent(); 1.1063 + 1.1064 + if (canBeDirectElementRef(t, tn, parentInfo)) { 1.1065 + if ((!t.getTarget().isSimpleType()) && (t.getTarget() instanceof ClassInfo) && collisionChecker.findDuplicate((ClassInfo<T, C>) t.getTarget())) { 1.1066 + e.ref(new QName(uri, tn.getLocalPart())); 1.1067 + } else { 1.1068 + 1.1069 + QName elemName = null; 1.1070 + if (t.getTarget() instanceof Element) { 1.1071 + Element te = (Element) t.getTarget(); 1.1072 + elemName = te.getElementName(); 1.1073 + } 1.1074 + 1.1075 + Collection<TypeInfo> refs = propInfo.ref(); 1.1076 + TypeInfo ti; 1.1077 + if ((refs != null) && (!refs.isEmpty()) && (elemName != null) 1.1078 + && ((ti = refs.iterator().next()) == null || ti instanceof ClassInfoImpl)) { 1.1079 + ClassInfoImpl cImpl = (ClassInfoImpl)ti; 1.1080 + if ((cImpl != null) && (cImpl.getElementName() != null)) { 1.1081 + e.ref(new QName(cImpl.getElementName().getNamespaceURI(), tn.getLocalPart())); 1.1082 + } else { 1.1083 + e.ref(new QName("", tn.getLocalPart())); 1.1084 + } 1.1085 + } else { 1.1086 + e.ref(tn); 1.1087 + } 1.1088 + } 1.1089 + } else { 1.1090 + e.name(tn.getLocalPart()); 1.1091 + writeTypeRef(e,t, "type"); 1.1092 + elementFormDefault.writeForm(e,tn); 1.1093 + } 1.1094 + 1.1095 + if (t.isNillable()) { 1.1096 + e.nillable(true); 1.1097 + } 1.1098 + if(t.getDefaultValue()!=null) 1.1099 + e._default(t.getDefaultValue()); 1.1100 + writeOccurs(e,isOptional,repeated); 1.1101 + } 1.1102 + }); 1.1103 + } 1.1104 + 1.1105 + final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children) 1.1106 + .makeOptional(!ep.isRequired()) 1.1107 + .makeRepeated(ep.isCollection()); // see Spec table 8-13 1.1108 + 1.1109 + 1.1110 + final QName ename = ep.getXmlName(); 1.1111 + if (ename != null) { // wrapped collection 1.1112 + return new Tree.Term() { 1.1113 + protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { 1.1114 + LocalElement e = parent.element(); 1.1115 + if(ename.getNamespaceURI().length()>0) { 1.1116 + if (!ename.getNamespaceURI().equals(uri)) { 1.1117 + // TODO: we need to generate the corresponding element declaration for this 1.1118 + // table 8-25: Property/field element wrapper with ref attribute 1.1119 + e.ref(new QName(ename.getNamespaceURI(), ename.getLocalPart())); 1.1120 + return; 1.1121 + } 1.1122 + } 1.1123 + e.name(ename.getLocalPart()); 1.1124 + elementFormDefault.writeForm(e,ename); 1.1125 + 1.1126 + if(ep.isCollectionNillable()) { 1.1127 + e.nillable(true); 1.1128 + } 1.1129 + writeOccurs(e,!ep.isCollectionRequired(),repeated); 1.1130 + 1.1131 + ComplexType p = e.complexType(); 1.1132 + choice.write(p); 1.1133 + } 1.1134 + }; 1.1135 + } else {// non-wrapped 1.1136 + return choice; 1.1137 + } 1.1138 + } 1.1139 + 1.1140 + /** 1.1141 + * Checks if we can collapse 1.1142 + * <element name='foo' type='t' /> to <element ref='foo' />. 1.1143 + * 1.1144 + * This is possible if we already have such declaration to begin with. 1.1145 + */ 1.1146 + private boolean canBeDirectElementRef(TypeRef<T, C> t, QName tn, TypeInfo parentInfo) { 1.1147 + Element te = null; 1.1148 + ClassInfo ci = null; 1.1149 + QName targetTagName = null; 1.1150 + 1.1151 + if(t.isNillable() || t.getDefaultValue()!=null) { 1.1152 + // can't put those attributes on <element ref> 1.1153 + return false; 1.1154 + } 1.1155 + 1.1156 + if (t.getTarget() instanceof Element) { 1.1157 + te = (Element) t.getTarget(); 1.1158 + targetTagName = te.getElementName(); 1.1159 + if (te instanceof ClassInfo) { 1.1160 + ci = (ClassInfo)te; 1.1161 + } 1.1162 + } 1.1163 + 1.1164 + String nsUri = tn.getNamespaceURI(); 1.1165 + if ((!nsUri.equals(uri) && nsUri.length()>0) && (!((parentInfo instanceof ClassInfo) && (((ClassInfo)parentInfo).getTypeName() == null)))) { 1.1166 + return true; 1.1167 + } 1.1168 + 1.1169 + if ((ci != null) && ((targetTagName != null) && (te.getScope() == null) && (targetTagName.getNamespaceURI() == null))) { 1.1170 + if (targetTagName.equals(tn)) { 1.1171 + return true; 1.1172 + } 1.1173 + } 1.1174 + 1.1175 + // we have the precise element defined already 1.1176 + if (te != null) { // it is instanceof Element 1.1177 + return targetTagName!=null && targetTagName.equals(tn); 1.1178 + } 1.1179 + 1.1180 + return false; 1.1181 + } 1.1182 + 1.1183 + 1.1184 + /** 1.1185 + * Generate an attribute for the specified property on the specified complexType 1.1186 + * 1.1187 + * @param ap the attribute 1.1188 + * @param attr the schema definition to which the attribute will be added 1.1189 + */ 1.1190 + private void handleAttributeProp(AttributePropertyInfo<T,C> ap, AttrDecls attr) { 1.1191 + // attr is either a top-level ComplexType or a ComplexExtension 1.1192 + // 1.1193 + // [RESULT] 1.1194 + // 1.1195 + // <complexType ...> 1.1196 + // <...>...</> 1.1197 + // <attribute name="foo" type="xs:int"/> 1.1198 + // </> 1.1199 + // 1.1200 + // or 1.1201 + // 1.1202 + // <complexType ...> 1.1203 + // <complexContent> 1.1204 + // <extension ...> 1.1205 + // <...>...</> 1.1206 + // </> 1.1207 + // </> 1.1208 + // <attribute name="foo" type="xs:int"/> 1.1209 + // </> 1.1210 + // 1.1211 + // or it could also be an in-lined type (attr ref) 1.1212 + // 1.1213 + LocalAttribute localAttribute = attr.attribute(); 1.1214 + 1.1215 + final String attrURI = ap.getXmlName().getNamespaceURI(); 1.1216 + if (attrURI.equals("") /*|| attrURI.equals(uri) --- those are generated as global attributes anyway, so use them.*/) { 1.1217 + localAttribute.name(ap.getXmlName().getLocalPart()); 1.1218 + 1.1219 + writeAttributeTypeRef(ap, localAttribute); 1.1220 + 1.1221 + attributeFormDefault.writeForm(localAttribute,ap.getXmlName()); 1.1222 + } else { // generate an attr ref 1.1223 + localAttribute.ref(ap.getXmlName()); 1.1224 + } 1.1225 + 1.1226 + if(ap.isRequired()) { 1.1227 + // TODO: not type safe 1.1228 + localAttribute.use("required"); 1.1229 + } 1.1230 + } 1.1231 + 1.1232 + private void writeAttributeTypeRef(AttributePropertyInfo<T,C> ap, AttributeType a) { 1.1233 + if( ap.isCollection() ) 1.1234 + writeTypeRef(a.simpleType().list(), ap, "itemType"); 1.1235 + else 1.1236 + writeTypeRef(a, ap, "type"); 1.1237 + } 1.1238 + 1.1239 + /** 1.1240 + * Generate the proper schema fragment for the given reference property into the 1.1241 + * specified schema compositor. 1.1242 + * 1.1243 + * The reference property may or may not refer to a collection and it may or may 1.1244 + * not be wrapped. 1.1245 + */ 1.1246 + private Tree handleReferenceProp(final ReferencePropertyInfo<T, C> rp) { 1.1247 + // fill in content model 1.1248 + ArrayList<Tree> children = new ArrayList<Tree>(); 1.1249 + 1.1250 + for (final Element<T,C> e : rp.getElements()) { 1.1251 + children.add(new Tree.Term() { 1.1252 + protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { 1.1253 + LocalElement eref = parent.element(); 1.1254 + 1.1255 + boolean local=false; 1.1256 + 1.1257 + QName en = e.getElementName(); 1.1258 + if(e.getScope()!=null) { 1.1259 + // scoped. needs to be inlined 1.1260 + boolean qualified = en.getNamespaceURI().equals(uri); 1.1261 + boolean unqualified = en.getNamespaceURI().equals(""); 1.1262 + if(qualified || unqualified) { 1.1263 + // can be inlined indeed 1.1264 + 1.1265 + // write form="..." if necessary 1.1266 + if(unqualified) { 1.1267 + if(elementFormDefault.isEffectivelyQualified) 1.1268 + eref.form("unqualified"); 1.1269 + } else { 1.1270 + if(!elementFormDefault.isEffectivelyQualified) 1.1271 + eref.form("qualified"); 1.1272 + } 1.1273 + 1.1274 + local = true; 1.1275 + eref.name(en.getLocalPart()); 1.1276 + 1.1277 + // write out type reference 1.1278 + if(e instanceof ClassInfo) { 1.1279 + writeTypeRef(eref,(ClassInfo<T,C>)e,"type"); 1.1280 + } else { 1.1281 + writeTypeRef(eref,((ElementInfo<T,C>)e).getContentType(),"type"); 1.1282 + } 1.1283 + } 1.1284 + } 1.1285 + if(!local) 1.1286 + eref.ref(en); 1.1287 + writeOccurs(eref,isOptional,repeated); 1.1288 + } 1.1289 + }); 1.1290 + } 1.1291 + 1.1292 + final WildcardMode wc = rp.getWildcard(); 1.1293 + if( wc != null ) { 1.1294 + children.add(new Tree.Term() { 1.1295 + protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { 1.1296 + Any any = parent.any(); 1.1297 + final String pcmode = getProcessContentsModeName(wc); 1.1298 + if( pcmode != null ) any.processContents(pcmode); 1.1299 + any.namespace("##other"); 1.1300 + writeOccurs(any,isOptional,repeated); 1.1301 + } 1.1302 + }); 1.1303 + } 1.1304 + 1.1305 + 1.1306 + final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children).makeRepeated(rp.isCollection()).makeOptional(!rp.isRequired()); 1.1307 + 1.1308 + final QName ename = rp.getXmlName(); 1.1309 + 1.1310 + if (ename != null) { // wrapped 1.1311 + return new Tree.Term() { 1.1312 + protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { 1.1313 + LocalElement e = parent.element().name(ename.getLocalPart()); 1.1314 + elementFormDefault.writeForm(e,ename); 1.1315 + if(rp.isCollectionNillable()) 1.1316 + e.nillable(true); 1.1317 + writeOccurs(e,true,repeated); 1.1318 + 1.1319 + ComplexType p = e.complexType(); 1.1320 + choice.write(p); 1.1321 + } 1.1322 + }; 1.1323 + } else { // unwrapped 1.1324 + return choice; 1.1325 + } 1.1326 + } 1.1327 + 1.1328 + /** 1.1329 + * Generate the proper schema fragment for the given map property into the 1.1330 + * specified schema compositor. 1.1331 + * 1.1332 + * @param mp the map property 1.1333 + */ 1.1334 + private Tree handleMapProp(final MapPropertyInfo<T,C> mp) { 1.1335 + return new Tree.Term() { 1.1336 + protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { 1.1337 + QName ename = mp.getXmlName(); 1.1338 + 1.1339 + LocalElement e = parent.element(); 1.1340 + elementFormDefault.writeForm(e,ename); 1.1341 + if(mp.isCollectionNillable()) 1.1342 + e.nillable(true); 1.1343 + 1.1344 + e = e.name(ename.getLocalPart()); 1.1345 + writeOccurs(e,isOptional,repeated); 1.1346 + ComplexType p = e.complexType(); 1.1347 + 1.1348 + // TODO: entry, key, and value are always unqualified. that needs to be fixed, too. 1.1349 + // TODO: we need to generate the corresponding element declaration, if they are qualified 1.1350 + e = p.sequence().element(); 1.1351 + e.name("entry").minOccurs(0).maxOccurs("unbounded"); 1.1352 + 1.1353 + ExplicitGroup seq = e.complexType().sequence(); 1.1354 + writeKeyOrValue(seq, "key", mp.getKeyType()); 1.1355 + writeKeyOrValue(seq, "value", mp.getValueType()); 1.1356 + } 1.1357 + }; 1.1358 + } 1.1359 + 1.1360 + private void writeKeyOrValue(ExplicitGroup seq, String tagName, NonElement<T, C> typeRef) { 1.1361 + LocalElement key = seq.element().name(tagName); 1.1362 + key.minOccurs(0); 1.1363 + writeTypeRef(key, typeRef, "type"); 1.1364 + } 1.1365 + 1.1366 + public void addGlobalAttribute(AttributePropertyInfo<T,C> ap) { 1.1367 + attributeDecls.put( ap.getXmlName().getLocalPart(), ap ); 1.1368 + addDependencyTo(ap.getTarget().getTypeName()); 1.1369 + } 1.1370 + 1.1371 + public void addGlobalElement(TypeRef<T,C> tref) { 1.1372 + elementDecls.put( tref.getTagName().getLocalPart(), new ElementWithType(false,tref.getTarget()) ); 1.1373 + addDependencyTo(tref.getTarget().getTypeName()); 1.1374 + } 1.1375 + 1.1376 + @Override 1.1377 + public String toString() { 1.1378 + StringBuilder buf = new StringBuilder(); 1.1379 + buf.append("[classes=").append(classes); 1.1380 + buf.append(",elementDecls=").append(elementDecls); 1.1381 + buf.append(",enums=").append(enums); 1.1382 + buf.append("]"); 1.1383 + return super.toString(); 1.1384 + } 1.1385 + 1.1386 + /** 1.1387 + * Represents a global element declaration to be written. 1.1388 + * 1.1389 + * <p> 1.1390 + * Because multiple properties can name the same global element even if 1.1391 + * they have different Java type, the schema generator first needs to 1.1392 + * walk through the model and decide what to generate for the given 1.1393 + * element declaration. 1.1394 + * 1.1395 + * <p> 1.1396 + * This class represents what will be written, and its {@link #equals(Object)} 1.1397 + * method is implemented in such a way that two identical declarations 1.1398 + * are considered as the same. 1.1399 + */ 1.1400 + abstract class ElementDeclaration { 1.1401 + /** 1.1402 + * Returns true if two {@link ElementDeclaration}s are representing 1.1403 + * the same schema fragment. 1.1404 + */ 1.1405 + @Override 1.1406 + public abstract boolean equals(Object o); 1.1407 + @Override 1.1408 + public abstract int hashCode(); 1.1409 + 1.1410 + /** 1.1411 + * Generates the declaration. 1.1412 + */ 1.1413 + public abstract void writeTo(String localName, Schema schema); 1.1414 + } 1.1415 + 1.1416 + /** 1.1417 + * {@link ElementDeclaration} that refers to a {@link NonElement}. 1.1418 + */ 1.1419 + class ElementWithType extends ElementDeclaration { 1.1420 + private final boolean nillable; 1.1421 + private final NonElement<T,C> type; 1.1422 + 1.1423 + public ElementWithType(boolean nillable,NonElement<T, C> type) { 1.1424 + this.type = type; 1.1425 + this.nillable = nillable; 1.1426 + } 1.1427 + 1.1428 + public void writeTo(String localName, Schema schema) { 1.1429 + TopLevelElement e = schema.element().name(localName); 1.1430 + if(nillable) 1.1431 + e.nillable(true); 1.1432 + if (type != null) { 1.1433 + writeTypeRef(e,type, "type"); 1.1434 + } else { 1.1435 + e.complexType(); // refer to the nested empty complex type 1.1436 + } 1.1437 + e.commit(); 1.1438 + } 1.1439 + 1.1440 + public boolean equals(Object o) { 1.1441 + if (this == o) return true; 1.1442 + if (o == null || getClass() != o.getClass()) return false; 1.1443 + 1.1444 + final ElementWithType that = (ElementWithType) o; 1.1445 + return type.equals(that.type); 1.1446 + } 1.1447 + 1.1448 + public int hashCode() { 1.1449 + return type.hashCode(); 1.1450 + } 1.1451 + } 1.1452 + } 1.1453 + 1.1454 + /** 1.1455 + * Examine the specified element ref and determine if a swaRef attribute needs to be generated 1.1456 + * @param typeRef 1.1457 + */ 1.1458 + private boolean generateSwaRefAdapter(NonElementRef<T,C> typeRef) { 1.1459 + return generateSwaRefAdapter(typeRef.getSource()); 1.1460 + } 1.1461 + 1.1462 + /** 1.1463 + * Examine the specified element ref and determine if a swaRef attribute needs to be generated 1.1464 + */ 1.1465 + private boolean generateSwaRefAdapter(PropertyInfo<T,C> prop) { 1.1466 + final Adapter<T,C> adapter = prop.getAdapter(); 1.1467 + if (adapter == null) return false; 1.1468 + final Object o = navigator.asDecl(SwaRefAdapter.class); 1.1469 + if (o == null) return false; 1.1470 + return (o.equals(adapter.adapterType)); 1.1471 + } 1.1472 + 1.1473 + /** 1.1474 + * Debug information of what's in this {@link XmlSchemaGenerator}. 1.1475 + */ 1.1476 + @Override 1.1477 + public String toString() { 1.1478 + StringBuilder buf = new StringBuilder(); 1.1479 + for (Namespace ns : namespaces.values()) { 1.1480 + if(buf.length()>0) buf.append(','); 1.1481 + buf.append(ns.uri).append('=').append(ns); 1.1482 + } 1.1483 + return super.toString()+'['+buf+']'; 1.1484 + } 1.1485 + 1.1486 + /** 1.1487 + * return the string representation of the processContents mode of the 1.1488 + * give wildcard, or null if it is the schema default "strict" 1.1489 + * 1.1490 + */ 1.1491 + private static String getProcessContentsModeName(WildcardMode wc) { 1.1492 + switch(wc) { 1.1493 + case LAX: 1.1494 + case SKIP: 1.1495 + return wc.name().toLowerCase(); 1.1496 + case STRICT: 1.1497 + return null; 1.1498 + default: 1.1499 + throw new IllegalStateException(); 1.1500 + } 1.1501 + } 1.1502 + 1.1503 + 1.1504 + /** 1.1505 + * Relativizes a URI by using another URI (base URI.) 1.1506 + * 1.1507 + * <p> 1.1508 + * For example, {@code relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"} 1.1509 + * 1.1510 + * <p> 1.1511 + * This method only works on hierarchical URI's, not opaque URI's (refer to the 1.1512 + * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html">java.net.URI</a> 1.1513 + * javadoc for complete definitions of these terms. 1.1514 + * 1.1515 + * <p> 1.1516 + * This method will not normalize the relative URI. 1.1517 + * 1.1518 + * @return the relative URI or the original URI if a relative one could not be computed 1.1519 + */ 1.1520 + protected static String relativize(String uri, String baseUri) { 1.1521 + try { 1.1522 + assert uri!=null; 1.1523 + 1.1524 + if(baseUri==null) return uri; 1.1525 + 1.1526 + URI theUri = new URI(escapeURI(uri)); 1.1527 + URI theBaseUri = new URI(escapeURI(baseUri)); 1.1528 + 1.1529 + if (theUri.isOpaque() || theBaseUri.isOpaque()) 1.1530 + return uri; 1.1531 + 1.1532 + if (!equalsIgnoreCase(theUri.getScheme(), theBaseUri.getScheme()) || 1.1533 + !equal(theUri.getAuthority(), theBaseUri.getAuthority())) 1.1534 + return uri; 1.1535 + 1.1536 + String uriPath = theUri.getPath(); 1.1537 + String basePath = theBaseUri.getPath(); 1.1538 + 1.1539 + // normalize base path 1.1540 + if (!basePath.endsWith("/")) { 1.1541 + basePath = normalizeUriPath(basePath); 1.1542 + } 1.1543 + 1.1544 + if( uriPath.equals(basePath)) 1.1545 + return "."; 1.1546 + 1.1547 + String relPath = calculateRelativePath(uriPath, basePath, fixNull(theUri.getScheme()).equals("file")); 1.1548 + 1.1549 + if (relPath == null) 1.1550 + return uri; // recursion found no commonality in the two uris at all 1.1551 + StringBuilder relUri = new StringBuilder(); 1.1552 + relUri.append(relPath); 1.1553 + if (theUri.getQuery() != null) 1.1554 + relUri.append('?').append(theUri.getQuery()); 1.1555 + if (theUri.getFragment() != null) 1.1556 + relUri.append('#').append(theUri.getFragment()); 1.1557 + 1.1558 + return relUri.toString(); 1.1559 + } catch (URISyntaxException e) { 1.1560 + throw new InternalError("Error escaping one of these uris:\n\t"+uri+"\n\t"+baseUri); 1.1561 + } 1.1562 + } 1.1563 + 1.1564 + private static String fixNull(String s) { 1.1565 + if(s==null) return ""; 1.1566 + else return s; 1.1567 + } 1.1568 + 1.1569 + private static String calculateRelativePath(String uri, String base, boolean fileUrl) { 1.1570 + // if this is a file URL (very likely), and if this is on a case-insensitive file system, 1.1571 + // then treat it accordingly. 1.1572 + boolean onWindows = File.pathSeparatorChar==';'; 1.1573 + 1.1574 + if (base == null) { 1.1575 + return null; 1.1576 + } 1.1577 + if ((fileUrl && onWindows && startsWithIgnoreCase(uri,base)) || uri.startsWith(base)) { 1.1578 + return uri.substring(base.length()); 1.1579 + } else { 1.1580 + return "../" + calculateRelativePath(uri, getParentUriPath(base), fileUrl); 1.1581 + } 1.1582 + } 1.1583 + 1.1584 + private static boolean startsWithIgnoreCase(String s, String t) { 1.1585 + return s.toUpperCase().startsWith(t.toUpperCase()); 1.1586 + } 1.1587 + 1.1588 + /** 1.1589 + * JAX-RPC wants the namespaces to be sorted in the reverse order 1.1590 + * so that the empty namespace "" comes to the very end. Don't ask me why. 1.1591 + */ 1.1592 + private static final Comparator<String> NAMESPACE_COMPARATOR = new Comparator<String>() { 1.1593 + public int compare(String lhs, String rhs) { 1.1594 + return -lhs.compareTo(rhs); 1.1595 + } 1.1596 + }; 1.1597 + 1.1598 + private static final String newline = "\n"; 1.1599 +}