/*
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.xml.internal.bind.v2.schemagen;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.activation.MimeType;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
import com.sun.istack.internal.Nullable;
import com.sun.istack.internal.NotNull;
import com.sun.xml.internal.bind.Util;
import com.sun.xml.internal.bind.api.CompositeStructure;
import com.sun.xml.internal.bind.api.ErrorListener;
import com.sun.xml.internal.bind.v2.TODO;
import com.sun.xml.internal.bind.v2.WellKnownNamespace;
import com.sun.xml.internal.bind.v2.util.CollisionCheckStack;
import com.sun.xml.internal.bind.v2.util.StackRecorder;
import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_SCHEMA;
import com.sun.xml.internal.bind.v2.model.core.Adapter;
import com.sun.xml.internal.bind.v2.model.core.ArrayInfo;
import com.sun.xml.internal.bind.v2.model.core.AttributePropertyInfo;
import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
import com.sun.xml.internal.bind.v2.model.core.Element;
import com.sun.xml.internal.bind.v2.model.core.ElementInfo;
import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo;
import com.sun.xml.internal.bind.v2.model.core.EnumConstant;
import com.sun.xml.internal.bind.v2.model.core.EnumLeafInfo;
import com.sun.xml.internal.bind.v2.model.core.MapPropertyInfo;
import com.sun.xml.internal.bind.v2.model.core.MaybeElement;
import com.sun.xml.internal.bind.v2.model.core.NonElement;
import com.sun.xml.internal.bind.v2.model.core.NonElementRef;
import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
import com.sun.xml.internal.bind.v2.model.core.ReferencePropertyInfo;
import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
import com.sun.xml.internal.bind.v2.model.core.TypeRef;
import com.sun.xml.internal.bind.v2.model.core.ValuePropertyInfo;
import com.sun.xml.internal.bind.v2.model.core.WildcardMode;
import com.sun.xml.internal.bind.v2.model.impl.ClassInfoImpl;
import com.sun.xml.internal.bind.v2.model.nav.Navigator;
import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;
import static com.sun.xml.internal.bind.v2.schemagen.Util.*;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Any;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttrDecls;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexExtension;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexType;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexTypeHost;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ExplicitGroup;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Import;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalAttribute;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalElement;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Schema;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleExtension;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleRestrictionModel;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleType;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleTypeHost;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelAttribute;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelElement;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeHost;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ContentModelContainer;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeDefParticle;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttributeType;
import com.sun.xml.internal.bind.v2.schemagen.episode.Bindings;
import com.sun.xml.internal.txw2.TXW;
import com.sun.xml.internal.txw2.TxwException;
import com.sun.xml.internal.txw2.TypedXmlWriter;
import com.sun.xml.internal.txw2.output.ResultFactory;
import com.sun.xml.internal.txw2.output.XmlSerializer;
import java.util.Collection;
import java.util.HashSet;
import org.xml.sax.SAXParseException;
Generates a set of W3C XML Schema documents from a set of Java classes.
A client must invoke methods in the following order:
- Create a new
XmlSchemaGenerator
- Invoke
add
methods, multiple times if necessary. - Invoke
write
- Discard the
XmlSchemaGenerator
.
Author: Ryan Shoemaker, Kohsuke Kawaguchi (kk@kohsuke.org)
/**
* Generates a set of W3C XML Schema documents from a set of Java classes.
*
* <p>
* A client must invoke methods in the following order:
* <ol>
* <li>Create a new {@link XmlSchemaGenerator}
* <li>Invoke {@link #add} methods, multiple times if necessary.
* <li>Invoke {@link #write}
* <li>Discard the {@link XmlSchemaGenerator}.
* </ol>
*
* @author Ryan Shoemaker
* @author Kohsuke Kawaguchi (kk@kohsuke.org)
*/
public final class XmlSchemaGenerator<T,C,F,M> {
private static final Logger logger = Util.getClassLogger();
Java classes to be written, organized by their namespace.
We use a TreeMap
here so that the suggested names will be consistent across JVMs.
See Also:
/**
* Java classes to be written, organized by their namespace.
*
* <p>
* We use a {@link TreeMap} here so that the suggested names will
* be consistent across JVMs.
*
* @see SchemaOutputResolver#createOutput(String, String)
*/
private final Map<String,Namespace> namespaces = new TreeMap<String,Namespace>(NAMESPACE_COMPARATOR);
ErrorListener
to send errors to. /**
* {@link ErrorListener} to send errors to.
*/
private ErrorListener errorListener;
model navigator /** model navigator **/
private Navigator<T,C,F,M> navigator;
private final TypeInfoSet<T,C,F,M> types;
Representation for xs:string.
/**
* Representation for xs:string.
*/
private final NonElement<T,C> stringType;
Represents xs:anyType.
/**
* Represents xs:anyType.
*/
private final NonElement<T,C> anyType;
Used to detect cycles in anonymous types.
/**
* Used to detect cycles in anonymous types.
*/
private final CollisionCheckStack<ClassInfo<T,C>> collisionChecker = new CollisionCheckStack<ClassInfo<T,C>>();
public XmlSchemaGenerator( Navigator<T,C,F,M> navigator, TypeInfoSet<T,C,F,M> types ) {
this.navigator = navigator;
this.types = types;
this.stringType = types.getTypeInfo(navigator.ref(String.class));
this.anyType = types.getAnyTypeInfo();
// populate the object
for( ClassInfo<T,C> ci : types.beans().values() )
add(ci);
for( ElementInfo<T,C> ei1 : types.getElementMappings(null).values() )
add(ei1);
for( EnumLeafInfo<T,C> ei : types.enums().values() )
add(ei);
for( ArrayInfo<T,C> a : types.arrays().values())
add(a);
}
private Namespace getNamespace(String uri) {
Namespace n = namespaces.get(uri);
if(n==null)
namespaces.put(uri,n=new Namespace(uri));
return n;
}
/**
* Adds a new class to the list of classes to be written.
*
* <p>
* A {@link ClassInfo} may have two namespaces --- one for the element name
* and the other for the type name. If they are different, we put the same
* {@link ClassInfo} to two {@link Namespace}s.
*/
public void add( ClassInfo<T,C> clazz ) {
assert clazz!=null;
String nsUri = null;
if(clazz.getClazz()==navigator.asDecl(CompositeStructure.class))
return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
if(clazz.isElement()) {
// put element -> type reference
nsUri = clazz.getElementName().getNamespaceURI();
Namespace ns = getNamespace(nsUri);
ns.classes.add(clazz);
ns.addDependencyTo(clazz.getTypeName());
// schedule writing this global element
add(clazz.getElementName(),false,clazz);
}
QName tn = clazz.getTypeName();
if(tn!=null) {
nsUri = tn.getNamespaceURI();
} else {
// anonymous type
if(nsUri==null)
return;
}
Namespace n = getNamespace(nsUri);
n.classes.add(clazz);
// search properties for foreign namespace references
for( PropertyInfo<T,C> p : clazz.getProperties()) {
n.processForeignNamespaces(p, 1);
if (p instanceof AttributePropertyInfo) {
AttributePropertyInfo<T,C> ap = (AttributePropertyInfo<T,C>) p;
String aUri = ap.getXmlName().getNamespaceURI();
if(aUri.length()>0) {
// global attribute
getNamespace(aUri).addGlobalAttribute(ap);
n.addDependencyTo(ap.getXmlName());
}
}
if (p instanceof ElementPropertyInfo) {
ElementPropertyInfo<T,C> ep = (ElementPropertyInfo<T,C>) p;
for (TypeRef<T,C> tref : ep.getTypes()) {
String eUri = tref.getTagName().getNamespaceURI();
if(eUri.length()>0 && !eUri.equals(n.uri)) {
getNamespace(eUri).addGlobalElement(tref);
n.addDependencyTo(tref.getTagName());
}
}
}
if(generateSwaRefAdapter(p))
n.useSwaRef = true;
MimeType mimeType = p.getExpectedMimeType();
if( mimeType != null ) {
n.useMimeNs = true;
}
}
// recurse on baseTypes to make sure that we can refer to them in the schema
ClassInfo<T,C> bc = clazz.getBaseClass();
if (bc != null) {
add(bc);
n.addDependencyTo(bc.getTypeName());
}
}
Adds a new element to the list of elements to be written.
/**
* Adds a new element to the list of elements to be written.
*/
public void add( ElementInfo<T,C> elem ) {
assert elem!=null;
@SuppressWarnings("UnusedAssignment")
boolean nillable = false; // default value
QName name = elem.getElementName();
Namespace n = getNamespace(name.getNamespaceURI());
ElementInfo ei;
if (elem.getScope() != null) { // (probably) never happens
ei = this.types.getElementInfo(elem.getScope().getClazz(), name);
} else {
ei = this.types.getElementInfo(null, name);
}
XmlElement xmlElem = ei.getProperty().readAnnotation(XmlElement.class);
if (xmlElem == null) {
nillable = false;
} else {
nillable = xmlElem.nillable();
}
n.elementDecls.put(name.getLocalPart(),n.new ElementWithType(nillable, elem.getContentType()));
// search for foreign namespace references
n.processForeignNamespaces(elem.getProperty(), 1);
}
public void add( EnumLeafInfo<T,C> envm ) {
assert envm!=null;
String nsUri = null;
if(envm.isElement()) {
// put element -> type reference
nsUri = envm.getElementName().getNamespaceURI();
Namespace ns = getNamespace(nsUri);
ns.enums.add(envm);
ns.addDependencyTo(envm.getTypeName());
// schedule writing this global element
add(envm.getElementName(),false,envm);
}
final QName typeName = envm.getTypeName();
if (typeName != null) {
nsUri = typeName.getNamespaceURI();
} else {
if(nsUri==null)
return; // anonymous type
}
Namespace n = getNamespace(nsUri);
n.enums.add(envm);
// search for foreign namespace references
n.addDependencyTo(envm.getBaseType().getTypeName());
}
public void add( ArrayInfo<T,C> a ) {
assert a!=null;
final String namespaceURI = a.getTypeName().getNamespaceURI();
Namespace n = getNamespace(namespaceURI);
n.arrays.add(a);
// search for foreign namespace references
n.addDependencyTo(a.getItemType().getTypeName());
}
Adds an additional element declaration.
Params: - tagName –
The name of the element declaration to be added.
- type –
The type this element refers to.
Can be null, in which case the element refers to an empty anonymous complex type.
/**
* Adds an additional element declaration.
*
* @param tagName
* The name of the element declaration to be added.
* @param type
* The type this element refers to.
* Can be null, in which case the element refers to an empty anonymous complex type.
*/
public void add( QName tagName, boolean isNillable, NonElement<T,C> type ) {
if(type!=null && type.getType()==navigator.ref(CompositeStructure.class))
return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
Namespace n = getNamespace(tagName.getNamespaceURI());
n.elementDecls.put(tagName.getLocalPart(), n.new ElementWithType(isNillable,type));
// search for foreign namespace references
if(type!=null)
n.addDependencyTo(type.getTypeName());
}
Writes out the episode file.
/**
* Writes out the episode file.
*/
public void writeEpisodeFile(XmlSerializer out) {
Bindings root = TXW.create(Bindings.class, out);
if(namespaces.containsKey("")) // otherwise jaxb binding NS should be the default namespace
root._namespace(WellKnownNamespace.JAXB,"jaxb");
root.version("2.1");
// TODO: don't we want to bake in versions?
// generate listing per schema
for (Map.Entry<String,Namespace> e : namespaces.entrySet()) {
Bindings group = root.bindings();
String prefix;
String tns = e.getKey();
if(!tns.equals("")) {
group._namespace(tns,"tns");
prefix = "tns:";
} else {
prefix = "";
}
group.scd("x-schema::"+(tns.equals("")?"":"tns"));
group.schemaBindings().map(false);
for (ClassInfo<T,C> ci : e.getValue().classes) {
if(ci.getTypeName()==null) continue; // local type
if(ci.getTypeName().getNamespaceURI().equals(tns)) {
Bindings child = group.bindings();
child.scd('~'+prefix+ci.getTypeName().getLocalPart());
child.klass().ref(ci.getName());
}
if(ci.isElement() && ci.getElementName().getNamespaceURI().equals(tns)) {
Bindings child = group.bindings();
child.scd(prefix+ci.getElementName().getLocalPart());
child.klass().ref(ci.getName());
}
}
for (EnumLeafInfo<T,C> en : e.getValue().enums) {
if(en.getTypeName()==null) continue; // local type
Bindings child = group.bindings();
child.scd('~'+prefix+en.getTypeName().getLocalPart());
child.klass().ref(navigator.getClassName(en.getClazz()));
}
group.commit(true);
}
root.commit();
}
Write out the schema documents.
/**
* Write out the schema documents.
*/
public void write(SchemaOutputResolver resolver, ErrorListener errorListener) throws IOException {
if(resolver==null)
throw new IllegalArgumentException();
if(logger.isLoggable(Level.FINE)) {
// debug logging to see what's going on.
logger.log(Level.FINE,"Writing XML Schema for "+toString(),new StackRecorder());
}
// make it fool-proof
resolver = new FoolProofResolver(resolver);
this.errorListener = errorListener;
Map<String, String> schemaLocations = types.getSchemaLocations();
Map<Namespace,Result> out = new HashMap<Namespace,Result>();
Map<Namespace,String> systemIds = new HashMap<Namespace,String>();
// we create a Namespace object for the XML Schema namespace
// as a side-effect, but we don't want to generate it.
namespaces.remove(WellKnownNamespace.XML_SCHEMA);
// first create the outputs for all so that we can resolve references among
// schema files when we write
for( Namespace n : namespaces.values() ) {
String schemaLocation = schemaLocations.get(n.uri);
if(schemaLocation!=null) {
systemIds.put(n,schemaLocation);
} else {
Result output = resolver.createOutput(n.uri,"schema"+(out.size()+1)+".xsd");
if(output!=null) { // null result means no schema for that namespace
out.put(n,output);
systemIds.put(n,output.getSystemId());
}
}
//Clear the namespace specific set with already written classes
n.resetWritten();
}
// then write'em all
for( Map.Entry<Namespace,Result> e : out.entrySet() ) {
Result result = e.getValue();
e.getKey().writeTo( result, systemIds );
if(result instanceof StreamResult) {
OutputStream outputStream = ((StreamResult)result).getOutputStream();
if(outputStream != null) {
outputStream.close(); // fix for bugid: 6291301
} else {
final Writer writer = ((StreamResult)result).getWriter();
if(writer != null) writer.close();
}
}
}
}
Schema components are organized per namespace.
/**
* Schema components are organized per namespace.
*/
private class Namespace {
final @NotNull String uri;
Other Namespace
s that this namespace depends on. /**
* Other {@link Namespace}s that this namespace depends on.
*/
private final Set<Namespace> depends = new LinkedHashSet<Namespace>();
If this schema refers to components from this schema by itself.
/**
* If this schema refers to components from this schema by itself.
*/
private boolean selfReference;
List of classes in this namespace.
/**
* List of classes in this namespace.
*/
private final Set<ClassInfo<T,C>> classes = new LinkedHashSet<ClassInfo<T,C>>();
Set of enums in this namespace
/**
* Set of enums in this namespace
*/
private final Set<EnumLeafInfo<T,C>> enums = new LinkedHashSet<EnumLeafInfo<T,C>>();
Set of arrays in this namespace
/**
* Set of arrays in this namespace
*/
private final Set<ArrayInfo<T,C>> arrays = new LinkedHashSet<ArrayInfo<T,C>>();
Global attribute declarations keyed by their local names.
/**
* Global attribute declarations keyed by their local names.
*/
private final MultiMap<String,AttributePropertyInfo<T,C>> attributeDecls = new MultiMap<String,AttributePropertyInfo<T,C>>(null);
Global element declarations to be written, keyed by their local names.
/**
* Global element declarations to be written, keyed by their local names.
*/
private final MultiMap<String,ElementDeclaration> elementDecls =
new MultiMap<String,ElementDeclaration>(new ElementWithType(true,anyType));
private Form attributeFormDefault;
private Form elementFormDefault;
Does schema in this namespace uses swaRef? If so, we need to generate import
statement.
/**
* Does schema in this namespace uses swaRef? If so, we need to generate import
* statement.
*/
private boolean useSwaRef;
Import for mime namespace needs to be generated.
See #856
/**
* Import for mime namespace needs to be generated.
* See #856
*/
private boolean useMimeNs;
Container for already processed classes
/**
* Container for already processed classes
*/
private final Set<ClassInfo> written = new HashSet<ClassInfo>();
public Namespace(String uri) {
this.uri = uri;
assert !XmlSchemaGenerator.this.namespaces.containsKey(uri);
XmlSchemaGenerator.this.namespaces.put(uri,this);
}
Clear out the set of already processed classes for this namespace
/**
* Clear out the set of already processed classes for this namespace
*/
void resetWritten() {
written.clear();
}
Process the given PropertyInfo looking for references to namespaces that are foreign to the given namespace. Any foreign namespace references found are added to the given namespaces dependency list and an <import>
is generated for it. Params: - p – the PropertyInfo
/**
* Process the given PropertyInfo looking for references to namespaces that
* are foreign to the given namespace. Any foreign namespace references
* found are added to the given namespaces dependency list and an {@code <import>}
* is generated for it.
*
* @param p the PropertyInfo
*/
private void processForeignNamespaces(PropertyInfo<T, C> p, int processingDepth) {
for (TypeInfo<T, C> t : p.ref()) {
if ((t instanceof ClassInfo) && (processingDepth > 0)) {
java.util.List<PropertyInfo> l = ((ClassInfo) t).getProperties();
for (PropertyInfo subp : l) {
processForeignNamespaces(subp, --processingDepth);
}
}
if (t instanceof Element) {
addDependencyTo(((Element) t).getElementName());
}
if (t instanceof NonElement) {
addDependencyTo(((NonElement) t).getTypeName());
}
}
}
private void addDependencyTo(@Nullable QName qname) {
// even though the Element interface says getElementName() returns non-null,
// ClassInfo always implements Element (even if an instance of ClassInfo might not be an Element).
// so this check is still necessary
if (qname==null) {
return;
}
String nsUri = qname.getNamespaceURI();
if (nsUri.equals(XML_SCHEMA)) {
// no need to explicitly refer to XSD namespace
return;
}
if (nsUri.equals(uri)) {
selfReference = true;
return;
}
// found a type in a foreign namespace, so make sure we generate an import for it
depends.add(getNamespace(nsUri));
}
Writes the schema document to the specified result.
Params: - systemIds –
System IDs of the other schema documents. "" indicates 'implied'.
/**
* Writes the schema document to the specified result.
*
* @param systemIds
* System IDs of the other schema documents. "" indicates 'implied'.
*/
private void writeTo(Result result, Map<Namespace,String> systemIds) throws IOException {
try {
Schema schema = TXW.create(Schema.class,ResultFactory.createSerializer(result));
// additional namespace declarations to be made.
Map<String, String> xmlNs = types.getXmlNs(uri);
for (Map.Entry<String, String> e : xmlNs.entrySet()) {
schema._namespace(e.getValue(),e.getKey());
}
if(useSwaRef)
schema._namespace(WellKnownNamespace.SWA_URI,"swaRef");
if(useMimeNs)
schema._namespace(WellKnownNamespace.XML_MIME_URI,"xmime");
attributeFormDefault = Form.get(types.getAttributeFormDefault(uri));
attributeFormDefault.declare("attributeFormDefault",schema);
elementFormDefault = Form.get(types.getElementFormDefault(uri));
// TODO: if elementFormDefault is UNSET, figure out the right default value to use
elementFormDefault.declare("elementFormDefault",schema);
// declare XML Schema namespace to be xs, but allow the user to override it.
// if 'xs' is used for other things, we'll just let TXW assign a random prefix
if(!xmlNs.containsValue(WellKnownNamespace.XML_SCHEMA)
&& !xmlNs.containsKey("xs"))
schema._namespace(WellKnownNamespace.XML_SCHEMA,"xs");
schema.version("1.0");
if(uri.length()!=0)
schema.targetNamespace(uri);
// declare prefixes for them at this level, so that we can avoid redundant
// namespace declarations
for (Namespace ns : depends) {
schema._namespace(ns.uri);
}
if(selfReference && uri.length()!=0) {
// use common 'tns' prefix for the own namespace
// if self-reference is needed
schema._namespace(uri,"tns");
}
schema._pcdata(newline);
// refer to other schemas
for( Namespace n : depends ) {
Import imp = schema._import();
if(n.uri.length()!=0)
imp.namespace(n.uri);
String refSystemId = systemIds.get(n);
if(refSystemId!=null && !refSystemId.equals("")) {
// "" means implied. null if the SchemaOutputResolver said "don't generate!"
imp.schemaLocation(relativize(refSystemId,result.getSystemId()));
}
schema._pcdata(newline);
}
if(useSwaRef) {
schema._import().namespace(WellKnownNamespace.SWA_URI).schemaLocation("http://ws-i.org/profiles/basic/1.1/swaref.xsd");
}
if(useMimeNs) {
schema._import().namespace(WellKnownNamespace.XML_MIME_URI).schemaLocation("http://www.w3.org/2005/05/xmlmime");
}
// then write each component
for (Map.Entry<String,ElementDeclaration> e : elementDecls.entrySet()) {
e.getValue().writeTo(e.getKey(),schema);
schema._pcdata(newline);
}
for (ClassInfo<T, C> c : classes) {
if (c.getTypeName()==null) {
// don't generate anything if it's an anonymous type
continue;
}
if(uri.equals(c.getTypeName().getNamespaceURI()))
writeClass(c, schema);
schema._pcdata(newline);
}
for (EnumLeafInfo<T, C> e : enums) {
if (e.getTypeName()==null) {
// don't generate anything if it's an anonymous type
continue;
}
if(uri.equals(e.getTypeName().getNamespaceURI()))
writeEnum(e,schema);
schema._pcdata(newline);
}
for (ArrayInfo<T, C> a : arrays) {
writeArray(a,schema);
schema._pcdata(newline);
}
for (Map.Entry<String,AttributePropertyInfo<T,C>> e : attributeDecls.entrySet()) {
TopLevelAttribute a = schema.attribute();
a.name(e.getKey());
if(e.getValue()==null)
writeTypeRef(a,stringType,"type");
else
writeAttributeTypeRef(e.getValue(),a);
schema._pcdata(newline);
}
// close the schema
schema.commit();
} catch( TxwException e ) {
logger.log(Level.INFO,e.getMessage(),e);
throw new IOException(e.getMessage());
}
}
Writes a type attribute (if the referenced type is a global type)
or writes out the definition of the anonymous type in place (if the referenced
type is not a global type.)
Also provides processing for ID/IDREF, MTOM @xmime, and swa:ref
ComplexTypeHost and SimpleTypeHost don't share an api for creating
and attribute in a type-safe way, so we will compromise for now and
use _attribute().
/**
* Writes a type attribute (if the referenced type is a global type)
* or writes out the definition of the anonymous type in place (if the referenced
* type is not a global type.)
*
* Also provides processing for ID/IDREF, MTOM @xmime, and swa:ref
*
* ComplexTypeHost and SimpleTypeHost don't share an api for creating
* and attribute in a type-safe way, so we will compromise for now and
* use _attribute().
*/
private void writeTypeRef(TypeHost th, NonElementRef<T, C> typeRef, String refAttName) {
// ID / IDREF handling
switch(typeRef.getSource().id()) {
case ID:
th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "ID"));
return;
case IDREF:
th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "IDREF"));
return;
case NONE:
// no ID/IDREF, so continue on and generate the type
break;
default:
throw new IllegalStateException();
}
// MTOM handling
MimeType mimeType = typeRef.getSource().getExpectedMimeType();
if( mimeType != null ) {
th._attribute(new QName(WellKnownNamespace.XML_MIME_URI, "expectedContentTypes", "xmime"), mimeType.toString());
}
// ref:swaRef handling
if(generateSwaRefAdapter(typeRef)) {
th._attribute(refAttName, new QName(WellKnownNamespace.SWA_URI, "swaRef", "ref"));
return;
}
// type name override
if(typeRef.getSource().getSchemaType()!=null) {
th._attribute(refAttName,typeRef.getSource().getSchemaType());
return;
}
// normal type generation
writeTypeRef(th, typeRef.getTarget(), refAttName);
}
Writes a type attribute (if the referenced type is a global type)
or writes out the definition of the anonymous type in place (if the referenced
type is not a global type.)
Params: - th –
the TXW interface to which the attribute will be written.
- type –
type to be referenced.
- refAttName –
The name of the attribute used when referencing a type by QName.
/**
* Writes a type attribute (if the referenced type is a global type)
* or writes out the definition of the anonymous type in place (if the referenced
* type is not a global type.)
*
* @param th
* the TXW interface to which the attribute will be written.
* @param type
* type to be referenced.
* @param refAttName
* The name of the attribute used when referencing a type by QName.
*/
private void writeTypeRef(TypeHost th, NonElement<T,C> type, String refAttName) {
Element e = null;
if (type instanceof MaybeElement) {
MaybeElement me = (MaybeElement)type;
boolean isElement = me.isElement();
if (isElement) e = me.asElement();
}
if (type instanceof Element) {
e = (Element)type;
}
if (type.getTypeName()==null) {
if ((e != null) && (e.getElementName() != null)) {
th.block(); // so that the caller may write other attributes
if(type instanceof ClassInfo) {
writeClass( (ClassInfo<T,C>)type, th );
} else {
writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th);
}
} else {
// anonymous
th.block(); // so that the caller may write other attributes
if(type instanceof ClassInfo) {
if(collisionChecker.push((ClassInfo<T,C>)type)) {
errorListener.warning(new SAXParseException(
Messages.ANONYMOUS_TYPE_CYCLE.format(collisionChecker.getCycleString()),
null
));
} else {
writeClass( (ClassInfo<T,C>)type, th );
}
collisionChecker.pop();
} else {
writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th);
}
}
} else {
th._attribute(refAttName,type.getTypeName());
}
}
writes the schema definition for the given array class
/**
* writes the schema definition for the given array class
*/
private void writeArray(ArrayInfo<T, C> a, Schema schema) {
ComplexType ct = schema.complexType().name(a.getTypeName().getLocalPart());
ct._final("#all");
LocalElement le = ct.sequence().element().name("item");
le.type(a.getItemType().getTypeName());
le.minOccurs(0).maxOccurs("unbounded");
le.nillable(true);
ct.commit();
}
writes the schema definition for the specified type-safe enum in the given TypeHost
/**
* writes the schema definition for the specified type-safe enum in the given TypeHost
*/
private void writeEnum(EnumLeafInfo<T, C> e, SimpleTypeHost th) {
SimpleType st = th.simpleType();
writeName(e,st);
SimpleRestrictionModel base = st.restriction();
writeTypeRef(base, e.getBaseType(), "base");
for (EnumConstant c : e.getConstants()) {
base.enumeration().value(c.getLexicalValue());
}
st.commit();
}
Writes the schema definition for the specified class to the schema writer.
Params: - c – the class info
- parent – the writer of the parent element into which the type will be defined
/**
* Writes the schema definition for the specified class to the schema writer.
*
* @param c the class info
* @param parent the writer of the parent element into which the type will be defined
*/
private void writeClass(ClassInfo<T,C> c, TypeHost parent) {
if (written.contains(c)) { // to avoid cycles let's check if we haven't already processed the class
return;
}
written.add(c);
// special handling for value properties
if (containsValueProp(c)) {
if (c.getProperties().size() == 1) {
// [RESULT 2 - simpleType if the value prop is the only prop]
//
// <simpleType name="foo">
// <xs:restriction base="xs:int"/>
// </>
ValuePropertyInfo<T,C> vp = (ValuePropertyInfo<T,C>)c.getProperties().get(0);
SimpleType st = ((SimpleTypeHost)parent).simpleType();
writeName(c, st);
if(vp.isCollection()) {
writeTypeRef(st.list(),vp.getTarget(),"itemType");
} else {
writeTypeRef(st.restriction(),vp.getTarget(),"base");
}
return;
} else {
// [RESULT 1 - complexType with simpleContent]
//
// <complexType name="foo">
// <simpleContent>
// <extension base="xs:int"/>
// <attribute name="b" type="xs:boolean"/>
// </>
// </>
// </>
// ...
// <element name="f" type="foo"/>
// ...
ComplexType ct = ((ComplexTypeHost)parent).complexType();
writeName(c,ct);
if(c.isFinal())
ct._final("extension restriction");
SimpleExtension se = ct.simpleContent().extension();
se.block(); // because we might have attribute before value
for (PropertyInfo<T,C> p : c.getProperties()) {
switch (p.kind()) {
case ATTRIBUTE:
handleAttributeProp((AttributePropertyInfo<T,C>)p,se);
break;
case VALUE:
TODO.checkSpec("what if vp.isCollection() == true?");
ValuePropertyInfo vp = (ValuePropertyInfo) p;
se.base(vp.getTarget().getTypeName());
break;
case ELEMENT: // error
case REFERENCE: // error
default:
assert false;
throw new IllegalStateException();
}
}
se.commit();
}
TODO.schemaGenerator("figure out what to do if bc != null");
TODO.checkSpec("handle sec 8.9.5.2, bullet #4");
// Java types containing value props can only contain properties of type
// ValuePropertyinfo and AttributePropertyInfo which have just been handled,
// so return.
return;
}
// we didn't fall into the special case for value props, so we
// need to initialize the ct.
// generate the complexType
ComplexType ct = ((ComplexTypeHost)parent).complexType();
writeName(c,ct);
if(c.isFinal())
ct._final("extension restriction");
if(c.isAbstract())
ct._abstract(true);
// these are where we write content model and attributes
AttrDecls contentModel = ct;
TypeDefParticle contentModelOwner = ct;
// if there is a base class, we need to generate an extension in the schema
final ClassInfo<T,C> bc = c.getBaseClass();
if (bc != null) {
if(bc.hasValueProperty()) {
// extending complex type with simple content
SimpleExtension se = ct.simpleContent().extension();
contentModel = se;
contentModelOwner = null;
se.base(bc.getTypeName());
} else {
ComplexExtension ce = ct.complexContent().extension();
contentModel = ce;
contentModelOwner = ce;
ce.base(bc.getTypeName());
// TODO: what if the base type is anonymous?
}
}
if(contentModelOwner!=null) {
// build the tree that represents the explicit content model from iterate over the properties
ArrayList<Tree> children = new ArrayList<Tree>();
for (PropertyInfo<T,C> p : c.getProperties()) {
// handling for <complexType @mixed='true' ...>
if(p instanceof ReferencePropertyInfo && ((ReferencePropertyInfo)p).isMixed()) {
ct.mixed(true);
}
Tree t = buildPropertyContentModel(p);
if(t!=null)
children.add(t);
}
Tree top = Tree.makeGroup( c.isOrdered() ? GroupKind.SEQUENCE : GroupKind.ALL, children);
// write the content model
top.write(contentModelOwner);
}
// then attributes
for (PropertyInfo<T,C> p : c.getProperties()) {
if (p instanceof AttributePropertyInfo) {
handleAttributeProp((AttributePropertyInfo<T,C>)p, contentModel);
}
}
if( c.hasAttributeWildcard()) {
contentModel.anyAttribute().namespace("##other").processContents("skip");
}
ct.commit();
}
Writes the name attribute if it's named.
/**
* Writes the name attribute if it's named.
*/
private void writeName(NonElement<T,C> c, TypedXmlWriter xw) {
QName tn = c.getTypeName();
if(tn!=null)
xw._attribute("name",tn.getLocalPart()); // named
}
private boolean containsValueProp(ClassInfo<T, C> c) {
for (PropertyInfo p : c.getProperties()) {
if (p instanceof ValuePropertyInfo) return true;
}
return false;
}
Builds content model writer for the specified property.
/**
* Builds content model writer for the specified property.
*/
private Tree buildPropertyContentModel(PropertyInfo<T,C> p) {
switch(p.kind()) {
case ELEMENT:
return handleElementProp((ElementPropertyInfo<T,C>)p);
case ATTRIBUTE:
// attribuets are handled later
return null;
case REFERENCE:
return handleReferenceProp((ReferencePropertyInfo<T,C>)p);
case MAP:
return handleMapProp((MapPropertyInfo<T,C>)p);
case VALUE:
// value props handled above in writeClass()
assert false;
throw new IllegalStateException();
default:
assert false;
throw new IllegalStateException();
}
}
Generate the proper schema fragment for the given element property into the
specified schema compositor.
The element property may or may not represent a collection and it may or may
not be wrapped.
Params: - ep – the element property
/**
* Generate the proper schema fragment for the given element property into the
* specified schema compositor.
*
* The element property may or may not represent a collection and it may or may
* not be wrapped.
*
* @param ep the element property
*/
private Tree handleElementProp(final ElementPropertyInfo<T,C> ep) {
if (ep.isValueList()) {
return new Tree.Term() {
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
TypeRef<T,C> t = ep.getTypes().get(0);
LocalElement e = parent.element();
e.block(); // we will write occurs later
QName tn = t.getTagName();
e.name(tn.getLocalPart());
List lst = e.simpleType().list();
writeTypeRef(lst,t, "itemType");
elementFormDefault.writeForm(e,tn);
writeOccurs(e,isOptional||!ep.isRequired(),repeated);
}
};
}
ArrayList<Tree> children = new ArrayList<Tree>();
for (final TypeRef<T,C> t : ep.getTypes()) {
children.add(new Tree.Term() {
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
LocalElement e = parent.element();
QName tn = t.getTagName();
PropertyInfo propInfo = t.getSource();
TypeInfo parentInfo = (propInfo == null) ? null : propInfo.parent();
if (canBeDirectElementRef(t, tn, parentInfo)) {
if ((!t.getTarget().isSimpleType()) && (t.getTarget() instanceof ClassInfo) && collisionChecker.findDuplicate((ClassInfo<T, C>) t.getTarget())) {
e.ref(new QName(uri, tn.getLocalPart()));
} else {
QName elemName = null;
if (t.getTarget() instanceof Element) {
Element te = (Element) t.getTarget();
elemName = te.getElementName();
}
Collection<TypeInfo> refs = propInfo.ref();
if ((refs != null) && (!refs.isEmpty()) && (elemName != null)){
ClassInfoImpl cImpl = null;
for (TypeInfo ref : refs) {
if (ref == null || ref instanceof ClassInfoImpl) {
if (elemName.equals(((ClassInfoImpl)ref).getElementName())) {
cImpl = (ClassInfoImpl) ref;
break;
}
}
}
if (cImpl != null) {
if (tn.getNamespaceURI() != null && tn.getNamespaceURI().trim().length() != 0) {
e.ref(new QName(tn.getNamespaceURI(), tn.getLocalPart()));
} else {
e.ref(new QName(cImpl.getElementName().getNamespaceURI(), tn.getLocalPart()));
}
} else
e.ref(new QName("", tn.getLocalPart()));
} else
e.ref(tn);
}
} else {
e.name(tn.getLocalPart());
writeTypeRef(e,t, "type");
elementFormDefault.writeForm(e,tn);
}
if (t.isNillable()) {
e.nillable(true);
}
if(t.getDefaultValue()!=null)
e._default(t.getDefaultValue());
writeOccurs(e,isOptional,repeated);
}
});
}
final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children)
.makeOptional(!ep.isRequired())
.makeRepeated(ep.isCollection()); // see Spec table 8-13
final QName ename = ep.getXmlName();
if (ename != null) { // wrapped collection
return new Tree.Term() {
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
LocalElement e = parent.element();
if(ename.getNamespaceURI().length()>0) {
if (!ename.getNamespaceURI().equals(uri)) {
// TODO: we need to generate the corresponding element declaration for this
// table 8-25: Property/field element wrapper with ref attribute
e.ref(new QName(ename.getNamespaceURI(), ename.getLocalPart()));
return;
}
}
e.name(ename.getLocalPart());
elementFormDefault.writeForm(e,ename);
if(ep.isCollectionNillable()) {
e.nillable(true);
}
writeOccurs(e,!ep.isCollectionRequired(),repeated);
ComplexType p = e.complexType();
choice.write(p);
}
};
} else {// non-wrapped
return choice;
}
}
Checks if we can collapse <element name='foo' type='t' />
to <element ref='foo' />
. This is possible if we already have such declaration to begin with. /**
* Checks if we can collapse
* {@code <element name='foo' type='t' />}
* to {@code <element ref='foo' />}.
*
* This is possible if we already have such declaration to begin with.
*/
private boolean canBeDirectElementRef(TypeRef<T, C> t, QName tn, TypeInfo parentInfo) {
Element te = null;
ClassInfo ci = null;
QName targetTagName = null;
if(t.isNillable() || t.getDefaultValue()!=null) {
// can't put those attributes on <element ref>
return false;
}
if (t.getTarget() instanceof Element) {
te = (Element) t.getTarget();
targetTagName = te.getElementName();
if (te instanceof ClassInfo) {
ci = (ClassInfo)te;
}
}
String nsUri = tn.getNamespaceURI();
if ((!nsUri.equals(uri) && nsUri.length()>0) && (!((parentInfo instanceof ClassInfo) && (((ClassInfo)parentInfo).getTypeName() == null)))) {
return true;
}
if ((ci != null) && ((targetTagName != null) && (te.getScope() == null) && (targetTagName.getNamespaceURI() == null))) {
if (targetTagName.equals(tn)) {
return true;
}
}
// we have the precise element defined already
if (te != null) { // it is instanceof Element
return targetTagName!=null && targetTagName.equals(tn);
}
return false;
}
Generate an attribute for the specified property on the specified complexType
Params: - ap – the attribute
- attr – the schema definition to which the attribute will be added
/**
* Generate an attribute for the specified property on the specified complexType
*
* @param ap the attribute
* @param attr the schema definition to which the attribute will be added
*/
private void handleAttributeProp(AttributePropertyInfo<T,C> ap, AttrDecls attr) {
// attr is either a top-level ComplexType or a ComplexExtension
//
// [RESULT]
//
// <complexType ...>
// <...>...</>
// <attribute name="foo" type="xs:int"/>
// </>
//
// or
//
// <complexType ...>
// <complexContent>
// <extension ...>
// <...>...</>
// </>
// </>
// <attribute name="foo" type="xs:int"/>
// </>
//
// or it could also be an in-lined type (attr ref)
//
LocalAttribute localAttribute = attr.attribute();
final String attrURI = ap.getXmlName().getNamespaceURI();
if (attrURI.equals("") /*|| attrURI.equals(uri) --- those are generated as global attributes anyway, so use them.*/) {
localAttribute.name(ap.getXmlName().getLocalPart());
writeAttributeTypeRef(ap, localAttribute);
attributeFormDefault.writeForm(localAttribute,ap.getXmlName());
} else { // generate an attr ref
localAttribute.ref(ap.getXmlName());
}
if(ap.isRequired()) {
// TODO: not type safe
localAttribute.use("required");
}
}
private void writeAttributeTypeRef(AttributePropertyInfo<T,C> ap, AttributeType a) {
if( ap.isCollection() )
writeTypeRef(a.simpleType().list(), ap, "itemType");
else
writeTypeRef(a, ap, "type");
}
Generate the proper schema fragment for the given reference property into the
specified schema compositor.
The reference property may or may not refer to a collection and it may or may
not be wrapped.
/**
* Generate the proper schema fragment for the given reference property into the
* specified schema compositor.
*
* The reference property may or may not refer to a collection and it may or may
* not be wrapped.
*/
private Tree handleReferenceProp(final ReferencePropertyInfo<T, C> rp) {
// fill in content model
ArrayList<Tree> children = new ArrayList<Tree>();
for (final Element<T,C> e : rp.getElements()) {
children.add(new Tree.Term() {
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
LocalElement eref = parent.element();
boolean local=false;
QName en = e.getElementName();
if(e.getScope()!=null) {
// scoped. needs to be inlined
boolean qualified = en.getNamespaceURI().equals(uri);
boolean unqualified = en.getNamespaceURI().equals("");
if(qualified || unqualified) {
// can be inlined indeed
// write form="..." if necessary
if(unqualified) {
if(elementFormDefault.isEffectivelyQualified)
eref.form("unqualified");
} else {
if(!elementFormDefault.isEffectivelyQualified)
eref.form("qualified");
}
local = true;
eref.name(en.getLocalPart());
// write out type reference
if(e instanceof ClassInfo) {
writeTypeRef(eref,(ClassInfo<T,C>)e,"type");
} else {
writeTypeRef(eref,((ElementInfo<T,C>)e).getContentType(),"type");
}
}
}
if(!local)
eref.ref(en);
writeOccurs(eref,isOptional,repeated);
}
});
}
final WildcardMode wc = rp.getWildcard();
if( wc != null ) {
children.add(new Tree.Term() {
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
Any any = parent.any();
final String pcmode = getProcessContentsModeName(wc);
if( pcmode != null ) any.processContents(pcmode);
any.namespace("##other");
writeOccurs(any,isOptional,repeated);
}
});
}
final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children).makeRepeated(rp.isCollection()).makeOptional(!rp.isRequired());
final QName ename = rp.getXmlName();
if (ename != null) { // wrapped
return new Tree.Term() {
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
LocalElement e = parent.element().name(ename.getLocalPart());
elementFormDefault.writeForm(e,ename);
if(rp.isCollectionNillable())
e.nillable(true);
writeOccurs(e,true,repeated);
ComplexType p = e.complexType();
choice.write(p);
}
};
} else { // unwrapped
return choice;
}
}
Generate the proper schema fragment for the given map property into the
specified schema compositor.
Params: - mp – the map property
/**
* Generate the proper schema fragment for the given map property into the
* specified schema compositor.
*
* @param mp the map property
*/
private Tree handleMapProp(final MapPropertyInfo<T,C> mp) {
return new Tree.Term() {
protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
QName ename = mp.getXmlName();
LocalElement e = parent.element();
elementFormDefault.writeForm(e,ename);
if(mp.isCollectionNillable())
e.nillable(true);
e = e.name(ename.getLocalPart());
writeOccurs(e,isOptional,repeated);
ComplexType p = e.complexType();
// TODO: entry, key, and value are always unqualified. that needs to be fixed, too.
// TODO: we need to generate the corresponding element declaration, if they are qualified
e = p.sequence().element();
e.name("entry").minOccurs(0).maxOccurs("unbounded");
ExplicitGroup seq = e.complexType().sequence();
writeKeyOrValue(seq, "key", mp.getKeyType());
writeKeyOrValue(seq, "value", mp.getValueType());
}
};
}
private void writeKeyOrValue(ExplicitGroup seq, String tagName, NonElement<T, C> typeRef) {
LocalElement key = seq.element().name(tagName);
key.minOccurs(0);
writeTypeRef(key, typeRef, "type");
}
public void addGlobalAttribute(AttributePropertyInfo<T,C> ap) {
attributeDecls.put( ap.getXmlName().getLocalPart(), ap );
addDependencyTo(ap.getTarget().getTypeName());
}
public void addGlobalElement(TypeRef<T,C> tref) {
elementDecls.put( tref.getTagName().getLocalPart(), new ElementWithType(false,tref.getTarget()) );
addDependencyTo(tref.getTarget().getTypeName());
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[classes=").append(classes);
buf.append(",elementDecls=").append(elementDecls);
buf.append(",enums=").append(enums);
buf.append("]");
return super.toString();
}
Represents a global element declaration to be written.
Because multiple properties can name the same global element even if
they have different Java type, the schema generator first needs to
walk through the model and decide what to generate for the given
element declaration.
This class represents what will be written, and its equals(Object)
method is implemented in such a way that two identical declarations are considered as the same.
/**
* Represents a global element declaration to be written.
*
* <p>
* Because multiple properties can name the same global element even if
* they have different Java type, the schema generator first needs to
* walk through the model and decide what to generate for the given
* element declaration.
*
* <p>
* This class represents what will be written, and its {@link #equals(Object)}
* method is implemented in such a way that two identical declarations
* are considered as the same.
*/
abstract class ElementDeclaration {
Returns true if two ElementDeclaration
s are representing the same schema fragment. /**
* Returns true if two {@link ElementDeclaration}s are representing
* the same schema fragment.
*/
@Override
public abstract boolean equals(Object o);
@Override
public abstract int hashCode();
Generates the declaration.
/**
* Generates the declaration.
*/
public abstract void writeTo(String localName, Schema schema);
}
ElementDeclaration
that refers to a NonElement
. /**
* {@link ElementDeclaration} that refers to a {@link NonElement}.
*/
class ElementWithType extends ElementDeclaration {
private final boolean nillable;
private final NonElement<T,C> type;
public ElementWithType(boolean nillable,NonElement<T, C> type) {
this.type = type;
this.nillable = nillable;
}
public void writeTo(String localName, Schema schema) {
TopLevelElement e = schema.element().name(localName);
if(nillable)
e.nillable(true);
if (type != null) {
writeTypeRef(e,type, "type");
} else {
e.complexType(); // refer to the nested empty complex type
}
e.commit();
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ElementWithType that = (ElementWithType) o;
return type.equals(that.type);
}
public int hashCode() {
return type.hashCode();
}
}
}
Examine the specified element ref and determine if a swaRef attribute needs to be generated
Params: - typeRef –
/**
* Examine the specified element ref and determine if a swaRef attribute needs to be generated
* @param typeRef
*/
private boolean generateSwaRefAdapter(NonElementRef<T,C> typeRef) {
return generateSwaRefAdapter(typeRef.getSource());
}
Examine the specified element ref and determine if a swaRef attribute needs to be generated
/**
* Examine the specified element ref and determine if a swaRef attribute needs to be generated
*/
private boolean generateSwaRefAdapter(PropertyInfo<T,C> prop) {
final Adapter<T,C> adapter = prop.getAdapter();
if (adapter == null) return false;
final Object o = navigator.asDecl(SwaRefAdapter.class);
if (o == null) return false;
return (o.equals(adapter.adapterType));
}
Debug information of what's in this XmlSchemaGenerator
. /**
* Debug information of what's in this {@link XmlSchemaGenerator}.
*/
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
for (Namespace ns : namespaces.values()) {
if(buf.length()>0) buf.append(',');
buf.append(ns.uri).append('=').append(ns);
}
return super.toString()+'['+buf+']';
}
return the string representation of the processContents mode of the
give wildcard, or null if it is the schema default "strict"
/**
* return the string representation of the processContents mode of the
* give wildcard, or null if it is the schema default "strict"
*
*/
private static String getProcessContentsModeName(WildcardMode wc) {
switch(wc) {
case LAX:
case SKIP:
return wc.name().toLowerCase();
case STRICT:
return null;
default:
throw new IllegalStateException();
}
}
Relativizes a URI by using another URI (base URI.)
For example, relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"
This method only works on hierarchical URI's, not opaque URI's (refer to the
java.net.URI
javadoc for complete definitions of these terms.
This method will not normalize the relative URI.
Returns: the relative URI or the original URI if a relative one could not be computed
/**
* Relativizes a URI by using another URI (base URI.)
*
* <p>
* For example, {@code relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"}
*
* <p>
* This method only works on hierarchical URI's, not opaque URI's (refer to the
* <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html">java.net.URI</a>
* javadoc for complete definitions of these terms.
*
* <p>
* This method will not normalize the relative URI.
*
* @return the relative URI or the original URI if a relative one could not be computed
*/
protected static String relativize(String uri, String baseUri) {
try {
assert uri!=null;
if(baseUri==null) return uri;
URI theUri = new URI(escapeURI(uri));
URI theBaseUri = new URI(escapeURI(baseUri));
if (theUri.isOpaque() || theBaseUri.isOpaque())
return uri;
if (!equalsIgnoreCase(theUri.getScheme(), theBaseUri.getScheme()) ||
!equal(theUri.getAuthority(), theBaseUri.getAuthority()))
return uri;
String uriPath = theUri.getPath();
String basePath = theBaseUri.getPath();
// normalize base path
if (!basePath.endsWith("/")) {
basePath = normalizeUriPath(basePath);
}
if( uriPath.equals(basePath))
return ".";
String relPath = calculateRelativePath(uriPath, basePath, fixNull(theUri.getScheme()).equals("file"));
if (relPath == null)
return uri; // recursion found no commonality in the two uris at all
StringBuilder relUri = new StringBuilder();
relUri.append(relPath);
if (theUri.getQuery() != null)
relUri.append('?').append(theUri.getQuery());
if (theUri.getFragment() != null)
relUri.append('#').append(theUri.getFragment());
return relUri.toString();
} catch (URISyntaxException e) {
throw new InternalError("Error escaping one of these uris:\n\t"+uri+"\n\t"+baseUri);
}
}
private static String fixNull(String s) {
if(s==null) return "";
else return s;
}
private static String calculateRelativePath(String uri, String base, boolean fileUrl) {
// if this is a file URL (very likely), and if this is on a case-insensitive file system,
// then treat it accordingly.
boolean onWindows = File.pathSeparatorChar==';';
if (base == null) {
return null;
}
if ((fileUrl && onWindows && startsWithIgnoreCase(uri,base)) || uri.startsWith(base)) {
return uri.substring(base.length());
} else {
return "../" + calculateRelativePath(uri, getParentUriPath(base), fileUrl);
}
}
private static boolean startsWithIgnoreCase(String s, String t) {
return s.toUpperCase().startsWith(t.toUpperCase());
}
JAX-RPC wants the namespaces to be sorted in the reverse order
so that the empty namespace "" comes to the very end. Don't ask me why.
/**
* JAX-RPC wants the namespaces to be sorted in the reverse order
* so that the empty namespace "" comes to the very end. Don't ask me why.
*/
private static final Comparator<String> NAMESPACE_COMPARATOR = new Comparator<String>() {
public int compare(String lhs, String rhs) {
return -lhs.compareTo(rhs);
}
};
private static final String newline = "\n";
}