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