src/share/jaxws_classes/com/sun/xml/internal/bind/v2/schemagen/XmlSchemaGenerator.java

Thu, 24 May 2018 16:44:14 +0800

author
aoqi
date
Thu, 24 May 2018 16:44:14 +0800
changeset 1288
f4ace6971570
parent 1101
bd88174c3095
parent 637
9c07ef4934dd
permissions
-rw-r--r--

Merge

aoqi@0 1 /*
aefimov@1101 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25
aoqi@0 26 package com.sun.xml.internal.bind.v2.schemagen;
aoqi@0 27
aoqi@0 28 import java.io.IOException;
aoqi@0 29 import java.io.OutputStream;
aoqi@0 30 import java.io.Writer;
aoqi@0 31 import java.io.File;
aoqi@0 32 import java.net.URI;
aoqi@0 33 import java.net.URISyntaxException;
aoqi@0 34 import java.util.Comparator;
aoqi@0 35 import java.util.HashMap;
aoqi@0 36 import java.util.LinkedHashSet;
aoqi@0 37 import java.util.Map;
aoqi@0 38 import java.util.Set;
aoqi@0 39 import java.util.TreeMap;
aoqi@0 40 import java.util.ArrayList;
aoqi@0 41 import java.util.logging.Level;
aoqi@0 42 import java.util.logging.Logger;
aoqi@0 43
aoqi@0 44 import javax.activation.MimeType;
aoqi@0 45 import javax.xml.bind.SchemaOutputResolver;
aoqi@0 46 import javax.xml.bind.annotation.XmlElement;
aoqi@0 47 import javax.xml.namespace.QName;
aoqi@0 48 import javax.xml.transform.Result;
aoqi@0 49 import javax.xml.transform.stream.StreamResult;
aoqi@0 50
aoqi@0 51 import com.sun.istack.internal.Nullable;
aoqi@0 52 import com.sun.istack.internal.NotNull;
aoqi@0 53 import com.sun.xml.internal.bind.Util;
aoqi@0 54 import com.sun.xml.internal.bind.api.CompositeStructure;
aoqi@0 55 import com.sun.xml.internal.bind.api.ErrorListener;
aoqi@0 56 import com.sun.xml.internal.bind.v2.TODO;
aoqi@0 57 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
aoqi@0 58 import com.sun.xml.internal.bind.v2.util.CollisionCheckStack;
aoqi@0 59 import com.sun.xml.internal.bind.v2.util.StackRecorder;
aoqi@0 60 import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_SCHEMA;
aoqi@0 61 import com.sun.xml.internal.bind.v2.model.core.Adapter;
aoqi@0 62 import com.sun.xml.internal.bind.v2.model.core.ArrayInfo;
aoqi@0 63 import com.sun.xml.internal.bind.v2.model.core.AttributePropertyInfo;
aoqi@0 64 import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
aoqi@0 65 import com.sun.xml.internal.bind.v2.model.core.Element;
aoqi@0 66 import com.sun.xml.internal.bind.v2.model.core.ElementInfo;
aoqi@0 67 import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo;
aoqi@0 68 import com.sun.xml.internal.bind.v2.model.core.EnumConstant;
aoqi@0 69 import com.sun.xml.internal.bind.v2.model.core.EnumLeafInfo;
aoqi@0 70 import com.sun.xml.internal.bind.v2.model.core.MapPropertyInfo;
aoqi@0 71 import com.sun.xml.internal.bind.v2.model.core.MaybeElement;
aoqi@0 72 import com.sun.xml.internal.bind.v2.model.core.NonElement;
aoqi@0 73 import com.sun.xml.internal.bind.v2.model.core.NonElementRef;
aoqi@0 74 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
aoqi@0 75 import com.sun.xml.internal.bind.v2.model.core.ReferencePropertyInfo;
aoqi@0 76 import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
aoqi@0 77 import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
aoqi@0 78 import com.sun.xml.internal.bind.v2.model.core.TypeRef;
aoqi@0 79 import com.sun.xml.internal.bind.v2.model.core.ValuePropertyInfo;
aoqi@0 80 import com.sun.xml.internal.bind.v2.model.core.WildcardMode;
aoqi@0 81 import com.sun.xml.internal.bind.v2.model.impl.ClassInfoImpl;
aoqi@0 82 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
aoqi@0 83 import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;
aoqi@0 84 import static com.sun.xml.internal.bind.v2.schemagen.Util.*;
aoqi@0 85 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Any;
aoqi@0 86 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttrDecls;
aoqi@0 87 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexExtension;
aoqi@0 88 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexType;
aoqi@0 89 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexTypeHost;
aoqi@0 90 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ExplicitGroup;
aoqi@0 91 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Import;
aoqi@0 92 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List;
aoqi@0 93 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalAttribute;
aoqi@0 94 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalElement;
aoqi@0 95 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Schema;
aoqi@0 96 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleExtension;
aoqi@0 97 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleRestrictionModel;
aoqi@0 98 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleType;
aoqi@0 99 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleTypeHost;
aoqi@0 100 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelAttribute;
aoqi@0 101 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelElement;
aoqi@0 102 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeHost;
aoqi@0 103 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ContentModelContainer;
aoqi@0 104 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeDefParticle;
aoqi@0 105 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttributeType;
aoqi@0 106 import com.sun.xml.internal.bind.v2.schemagen.episode.Bindings;
aoqi@0 107 import com.sun.xml.internal.txw2.TXW;
aoqi@0 108 import com.sun.xml.internal.txw2.TxwException;
aoqi@0 109 import com.sun.xml.internal.txw2.TypedXmlWriter;
aoqi@0 110 import com.sun.xml.internal.txw2.output.ResultFactory;
aoqi@0 111 import com.sun.xml.internal.txw2.output.XmlSerializer;
aoqi@0 112 import java.util.Collection;
aefimov@1101 113 import java.util.HashSet;
aoqi@0 114 import org.xml.sax.SAXParseException;
aoqi@0 115
aoqi@0 116 /**
aoqi@0 117 * Generates a set of W3C XML Schema documents from a set of Java classes.
aoqi@0 118 *
aoqi@0 119 * <p>
aoqi@0 120 * A client must invoke methods in the following order:
aoqi@0 121 * <ol>
aoqi@0 122 * <li>Create a new {@link XmlSchemaGenerator}
aoqi@0 123 * <li>Invoke {@link #add} methods, multiple times if necessary.
aoqi@0 124 * <li>Invoke {@link #write}
aoqi@0 125 * <li>Discard the {@link XmlSchemaGenerator}.
aoqi@0 126 * </ol>
aoqi@0 127 *
aoqi@0 128 * @author Ryan Shoemaker
aoqi@0 129 * @author Kohsuke Kawaguchi (kk@kohsuke.org)
aoqi@0 130 */
aoqi@0 131 public final class XmlSchemaGenerator<T,C,F,M> {
aoqi@0 132
aoqi@0 133 private static final Logger logger = Util.getClassLogger();
aoqi@0 134
aoqi@0 135 /**
aoqi@0 136 * Java classes to be written, organized by their namespace.
aoqi@0 137 *
aoqi@0 138 * <p>
aoqi@0 139 * We use a {@link TreeMap} here so that the suggested names will
aoqi@0 140 * be consistent across JVMs.
aoqi@0 141 *
aoqi@0 142 * @see SchemaOutputResolver#createOutput(String, String)
aoqi@0 143 */
aoqi@0 144 private final Map<String,Namespace> namespaces = new TreeMap<String,Namespace>(NAMESPACE_COMPARATOR);
aoqi@0 145
aoqi@0 146 /**
aoqi@0 147 * {@link ErrorListener} to send errors to.
aoqi@0 148 */
aoqi@0 149 private ErrorListener errorListener;
aoqi@0 150
aoqi@0 151 /** model navigator **/
aoqi@0 152 private Navigator<T,C,F,M> navigator;
aoqi@0 153
aoqi@0 154 private final TypeInfoSet<T,C,F,M> types;
aoqi@0 155
aoqi@0 156 /**
aoqi@0 157 * Representation for xs:string.
aoqi@0 158 */
aoqi@0 159 private final NonElement<T,C> stringType;
aoqi@0 160
aoqi@0 161 /**
aoqi@0 162 * Represents xs:anyType.
aoqi@0 163 */
aoqi@0 164 private final NonElement<T,C> anyType;
aoqi@0 165
aoqi@0 166 /**
aoqi@0 167 * Used to detect cycles in anonymous types.
aoqi@0 168 */
aoqi@0 169 private final CollisionCheckStack<ClassInfo<T,C>> collisionChecker = new CollisionCheckStack<ClassInfo<T,C>>();
aoqi@0 170
aoqi@0 171 public XmlSchemaGenerator( Navigator<T,C,F,M> navigator, TypeInfoSet<T,C,F,M> types ) {
aoqi@0 172 this.navigator = navigator;
aoqi@0 173 this.types = types;
aoqi@0 174
aoqi@0 175 this.stringType = types.getTypeInfo(navigator.ref(String.class));
aoqi@0 176 this.anyType = types.getAnyTypeInfo();
aoqi@0 177
aoqi@0 178 // populate the object
aoqi@0 179 for( ClassInfo<T,C> ci : types.beans().values() )
aoqi@0 180 add(ci);
aoqi@0 181 for( ElementInfo<T,C> ei1 : types.getElementMappings(null).values() )
aoqi@0 182 add(ei1);
aoqi@0 183 for( EnumLeafInfo<T,C> ei : types.enums().values() )
aoqi@0 184 add(ei);
aoqi@0 185 for( ArrayInfo<T,C> a : types.arrays().values())
aoqi@0 186 add(a);
aoqi@0 187 }
aoqi@0 188
aoqi@0 189 private Namespace getNamespace(String uri) {
aoqi@0 190 Namespace n = namespaces.get(uri);
aoqi@0 191 if(n==null)
aoqi@0 192 namespaces.put(uri,n=new Namespace(uri));
aoqi@0 193 return n;
aoqi@0 194 }
aoqi@0 195
aoqi@0 196 /**
aoqi@0 197 * Adds a new class to the list of classes to be written.
aoqi@0 198 *
aoqi@0 199 * <p>
aoqi@0 200 * A {@link ClassInfo} may have two namespaces --- one for the element name
aoqi@0 201 * and the other for the type name. If they are different, we put the same
aoqi@0 202 * {@link ClassInfo} to two {@link Namespace}s.
aoqi@0 203 */
aoqi@0 204 public void add( ClassInfo<T,C> clazz ) {
aoqi@0 205 assert clazz!=null;
aoqi@0 206
aoqi@0 207 String nsUri = null;
aoqi@0 208
aoqi@0 209 if(clazz.getClazz()==navigator.asDecl(CompositeStructure.class))
aoqi@0 210 return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
aoqi@0 211
aoqi@0 212 if(clazz.isElement()) {
aoqi@0 213 // put element -> type reference
aoqi@0 214 nsUri = clazz.getElementName().getNamespaceURI();
aoqi@0 215 Namespace ns = getNamespace(nsUri);
aoqi@0 216 ns.classes.add(clazz);
aoqi@0 217 ns.addDependencyTo(clazz.getTypeName());
aoqi@0 218
aoqi@0 219 // schedule writing this global element
aoqi@0 220 add(clazz.getElementName(),false,clazz);
aoqi@0 221 }
aoqi@0 222
aoqi@0 223 QName tn = clazz.getTypeName();
aoqi@0 224 if(tn!=null) {
aoqi@0 225 nsUri = tn.getNamespaceURI();
aoqi@0 226 } else {
aoqi@0 227 // anonymous type
aoqi@0 228 if(nsUri==null)
aoqi@0 229 return;
aoqi@0 230 }
aoqi@0 231
aoqi@0 232 Namespace n = getNamespace(nsUri);
aoqi@0 233 n.classes.add(clazz);
aoqi@0 234
aoqi@0 235 // search properties for foreign namespace references
aoqi@0 236 for( PropertyInfo<T,C> p : clazz.getProperties()) {
aoqi@0 237 n.processForeignNamespaces(p, 1);
aoqi@0 238 if (p instanceof AttributePropertyInfo) {
aoqi@0 239 AttributePropertyInfo<T,C> ap = (AttributePropertyInfo<T,C>) p;
aoqi@0 240 String aUri = ap.getXmlName().getNamespaceURI();
aoqi@0 241 if(aUri.length()>0) {
aoqi@0 242 // global attribute
aoqi@0 243 getNamespace(aUri).addGlobalAttribute(ap);
aoqi@0 244 n.addDependencyTo(ap.getXmlName());
aoqi@0 245 }
aoqi@0 246 }
aoqi@0 247 if (p instanceof ElementPropertyInfo) {
aoqi@0 248 ElementPropertyInfo<T,C> ep = (ElementPropertyInfo<T,C>) p;
aoqi@0 249 for (TypeRef<T,C> tref : ep.getTypes()) {
aoqi@0 250 String eUri = tref.getTagName().getNamespaceURI();
aoqi@0 251 if(eUri.length()>0 && !eUri.equals(n.uri)) {
aoqi@0 252 getNamespace(eUri).addGlobalElement(tref);
aoqi@0 253 n.addDependencyTo(tref.getTagName());
aoqi@0 254 }
aoqi@0 255 }
aoqi@0 256 }
aoqi@0 257
aoqi@0 258 if(generateSwaRefAdapter(p))
aoqi@0 259 n.useSwaRef = true;
aoqi@0 260
aoqi@0 261 MimeType mimeType = p.getExpectedMimeType();
aoqi@0 262 if( mimeType != null ) {
aoqi@0 263 n.useMimeNs = true;
aoqi@0 264 }
aoqi@0 265
aoqi@0 266 }
aoqi@0 267
aoqi@0 268 // recurse on baseTypes to make sure that we can refer to them in the schema
aoqi@0 269 ClassInfo<T,C> bc = clazz.getBaseClass();
aoqi@0 270 if (bc != null) {
aoqi@0 271 add(bc);
aoqi@0 272 n.addDependencyTo(bc.getTypeName());
aoqi@0 273 }
aoqi@0 274 }
aoqi@0 275
aoqi@0 276 /**
aoqi@0 277 * Adds a new element to the list of elements to be written.
aoqi@0 278 */
aoqi@0 279 public void add( ElementInfo<T,C> elem ) {
aoqi@0 280 assert elem!=null;
aoqi@0 281
aoqi@0 282 @SuppressWarnings("UnusedAssignment")
aoqi@0 283 boolean nillable = false; // default value
aoqi@0 284
aoqi@0 285 QName name = elem.getElementName();
aoqi@0 286 Namespace n = getNamespace(name.getNamespaceURI());
aoqi@0 287 ElementInfo ei;
aoqi@0 288
aoqi@0 289 if (elem.getScope() != null) { // (probably) never happens
aoqi@0 290 ei = this.types.getElementInfo(elem.getScope().getClazz(), name);
aoqi@0 291 } else {
aoqi@0 292 ei = this.types.getElementInfo(null, name);
aoqi@0 293 }
aoqi@0 294
aoqi@0 295 XmlElement xmlElem = ei.getProperty().readAnnotation(XmlElement.class);
aoqi@0 296
aoqi@0 297 if (xmlElem == null) {
aoqi@0 298 nillable = false;
aoqi@0 299 } else {
aoqi@0 300 nillable = xmlElem.nillable();
aoqi@0 301 }
aoqi@0 302
aoqi@0 303 n.elementDecls.put(name.getLocalPart(),n.new ElementWithType(nillable, elem.getContentType()));
aoqi@0 304
aoqi@0 305 // search for foreign namespace references
aoqi@0 306 n.processForeignNamespaces(elem.getProperty(), 1);
aoqi@0 307 }
aoqi@0 308
aoqi@0 309 public void add( EnumLeafInfo<T,C> envm ) {
aoqi@0 310 assert envm!=null;
aoqi@0 311
aoqi@0 312 String nsUri = null;
aoqi@0 313
aoqi@0 314 if(envm.isElement()) {
aoqi@0 315 // put element -> type reference
aoqi@0 316 nsUri = envm.getElementName().getNamespaceURI();
aoqi@0 317 Namespace ns = getNamespace(nsUri);
aoqi@0 318 ns.enums.add(envm);
aoqi@0 319 ns.addDependencyTo(envm.getTypeName());
aoqi@0 320
aoqi@0 321 // schedule writing this global element
aoqi@0 322 add(envm.getElementName(),false,envm);
aoqi@0 323 }
aoqi@0 324
aoqi@0 325 final QName typeName = envm.getTypeName();
aoqi@0 326 if (typeName != null) {
aoqi@0 327 nsUri = typeName.getNamespaceURI();
aoqi@0 328 } else {
aoqi@0 329 if(nsUri==null)
aoqi@0 330 return; // anonymous type
aoqi@0 331 }
aoqi@0 332
aoqi@0 333 Namespace n = getNamespace(nsUri);
aoqi@0 334 n.enums.add(envm);
aoqi@0 335
aoqi@0 336 // search for foreign namespace references
aoqi@0 337 n.addDependencyTo(envm.getBaseType().getTypeName());
aoqi@0 338 }
aoqi@0 339
aoqi@0 340 public void add( ArrayInfo<T,C> a ) {
aoqi@0 341 assert a!=null;
aoqi@0 342
aoqi@0 343 final String namespaceURI = a.getTypeName().getNamespaceURI();
aoqi@0 344 Namespace n = getNamespace(namespaceURI);
aoqi@0 345 n.arrays.add(a);
aoqi@0 346
aoqi@0 347 // search for foreign namespace references
aoqi@0 348 n.addDependencyTo(a.getItemType().getTypeName());
aoqi@0 349 }
aoqi@0 350
aoqi@0 351 /**
aoqi@0 352 * Adds an additional element declaration.
aoqi@0 353 *
aoqi@0 354 * @param tagName
aoqi@0 355 * The name of the element declaration to be added.
aoqi@0 356 * @param type
aoqi@0 357 * The type this element refers to.
aoqi@0 358 * Can be null, in which case the element refers to an empty anonymous complex type.
aoqi@0 359 */
aoqi@0 360 public void add( QName tagName, boolean isNillable, NonElement<T,C> type ) {
aoqi@0 361
aoqi@0 362 if(type!=null && type.getType()==navigator.ref(CompositeStructure.class))
aoqi@0 363 return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
aoqi@0 364
aoqi@0 365
aoqi@0 366 Namespace n = getNamespace(tagName.getNamespaceURI());
aoqi@0 367 n.elementDecls.put(tagName.getLocalPart(), n.new ElementWithType(isNillable,type));
aoqi@0 368
aoqi@0 369 // search for foreign namespace references
aoqi@0 370 if(type!=null)
aoqi@0 371 n.addDependencyTo(type.getTypeName());
aoqi@0 372 }
aoqi@0 373
aoqi@0 374 /**
aoqi@0 375 * Writes out the episode file.
aoqi@0 376 */
aoqi@0 377 public void writeEpisodeFile(XmlSerializer out) {
aoqi@0 378 Bindings root = TXW.create(Bindings.class, out);
aoqi@0 379
aoqi@0 380 if(namespaces.containsKey("")) // otherwise jaxb binding NS should be the default namespace
aoqi@0 381 root._namespace(WellKnownNamespace.JAXB,"jaxb");
aoqi@0 382 root.version("2.1");
aoqi@0 383 // TODO: don't we want to bake in versions?
aoqi@0 384
aoqi@0 385 // generate listing per schema
aoqi@0 386 for (Map.Entry<String,Namespace> e : namespaces.entrySet()) {
aoqi@0 387 Bindings group = root.bindings();
aoqi@0 388
aoqi@0 389 String prefix;
aoqi@0 390 String tns = e.getKey();
aoqi@0 391 if(!tns.equals("")) {
aoqi@0 392 group._namespace(tns,"tns");
aoqi@0 393 prefix = "tns:";
aoqi@0 394 } else {
aoqi@0 395 prefix = "";
aoqi@0 396 }
aoqi@0 397
aoqi@0 398 group.scd("x-schema::"+(tns.equals("")?"":"tns"));
aoqi@0 399 group.schemaBindings().map(false);
aoqi@0 400
aoqi@0 401 for (ClassInfo<T,C> ci : e.getValue().classes) {
aoqi@0 402 if(ci.getTypeName()==null) continue; // local type
aoqi@0 403
aoqi@0 404 if(ci.getTypeName().getNamespaceURI().equals(tns)) {
aoqi@0 405 Bindings child = group.bindings();
aoqi@0 406 child.scd('~'+prefix+ci.getTypeName().getLocalPart());
aoqi@0 407 child.klass().ref(ci.getName());
aoqi@0 408 }
aoqi@0 409
aoqi@0 410 if(ci.isElement() && ci.getElementName().getNamespaceURI().equals(tns)) {
aoqi@0 411 Bindings child = group.bindings();
aoqi@0 412 child.scd(prefix+ci.getElementName().getLocalPart());
aoqi@0 413 child.klass().ref(ci.getName());
aoqi@0 414 }
aoqi@0 415 }
aoqi@0 416
aoqi@0 417 for (EnumLeafInfo<T,C> en : e.getValue().enums) {
aoqi@0 418 if(en.getTypeName()==null) continue; // local type
aoqi@0 419
aoqi@0 420 Bindings child = group.bindings();
aoqi@0 421 child.scd('~'+prefix+en.getTypeName().getLocalPart());
aoqi@0 422 child.klass().ref(navigator.getClassName(en.getClazz()));
aoqi@0 423 }
aoqi@0 424
aoqi@0 425 group.commit(true);
aoqi@0 426 }
aoqi@0 427
aoqi@0 428 root.commit();
aoqi@0 429 }
aoqi@0 430
aoqi@0 431 /**
aoqi@0 432 * Write out the schema documents.
aoqi@0 433 */
aoqi@0 434 public void write(SchemaOutputResolver resolver, ErrorListener errorListener) throws IOException {
aoqi@0 435 if(resolver==null)
aoqi@0 436 throw new IllegalArgumentException();
aoqi@0 437
aoqi@0 438 if(logger.isLoggable(Level.FINE)) {
aoqi@0 439 // debug logging to see what's going on.
aefimov@1101 440 logger.log(Level.FINE,"Writing XML Schema for "+toString(),new StackRecorder());
aoqi@0 441 }
aoqi@0 442
aoqi@0 443 // make it fool-proof
aoqi@0 444 resolver = new FoolProofResolver(resolver);
aoqi@0 445 this.errorListener = errorListener;
aoqi@0 446
aoqi@0 447 Map<String, String> schemaLocations = types.getSchemaLocations();
aoqi@0 448
aoqi@0 449 Map<Namespace,Result> out = new HashMap<Namespace,Result>();
aoqi@0 450 Map<Namespace,String> systemIds = new HashMap<Namespace,String>();
aoqi@0 451
aoqi@0 452 // we create a Namespace object for the XML Schema namespace
aoqi@0 453 // as a side-effect, but we don't want to generate it.
aoqi@0 454 namespaces.remove(WellKnownNamespace.XML_SCHEMA);
aoqi@0 455
aoqi@0 456 // first create the outputs for all so that we can resolve references among
aoqi@0 457 // schema files when we write
aoqi@0 458 for( Namespace n : namespaces.values() ) {
aoqi@0 459 String schemaLocation = schemaLocations.get(n.uri);
aoqi@0 460 if(schemaLocation!=null) {
aoqi@0 461 systemIds.put(n,schemaLocation);
aoqi@0 462 } else {
aoqi@0 463 Result output = resolver.createOutput(n.uri,"schema"+(out.size()+1)+".xsd");
aoqi@0 464 if(output!=null) { // null result means no schema for that namespace
aoqi@0 465 out.put(n,output);
aoqi@0 466 systemIds.put(n,output.getSystemId());
aoqi@0 467 }
aoqi@0 468 }
aefimov@1101 469 //Clear the namespace specific set with already written classes
aefimov@1101 470 n.resetWritten();
aoqi@0 471 }
aoqi@0 472
aoqi@0 473 // then write'em all
aoqi@0 474 for( Map.Entry<Namespace,Result> e : out.entrySet() ) {
aoqi@0 475 Result result = e.getValue();
aoqi@0 476 e.getKey().writeTo( result, systemIds );
aoqi@0 477 if(result instanceof StreamResult) {
aoqi@0 478 OutputStream outputStream = ((StreamResult)result).getOutputStream();
aoqi@0 479 if(outputStream != null) {
aoqi@0 480 outputStream.close(); // fix for bugid: 6291301
aoqi@0 481 } else {
aoqi@0 482 final Writer writer = ((StreamResult)result).getWriter();
aoqi@0 483 if(writer != null) writer.close();
aoqi@0 484 }
aoqi@0 485 }
aoqi@0 486 }
aoqi@0 487 }
aoqi@0 488
aoqi@0 489
aoqi@0 490
aoqi@0 491 /**
aoqi@0 492 * Schema components are organized per namespace.
aoqi@0 493 */
aoqi@0 494 private class Namespace {
aoqi@0 495 final @NotNull String uri;
aoqi@0 496
aoqi@0 497 /**
aoqi@0 498 * Other {@link Namespace}s that this namespace depends on.
aoqi@0 499 */
aoqi@0 500 private final Set<Namespace> depends = new LinkedHashSet<Namespace>();
aoqi@0 501
aoqi@0 502 /**
aoqi@0 503 * If this schema refers to components from this schema by itself.
aoqi@0 504 */
aoqi@0 505 private boolean selfReference;
aoqi@0 506
aoqi@0 507 /**
aoqi@0 508 * List of classes in this namespace.
aoqi@0 509 */
aoqi@0 510 private final Set<ClassInfo<T,C>> classes = new LinkedHashSet<ClassInfo<T,C>>();
aoqi@0 511
aoqi@0 512 /**
aoqi@0 513 * Set of enums in this namespace
aoqi@0 514 */
aoqi@0 515 private final Set<EnumLeafInfo<T,C>> enums = new LinkedHashSet<EnumLeafInfo<T,C>>();
aoqi@0 516
aoqi@0 517 /**
aoqi@0 518 * Set of arrays in this namespace
aoqi@0 519 */
aoqi@0 520 private final Set<ArrayInfo<T,C>> arrays = new LinkedHashSet<ArrayInfo<T,C>>();
aoqi@0 521
aoqi@0 522 /**
aoqi@0 523 * Global attribute declarations keyed by their local names.
aoqi@0 524 */
aoqi@0 525 private final MultiMap<String,AttributePropertyInfo<T,C>> attributeDecls = new MultiMap<String,AttributePropertyInfo<T,C>>(null);
aoqi@0 526
aoqi@0 527 /**
aoqi@0 528 * Global element declarations to be written, keyed by their local names.
aoqi@0 529 */
aoqi@0 530 private final MultiMap<String,ElementDeclaration> elementDecls =
aoqi@0 531 new MultiMap<String,ElementDeclaration>(new ElementWithType(true,anyType));
aoqi@0 532
aoqi@0 533 private Form attributeFormDefault;
aoqi@0 534 private Form elementFormDefault;
aoqi@0 535
aoqi@0 536 /**
aoqi@0 537 * Does schema in this namespace uses swaRef? If so, we need to generate import
aoqi@0 538 * statement.
aoqi@0 539 */
aoqi@0 540 private boolean useSwaRef;
aoqi@0 541
aoqi@0 542 /**
aoqi@0 543 * Import for mime namespace needs to be generated.
aoqi@0 544 * See #856
aoqi@0 545 */
aoqi@0 546 private boolean useMimeNs;
aoqi@0 547
aefimov@1101 548 /**
aefimov@1101 549 * Container for already processed classes
aefimov@1101 550 */
aefimov@1101 551 private final Set<ClassInfo> written = new HashSet<ClassInfo>();
aefimov@1101 552
aoqi@0 553 public Namespace(String uri) {
aoqi@0 554 this.uri = uri;
aoqi@0 555 assert !XmlSchemaGenerator.this.namespaces.containsKey(uri);
aoqi@0 556 XmlSchemaGenerator.this.namespaces.put(uri,this);
aoqi@0 557 }
aoqi@0 558
aoqi@0 559 /**
aefimov@1101 560 * Clear out the set of already processed classes for this namespace
aefimov@1101 561 */
aefimov@1101 562 void resetWritten() {
aefimov@1101 563 written.clear();
aefimov@1101 564 }
aefimov@1101 565
aefimov@1101 566 /**
aoqi@0 567 * Process the given PropertyInfo looking for references to namespaces that
aoqi@0 568 * are foreign to the given namespace. Any foreign namespace references
aoqi@0 569 * found are added to the given namespaces dependency list and an &lt;import>
aoqi@0 570 * is generated for it.
aoqi@0 571 *
aoqi@0 572 * @param p the PropertyInfo
aoqi@0 573 */
aoqi@0 574 private void processForeignNamespaces(PropertyInfo<T, C> p, int processingDepth) {
aoqi@0 575 for (TypeInfo<T, C> t : p.ref()) {
aoqi@0 576 if ((t instanceof ClassInfo) && (processingDepth > 0)) {
aoqi@0 577 java.util.List<PropertyInfo> l = ((ClassInfo) t).getProperties();
aoqi@0 578 for (PropertyInfo subp : l) {
aoqi@0 579 processForeignNamespaces(subp, --processingDepth);
aoqi@0 580 }
aoqi@0 581 }
aoqi@0 582 if (t instanceof Element) {
aoqi@0 583 addDependencyTo(((Element) t).getElementName());
aoqi@0 584 }
aoqi@0 585 if (t instanceof NonElement) {
aoqi@0 586 addDependencyTo(((NonElement) t).getTypeName());
aoqi@0 587 }
aoqi@0 588 }
aoqi@0 589 }
aoqi@0 590
aoqi@0 591 private void addDependencyTo(@Nullable QName qname) {
aoqi@0 592 // even though the Element interface says getElementName() returns non-null,
aoqi@0 593 // ClassInfo always implements Element (even if an instance of ClassInfo might not be an Element).
aoqi@0 594 // so this check is still necessary
aoqi@0 595 if (qname==null) {
aoqi@0 596 return;
aoqi@0 597 }
aoqi@0 598
aoqi@0 599 String nsUri = qname.getNamespaceURI();
aoqi@0 600
aoqi@0 601 if (nsUri.equals(XML_SCHEMA)) {
aoqi@0 602 // no need to explicitly refer to XSD namespace
aoqi@0 603 return;
aoqi@0 604 }
aoqi@0 605
aoqi@0 606 if (nsUri.equals(uri)) {
aoqi@0 607 selfReference = true;
aoqi@0 608 return;
aoqi@0 609 }
aoqi@0 610
aoqi@0 611 // found a type in a foreign namespace, so make sure we generate an import for it
aoqi@0 612 depends.add(getNamespace(nsUri));
aoqi@0 613 }
aoqi@0 614
aoqi@0 615 /**
aoqi@0 616 * Writes the schema document to the specified result.
aoqi@0 617 *
aoqi@0 618 * @param systemIds
aoqi@0 619 * System IDs of the other schema documents. "" indicates 'implied'.
aoqi@0 620 */
aoqi@0 621 private void writeTo(Result result, Map<Namespace,String> systemIds) throws IOException {
aoqi@0 622 try {
aoqi@0 623 Schema schema = TXW.create(Schema.class,ResultFactory.createSerializer(result));
aoqi@0 624
aoqi@0 625 // additional namespace declarations to be made.
aoqi@0 626 Map<String, String> xmlNs = types.getXmlNs(uri);
aoqi@0 627
aoqi@0 628 for (Map.Entry<String, String> e : xmlNs.entrySet()) {
aoqi@0 629 schema._namespace(e.getValue(),e.getKey());
aoqi@0 630 }
aoqi@0 631
aoqi@0 632 if(useSwaRef)
aoqi@0 633 schema._namespace(WellKnownNamespace.SWA_URI,"swaRef");
aoqi@0 634
aoqi@0 635 if(useMimeNs)
aoqi@0 636 schema._namespace(WellKnownNamespace.XML_MIME_URI,"xmime");
aoqi@0 637
aoqi@0 638 attributeFormDefault = Form.get(types.getAttributeFormDefault(uri));
aoqi@0 639 attributeFormDefault.declare("attributeFormDefault",schema);
aoqi@0 640
aoqi@0 641 elementFormDefault = Form.get(types.getElementFormDefault(uri));
aoqi@0 642 // TODO: if elementFormDefault is UNSET, figure out the right default value to use
aoqi@0 643 elementFormDefault.declare("elementFormDefault",schema);
aoqi@0 644
aoqi@0 645
aoqi@0 646 // declare XML Schema namespace to be xs, but allow the user to override it.
aoqi@0 647 // if 'xs' is used for other things, we'll just let TXW assign a random prefix
aoqi@0 648 if(!xmlNs.containsValue(WellKnownNamespace.XML_SCHEMA)
aoqi@0 649 && !xmlNs.containsKey("xs"))
aoqi@0 650 schema._namespace(WellKnownNamespace.XML_SCHEMA,"xs");
aoqi@0 651 schema.version("1.0");
aoqi@0 652
aoqi@0 653 if(uri.length()!=0)
aoqi@0 654 schema.targetNamespace(uri);
aoqi@0 655
aoqi@0 656 // declare prefixes for them at this level, so that we can avoid redundant
aoqi@0 657 // namespace declarations
aoqi@0 658 for (Namespace ns : depends) {
aoqi@0 659 schema._namespace(ns.uri);
aoqi@0 660 }
aoqi@0 661
aoqi@0 662 if(selfReference && uri.length()!=0) {
aoqi@0 663 // use common 'tns' prefix for the own namespace
aoqi@0 664 // if self-reference is needed
aoqi@0 665 schema._namespace(uri,"tns");
aoqi@0 666 }
aoqi@0 667
aoqi@0 668 schema._pcdata(newline);
aoqi@0 669
aoqi@0 670 // refer to other schemas
aoqi@0 671 for( Namespace n : depends ) {
aoqi@0 672 Import imp = schema._import();
aoqi@0 673 if(n.uri.length()!=0)
aoqi@0 674 imp.namespace(n.uri);
aoqi@0 675 String refSystemId = systemIds.get(n);
aoqi@0 676 if(refSystemId!=null && !refSystemId.equals("")) {
aoqi@0 677 // "" means implied. null if the SchemaOutputResolver said "don't generate!"
aoqi@0 678 imp.schemaLocation(relativize(refSystemId,result.getSystemId()));
aoqi@0 679 }
aoqi@0 680 schema._pcdata(newline);
aoqi@0 681 }
aoqi@0 682 if(useSwaRef) {
aoqi@0 683 schema._import().namespace(WellKnownNamespace.SWA_URI).schemaLocation("http://ws-i.org/profiles/basic/1.1/swaref.xsd");
aoqi@0 684 }
aoqi@0 685 if(useMimeNs) {
aoqi@0 686 schema._import().namespace(WellKnownNamespace.XML_MIME_URI).schemaLocation("http://www.w3.org/2005/05/xmlmime");
aoqi@0 687 }
aoqi@0 688
aoqi@0 689 // then write each component
aoqi@0 690 for (Map.Entry<String,ElementDeclaration> e : elementDecls.entrySet()) {
aoqi@0 691 e.getValue().writeTo(e.getKey(),schema);
aoqi@0 692 schema._pcdata(newline);
aoqi@0 693 }
aoqi@0 694 for (ClassInfo<T, C> c : classes) {
aoqi@0 695 if (c.getTypeName()==null) {
aoqi@0 696 // don't generate anything if it's an anonymous type
aoqi@0 697 continue;
aoqi@0 698 }
aoqi@0 699 if(uri.equals(c.getTypeName().getNamespaceURI()))
aoqi@0 700 writeClass(c, schema);
aoqi@0 701 schema._pcdata(newline);
aoqi@0 702 }
aoqi@0 703 for (EnumLeafInfo<T, C> e : enums) {
aoqi@0 704 if (e.getTypeName()==null) {
aoqi@0 705 // don't generate anything if it's an anonymous type
aoqi@0 706 continue;
aoqi@0 707 }
aoqi@0 708 if(uri.equals(e.getTypeName().getNamespaceURI()))
aoqi@0 709 writeEnum(e,schema);
aoqi@0 710 schema._pcdata(newline);
aoqi@0 711 }
aoqi@0 712 for (ArrayInfo<T, C> a : arrays) {
aoqi@0 713 writeArray(a,schema);
aoqi@0 714 schema._pcdata(newline);
aoqi@0 715 }
aoqi@0 716 for (Map.Entry<String,AttributePropertyInfo<T,C>> e : attributeDecls.entrySet()) {
aoqi@0 717 TopLevelAttribute a = schema.attribute();
aoqi@0 718 a.name(e.getKey());
aoqi@0 719 if(e.getValue()==null)
aoqi@0 720 writeTypeRef(a,stringType,"type");
aoqi@0 721 else
aoqi@0 722 writeAttributeTypeRef(e.getValue(),a);
aoqi@0 723 schema._pcdata(newline);
aoqi@0 724 }
aoqi@0 725
aoqi@0 726 // close the schema
aoqi@0 727 schema.commit();
aoqi@0 728 } catch( TxwException e ) {
aoqi@0 729 logger.log(Level.INFO,e.getMessage(),e);
aoqi@0 730 throw new IOException(e.getMessage());
aoqi@0 731 }
aoqi@0 732 }
aoqi@0 733
aoqi@0 734 /**
aoqi@0 735 * Writes a type attribute (if the referenced type is a global type)
aoqi@0 736 * or writes out the definition of the anonymous type in place (if the referenced
aoqi@0 737 * type is not a global type.)
aoqi@0 738 *
aoqi@0 739 * Also provides processing for ID/IDREF, MTOM @xmime, and swa:ref
aoqi@0 740 *
aoqi@0 741 * ComplexTypeHost and SimpleTypeHost don't share an api for creating
aoqi@0 742 * and attribute in a type-safe way, so we will compromise for now and
aoqi@0 743 * use _attribute().
aoqi@0 744 */
aoqi@0 745 private void writeTypeRef(TypeHost th, NonElementRef<T, C> typeRef, String refAttName) {
aoqi@0 746 // ID / IDREF handling
aoqi@0 747 switch(typeRef.getSource().id()) {
aoqi@0 748 case ID:
aoqi@0 749 th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "ID"));
aoqi@0 750 return;
aoqi@0 751 case IDREF:
aoqi@0 752 th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "IDREF"));
aoqi@0 753 return;
aoqi@0 754 case NONE:
aoqi@0 755 // no ID/IDREF, so continue on and generate the type
aoqi@0 756 break;
aoqi@0 757 default:
aoqi@0 758 throw new IllegalStateException();
aoqi@0 759 }
aoqi@0 760
aoqi@0 761 // MTOM handling
aoqi@0 762 MimeType mimeType = typeRef.getSource().getExpectedMimeType();
aoqi@0 763 if( mimeType != null ) {
aoqi@0 764 th._attribute(new QName(WellKnownNamespace.XML_MIME_URI, "expectedContentTypes", "xmime"), mimeType.toString());
aoqi@0 765 }
aoqi@0 766
aoqi@0 767 // ref:swaRef handling
aoqi@0 768 if(generateSwaRefAdapter(typeRef)) {
aoqi@0 769 th._attribute(refAttName, new QName(WellKnownNamespace.SWA_URI, "swaRef", "ref"));
aoqi@0 770 return;
aoqi@0 771 }
aoqi@0 772
aoqi@0 773 // type name override
aoqi@0 774 if(typeRef.getSource().getSchemaType()!=null) {
aoqi@0 775 th._attribute(refAttName,typeRef.getSource().getSchemaType());
aoqi@0 776 return;
aoqi@0 777 }
aoqi@0 778
aoqi@0 779 // normal type generation
aoqi@0 780 writeTypeRef(th, typeRef.getTarget(), refAttName);
aoqi@0 781 }
aoqi@0 782
aoqi@0 783 /**
aoqi@0 784 * Writes a type attribute (if the referenced type is a global type)
aoqi@0 785 * or writes out the definition of the anonymous type in place (if the referenced
aoqi@0 786 * type is not a global type.)
aoqi@0 787 *
aoqi@0 788 * @param th
aoqi@0 789 * the TXW interface to which the attribute will be written.
aoqi@0 790 * @param type
aoqi@0 791 * type to be referenced.
aoqi@0 792 * @param refAttName
aoqi@0 793 * The name of the attribute used when referencing a type by QName.
aoqi@0 794 */
aoqi@0 795 private void writeTypeRef(TypeHost th, NonElement<T,C> type, String refAttName) {
aoqi@0 796 Element e = null;
aoqi@0 797 if (type instanceof MaybeElement) {
aoqi@0 798 MaybeElement me = (MaybeElement)type;
aoqi@0 799 boolean isElement = me.isElement();
aoqi@0 800 if (isElement) e = me.asElement();
aoqi@0 801 }
aoqi@0 802 if (type instanceof Element) {
aoqi@0 803 e = (Element)type;
aoqi@0 804 }
aoqi@0 805 if (type.getTypeName()==null) {
aoqi@0 806 if ((e != null) && (e.getElementName() != null)) {
aoqi@0 807 th.block(); // so that the caller may write other attributes
aoqi@0 808 if(type instanceof ClassInfo) {
aoqi@0 809 writeClass( (ClassInfo<T,C>)type, th );
aoqi@0 810 } else {
aoqi@0 811 writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th);
aoqi@0 812 }
aoqi@0 813 } else {
aoqi@0 814 // anonymous
aoqi@0 815 th.block(); // so that the caller may write other attributes
aoqi@0 816 if(type instanceof ClassInfo) {
aoqi@0 817 if(collisionChecker.push((ClassInfo<T,C>)type)) {
aoqi@0 818 errorListener.warning(new SAXParseException(
aoqi@0 819 Messages.ANONYMOUS_TYPE_CYCLE.format(collisionChecker.getCycleString()),
aoqi@0 820 null
aoqi@0 821 ));
aoqi@0 822 } else {
aoqi@0 823 writeClass( (ClassInfo<T,C>)type, th );
aoqi@0 824 }
aoqi@0 825 collisionChecker.pop();
aoqi@0 826 } else {
aoqi@0 827 writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th);
aoqi@0 828 }
aoqi@0 829 }
aoqi@0 830 } else {
aoqi@0 831 th._attribute(refAttName,type.getTypeName());
aoqi@0 832 }
aoqi@0 833 }
aoqi@0 834
aoqi@0 835 /**
aoqi@0 836 * writes the schema definition for the given array class
aoqi@0 837 */
aoqi@0 838 private void writeArray(ArrayInfo<T, C> a, Schema schema) {
aoqi@0 839 ComplexType ct = schema.complexType().name(a.getTypeName().getLocalPart());
aoqi@0 840 ct._final("#all");
aoqi@0 841 LocalElement le = ct.sequence().element().name("item");
aoqi@0 842 le.type(a.getItemType().getTypeName());
aoqi@0 843 le.minOccurs(0).maxOccurs("unbounded");
aoqi@0 844 le.nillable(true);
aoqi@0 845 ct.commit();
aoqi@0 846 }
aoqi@0 847
aoqi@0 848 /**
aoqi@0 849 * writes the schema definition for the specified type-safe enum in the given TypeHost
aoqi@0 850 */
aoqi@0 851 private void writeEnum(EnumLeafInfo<T, C> e, SimpleTypeHost th) {
aoqi@0 852 SimpleType st = th.simpleType();
aoqi@0 853 writeName(e,st);
aoqi@0 854
aoqi@0 855 SimpleRestrictionModel base = st.restriction();
aoqi@0 856 writeTypeRef(base, e.getBaseType(), "base");
aoqi@0 857
aoqi@0 858 for (EnumConstant c : e.getConstants()) {
aoqi@0 859 base.enumeration().value(c.getLexicalValue());
aoqi@0 860 }
aoqi@0 861 st.commit();
aoqi@0 862 }
aoqi@0 863
aoqi@0 864 /**
aoqi@0 865 * Writes the schema definition for the specified class to the schema writer.
aoqi@0 866 *
aoqi@0 867 * @param c the class info
aoqi@0 868 * @param parent the writer of the parent element into which the type will be defined
aoqi@0 869 */
aoqi@0 870 private void writeClass(ClassInfo<T,C> c, TypeHost parent) {
aefimov@1101 871 if (written.contains(c)) { // to avoid cycles let's check if we haven't already processed the class
aefimov@1101 872 return;
aefimov@1101 873 }
aefimov@1101 874 written.add(c);
aoqi@0 875 // special handling for value properties
aoqi@0 876 if (containsValueProp(c)) {
aoqi@0 877 if (c.getProperties().size() == 1) {
aoqi@0 878 // [RESULT 2 - simpleType if the value prop is the only prop]
aoqi@0 879 //
aoqi@0 880 // <simpleType name="foo">
aoqi@0 881 // <xs:restriction base="xs:int"/>
aoqi@0 882 // </>
aoqi@0 883 ValuePropertyInfo<T,C> vp = (ValuePropertyInfo<T,C>)c.getProperties().get(0);
aoqi@0 884 SimpleType st = ((SimpleTypeHost)parent).simpleType();
aoqi@0 885 writeName(c, st);
aoqi@0 886 if(vp.isCollection()) {
aoqi@0 887 writeTypeRef(st.list(),vp.getTarget(),"itemType");
aoqi@0 888 } else {
aoqi@0 889 writeTypeRef(st.restriction(),vp.getTarget(),"base");
aoqi@0 890 }
aoqi@0 891 return;
aoqi@0 892 } else {
aoqi@0 893 // [RESULT 1 - complexType with simpleContent]
aoqi@0 894 //
aoqi@0 895 // <complexType name="foo">
aoqi@0 896 // <simpleContent>
aoqi@0 897 // <extension base="xs:int"/>
aoqi@0 898 // <attribute name="b" type="xs:boolean"/>
aoqi@0 899 // </>
aoqi@0 900 // </>
aoqi@0 901 // </>
aoqi@0 902 // ...
aoqi@0 903 // <element name="f" type="foo"/>
aoqi@0 904 // ...
aoqi@0 905 ComplexType ct = ((ComplexTypeHost)parent).complexType();
aoqi@0 906 writeName(c,ct);
aoqi@0 907 if(c.isFinal())
aoqi@0 908 ct._final("extension restriction");
aoqi@0 909
aoqi@0 910 SimpleExtension se = ct.simpleContent().extension();
aoqi@0 911 se.block(); // because we might have attribute before value
aoqi@0 912 for (PropertyInfo<T,C> p : c.getProperties()) {
aoqi@0 913 switch (p.kind()) {
aoqi@0 914 case ATTRIBUTE:
aoqi@0 915 handleAttributeProp((AttributePropertyInfo<T,C>)p,se);
aoqi@0 916 break;
aoqi@0 917 case VALUE:
aoqi@0 918 TODO.checkSpec("what if vp.isCollection() == true?");
aoqi@0 919 ValuePropertyInfo vp = (ValuePropertyInfo) p;
aoqi@0 920 se.base(vp.getTarget().getTypeName());
aoqi@0 921 break;
aoqi@0 922 case ELEMENT: // error
aoqi@0 923 case REFERENCE: // error
aoqi@0 924 default:
aoqi@0 925 assert false;
aoqi@0 926 throw new IllegalStateException();
aoqi@0 927 }
aoqi@0 928 }
aoqi@0 929 se.commit();
aoqi@0 930 }
aoqi@0 931 TODO.schemaGenerator("figure out what to do if bc != null");
aoqi@0 932 TODO.checkSpec("handle sec 8.9.5.2, bullet #4");
aoqi@0 933 // Java types containing value props can only contain properties of type
aoqi@0 934 // ValuePropertyinfo and AttributePropertyInfo which have just been handled,
aoqi@0 935 // so return.
aoqi@0 936 return;
aoqi@0 937 }
aoqi@0 938
aoqi@0 939 // we didn't fall into the special case for value props, so we
aoqi@0 940 // need to initialize the ct.
aoqi@0 941 // generate the complexType
aoqi@0 942 ComplexType ct = ((ComplexTypeHost)parent).complexType();
aoqi@0 943 writeName(c,ct);
aoqi@0 944 if(c.isFinal())
aoqi@0 945 ct._final("extension restriction");
aoqi@0 946 if(c.isAbstract())
aoqi@0 947 ct._abstract(true);
aoqi@0 948
aoqi@0 949 // these are where we write content model and attributes
aoqi@0 950 AttrDecls contentModel = ct;
aoqi@0 951 TypeDefParticle contentModelOwner = ct;
aoqi@0 952
aoqi@0 953 // if there is a base class, we need to generate an extension in the schema
aoqi@0 954 final ClassInfo<T,C> bc = c.getBaseClass();
aoqi@0 955 if (bc != null) {
aoqi@0 956 if(bc.hasValueProperty()) {
aoqi@0 957 // extending complex type with simple content
aoqi@0 958 SimpleExtension se = ct.simpleContent().extension();
aoqi@0 959 contentModel = se;
aoqi@0 960 contentModelOwner = null;
aoqi@0 961 se.base(bc.getTypeName());
aoqi@0 962 } else {
aoqi@0 963 ComplexExtension ce = ct.complexContent().extension();
aoqi@0 964 contentModel = ce;
aoqi@0 965 contentModelOwner = ce;
aoqi@0 966
aoqi@0 967 ce.base(bc.getTypeName());
aoqi@0 968 // TODO: what if the base type is anonymous?
aoqi@0 969 }
aoqi@0 970 }
aoqi@0 971
aoqi@0 972 if(contentModelOwner!=null) {
aoqi@0 973 // build the tree that represents the explicit content model from iterate over the properties
aoqi@0 974 ArrayList<Tree> children = new ArrayList<Tree>();
aoqi@0 975 for (PropertyInfo<T,C> p : c.getProperties()) {
aoqi@0 976 // handling for <complexType @mixed='true' ...>
aoqi@0 977 if(p instanceof ReferencePropertyInfo && ((ReferencePropertyInfo)p).isMixed()) {
aoqi@0 978 ct.mixed(true);
aoqi@0 979 }
aoqi@0 980 Tree t = buildPropertyContentModel(p);
aoqi@0 981 if(t!=null)
aoqi@0 982 children.add(t);
aoqi@0 983 }
aoqi@0 984
aoqi@0 985 Tree top = Tree.makeGroup( c.isOrdered() ? GroupKind.SEQUENCE : GroupKind.ALL, children);
aoqi@0 986
aoqi@0 987 // write the content model
aoqi@0 988 top.write(contentModelOwner);
aoqi@0 989 }
aoqi@0 990
aoqi@0 991 // then attributes
aoqi@0 992 for (PropertyInfo<T,C> p : c.getProperties()) {
aoqi@0 993 if (p instanceof AttributePropertyInfo) {
aoqi@0 994 handleAttributeProp((AttributePropertyInfo<T,C>)p, contentModel);
aoqi@0 995 }
aoqi@0 996 }
aoqi@0 997 if( c.hasAttributeWildcard()) {
aoqi@0 998 contentModel.anyAttribute().namespace("##other").processContents("skip");
aoqi@0 999 }
aoqi@0 1000 ct.commit();
aoqi@0 1001 }
aoqi@0 1002
aoqi@0 1003 /**
aoqi@0 1004 * Writes the name attribute if it's named.
aoqi@0 1005 */
aoqi@0 1006 private void writeName(NonElement<T,C> c, TypedXmlWriter xw) {
aoqi@0 1007 QName tn = c.getTypeName();
aoqi@0 1008 if(tn!=null)
aoqi@0 1009 xw._attribute("name",tn.getLocalPart()); // named
aoqi@0 1010 }
aoqi@0 1011
aoqi@0 1012 private boolean containsValueProp(ClassInfo<T, C> c) {
aoqi@0 1013 for (PropertyInfo p : c.getProperties()) {
aoqi@0 1014 if (p instanceof ValuePropertyInfo) return true;
aoqi@0 1015 }
aoqi@0 1016 return false;
aoqi@0 1017 }
aoqi@0 1018
aoqi@0 1019 /**
aoqi@0 1020 * Builds content model writer for the specified property.
aoqi@0 1021 */
aoqi@0 1022 private Tree buildPropertyContentModel(PropertyInfo<T,C> p) {
aoqi@0 1023 switch(p.kind()) {
aoqi@0 1024 case ELEMENT:
aoqi@0 1025 return handleElementProp((ElementPropertyInfo<T,C>)p);
aoqi@0 1026 case ATTRIBUTE:
aoqi@0 1027 // attribuets are handled later
aoqi@0 1028 return null;
aoqi@0 1029 case REFERENCE:
aoqi@0 1030 return handleReferenceProp((ReferencePropertyInfo<T,C>)p);
aoqi@0 1031 case MAP:
aoqi@0 1032 return handleMapProp((MapPropertyInfo<T,C>)p);
aoqi@0 1033 case VALUE:
aoqi@0 1034 // value props handled above in writeClass()
aoqi@0 1035 assert false;
aoqi@0 1036 throw new IllegalStateException();
aoqi@0 1037 default:
aoqi@0 1038 assert false;
aoqi@0 1039 throw new IllegalStateException();
aoqi@0 1040 }
aoqi@0 1041 }
aoqi@0 1042
aoqi@0 1043 /**
aoqi@0 1044 * Generate the proper schema fragment for the given element property into the
aoqi@0 1045 * specified schema compositor.
aoqi@0 1046 *
aoqi@0 1047 * The element property may or may not represent a collection and it may or may
aoqi@0 1048 * not be wrapped.
aoqi@0 1049 *
aoqi@0 1050 * @param ep the element property
aoqi@0 1051 */
aoqi@0 1052 private Tree handleElementProp(final ElementPropertyInfo<T,C> ep) {
aoqi@0 1053 if (ep.isValueList()) {
aoqi@0 1054 return new Tree.Term() {
aoqi@0 1055 protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
aoqi@0 1056 TypeRef<T,C> t = ep.getTypes().get(0);
aoqi@0 1057 LocalElement e = parent.element();
aoqi@0 1058 e.block(); // we will write occurs later
aoqi@0 1059 QName tn = t.getTagName();
aoqi@0 1060 e.name(tn.getLocalPart());
aoqi@0 1061 List lst = e.simpleType().list();
aoqi@0 1062 writeTypeRef(lst,t, "itemType");
aoqi@0 1063 elementFormDefault.writeForm(e,tn);
aoqi@0 1064 writeOccurs(e,isOptional||!ep.isRequired(),repeated);
aoqi@0 1065 }
aoqi@0 1066 };
aoqi@0 1067 }
aoqi@0 1068
aoqi@0 1069 ArrayList<Tree> children = new ArrayList<Tree>();
aoqi@0 1070 for (final TypeRef<T,C> t : ep.getTypes()) {
aoqi@0 1071 children.add(new Tree.Term() {
aoqi@0 1072 protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
aoqi@0 1073 LocalElement e = parent.element();
aoqi@0 1074
aoqi@0 1075 QName tn = t.getTagName();
aoqi@0 1076
aoqi@0 1077 PropertyInfo propInfo = t.getSource();
aoqi@0 1078 TypeInfo parentInfo = (propInfo == null) ? null : propInfo.parent();
aoqi@0 1079
aoqi@0 1080 if (canBeDirectElementRef(t, tn, parentInfo)) {
aoqi@0 1081 if ((!t.getTarget().isSimpleType()) && (t.getTarget() instanceof ClassInfo) && collisionChecker.findDuplicate((ClassInfo<T, C>) t.getTarget())) {
aoqi@0 1082 e.ref(new QName(uri, tn.getLocalPart()));
aoqi@0 1083 } else {
aoqi@0 1084
aoqi@0 1085 QName elemName = null;
aoqi@0 1086 if (t.getTarget() instanceof Element) {
aoqi@0 1087 Element te = (Element) t.getTarget();
aoqi@0 1088 elemName = te.getElementName();
aoqi@0 1089 }
aoqi@0 1090
aoqi@0 1091 Collection<TypeInfo> refs = propInfo.ref();
aoqi@0 1092 TypeInfo ti;
aoqi@0 1093 if ((refs != null) && (!refs.isEmpty()) && (elemName != null)
aoqi@0 1094 && ((ti = refs.iterator().next()) == null || ti instanceof ClassInfoImpl)) {
aoqi@0 1095 ClassInfoImpl cImpl = (ClassInfoImpl)ti;
aoqi@0 1096 if ((cImpl != null) && (cImpl.getElementName() != null)) {
aoqi@0 1097 e.ref(new QName(cImpl.getElementName().getNamespaceURI(), tn.getLocalPart()));
aoqi@0 1098 } else {
aoqi@0 1099 e.ref(new QName("", tn.getLocalPart()));
aoqi@0 1100 }
aoqi@0 1101 } else {
aoqi@0 1102 e.ref(tn);
aoqi@0 1103 }
aoqi@0 1104 }
aoqi@0 1105 } else {
aoqi@0 1106 e.name(tn.getLocalPart());
aoqi@0 1107 writeTypeRef(e,t, "type");
aoqi@0 1108 elementFormDefault.writeForm(e,tn);
aoqi@0 1109 }
aoqi@0 1110
aoqi@0 1111 if (t.isNillable()) {
aoqi@0 1112 e.nillable(true);
aoqi@0 1113 }
aoqi@0 1114 if(t.getDefaultValue()!=null)
aoqi@0 1115 e._default(t.getDefaultValue());
aoqi@0 1116 writeOccurs(e,isOptional,repeated);
aoqi@0 1117 }
aoqi@0 1118 });
aoqi@0 1119 }
aoqi@0 1120
aoqi@0 1121 final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children)
aoqi@0 1122 .makeOptional(!ep.isRequired())
aoqi@0 1123 .makeRepeated(ep.isCollection()); // see Spec table 8-13
aoqi@0 1124
aoqi@0 1125
aoqi@0 1126 final QName ename = ep.getXmlName();
aoqi@0 1127 if (ename != null) { // wrapped collection
aoqi@0 1128 return new Tree.Term() {
aoqi@0 1129 protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
aoqi@0 1130 LocalElement e = parent.element();
aoqi@0 1131 if(ename.getNamespaceURI().length()>0) {
aoqi@0 1132 if (!ename.getNamespaceURI().equals(uri)) {
aoqi@0 1133 // TODO: we need to generate the corresponding element declaration for this
aoqi@0 1134 // table 8-25: Property/field element wrapper with ref attribute
aoqi@0 1135 e.ref(new QName(ename.getNamespaceURI(), ename.getLocalPart()));
aoqi@0 1136 return;
aoqi@0 1137 }
aoqi@0 1138 }
aoqi@0 1139 e.name(ename.getLocalPart());
aoqi@0 1140 elementFormDefault.writeForm(e,ename);
aoqi@0 1141
aoqi@0 1142 if(ep.isCollectionNillable()) {
aoqi@0 1143 e.nillable(true);
aoqi@0 1144 }
aoqi@0 1145 writeOccurs(e,!ep.isCollectionRequired(),repeated);
aoqi@0 1146
aoqi@0 1147 ComplexType p = e.complexType();
aoqi@0 1148 choice.write(p);
aoqi@0 1149 }
aoqi@0 1150 };
aoqi@0 1151 } else {// non-wrapped
aoqi@0 1152 return choice;
aoqi@0 1153 }
aoqi@0 1154 }
aoqi@0 1155
aoqi@0 1156 /**
aoqi@0 1157 * Checks if we can collapse
aoqi@0 1158 * &lt;element name='foo' type='t' /> to &lt;element ref='foo' />.
aoqi@0 1159 *
aoqi@0 1160 * This is possible if we already have such declaration to begin with.
aoqi@0 1161 */
aoqi@0 1162 private boolean canBeDirectElementRef(TypeRef<T, C> t, QName tn, TypeInfo parentInfo) {
aoqi@0 1163 Element te = null;
aoqi@0 1164 ClassInfo ci = null;
aoqi@0 1165 QName targetTagName = null;
aoqi@0 1166
aoqi@0 1167 if(t.isNillable() || t.getDefaultValue()!=null) {
aoqi@0 1168 // can't put those attributes on <element ref>
aoqi@0 1169 return false;
aoqi@0 1170 }
aoqi@0 1171
aoqi@0 1172 if (t.getTarget() instanceof Element) {
aoqi@0 1173 te = (Element) t.getTarget();
aoqi@0 1174 targetTagName = te.getElementName();
aoqi@0 1175 if (te instanceof ClassInfo) {
aoqi@0 1176 ci = (ClassInfo)te;
aoqi@0 1177 }
aoqi@0 1178 }
aoqi@0 1179
aoqi@0 1180 String nsUri = tn.getNamespaceURI();
aoqi@0 1181 if ((!nsUri.equals(uri) && nsUri.length()>0) && (!((parentInfo instanceof ClassInfo) && (((ClassInfo)parentInfo).getTypeName() == null)))) {
aoqi@0 1182 return true;
aoqi@0 1183 }
aoqi@0 1184
aoqi@0 1185 if ((ci != null) && ((targetTagName != null) && (te.getScope() == null) && (targetTagName.getNamespaceURI() == null))) {
aoqi@0 1186 if (targetTagName.equals(tn)) {
aoqi@0 1187 return true;
aoqi@0 1188 }
aoqi@0 1189 }
aoqi@0 1190
aoqi@0 1191 // we have the precise element defined already
aoqi@0 1192 if (te != null) { // it is instanceof Element
aoqi@0 1193 return targetTagName!=null && targetTagName.equals(tn);
aoqi@0 1194 }
aoqi@0 1195
aoqi@0 1196 return false;
aoqi@0 1197 }
aoqi@0 1198
aoqi@0 1199
aoqi@0 1200 /**
aoqi@0 1201 * Generate an attribute for the specified property on the specified complexType
aoqi@0 1202 *
aoqi@0 1203 * @param ap the attribute
aoqi@0 1204 * @param attr the schema definition to which the attribute will be added
aoqi@0 1205 */
aoqi@0 1206 private void handleAttributeProp(AttributePropertyInfo<T,C> ap, AttrDecls attr) {
aoqi@0 1207 // attr is either a top-level ComplexType or a ComplexExtension
aoqi@0 1208 //
aoqi@0 1209 // [RESULT]
aoqi@0 1210 //
aoqi@0 1211 // <complexType ...>
aoqi@0 1212 // <...>...</>
aoqi@0 1213 // <attribute name="foo" type="xs:int"/>
aoqi@0 1214 // </>
aoqi@0 1215 //
aoqi@0 1216 // or
aoqi@0 1217 //
aoqi@0 1218 // <complexType ...>
aoqi@0 1219 // <complexContent>
aoqi@0 1220 // <extension ...>
aoqi@0 1221 // <...>...</>
aoqi@0 1222 // </>
aoqi@0 1223 // </>
aoqi@0 1224 // <attribute name="foo" type="xs:int"/>
aoqi@0 1225 // </>
aoqi@0 1226 //
aoqi@0 1227 // or it could also be an in-lined type (attr ref)
aoqi@0 1228 //
aoqi@0 1229 LocalAttribute localAttribute = attr.attribute();
aoqi@0 1230
aoqi@0 1231 final String attrURI = ap.getXmlName().getNamespaceURI();
aoqi@0 1232 if (attrURI.equals("") /*|| attrURI.equals(uri) --- those are generated as global attributes anyway, so use them.*/) {
aoqi@0 1233 localAttribute.name(ap.getXmlName().getLocalPart());
aoqi@0 1234
aoqi@0 1235 writeAttributeTypeRef(ap, localAttribute);
aoqi@0 1236
aoqi@0 1237 attributeFormDefault.writeForm(localAttribute,ap.getXmlName());
aoqi@0 1238 } else { // generate an attr ref
aoqi@0 1239 localAttribute.ref(ap.getXmlName());
aoqi@0 1240 }
aoqi@0 1241
aoqi@0 1242 if(ap.isRequired()) {
aoqi@0 1243 // TODO: not type safe
aoqi@0 1244 localAttribute.use("required");
aoqi@0 1245 }
aoqi@0 1246 }
aoqi@0 1247
aoqi@0 1248 private void writeAttributeTypeRef(AttributePropertyInfo<T,C> ap, AttributeType a) {
aoqi@0 1249 if( ap.isCollection() )
aoqi@0 1250 writeTypeRef(a.simpleType().list(), ap, "itemType");
aoqi@0 1251 else
aoqi@0 1252 writeTypeRef(a, ap, "type");
aoqi@0 1253 }
aoqi@0 1254
aoqi@0 1255 /**
aoqi@0 1256 * Generate the proper schema fragment for the given reference property into the
aoqi@0 1257 * specified schema compositor.
aoqi@0 1258 *
aoqi@0 1259 * The reference property may or may not refer to a collection and it may or may
aoqi@0 1260 * not be wrapped.
aoqi@0 1261 */
aoqi@0 1262 private Tree handleReferenceProp(final ReferencePropertyInfo<T, C> rp) {
aoqi@0 1263 // fill in content model
aoqi@0 1264 ArrayList<Tree> children = new ArrayList<Tree>();
aoqi@0 1265
aoqi@0 1266 for (final Element<T,C> e : rp.getElements()) {
aoqi@0 1267 children.add(new Tree.Term() {
aoqi@0 1268 protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
aoqi@0 1269 LocalElement eref = parent.element();
aoqi@0 1270
aoqi@0 1271 boolean local=false;
aoqi@0 1272
aoqi@0 1273 QName en = e.getElementName();
aoqi@0 1274 if(e.getScope()!=null) {
aoqi@0 1275 // scoped. needs to be inlined
aoqi@0 1276 boolean qualified = en.getNamespaceURI().equals(uri);
aoqi@0 1277 boolean unqualified = en.getNamespaceURI().equals("");
aoqi@0 1278 if(qualified || unqualified) {
aoqi@0 1279 // can be inlined indeed
aoqi@0 1280
aoqi@0 1281 // write form="..." if necessary
aoqi@0 1282 if(unqualified) {
aoqi@0 1283 if(elementFormDefault.isEffectivelyQualified)
aoqi@0 1284 eref.form("unqualified");
aoqi@0 1285 } else {
aoqi@0 1286 if(!elementFormDefault.isEffectivelyQualified)
aoqi@0 1287 eref.form("qualified");
aoqi@0 1288 }
aoqi@0 1289
aoqi@0 1290 local = true;
aoqi@0 1291 eref.name(en.getLocalPart());
aoqi@0 1292
aoqi@0 1293 // write out type reference
aoqi@0 1294 if(e instanceof ClassInfo) {
aoqi@0 1295 writeTypeRef(eref,(ClassInfo<T,C>)e,"type");
aoqi@0 1296 } else {
aoqi@0 1297 writeTypeRef(eref,((ElementInfo<T,C>)e).getContentType(),"type");
aoqi@0 1298 }
aoqi@0 1299 }
aoqi@0 1300 }
aoqi@0 1301 if(!local)
aoqi@0 1302 eref.ref(en);
aoqi@0 1303 writeOccurs(eref,isOptional,repeated);
aoqi@0 1304 }
aoqi@0 1305 });
aoqi@0 1306 }
aoqi@0 1307
aoqi@0 1308 final WildcardMode wc = rp.getWildcard();
aoqi@0 1309 if( wc != null ) {
aoqi@0 1310 children.add(new Tree.Term() {
aoqi@0 1311 protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
aoqi@0 1312 Any any = parent.any();
aoqi@0 1313 final String pcmode = getProcessContentsModeName(wc);
aoqi@0 1314 if( pcmode != null ) any.processContents(pcmode);
aoqi@0 1315 any.namespace("##other");
aoqi@0 1316 writeOccurs(any,isOptional,repeated);
aoqi@0 1317 }
aoqi@0 1318 });
aoqi@0 1319 }
aoqi@0 1320
aoqi@0 1321
aoqi@0 1322 final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children).makeRepeated(rp.isCollection()).makeOptional(!rp.isRequired());
aoqi@0 1323
aoqi@0 1324 final QName ename = rp.getXmlName();
aoqi@0 1325
aoqi@0 1326 if (ename != null) { // wrapped
aoqi@0 1327 return new Tree.Term() {
aoqi@0 1328 protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
aoqi@0 1329 LocalElement e = parent.element().name(ename.getLocalPart());
aoqi@0 1330 elementFormDefault.writeForm(e,ename);
aoqi@0 1331 if(rp.isCollectionNillable())
aoqi@0 1332 e.nillable(true);
aoqi@0 1333 writeOccurs(e,true,repeated);
aoqi@0 1334
aoqi@0 1335 ComplexType p = e.complexType();
aoqi@0 1336 choice.write(p);
aoqi@0 1337 }
aoqi@0 1338 };
aoqi@0 1339 } else { // unwrapped
aoqi@0 1340 return choice;
aoqi@0 1341 }
aoqi@0 1342 }
aoqi@0 1343
aoqi@0 1344 /**
aoqi@0 1345 * Generate the proper schema fragment for the given map property into the
aoqi@0 1346 * specified schema compositor.
aoqi@0 1347 *
aoqi@0 1348 * @param mp the map property
aoqi@0 1349 */
aoqi@0 1350 private Tree handleMapProp(final MapPropertyInfo<T,C> mp) {
aoqi@0 1351 return new Tree.Term() {
aoqi@0 1352 protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
aoqi@0 1353 QName ename = mp.getXmlName();
aoqi@0 1354
aoqi@0 1355 LocalElement e = parent.element();
aoqi@0 1356 elementFormDefault.writeForm(e,ename);
aoqi@0 1357 if(mp.isCollectionNillable())
aoqi@0 1358 e.nillable(true);
aoqi@0 1359
aoqi@0 1360 e = e.name(ename.getLocalPart());
aoqi@0 1361 writeOccurs(e,isOptional,repeated);
aoqi@0 1362 ComplexType p = e.complexType();
aoqi@0 1363
aoqi@0 1364 // TODO: entry, key, and value are always unqualified. that needs to be fixed, too.
aoqi@0 1365 // TODO: we need to generate the corresponding element declaration, if they are qualified
aoqi@0 1366 e = p.sequence().element();
aoqi@0 1367 e.name("entry").minOccurs(0).maxOccurs("unbounded");
aoqi@0 1368
aoqi@0 1369 ExplicitGroup seq = e.complexType().sequence();
aoqi@0 1370 writeKeyOrValue(seq, "key", mp.getKeyType());
aoqi@0 1371 writeKeyOrValue(seq, "value", mp.getValueType());
aoqi@0 1372 }
aoqi@0 1373 };
aoqi@0 1374 }
aoqi@0 1375
aoqi@0 1376 private void writeKeyOrValue(ExplicitGroup seq, String tagName, NonElement<T, C> typeRef) {
aoqi@0 1377 LocalElement key = seq.element().name(tagName);
aoqi@0 1378 key.minOccurs(0);
aoqi@0 1379 writeTypeRef(key, typeRef, "type");
aoqi@0 1380 }
aoqi@0 1381
aoqi@0 1382 public void addGlobalAttribute(AttributePropertyInfo<T,C> ap) {
aoqi@0 1383 attributeDecls.put( ap.getXmlName().getLocalPart(), ap );
aoqi@0 1384 addDependencyTo(ap.getTarget().getTypeName());
aoqi@0 1385 }
aoqi@0 1386
aoqi@0 1387 public void addGlobalElement(TypeRef<T,C> tref) {
aoqi@0 1388 elementDecls.put( tref.getTagName().getLocalPart(), new ElementWithType(false,tref.getTarget()) );
aoqi@0 1389 addDependencyTo(tref.getTarget().getTypeName());
aoqi@0 1390 }
aoqi@0 1391
aoqi@0 1392 @Override
aoqi@0 1393 public String toString() {
aoqi@0 1394 StringBuilder buf = new StringBuilder();
aoqi@0 1395 buf.append("[classes=").append(classes);
aoqi@0 1396 buf.append(",elementDecls=").append(elementDecls);
aoqi@0 1397 buf.append(",enums=").append(enums);
aoqi@0 1398 buf.append("]");
aoqi@0 1399 return super.toString();
aoqi@0 1400 }
aoqi@0 1401
aoqi@0 1402 /**
aoqi@0 1403 * Represents a global element declaration to be written.
aoqi@0 1404 *
aoqi@0 1405 * <p>
aoqi@0 1406 * Because multiple properties can name the same global element even if
aoqi@0 1407 * they have different Java type, the schema generator first needs to
aoqi@0 1408 * walk through the model and decide what to generate for the given
aoqi@0 1409 * element declaration.
aoqi@0 1410 *
aoqi@0 1411 * <p>
aoqi@0 1412 * This class represents what will be written, and its {@link #equals(Object)}
aoqi@0 1413 * method is implemented in such a way that two identical declarations
aoqi@0 1414 * are considered as the same.
aoqi@0 1415 */
aoqi@0 1416 abstract class ElementDeclaration {
aoqi@0 1417 /**
aoqi@0 1418 * Returns true if two {@link ElementDeclaration}s are representing
aoqi@0 1419 * the same schema fragment.
aoqi@0 1420 */
aoqi@0 1421 @Override
aoqi@0 1422 public abstract boolean equals(Object o);
aoqi@0 1423 @Override
aoqi@0 1424 public abstract int hashCode();
aoqi@0 1425
aoqi@0 1426 /**
aoqi@0 1427 * Generates the declaration.
aoqi@0 1428 */
aoqi@0 1429 public abstract void writeTo(String localName, Schema schema);
aoqi@0 1430 }
aoqi@0 1431
aoqi@0 1432 /**
aoqi@0 1433 * {@link ElementDeclaration} that refers to a {@link NonElement}.
aoqi@0 1434 */
aoqi@0 1435 class ElementWithType extends ElementDeclaration {
aoqi@0 1436 private final boolean nillable;
aoqi@0 1437 private final NonElement<T,C> type;
aoqi@0 1438
aoqi@0 1439 public ElementWithType(boolean nillable,NonElement<T, C> type) {
aoqi@0 1440 this.type = type;
aoqi@0 1441 this.nillable = nillable;
aoqi@0 1442 }
aoqi@0 1443
aoqi@0 1444 public void writeTo(String localName, Schema schema) {
aoqi@0 1445 TopLevelElement e = schema.element().name(localName);
aoqi@0 1446 if(nillable)
aoqi@0 1447 e.nillable(true);
aoqi@0 1448 if (type != null) {
aoqi@0 1449 writeTypeRef(e,type, "type");
aoqi@0 1450 } else {
aoqi@0 1451 e.complexType(); // refer to the nested empty complex type
aoqi@0 1452 }
aoqi@0 1453 e.commit();
aoqi@0 1454 }
aoqi@0 1455
aoqi@0 1456 public boolean equals(Object o) {
aoqi@0 1457 if (this == o) return true;
aoqi@0 1458 if (o == null || getClass() != o.getClass()) return false;
aoqi@0 1459
aoqi@0 1460 final ElementWithType that = (ElementWithType) o;
aoqi@0 1461 return type.equals(that.type);
aoqi@0 1462 }
aoqi@0 1463
aoqi@0 1464 public int hashCode() {
aoqi@0 1465 return type.hashCode();
aoqi@0 1466 }
aoqi@0 1467 }
aoqi@0 1468 }
aoqi@0 1469
aoqi@0 1470 /**
aoqi@0 1471 * Examine the specified element ref and determine if a swaRef attribute needs to be generated
aoqi@0 1472 * @param typeRef
aoqi@0 1473 */
aoqi@0 1474 private boolean generateSwaRefAdapter(NonElementRef<T,C> typeRef) {
aoqi@0 1475 return generateSwaRefAdapter(typeRef.getSource());
aoqi@0 1476 }
aoqi@0 1477
aoqi@0 1478 /**
aoqi@0 1479 * Examine the specified element ref and determine if a swaRef attribute needs to be generated
aoqi@0 1480 */
aoqi@0 1481 private boolean generateSwaRefAdapter(PropertyInfo<T,C> prop) {
aoqi@0 1482 final Adapter<T,C> adapter = prop.getAdapter();
aoqi@0 1483 if (adapter == null) return false;
aoqi@0 1484 final Object o = navigator.asDecl(SwaRefAdapter.class);
aoqi@0 1485 if (o == null) return false;
aoqi@0 1486 return (o.equals(adapter.adapterType));
aoqi@0 1487 }
aoqi@0 1488
aoqi@0 1489 /**
aoqi@0 1490 * Debug information of what's in this {@link XmlSchemaGenerator}.
aoqi@0 1491 */
aoqi@0 1492 @Override
aoqi@0 1493 public String toString() {
aoqi@0 1494 StringBuilder buf = new StringBuilder();
aoqi@0 1495 for (Namespace ns : namespaces.values()) {
aoqi@0 1496 if(buf.length()>0) buf.append(',');
aoqi@0 1497 buf.append(ns.uri).append('=').append(ns);
aoqi@0 1498 }
aoqi@0 1499 return super.toString()+'['+buf+']';
aoqi@0 1500 }
aoqi@0 1501
aoqi@0 1502 /**
aoqi@0 1503 * return the string representation of the processContents mode of the
aoqi@0 1504 * give wildcard, or null if it is the schema default "strict"
aoqi@0 1505 *
aoqi@0 1506 */
aoqi@0 1507 private static String getProcessContentsModeName(WildcardMode wc) {
aoqi@0 1508 switch(wc) {
aoqi@0 1509 case LAX:
aoqi@0 1510 case SKIP:
aoqi@0 1511 return wc.name().toLowerCase();
aoqi@0 1512 case STRICT:
aoqi@0 1513 return null;
aoqi@0 1514 default:
aoqi@0 1515 throw new IllegalStateException();
aoqi@0 1516 }
aoqi@0 1517 }
aoqi@0 1518
aoqi@0 1519
aoqi@0 1520 /**
aoqi@0 1521 * Relativizes a URI by using another URI (base URI.)
aoqi@0 1522 *
aoqi@0 1523 * <p>
aoqi@0 1524 * For example, {@code relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"}
aoqi@0 1525 *
aoqi@0 1526 * <p>
aoqi@0 1527 * This method only works on hierarchical URI's, not opaque URI's (refer to the
aoqi@0 1528 * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html">java.net.URI</a>
aoqi@0 1529 * javadoc for complete definitions of these terms.
aoqi@0 1530 *
aoqi@0 1531 * <p>
aoqi@0 1532 * This method will not normalize the relative URI.
aoqi@0 1533 *
aoqi@0 1534 * @return the relative URI or the original URI if a relative one could not be computed
aoqi@0 1535 */
aoqi@0 1536 protected static String relativize(String uri, String baseUri) {
aoqi@0 1537 try {
aoqi@0 1538 assert uri!=null;
aoqi@0 1539
aoqi@0 1540 if(baseUri==null) return uri;
aoqi@0 1541
aoqi@0 1542 URI theUri = new URI(escapeURI(uri));
aoqi@0 1543 URI theBaseUri = new URI(escapeURI(baseUri));
aoqi@0 1544
aoqi@0 1545 if (theUri.isOpaque() || theBaseUri.isOpaque())
aoqi@0 1546 return uri;
aoqi@0 1547
aoqi@0 1548 if (!equalsIgnoreCase(theUri.getScheme(), theBaseUri.getScheme()) ||
aoqi@0 1549 !equal(theUri.getAuthority(), theBaseUri.getAuthority()))
aoqi@0 1550 return uri;
aoqi@0 1551
aoqi@0 1552 String uriPath = theUri.getPath();
aoqi@0 1553 String basePath = theBaseUri.getPath();
aoqi@0 1554
aoqi@0 1555 // normalize base path
aoqi@0 1556 if (!basePath.endsWith("/")) {
aoqi@0 1557 basePath = normalizeUriPath(basePath);
aoqi@0 1558 }
aoqi@0 1559
aoqi@0 1560 if( uriPath.equals(basePath))
aoqi@0 1561 return ".";
aoqi@0 1562
aoqi@0 1563 String relPath = calculateRelativePath(uriPath, basePath, fixNull(theUri.getScheme()).equals("file"));
aoqi@0 1564
aoqi@0 1565 if (relPath == null)
aoqi@0 1566 return uri; // recursion found no commonality in the two uris at all
aoqi@0 1567 StringBuilder relUri = new StringBuilder();
aoqi@0 1568 relUri.append(relPath);
aoqi@0 1569 if (theUri.getQuery() != null)
aoqi@0 1570 relUri.append('?').append(theUri.getQuery());
aoqi@0 1571 if (theUri.getFragment() != null)
aoqi@0 1572 relUri.append('#').append(theUri.getFragment());
aoqi@0 1573
aoqi@0 1574 return relUri.toString();
aoqi@0 1575 } catch (URISyntaxException e) {
aoqi@0 1576 throw new InternalError("Error escaping one of these uris:\n\t"+uri+"\n\t"+baseUri);
aoqi@0 1577 }
aoqi@0 1578 }
aoqi@0 1579
aoqi@0 1580 private static String fixNull(String s) {
aoqi@0 1581 if(s==null) return "";
aoqi@0 1582 else return s;
aoqi@0 1583 }
aoqi@0 1584
aoqi@0 1585 private static String calculateRelativePath(String uri, String base, boolean fileUrl) {
aoqi@0 1586 // if this is a file URL (very likely), and if this is on a case-insensitive file system,
aoqi@0 1587 // then treat it accordingly.
aoqi@0 1588 boolean onWindows = File.pathSeparatorChar==';';
aoqi@0 1589
aoqi@0 1590 if (base == null) {
aoqi@0 1591 return null;
aoqi@0 1592 }
aoqi@0 1593 if ((fileUrl && onWindows && startsWithIgnoreCase(uri,base)) || uri.startsWith(base)) {
aoqi@0 1594 return uri.substring(base.length());
aoqi@0 1595 } else {
aoqi@0 1596 return "../" + calculateRelativePath(uri, getParentUriPath(base), fileUrl);
aoqi@0 1597 }
aoqi@0 1598 }
aoqi@0 1599
aoqi@0 1600 private static boolean startsWithIgnoreCase(String s, String t) {
aoqi@0 1601 return s.toUpperCase().startsWith(t.toUpperCase());
aoqi@0 1602 }
aoqi@0 1603
aoqi@0 1604 /**
aoqi@0 1605 * JAX-RPC wants the namespaces to be sorted in the reverse order
aoqi@0 1606 * so that the empty namespace "" comes to the very end. Don't ask me why.
aoqi@0 1607 */
aoqi@0 1608 private static final Comparator<String> NAMESPACE_COMPARATOR = new Comparator<String>() {
aoqi@0 1609 public int compare(String lhs, String rhs) {
aoqi@0 1610 return -lhs.compareTo(rhs);
aoqi@0 1611 }
aoqi@0 1612 };
aoqi@0 1613
aoqi@0 1614 private static final String newline = "\n";
aoqi@0 1615 }

mercurial