0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common Development
0008: * and Distribution License("CDDL") (collectively, the "License"). You
0009: * may not use this file except in compliance with the License. You can obtain
0010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
0011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
0012: * language governing permissions and limitations under the License.
0013: *
0014: * When distributing the software, include this License Header Notice in each
0015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
0016: * Sun designates this particular file as subject to the "Classpath" exception
0017: * as provided by Sun in the GPL Version 2 section of the License file that
0018: * accompanied this code. If applicable, add the following below the License
0019: * Header, with the fields enclosed by brackets [] replaced by your own
0020: * identifying information: "Portions Copyrighted [year]
0021: * [name of copyright owner]"
0022: *
0023: * Contributor(s):
0024: *
0025: * If you wish your version of this file to be governed by only the CDDL or
0026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
0027: * elects to include this software in this distribution under the [CDDL or GPL
0028: * Version 2] license." If you don't indicate a single choice of license, a
0029: * recipient has the option to distribute your version of this file under
0030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
0031: * its licensees as provided above. However, if you add GPL Version 2 code
0032: * and therefore, elected the GPL Version 2 license, then the option applies
0033: * only if the new code is made subject to such option by the copyright
0034: * holder.
0035: */
0036:
0037: package com.sun.xml.bind.v2.schemagen;
0038:
0039: import java.io.IOException;
0040: import java.io.OutputStream;
0041: import java.io.Writer;
0042: import java.io.File;
0043: import java.net.URI;
0044: import java.net.URISyntaxException;
0045: import java.util.Comparator;
0046: import java.util.HashMap;
0047: import java.util.LinkedHashSet;
0048: import java.util.Map;
0049: import java.util.Set;
0050: import java.util.TreeMap;
0051: import java.util.ArrayList;
0052: import java.util.logging.Level;
0053: import java.util.logging.Logger;
0054:
0055: import javax.activation.MimeType;
0056: import javax.xml.bind.SchemaOutputResolver;
0057: import javax.xml.namespace.QName;
0058: import javax.xml.transform.Result;
0059: import javax.xml.transform.stream.StreamResult;
0060:
0061: import com.sun.istack.Nullable;
0062: import com.sun.istack.NotNull;
0063: import com.sun.xml.bind.Util;
0064: import com.sun.xml.bind.api.CompositeStructure;
0065: import com.sun.xml.bind.api.ErrorListener;
0066: import com.sun.xml.bind.v2.TODO;
0067: import com.sun.xml.bind.v2.WellKnownNamespace;
0068: import com.sun.xml.bind.v2.util.CollisionCheckStack;
0069: import static com.sun.xml.bind.v2.WellKnownNamespace.XML_SCHEMA;
0070: import com.sun.xml.bind.v2.model.core.Adapter;
0071: import com.sun.xml.bind.v2.model.core.ArrayInfo;
0072: import com.sun.xml.bind.v2.model.core.AttributePropertyInfo;
0073: import com.sun.xml.bind.v2.model.core.ClassInfo;
0074: import com.sun.xml.bind.v2.model.core.Element;
0075: import com.sun.xml.bind.v2.model.core.ElementInfo;
0076: import com.sun.xml.bind.v2.model.core.ElementPropertyInfo;
0077: import com.sun.xml.bind.v2.model.core.EnumConstant;
0078: import com.sun.xml.bind.v2.model.core.EnumLeafInfo;
0079: import com.sun.xml.bind.v2.model.core.MapPropertyInfo;
0080: import com.sun.xml.bind.v2.model.core.NonElement;
0081: import com.sun.xml.bind.v2.model.core.NonElementRef;
0082: import com.sun.xml.bind.v2.model.core.PropertyInfo;
0083: import com.sun.xml.bind.v2.model.core.ReferencePropertyInfo;
0084: import com.sun.xml.bind.v2.model.core.TypeInfo;
0085: import com.sun.xml.bind.v2.model.core.TypeInfoSet;
0086: import com.sun.xml.bind.v2.model.core.TypeRef;
0087: import com.sun.xml.bind.v2.model.core.ValuePropertyInfo;
0088: import com.sun.xml.bind.v2.model.core.WildcardMode;
0089: import com.sun.xml.bind.v2.model.nav.Navigator;
0090: import com.sun.xml.bind.v2.runtime.SwaRefAdapter;
0091: import static com.sun.xml.bind.v2.schemagen.Util.*;
0092: import com.sun.xml.bind.v2.schemagen.xmlschema.Any;
0093: import com.sun.xml.bind.v2.schemagen.xmlschema.AttrDecls;
0094: import com.sun.xml.bind.v2.schemagen.xmlschema.ComplexExtension;
0095: import com.sun.xml.bind.v2.schemagen.xmlschema.ComplexType;
0096: import com.sun.xml.bind.v2.schemagen.xmlschema.ComplexTypeHost;
0097: import com.sun.xml.bind.v2.schemagen.xmlschema.ExplicitGroup;
0098: import com.sun.xml.bind.v2.schemagen.xmlschema.Import;
0099: import com.sun.xml.bind.v2.schemagen.xmlschema.List;
0100: import com.sun.xml.bind.v2.schemagen.xmlschema.LocalAttribute;
0101: import com.sun.xml.bind.v2.schemagen.xmlschema.LocalElement;
0102: import com.sun.xml.bind.v2.schemagen.xmlschema.Schema;
0103: import com.sun.xml.bind.v2.schemagen.xmlschema.SimpleExtension;
0104: import com.sun.xml.bind.v2.schemagen.xmlschema.SimpleRestrictionModel;
0105: import com.sun.xml.bind.v2.schemagen.xmlschema.SimpleType;
0106: import com.sun.xml.bind.v2.schemagen.xmlschema.SimpleTypeHost;
0107: import com.sun.xml.bind.v2.schemagen.xmlschema.TopLevelAttribute;
0108: import com.sun.xml.bind.v2.schemagen.xmlschema.TopLevelElement;
0109: import com.sun.xml.bind.v2.schemagen.xmlschema.TypeHost;
0110: import com.sun.xml.bind.v2.schemagen.xmlschema.ContentModelContainer;
0111: import com.sun.xml.bind.v2.schemagen.xmlschema.TypeDefParticle;
0112: import com.sun.xml.bind.v2.schemagen.xmlschema.AttributeType;
0113: import com.sun.xml.bind.v2.schemagen.episode.Bindings;
0114: import com.sun.xml.txw2.TXW;
0115: import com.sun.xml.txw2.TxwException;
0116: import com.sun.xml.txw2.TypedXmlWriter;
0117: import com.sun.xml.txw2.output.ResultFactory;
0118: import com.sun.xml.txw2.output.XmlSerializer;
0119:
0120: import org.xml.sax.SAXParseException;
0121:
0122: /**
0123: * Generates a set of W3C XML Schema documents from a set of Java classes.
0124: *
0125: * <p>
0126: * A client must invoke methods in the following order:
0127: * <ol>
0128: * <li>Create a new {@link XmlSchemaGenerator}
0129: * <li>Invoke {@link #add} methods, multiple times if necessary.
0130: * <li>Invoke {@link #write}
0131: * <li>Discard the {@link XmlSchemaGenerator}.
0132: * </ol>
0133: *
0134: * @author Ryan Shoemaker
0135: * @author Kohsuke Kawaguchi (kk@kohsuke.org)
0136: */
0137: public final class XmlSchemaGenerator<T, C, F, M> {
0138:
0139: private static final Logger logger = Util.getClassLogger();
0140:
0141: /**
0142: * Java classes to be written, organized by their namespace.
0143: *
0144: * <p>
0145: * We use a {@link TreeMap} here so that the suggested names will
0146: * be consistent across JVMs.
0147: *
0148: * @see SchemaOutputResolver#createOutput(String, String)
0149: */
0150: private final Map<String, Namespace> namespaces = new TreeMap<String, Namespace>(
0151: NAMESPACE_COMPARATOR);
0152:
0153: /**
0154: * {@link ErrorListener} to send errors to.
0155: */
0156: private ErrorListener errorListener;
0157:
0158: /** model navigator **/
0159: private Navigator<T, C, F, M> navigator;
0160:
0161: private final TypeInfoSet<T, C, F, M> types;
0162:
0163: /**
0164: * Representation for xs:string.
0165: */
0166: private final NonElement<T, C> stringType;
0167:
0168: /**
0169: * Represents xs:anyType.
0170: */
0171: private final NonElement<T, C> anyType;
0172:
0173: /**
0174: * Used to detect cycles in anonymous types.
0175: */
0176: private final CollisionCheckStack<ClassInfo<T, C>> collisionChecker = new CollisionCheckStack<ClassInfo<T, C>>();
0177:
0178: public XmlSchemaGenerator(Navigator<T, C, F, M> navigator,
0179: TypeInfoSet<T, C, F, M> types) {
0180: this .navigator = navigator;
0181: this .types = types;
0182:
0183: this .stringType = types
0184: .getTypeInfo(navigator.ref(String.class));
0185: this .anyType = types.getAnyTypeInfo();
0186:
0187: // populate the object
0188: for (ClassInfo<T, C> ci : types.beans().values())
0189: add(ci);
0190: for (ElementInfo<T, C> ei1 : types.getElementMappings(null)
0191: .values())
0192: add(ei1);
0193: for (EnumLeafInfo<T, C> ei : types.enums().values())
0194: add(ei);
0195: for (ArrayInfo<T, C> a : types.arrays().values())
0196: add(a);
0197: }
0198:
0199: private Namespace getNamespace(String uri) {
0200: Namespace n = namespaces.get(uri);
0201: if (n == null)
0202: namespaces.put(uri, n = new Namespace(uri));
0203: return n;
0204: }
0205:
0206: /**
0207: * Adds a new class to the list of classes to be written.
0208: *
0209: * <p>
0210: * A {@link ClassInfo} may have two namespaces --- one for the element name
0211: * and the other for the type name. If they are different, we put the same
0212: * {@link ClassInfo} to two {@link Namespace}s.
0213: */
0214: public void add(ClassInfo<T, C> clazz) {
0215: assert clazz != null;
0216:
0217: String nsUri = null;
0218:
0219: if (clazz.getClazz() == navigator
0220: .asDecl(CompositeStructure.class))
0221: return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
0222:
0223: if (clazz.isElement()) {
0224: // put element -> type reference
0225: nsUri = clazz.getElementName().getNamespaceURI();
0226: Namespace ns = getNamespace(nsUri);
0227: ns.classes.add(clazz);
0228: ns.addDependencyTo(clazz.getTypeName());
0229:
0230: // schedule writing this global element
0231: add(clazz.getElementName(), false, clazz);
0232: }
0233:
0234: QName tn = clazz.getTypeName();
0235: if (tn != null) {
0236: nsUri = tn.getNamespaceURI();
0237: } else {
0238: // anonymous type
0239: if (nsUri == null)
0240: return;
0241: }
0242:
0243: Namespace n = getNamespace(nsUri);
0244: n.classes.add(clazz);
0245:
0246: // search properties for foreign namespace references
0247: for (PropertyInfo<T, C> p : clazz.getProperties()) {
0248: n.processForeignNamespaces(p);
0249: if (p instanceof AttributePropertyInfo) {
0250: AttributePropertyInfo<T, C> ap = (AttributePropertyInfo<T, C>) p;
0251: String aUri = ap.getXmlName().getNamespaceURI();
0252: if (aUri.length() > 0) {
0253: // global attribute
0254: getNamespace(aUri).addGlobalAttribute(ap);
0255: n.addDependencyTo(ap.getXmlName());
0256: }
0257: }
0258: if (p instanceof ElementPropertyInfo) {
0259: ElementPropertyInfo<T, C> ep = (ElementPropertyInfo<T, C>) p;
0260: for (TypeRef<T, C> tref : ep.getTypes()) {
0261: String eUri = tref.getTagName().getNamespaceURI();
0262: if (eUri.length() > 0 && !eUri.equals(n.uri)) {
0263: getNamespace(eUri).addGlobalElement(tref);
0264: n.addDependencyTo(tref.getTagName());
0265: }
0266: }
0267: }
0268:
0269: if (generateSwaRefAdapter(p))
0270: n.useSwaRef = true;
0271: }
0272:
0273: // recurse on baseTypes to make sure that we can refer to them in the schema
0274: ClassInfo<T, C> bc = clazz.getBaseClass();
0275: if (bc != null) {
0276: add(bc);
0277: n.addDependencyTo(bc.getTypeName());
0278: }
0279: }
0280:
0281: /**
0282: * Adds a new element to the list of elements to be written.
0283: */
0284: public void add(ElementInfo<T, C> elem) {
0285: assert elem != null;
0286:
0287: QName name = elem.getElementName();
0288: Namespace n = getNamespace(name.getNamespaceURI());
0289: n.elementDecls.put(name.getLocalPart(), n.new ElementWithType(
0290: true, elem.getContentType()));
0291:
0292: // search for foreign namespace references
0293: n.processForeignNamespaces(elem.getProperty());
0294: }
0295:
0296: public void add(EnumLeafInfo<T, C> envm) {
0297: assert envm != null;
0298:
0299: String nsUri = null;
0300:
0301: if (envm.isElement()) {
0302: // put element -> type reference
0303: nsUri = envm.getElementName().getNamespaceURI();
0304: Namespace ns = getNamespace(nsUri);
0305: ns.enums.add(envm);
0306: ns.addDependencyTo(envm.getTypeName());
0307:
0308: // schedule writing this global element
0309: add(envm.getElementName(), false, envm);
0310: }
0311:
0312: final QName typeName = envm.getTypeName();
0313: if (typeName != null) {
0314: nsUri = typeName.getNamespaceURI();
0315: } else {
0316: if (nsUri == null)
0317: return; // anonymous type
0318: }
0319:
0320: Namespace n = getNamespace(nsUri);
0321: n.enums.add(envm);
0322:
0323: // search for foreign namespace references
0324: n.addDependencyTo(envm.getBaseType().getTypeName());
0325: }
0326:
0327: public void add(ArrayInfo<T, C> a) {
0328: assert a != null;
0329:
0330: final String namespaceURI = a.getTypeName().getNamespaceURI();
0331: Namespace n = getNamespace(namespaceURI);
0332: n.arrays.add(a);
0333:
0334: // search for foreign namespace references
0335: n.addDependencyTo(a.getItemType().getTypeName());
0336: }
0337:
0338: /**
0339: * Adds an additional element declaration.
0340: *
0341: * @param tagName
0342: * The name of the element declaration to be added.
0343: * @param type
0344: * The type this element refers to.
0345: * Can be null, in which case the element refers to an empty anonymous complex type.
0346: */
0347: public void add(QName tagName, boolean isNillable,
0348: NonElement<T, C> type) {
0349:
0350: if (type != null
0351: && type.getType() == navigator
0352: .ref(CompositeStructure.class))
0353: return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
0354:
0355: Namespace n = getNamespace(tagName.getNamespaceURI());
0356: n.elementDecls.put(tagName.getLocalPart(),
0357: n.new ElementWithType(isNillable, type));
0358:
0359: // search for foreign namespace references
0360: if (type != null)
0361: n.addDependencyTo(type.getTypeName());
0362: }
0363:
0364: /**
0365: * Writes out the episode file.
0366: */
0367: public void writeEpisodeFile(XmlSerializer out) {
0368: Bindings root = TXW.create(Bindings.class, out);
0369:
0370: if (namespaces.containsKey("")) // otherwise jaxb binding NS should be the default namespace
0371: root._namespace(WellKnownNamespace.JAXB, "jaxb");
0372: root.version("2.1");
0373: // TODO: don't we want to bake in versions?
0374:
0375: // generate listing per schema
0376: for (Map.Entry<String, Namespace> e : namespaces.entrySet()) {
0377: Bindings group = root.bindings();
0378:
0379: String prefix;
0380: String tns = e.getKey();
0381: if (!tns.equals("")) {
0382: group._namespace(tns, "tns");
0383: prefix = "tns:";
0384: } else {
0385: prefix = "";
0386: }
0387:
0388: group.scd("x-schema::" + (tns.equals("") ? "" : "tns"));
0389: group.schemaBindings().map(false);
0390:
0391: for (ClassInfo<T, C> ci : e.getValue().classes) {
0392: if (ci.getTypeName() == null)
0393: continue; // local type
0394:
0395: if (ci.getTypeName().getNamespaceURI().equals(tns)) {
0396: Bindings child = group.bindings();
0397: child.scd('~' + prefix
0398: + ci.getTypeName().getLocalPart());
0399: child.klass().ref(ci.getName());
0400: }
0401:
0402: if (ci.isElement()
0403: && ci.getElementName().getNamespaceURI()
0404: .equals(tns)) {
0405: Bindings child = group.bindings();
0406: child.scd(prefix
0407: + ci.getElementName().getLocalPart());
0408: child.klass().ref(ci.getName());
0409: }
0410: }
0411:
0412: for (EnumLeafInfo<T, C> en : e.getValue().enums) {
0413: if (en.getTypeName() == null)
0414: continue; // local type
0415:
0416: Bindings child = group.bindings();
0417: child.scd('~' + prefix
0418: + en.getTypeName().getLocalPart());
0419: child.klass()
0420: .ref(navigator.getClassName(en.getClazz()));
0421: }
0422:
0423: group.commit(true);
0424: }
0425:
0426: root.commit();
0427: }
0428:
0429: /**
0430: * Write out the schema documents.
0431: */
0432: public void write(SchemaOutputResolver resolver,
0433: ErrorListener errorListener) throws IOException {
0434: if (resolver == null)
0435: throw new IllegalArgumentException();
0436:
0437: // make it fool-proof
0438: resolver = new FoolProofResolver(resolver);
0439: this .errorListener = errorListener;
0440:
0441: Map<String, String> schemaLocations = types
0442: .getSchemaLocations();
0443:
0444: Map<Namespace, Result> out = new HashMap<Namespace, Result>();
0445: Map<Namespace, String> systemIds = new HashMap<Namespace, String>();
0446:
0447: // we create a Namespace object for the XML Schema namespace
0448: // as a side-effect, but we don't want to generate it.
0449: namespaces.remove(WellKnownNamespace.XML_SCHEMA);
0450:
0451: // first create the outputs for all so that we can resolve references among
0452: // schema files when we write
0453: for (Namespace n : namespaces.values()) {
0454: String schemaLocation = schemaLocations.get(n.uri);
0455: if (schemaLocation != null) {
0456: systemIds.put(n, schemaLocation);
0457: } else {
0458: Result output = resolver.createOutput(n.uri, "schema"
0459: + (out.size() + 1) + ".xsd");
0460: if (output != null) { // null result means no schema for that namespace
0461: out.put(n, output);
0462: systemIds.put(n, output.getSystemId());
0463: }
0464: }
0465: }
0466:
0467: // then write'em all
0468: for (Map.Entry<Namespace, Result> e : out.entrySet()) {
0469: Result result = e.getValue();
0470: e.getKey().writeTo(result, systemIds);
0471: if (result instanceof StreamResult) {
0472: OutputStream outputStream = ((StreamResult) result)
0473: .getOutputStream();
0474: if (outputStream != null) {
0475: outputStream.close(); // fix for bugid: 6291301
0476: } else {
0477: final Writer writer = ((StreamResult) result)
0478: .getWriter();
0479: if (writer != null)
0480: writer.close();
0481: }
0482: }
0483: }
0484: }
0485:
0486: /**
0487: * Schema components are organized per namespace.
0488: */
0489: private class Namespace {
0490: final @NotNull
0491: String uri;
0492:
0493: /**
0494: * Other {@link Namespace}s that this namespace depends on.
0495: */
0496: private final Set<Namespace> depends = new LinkedHashSet<Namespace>();
0497:
0498: /**
0499: * If this schema refers to components from this schema by itself.
0500: */
0501: private boolean selfReference;
0502:
0503: /**
0504: * List of classes in this namespace.
0505: */
0506: private final Set<ClassInfo<T, C>> classes = new LinkedHashSet<ClassInfo<T, C>>();
0507:
0508: /**
0509: * Set of enums in this namespace
0510: */
0511: private final Set<EnumLeafInfo<T, C>> enums = new LinkedHashSet<EnumLeafInfo<T, C>>();
0512:
0513: /**
0514: * Set of arrays in this namespace
0515: */
0516: private final Set<ArrayInfo<T, C>> arrays = new LinkedHashSet<ArrayInfo<T, C>>();
0517:
0518: /**
0519: * Global attribute declarations keyed by their local names.
0520: */
0521: private final MultiMap<String, AttributePropertyInfo<T, C>> attributeDecls = new MultiMap<String, AttributePropertyInfo<T, C>>(
0522: null);
0523:
0524: /**
0525: * Global element declarations to be written, keyed by their local names.
0526: */
0527: private final MultiMap<String, ElementDeclaration> elementDecls = new MultiMap<String, ElementDeclaration>(
0528: new ElementWithType(true, anyType));
0529:
0530: private Form attributeFormDefault;
0531: private Form elementFormDefault;
0532:
0533: /**
0534: * Does schema in this namespace uses swaRef? If so, we need to generate import
0535: * statement.
0536: */
0537: private boolean useSwaRef;
0538:
0539: public Namespace(String uri) {
0540: this .uri = uri;
0541: assert !XmlSchemaGenerator.this .namespaces.containsKey(uri);
0542: XmlSchemaGenerator.this .namespaces.put(uri, this );
0543: }
0544:
0545: /**
0546: * Process the given PropertyInfo looking for references to namespaces that
0547: * are foreign to the given namespace. Any foreign namespace references
0548: * found are added to the given namespaces dependency list and an <import>
0549: * is generated for it.
0550: *
0551: * @param p the PropertyInfo
0552: */
0553: private void processForeignNamespaces(PropertyInfo<T, C> p) {
0554: // TODO: missing the correct handling of anonymous type,
0555: // which requires recursive checks
0556: for (TypeInfo<T, C> t : p.ref()) {
0557: if (t instanceof Element) {
0558: addDependencyTo(((Element) t).getElementName());
0559: }
0560: if (t instanceof NonElement) {
0561: addDependencyTo(((NonElement) t).getTypeName());
0562: }
0563: }
0564: }
0565:
0566: private void addDependencyTo(@Nullable
0567: QName qname) {
0568: // even though the Element interface says getElementName() returns non-null,
0569: // ClassInfo always implements Element (even if an instance of ClassInfo might not be an Element).
0570: // so this check is still necessary
0571: if (qname == null)
0572: return;
0573:
0574: String nsUri = qname.getNamespaceURI();
0575:
0576: if (nsUri.equals(XML_SCHEMA))
0577: // no need to explicitly refer to XSD namespace
0578: return;
0579:
0580: if (nsUri.equals(uri)) {
0581: selfReference = true;
0582: return;
0583: }
0584:
0585: // found a type in a foreign namespace, so make sure we generate an import for it
0586: depends.add(getNamespace(nsUri));
0587: }
0588:
0589: /**
0590: * Writes the schema document to the specified result.
0591: *
0592: * @param systemIds
0593: * System IDs of the other schema documents. "" indicates 'implied'.
0594: */
0595: private void writeTo(Result result,
0596: Map<Namespace, String> systemIds) throws IOException {
0597: try {
0598: Schema schema = TXW.create(Schema.class, ResultFactory
0599: .createSerializer(result));
0600:
0601: // additional namespace declarations to be made.
0602: Map<String, String> xmlNs = types.getXmlNs(uri);
0603:
0604: for (Map.Entry<String, String> e : xmlNs.entrySet()) {
0605: schema._namespace(e.getValue(), e.getKey());
0606: }
0607:
0608: if (useSwaRef)
0609: schema._namespace(WellKnownNamespace.SWA_URI,
0610: "swaRef");
0611:
0612: attributeFormDefault = Form.get(types
0613: .getAttributeFormDefault(uri));
0614: attributeFormDefault.declare("attributeFormDefault",
0615: schema);
0616:
0617: elementFormDefault = Form.get(types
0618: .getElementFormDefault(uri));
0619: // TODO: if elementFormDefault is UNSET, figure out the right default value to use
0620: elementFormDefault
0621: .declare("elementFormDefault", schema);
0622:
0623: // declare XML Schema namespace to be xs, but allow the user to override it.
0624: // if 'xs' is used for other things, we'll just let TXW assign a random prefix
0625: if (!xmlNs.containsValue(WellKnownNamespace.XML_SCHEMA)
0626: && !xmlNs.containsKey("xs"))
0627: schema._namespace(WellKnownNamespace.XML_SCHEMA,
0628: "xs");
0629: schema.version("1.0");
0630:
0631: if (uri.length() != 0)
0632: schema.targetNamespace(uri);
0633:
0634: // declare prefixes for them at this level, so that we can avoid redundant
0635: // namespace declarations
0636: for (Namespace ns : depends) {
0637: schema._namespace(ns.uri);
0638: }
0639:
0640: if (selfReference && uri.length() != 0) {
0641: // use common 'tns' prefix for the own namespace
0642: // if self-reference is needed
0643: schema._namespace(uri, "tns");
0644: }
0645:
0646: schema._pcdata(newline);
0647:
0648: // refer to other schemas
0649: for (Namespace n : depends) {
0650: Import imp = schema._import();
0651: if (n.uri.length() != 0)
0652: imp.namespace(n.uri);
0653: String refSystemId = systemIds.get(n);
0654: if (refSystemId != null && !refSystemId.equals("")) {
0655: // "" means implied. null if the SchemaOutputResolver said "don't generate!"
0656: imp.schemaLocation(relativize(refSystemId,
0657: result.getSystemId()));
0658: }
0659: schema._pcdata(newline);
0660: }
0661: if (useSwaRef) {
0662: schema
0663: ._import()
0664: .namespace(WellKnownNamespace.SWA_URI)
0665: .schemaLocation(
0666: "http://ws-i.org/profiles/basic/1.1/swaref.xsd");
0667: }
0668:
0669: // then write each component
0670: for (Map.Entry<String, ElementDeclaration> e : elementDecls
0671: .entrySet()) {
0672: e.getValue().writeTo(e.getKey(), schema);
0673: schema._pcdata(newline);
0674: }
0675: for (ClassInfo<T, C> c : classes) {
0676: if (c.getTypeName() == null) {
0677: // don't generate anything if it's an anonymous type
0678: continue;
0679: }
0680: if (uri.equals(c.getTypeName().getNamespaceURI()))
0681: writeClass(c, schema);
0682: schema._pcdata(newline);
0683: }
0684: for (EnumLeafInfo<T, C> e : enums) {
0685: if (e.getTypeName() == null) {
0686: // don't generate anything if it's an anonymous type
0687: continue;
0688: }
0689: if (uri.equals(e.getTypeName().getNamespaceURI()))
0690: writeEnum(e, schema);
0691: schema._pcdata(newline);
0692: }
0693: for (ArrayInfo<T, C> a : arrays) {
0694: writeArray(a, schema);
0695: schema._pcdata(newline);
0696: }
0697: for (Map.Entry<String, AttributePropertyInfo<T, C>> e : attributeDecls
0698: .entrySet()) {
0699: TopLevelAttribute a = schema.attribute();
0700: a.name(e.getKey());
0701: if (e.getValue() == null)
0702: writeTypeRef(a, stringType, "type");
0703: else
0704: writeAttributeTypeRef(e.getValue(), a);
0705: schema._pcdata(newline);
0706: }
0707:
0708: // close the schema
0709: schema.commit();
0710: } catch (TxwException e) {
0711: logger.log(Level.INFO, e.getMessage(), e);
0712: throw new IOException(e.getMessage());
0713: }
0714: }
0715:
0716: /**
0717: * Writes a type attribute (if the referenced type is a global type)
0718: * or writes out the definition of the anonymous type in place (if the referenced
0719: * type is not a global type.)
0720: *
0721: * Also provides processing for ID/IDREF, MTOM @xmime, and swa:ref
0722: *
0723: * ComplexTypeHost and SimpleTypeHost don't share an api for creating
0724: * and attribute in a type-safe way, so we will compromise for now and
0725: * use _attribute().
0726: */
0727: private void writeTypeRef(TypeHost th,
0728: NonElementRef<T, C> typeRef, String refAttName) {
0729: // ID / IDREF handling
0730: switch (typeRef.getSource().id()) {
0731: case ID:
0732: th._attribute(refAttName, new QName(
0733: WellKnownNamespace.XML_SCHEMA, "ID"));
0734: return;
0735: case IDREF:
0736: th._attribute(refAttName, new QName(
0737: WellKnownNamespace.XML_SCHEMA, "IDREF"));
0738: return;
0739: case NONE:
0740: // no ID/IDREF, so continue on and generate the type
0741: break;
0742: default:
0743: throw new IllegalStateException();
0744: }
0745:
0746: // MTOM handling
0747: MimeType mimeType = typeRef.getSource()
0748: .getExpectedMimeType();
0749: if (mimeType != null) {
0750: th._attribute(new QName(
0751: WellKnownNamespace.XML_MIME_URI,
0752: "expectedContentTypes", "xmime"), mimeType
0753: .toString());
0754: }
0755:
0756: // ref:swaRef handling
0757: if (generateSwaRefAdapter(typeRef)) {
0758: th._attribute(refAttName, new QName(
0759: WellKnownNamespace.SWA_URI, "swaRef", "ref"));
0760: return;
0761: }
0762:
0763: // type name override
0764: if (typeRef.getSource().getSchemaType() != null) {
0765: th._attribute(refAttName, typeRef.getSource()
0766: .getSchemaType());
0767: return;
0768: }
0769:
0770: // normal type generation
0771: writeTypeRef(th, typeRef.getTarget(), refAttName);
0772: }
0773:
0774: /**
0775: * Writes a type attribute (if the referenced type is a global type)
0776: * or writes out the definition of the anonymous type in place (if the referenced
0777: * type is not a global type.)
0778: *
0779: * @param th
0780: * the TXW interface to which the attribute will be written.
0781: * @param type
0782: * type to be referenced.
0783: * @param refAttName
0784: * The name of the attribute used when referencing a type by QName.
0785: */
0786: private void writeTypeRef(TypeHost th, NonElement<T, C> type,
0787: String refAttName) {
0788: if (type.getTypeName() == null) {
0789: // anonymous
0790: th.block(); // so that the caller may write other attribuets
0791: if (type instanceof ClassInfo) {
0792: if (collisionChecker.push((ClassInfo<T, C>) type)) {
0793: errorListener.error(new SAXParseException(
0794: Messages.ANONYMOUS_TYPE_CYCLE
0795: .format(collisionChecker
0796: .getCycleString()),
0797: null));
0798: } else {
0799: writeClass((ClassInfo<T, C>) type, th);
0800: }
0801: collisionChecker.pop();
0802: } else {
0803: writeEnum((EnumLeafInfo<T, C>) type,
0804: (SimpleTypeHost) th);
0805: }
0806: } else {
0807: th._attribute(refAttName, type.getTypeName());
0808: }
0809: }
0810:
0811: /**
0812: * writes the schema definition for the given array class
0813: */
0814: private void writeArray(ArrayInfo<T, C> a, Schema schema) {
0815: ComplexType ct = schema.complexType().name(
0816: a.getTypeName().getLocalPart());
0817: ct._final("#all");
0818: LocalElement le = ct.sequence().element().name("item");
0819: le.type(a.getItemType().getTypeName());
0820: le.minOccurs(0).maxOccurs("unbounded");
0821: le.nillable(true);
0822: ct.commit();
0823: }
0824:
0825: /**
0826: * writes the schema definition for the specified type-safe enum in the given TypeHost
0827: */
0828: private void writeEnum(EnumLeafInfo<T, C> e, SimpleTypeHost th) {
0829: SimpleType st = th.simpleType();
0830: writeName(e, st);
0831:
0832: SimpleRestrictionModel base = st.restriction();
0833: writeTypeRef(base, e.getBaseType(), "base");
0834:
0835: for (EnumConstant c : e.getConstants()) {
0836: base.enumeration().value(c.getLexicalValue());
0837: }
0838: st.commit();
0839: }
0840:
0841: /**
0842: * Writes the schema definition for the specified class to the schema writer.
0843: *
0844: * @param c the class info
0845: * @param parent the writer of the parent element into which the type will be defined
0846: */
0847: private void writeClass(ClassInfo<T, C> c, TypeHost parent) {
0848: // special handling for value properties
0849: if (containsValueProp(c)) {
0850: if (c.getProperties().size() == 1) {
0851: // [RESULT 2 - simpleType if the value prop is the only prop]
0852: //
0853: // <simpleType name="foo">
0854: // <xs:restriction base="xs:int"/>
0855: // </>
0856: ValuePropertyInfo<T, C> vp = (ValuePropertyInfo<T, C>) c
0857: .getProperties().get(0);
0858: SimpleType st = ((SimpleTypeHost) parent)
0859: .simpleType();
0860: writeName(c, st);
0861: if (vp.isCollection()) {
0862: writeTypeRef(st.list(), vp.getTarget(),
0863: "itemType");
0864: } else {
0865: writeTypeRef(st.restriction(), vp.getTarget(),
0866: "base");
0867: }
0868: return;
0869: } else {
0870: // [RESULT 1 - complexType with simpleContent]
0871: //
0872: // <complexType name="foo">
0873: // <simpleContent>
0874: // <extension base="xs:int"/>
0875: // <attribute name="b" type="xs:boolean"/>
0876: // </>
0877: // </>
0878: // </>
0879: // ...
0880: // <element name="f" type="foo"/>
0881: // ...
0882: ComplexType ct = ((ComplexTypeHost) parent)
0883: .complexType();
0884: writeName(c, ct);
0885: if (c.isFinal())
0886: ct._final("extension restriction");
0887:
0888: SimpleExtension se = ct.simpleContent().extension();
0889: se.block(); // because we might have attribute before value
0890: for (PropertyInfo<T, C> p : c.getProperties()) {
0891: switch (p.kind()) {
0892: case ATTRIBUTE:
0893: handleAttributeProp(
0894: (AttributePropertyInfo<T, C>) p, se);
0895: break;
0896: case VALUE:
0897: TODO
0898: .checkSpec("what if vp.isCollection() == true?");
0899: ValuePropertyInfo vp = (ValuePropertyInfo) p;
0900: se.base(vp.getTarget().getTypeName());
0901: break;
0902: case ELEMENT: // error
0903: case REFERENCE: // error
0904: default:
0905: assert false;
0906: throw new IllegalStateException();
0907: }
0908: }
0909: se.commit();
0910: }
0911: TODO
0912: .schemaGenerator("figure out what to do if bc != null");
0913: TODO.checkSpec("handle sec 8.9.5.2, bullet #4");
0914: // Java types containing value props can only contain properties of type
0915: // ValuePropertyinfo and AttributePropertyInfo which have just been handled,
0916: // so return.
0917: return;
0918: }
0919:
0920: // we didn't fall into the special case for value props, so we
0921: // need to initialize the ct.
0922: // generate the complexType
0923: ComplexType ct = ((ComplexTypeHost) parent).complexType();
0924: writeName(c, ct);
0925: if (c.isFinal())
0926: ct._final("extension restriction");
0927: if (c.isAbstract())
0928: ct._abstract(true);
0929:
0930: // these are where we write content model and attributes
0931: AttrDecls contentModel = ct;
0932: TypeDefParticle contentModelOwner = ct;
0933:
0934: // if there is a base class, we need to generate an extension in the schema
0935: final ClassInfo<T, C> bc = c.getBaseClass();
0936: if (bc != null) {
0937: if (bc.hasValueProperty()) {
0938: // extending complex type with simple content
0939: SimpleExtension se = ct.simpleContent().extension();
0940: contentModel = se;
0941: contentModelOwner = null;
0942: se.base(bc.getTypeName());
0943: } else {
0944: ComplexExtension ce = ct.complexContent()
0945: .extension();
0946: contentModel = ce;
0947: contentModelOwner = ce;
0948:
0949: ce.base(bc.getTypeName());
0950: // TODO: what if the base type is anonymous?
0951: }
0952: }
0953:
0954: if (contentModelOwner != null) {
0955: // build the tree that represents the explicit content model from iterate over the properties
0956: ArrayList<Tree> children = new ArrayList<Tree>();
0957: for (PropertyInfo<T, C> p : c.getProperties()) {
0958: // handling for <complexType @mixed='true' ...>
0959: if (p instanceof ReferencePropertyInfo
0960: && ((ReferencePropertyInfo) p).isMixed()) {
0961: ct.mixed(true);
0962: }
0963: Tree t = buildPropertyContentModel(p);
0964: if (t != null)
0965: children.add(t);
0966: }
0967:
0968: Tree top = Tree.makeGroup(
0969: c.isOrdered() ? GroupKind.SEQUENCE
0970: : GroupKind.ALL, children);
0971:
0972: // write the content model
0973: top.write(contentModelOwner);
0974: }
0975:
0976: // then attributes
0977: for (PropertyInfo<T, C> p : c.getProperties()) {
0978: if (p instanceof AttributePropertyInfo) {
0979: handleAttributeProp(
0980: (AttributePropertyInfo<T, C>) p,
0981: contentModel);
0982: }
0983: }
0984: if (c.hasAttributeWildcard()) {
0985: contentModel.anyAttribute().namespace("##other")
0986: .processContents("skip");
0987: }
0988: ct.commit();
0989: }
0990:
0991: /**
0992: * Writes the name attribute if it's named.
0993: */
0994: private void writeName(NonElement<T, C> c, TypedXmlWriter xw) {
0995: QName tn = c.getTypeName();
0996: if (tn != null)
0997: xw._attribute("name", tn.getLocalPart()); // named
0998: }
0999:
1000: private boolean containsValueProp(ClassInfo<T, C> c) {
1001: for (PropertyInfo p : c.getProperties()) {
1002: if (p instanceof ValuePropertyInfo)
1003: return true;
1004: }
1005: return false;
1006: }
1007:
1008: /**
1009: * Builds content model writer for the specified property.
1010: */
1011: private Tree buildPropertyContentModel(PropertyInfo<T, C> p) {
1012: switch (p.kind()) {
1013: case ELEMENT:
1014: return handleElementProp((ElementPropertyInfo<T, C>) p);
1015: case ATTRIBUTE:
1016: // attribuets are handled later
1017: return null;
1018: case REFERENCE:
1019: return handleReferenceProp((ReferencePropertyInfo<T, C>) p);
1020: case MAP:
1021: return handleMapProp((MapPropertyInfo<T, C>) p);
1022: case VALUE:
1023: // value props handled above in writeClass()
1024: assert false;
1025: throw new IllegalStateException();
1026: default:
1027: assert false;
1028: throw new IllegalStateException();
1029: }
1030: }
1031:
1032: /**
1033: * Generate the proper schema fragment for the given element property into the
1034: * specified schema compositor.
1035: *
1036: * The element property may or may not represent a collection and it may or may
1037: * not be wrapped.
1038: *
1039: * @param ep the element property
1040: */
1041: private Tree handleElementProp(
1042: final ElementPropertyInfo<T, C> ep) {
1043: if (ep.isValueList()) {
1044: return new Tree.Term() {
1045: protected void write(ContentModelContainer parent,
1046: boolean isOptional, boolean repeated) {
1047: TypeRef<T, C> t = ep.getTypes().get(0);
1048: LocalElement e = parent.element();
1049: e.block(); // we will write occurs later
1050: QName tn = t.getTagName();
1051: e.name(tn.getLocalPart());
1052: List lst = e.simpleType().list();
1053: writeTypeRef(lst, t, "itemType");
1054: elementFormDefault.writeForm(e, tn);
1055: writeOccurs(e, isOptional || !ep.isRequired(),
1056: repeated);
1057: }
1058: };
1059: }
1060:
1061: ArrayList<Tree> children = new ArrayList<Tree>();
1062: for (final TypeRef<T, C> t : ep.getTypes()) {
1063: children.add(new Tree.Term() {
1064: protected void write(ContentModelContainer parent,
1065: boolean isOptional, boolean repeated) {
1066: LocalElement e = parent.element();
1067:
1068: QName tn = t.getTagName();
1069:
1070: if (canBeDirectElementRef(t, tn)
1071: || (!tn.getNamespaceURI().equals(uri) && tn
1072: .getNamespaceURI().length() > 0)) {
1073: e.ref(tn);
1074: } else {
1075: e.name(tn.getLocalPart());
1076: writeTypeRef(e, t, "type");
1077: elementFormDefault.writeForm(e, tn);
1078: }
1079:
1080: if (t.isNillable()) {
1081: e.nillable(true);
1082: }
1083: if (t.getDefaultValue() != null)
1084: e._default(t.getDefaultValue());
1085: writeOccurs(e, isOptional, repeated);
1086: }
1087: });
1088: }
1089:
1090: final Tree choice = Tree.makeGroup(GroupKind.CHOICE,
1091: children).makeOptional(!ep.isRequired())
1092: .makeRepeated(ep.isCollection()); // see Spec table 8-13
1093:
1094: final QName ename = ep.getXmlName();
1095: if (ename != null) { // wrapped collection
1096: return new Tree.Term() {
1097: protected void write(ContentModelContainer parent,
1098: boolean isOptional, boolean repeated) {
1099: LocalElement e = parent.element();
1100: if (ename.getNamespaceURI().length() > 0) {
1101: if (!ename.getNamespaceURI().equals(uri)) {
1102: // TODO: we need to generate the corresponding element declaration for this
1103: // table 8-25: Property/field element wrapper with ref attribute
1104: e.ref(new QName(
1105: ename.getNamespaceURI(), ename
1106: .getLocalPart()));
1107: return;
1108: }
1109: }
1110: e.name(ename.getLocalPart());
1111: elementFormDefault.writeForm(e, ename);
1112:
1113: if (ep.isCollectionNillable()) {
1114: e.nillable(true);
1115: }
1116: writeOccurs(e, !ep.isCollectionRequired(),
1117: repeated);
1118:
1119: ComplexType p = e.complexType();
1120: choice.write(p);
1121: }
1122: };
1123: } else {// non-wrapped
1124: return choice;
1125: }
1126: }
1127:
1128: /**
1129: * Checks if we can collapse
1130: * <element name='foo' type='t' /> to <element ref='foo' />.
1131: *
1132: * This is possible if we already have such declaration to begin with.
1133: */
1134: private boolean canBeDirectElementRef(TypeRef<T, C> t, QName tn) {
1135: if (t.isNillable() || t.getDefaultValue() != null)
1136: // can't put those attributes on <element ref>
1137: return false;
1138:
1139: if (t.getTarget() instanceof Element) {
1140: Element te = (Element) t.getTarget();
1141: QName targetTagName = te.getElementName();
1142: return targetTagName != null
1143: && targetTagName.equals(tn);
1144: }
1145:
1146: return false;
1147: }
1148:
1149: /**
1150: * Generate an attribute for the specified property on the specified complexType
1151: *
1152: * @param ap the attribute
1153: * @param attr the schema definition to which the attribute will be added
1154: */
1155: private void handleAttributeProp(
1156: AttributePropertyInfo<T, C> ap, AttrDecls attr) {
1157: // attr is either a top-level ComplexType or a ComplexExtension
1158: //
1159: // [RESULT]
1160: //
1161: // <complexType ...>
1162: // <...>...</>
1163: // <attribute name="foo" type="xs:int"/>
1164: // </>
1165: //
1166: // or
1167: //
1168: // <complexType ...>
1169: // <complexContent>
1170: // <extension ...>
1171: // <...>...</>
1172: // </>
1173: // </>
1174: // <attribute name="foo" type="xs:int"/>
1175: // </>
1176: //
1177: // or it could also be an in-lined type (attr ref)
1178: //
1179: LocalAttribute localAttribute = attr.attribute();
1180:
1181: final String attrURI = ap.getXmlName().getNamespaceURI();
1182: if (attrURI.equals("") /*|| attrURI.equals(uri) --- those are generated as global attributes anyway, so use them.*/) {
1183: localAttribute.name(ap.getXmlName().getLocalPart());
1184:
1185: writeAttributeTypeRef(ap, localAttribute);
1186:
1187: attributeFormDefault.writeForm(localAttribute, ap
1188: .getXmlName());
1189: } else { // generate an attr ref
1190: localAttribute.ref(ap.getXmlName());
1191: }
1192:
1193: if (ap.isRequired()) {
1194: // TODO: not type safe
1195: localAttribute.use("required");
1196: }
1197: }
1198:
1199: private void writeAttributeTypeRef(
1200: AttributePropertyInfo<T, C> ap, AttributeType a) {
1201: if (ap.isCollection())
1202: writeTypeRef(a.simpleType().list(), ap, "itemType");
1203: else
1204: writeTypeRef(a, ap, "type");
1205: }
1206:
1207: /**
1208: * Generate the proper schema fragment for the given reference property into the
1209: * specified schema compositor.
1210: *
1211: * The reference property may or may not refer to a collection and it may or may
1212: * not be wrapped.
1213: */
1214: private Tree handleReferenceProp(
1215: final ReferencePropertyInfo<T, C> rp) {
1216: // fill in content model
1217: ArrayList<Tree> children = new ArrayList<Tree>();
1218:
1219: for (final Element<T, C> e : rp.getElements()) {
1220: children.add(new Tree.Term() {
1221: protected void write(ContentModelContainer parent,
1222: boolean isOptional, boolean repeated) {
1223: LocalElement eref = parent.element();
1224:
1225: boolean local = false;
1226:
1227: QName en = e.getElementName();
1228: if (e.getScope() != null) {
1229: // scoped. needs to be inlined
1230: boolean qualified = en.getNamespaceURI()
1231: .equals(uri);
1232: boolean unqualified = en.getNamespaceURI()
1233: .equals("");
1234: if (qualified || unqualified) {
1235: // can be inlined indeed
1236:
1237: // write form="..." if necessary
1238: if (unqualified) {
1239: if (elementFormDefault.isEffectivelyQualified)
1240: eref.form("unqualified");
1241: } else {
1242: if (!elementFormDefault.isEffectivelyQualified)
1243: eref.form("qualified");
1244: }
1245:
1246: local = true;
1247: eref.name(en.getLocalPart());
1248:
1249: // write out type reference
1250: if (e instanceof ClassInfo) {
1251: writeTypeRef(eref,
1252: (ClassInfo<T, C>) e, "type");
1253: } else {
1254: writeTypeRef(eref,
1255: ((ElementInfo<T, C>) e)
1256: .getContentType(),
1257: "type");
1258: }
1259: }
1260: }
1261: if (!local)
1262: eref.ref(en);
1263: writeOccurs(eref, isOptional, repeated);
1264: }
1265: });
1266: }
1267:
1268: final WildcardMode wc = rp.getWildcard();
1269: if (wc != null) {
1270: children.add(new Tree.Term() {
1271: protected void write(ContentModelContainer parent,
1272: boolean isOptional, boolean repeated) {
1273: Any any = parent.any();
1274: final String pcmode = getProcessContentsModeName(wc);
1275: if (pcmode != null)
1276: any.processContents(pcmode);
1277: any.namespace("##other");
1278: writeOccurs(any, isOptional, repeated);
1279: }
1280: });
1281: }
1282:
1283: final Tree choice = Tree.makeGroup(GroupKind.CHOICE,
1284: children).makeRepeated(rp.isCollection())
1285: .makeOptional(rp.isCollection());
1286: // it's a curious omission that XmlElementRef doesn't have required().
1287: // instead right now a collection will make it [0,unbounded]
1288:
1289: final QName ename = rp.getXmlName();
1290:
1291: if (ename != null) { // wrapped
1292: return new Tree.Term() {
1293: protected void write(ContentModelContainer parent,
1294: boolean isOptional, boolean repeated) {
1295: LocalElement e = parent.element().name(
1296: ename.getLocalPart());
1297: elementFormDefault.writeForm(e, ename);
1298: if (rp.isCollectionNillable())
1299: e.nillable(true);
1300: writeOccurs(e, true, repeated);
1301:
1302: ComplexType p = e.complexType();
1303: choice.write(p);
1304: }
1305: };
1306: } else { // unwrapped
1307: return choice;
1308: }
1309: }
1310:
1311: /**
1312: * Generate the proper schema fragment for the given map property into the
1313: * specified schema compositor.
1314: *
1315: * @param mp the map property
1316: */
1317: private Tree handleMapProp(final MapPropertyInfo<T, C> mp) {
1318: return new Tree.Term() {
1319: protected void write(ContentModelContainer parent,
1320: boolean isOptional, boolean repeated) {
1321: QName ename = mp.getXmlName();
1322:
1323: LocalElement e = parent.element();
1324: elementFormDefault.writeForm(e, ename);
1325: if (mp.isCollectionNillable())
1326: e.nillable(true);
1327:
1328: e = e.name(ename.getLocalPart());
1329: writeOccurs(e, isOptional, repeated);
1330: ComplexType p = e.complexType();
1331:
1332: // TODO: entry, key, and value are always unqualified. that needs to be fixed, too.
1333: // TODO: we need to generate the corresponding element declaration, if they are qualified
1334: e = p.sequence().element();
1335: e.name("entry").minOccurs(0).maxOccurs("unbounded");
1336:
1337: ExplicitGroup seq = e.complexType().sequence();
1338: writeKeyOrValue(seq, "key", mp.getKeyType());
1339: writeKeyOrValue(seq, "value", mp.getValueType());
1340: }
1341: };
1342: }
1343:
1344: private void writeKeyOrValue(ExplicitGroup seq, String tagName,
1345: NonElement<T, C> typeRef) {
1346: LocalElement key = seq.element().name(tagName);
1347: key.minOccurs(0);
1348: writeTypeRef(key, typeRef, "type");
1349: }
1350:
1351: public void addGlobalAttribute(AttributePropertyInfo<T, C> ap) {
1352: attributeDecls.put(ap.getXmlName().getLocalPart(), ap);
1353: addDependencyTo(ap.getTarget().getTypeName());
1354: }
1355:
1356: public void addGlobalElement(TypeRef<T, C> tref) {
1357: elementDecls.put(tref.getTagName().getLocalPart(),
1358: new ElementWithType(false, tref.getTarget()));
1359: addDependencyTo(tref.getTarget().getTypeName());
1360: }
1361:
1362: /**
1363: * Represents a global element declaration to be written.
1364: *
1365: * <p>
1366: * Because multiple properties can name the same global element even if
1367: * they have different Java type, the schema generator first needs to
1368: * walk through the model and decide what to generate for the given
1369: * element declaration.
1370: *
1371: * <p>
1372: * This class represents what will be written, and its {@link #equals(Object)}
1373: * method is implemented in such a way that two identical declarations
1374: * are considered as the same.
1375: */
1376: abstract class ElementDeclaration {
1377: /**
1378: * Returns true if two {@link ElementDeclaration}s are representing
1379: * the same schema fragment.
1380: */
1381: public abstract boolean equals(Object o);
1382:
1383: public abstract int hashCode();
1384:
1385: /**
1386: * Generates the declaration.
1387: */
1388: public abstract void writeTo(String localName, Schema schema);
1389: }
1390:
1391: /**
1392: * {@link ElementDeclaration} that refers to a {@link NonElement}.
1393: */
1394: class ElementWithType extends ElementDeclaration {
1395: private final boolean nillable;
1396: private final NonElement<T, C> type;
1397:
1398: public ElementWithType(boolean nillable,
1399: NonElement<T, C> type) {
1400: this .type = type;
1401: this .nillable = nillable;
1402: }
1403:
1404: public void writeTo(String localName, Schema schema) {
1405: TopLevelElement e = schema.element().name(localName);
1406: if (nillable)
1407: e.nillable(true);
1408: if (type != null) {
1409: writeTypeRef(e, type, "type");
1410: } else {
1411: e.complexType(); // refer to the nested empty complex type
1412: }
1413: e.commit();
1414: }
1415:
1416: public boolean equals(Object o) {
1417: if (this == o)
1418: return true;
1419: if (o == null || getClass() != o.getClass())
1420: return false;
1421:
1422: final ElementWithType that = (ElementWithType) o;
1423: return type.equals(that.type);
1424: }
1425:
1426: public int hashCode() {
1427: return type.hashCode();
1428: }
1429: }
1430: }
1431:
1432: /**
1433: * Examine the specified element ref and determine if a swaRef attribute needs to be generated
1434: * @param typeRef
1435: */
1436: private boolean generateSwaRefAdapter(NonElementRef<T, C> typeRef) {
1437: return generateSwaRefAdapter(typeRef.getSource());
1438: }
1439:
1440: /**
1441: * Examine the specified element ref and determine if a swaRef attribute needs to be generated
1442: */
1443: private boolean generateSwaRefAdapter(PropertyInfo<T, C> prop) {
1444: final Adapter<T, C> adapter = prop.getAdapter();
1445: if (adapter == null)
1446: return false;
1447: final Object o = navigator.asDecl(SwaRefAdapter.class);
1448: if (o == null)
1449: return false;
1450: return (o.equals(adapter.adapterType));
1451: }
1452:
1453: /**
1454: * return the string representation of the processContents mode of the
1455: * give wildcard, or null if it is the schema default "strict"
1456: *
1457: */
1458: private static String getProcessContentsModeName(WildcardMode wc) {
1459: switch (wc) {
1460: case LAX:
1461: case SKIP:
1462: return wc.name().toLowerCase();
1463: case STRICT:
1464: return null;
1465: default:
1466: throw new IllegalStateException();
1467: }
1468: }
1469:
1470: /**
1471: * TODO: JAX-WS dependency on this method - consider moving this method into com.sun.tools.jxc.util.Util
1472: *
1473: * Relativizes a URI by using another URI (base URI.)
1474: *
1475: * <p>
1476: * For example, {@code relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"}
1477: *
1478: * <p>
1479: * This method only works on hierarchical URI's, not opaque URI's (refer to the
1480: * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html">java.net.URI</a>
1481: * javadoc for complete definitions of these terms.
1482: *
1483: * <p>
1484: * This method will not normalize the relative URI.
1485: *
1486: * @return the relative URI or the original URI if a relative one could not be computed
1487: */
1488: protected static String relativize(String uri, String baseUri) {
1489: try {
1490: assert uri != null;
1491:
1492: if (baseUri == null)
1493: return uri;
1494:
1495: URI theUri = new URI(escapeURI(uri));
1496: URI theBaseUri = new URI(escapeURI(baseUri));
1497:
1498: if (theUri.isOpaque() || theBaseUri.isOpaque())
1499: return uri;
1500:
1501: if (!equalsIgnoreCase(theUri.getScheme(), theBaseUri
1502: .getScheme())
1503: || !equal(theUri.getAuthority(), theBaseUri
1504: .getAuthority()))
1505: return uri;
1506:
1507: String uriPath = theUri.getPath();
1508: String basePath = theBaseUri.getPath();
1509:
1510: // normalize base path
1511: if (!basePath.endsWith("/")) {
1512: basePath = normalizeUriPath(basePath);
1513: }
1514:
1515: if (uriPath.equals(basePath))
1516: return ".";
1517:
1518: String relPath = calculateRelativePath(uriPath, basePath,
1519: fixNull(theUri.getScheme()).equals("file"));
1520:
1521: if (relPath == null)
1522: return uri; // recursion found no commonality in the two uris at all
1523: StringBuffer relUri = new StringBuffer();
1524: relUri.append(relPath);
1525: if (theUri.getQuery() != null)
1526: relUri.append('?').append(theUri.getQuery());
1527: if (theUri.getFragment() != null)
1528: relUri.append('#').append(theUri.getFragment());
1529:
1530: return relUri.toString();
1531: } catch (URISyntaxException e) {
1532: throw new InternalError(
1533: "Error escaping one of these uris:\n\t" + uri
1534: + "\n\t" + baseUri);
1535: }
1536: }
1537:
1538: private static String fixNull(String s) {
1539: if (s == null)
1540: return "";
1541: else
1542: return s;
1543: }
1544:
1545: private static String calculateRelativePath(String uri,
1546: String base, boolean fileUrl) {
1547: // if this is a file URL (very likely), and if this is on a case-insensitive file system,
1548: // then treat it accordingly.
1549: boolean onWindows = File.pathSeparatorChar == ';';
1550:
1551: if (base == null) {
1552: return null;
1553: }
1554: if ((fileUrl && onWindows && startsWithIgnoreCase(uri, base))
1555: || uri.startsWith(base)) {
1556: return uri.substring(base.length());
1557: } else {
1558: return "../"
1559: + calculateRelativePath(uri,
1560: getParentUriPath(base), fileUrl);
1561: }
1562: }
1563:
1564: private static boolean startsWithIgnoreCase(String s, String t) {
1565: return s.toUpperCase().startsWith(t.toUpperCase());
1566: }
1567:
1568: /**
1569: * JAX-RPC wants the namespaces to be sorted in the reverse order
1570: * so that the empty namespace "" comes to the very end. Don't ask me why.
1571: */
1572: private static final Comparator<String> NAMESPACE_COMPARATOR = new Comparator<String>() {
1573: public int compare(String lhs, String rhs) {
1574: return -lhs.compareTo(rhs);
1575: }
1576: };
1577:
1578: private static final String newline = "\n";
1579: }
|