/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* 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> {
/**
* 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);
/**
* {@link ErrorListener} to send errors to.
*/
/** model navigator **/
/**
* Representation for xs:string.
*/
/**
* Represents xs:anyType.
*/
/**
* Used to detect cycles in anonymous types.
*/
private final CollisionCheckStack<ClassInfo<T,C>> collisionChecker = new CollisionCheckStack<ClassInfo<T,C>>();
// populate the object
add(a);
}
if(n==null)
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.
*/
return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
// put element -> type reference
// schedule writing this global element
}
} else {
// anonymous type
return;
}
// search properties for foreign namespace references
n.processForeignNamespaces(p, 1);
if (p instanceof AttributePropertyInfo) {
// global attribute
}
}
if (p instanceof ElementPropertyInfo) {
}
}
}
if(generateSwaRefAdapter(p))
n.useSwaRef = true;
n.useMimeNs = true;
}
}
// recurse on baseTypes to make sure that we can refer to them in the schema
}
}
/**
* Adds a new element to the list of elements to be written.
*/
boolean nillable = false; // default value
} else {
}
nillable = false;
} else {
}
// search for foreign namespace references
}
// put element -> type reference
// schedule writing this global element
}
} else {
return; // anonymous type
}
// search for foreign namespace references
}
assert a!=null;
// search for foreign namespace references
}
/**
* 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.
*/
return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
// search for foreign namespace references
}
/**
* Writes out the episode file.
*/
// TODO: don't we want to bake in versions?
// generate listing per schema
prefix = "tns:";
} else {
prefix = "";
}
}
}
}
}
}
}
/**
* Write out the schema documents.
*/
throw new IllegalArgumentException();
// debug logging to see what's going on.
}
// make it fool-proof
this.errorListener = errorListener;
// we create a Namespace object for the XML Schema namespace
// as a side-effect, but we don't want to generate it.
// first create the outputs for all so that we can resolve references among
// schema files when we write
if(schemaLocation!=null) {
} else {
}
}
}
// then write'em all
if(result instanceof StreamResult) {
if(outputStream != null) {
} else {
}
}
}
}
/**
* Schema components are organized per namespace.
*/
private class Namespace {
/**
* Other {@link Namespace}s that this namespace depends on.
*/
/**
* If this schema refers to components from this schema by itself.
*/
private boolean selfReference;
/**
* List of classes in this namespace.
*/
/**
* Set of enums in this namespace
*/
/**
* Set of arrays in this namespace
*/
/**
* 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.
*/
/**
* 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
*/
private boolean useMimeNs;
}
/**
* 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.
*
* @param p the PropertyInfo
*/
for (PropertyInfo subp : l) {
}
}
if (t instanceof Element) {
}
if (t instanceof NonElement) {
}
}
}
// 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
return;
}
// no need to explicitly refer to XSD namespace
return;
}
selfReference = true;
return;
}
// found a type in a foreign namespace, so make sure we generate an import for it
}
/**
* Writes the schema document to the specified result.
*
* @param systemIds
* System IDs of the other schema documents. "" indicates 'implied'.
*/
try {
// additional namespace declarations to be made.
}
if(useSwaRef)
if(useMimeNs)
// TODO: if elementFormDefault is UNSET, figure out the right default value to use
// 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
// declare prefixes for them at this level, so that we can avoid redundant
// namespace declarations
}
// use common 'tns' prefix for the own namespace
// if self-reference is needed
}
// refer to other schemas
// "" means implied. null if the SchemaOutputResolver said "don't generate!"
}
}
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
}
if (c.getTypeName()==null) {
// don't generate anything if it's an anonymous type
continue;
}
writeClass(c, schema);
}
for (EnumLeafInfo<T, C> e : enums) {
if (e.getTypeName()==null) {
// don't generate anything if it's an anonymous type
continue;
}
}
writeArray(a,schema);
}
else
writeAttributeTypeRef(e.getValue(),a);
}
// close the schema
} catch( TxwException 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.)
*
*
* 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().
*/
// ID / IDREF handling
case ID:
return;
case IDREF:
return;
case NONE:
break;
default:
throw new IllegalStateException();
}
// MTOM handling
th._attribute(new QName(WellKnownNamespace.XML_MIME_URI, "expectedContentTypes", "xmime"), mimeType.toString());
}
// ref:swaRef handling
if(generateSwaRefAdapter(typeRef)) {
return;
}
// type name override
return;
}
// normal type generation
}
/**
* 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.
*/
if (type instanceof MaybeElement) {
}
}
} else {
}
} else {
// anonymous
));
} else {
}
} else {
}
}
} else {
}
}
/**
* writes the schema definition for the given array class
*/
}
/**
* writes the schema definition for the specified type-safe enum in the given TypeHost
*/
for (EnumConstant c : e.getConstants()) {
}
}
/**
* 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
*/
// special handling for value properties
if (containsValueProp(c)) {
// [RESULT 2 - simpleType if the value prop is the only prop]
//
// <simpleType name="foo">
// <xs:restriction base="xs:int"/>
// </>
if(vp.isCollection()) {
} else {
}
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"/>
// ...
if(c.isFinal())
for (PropertyInfo<T,C> p : c.getProperties()) {
switch (p.kind()) {
case ATTRIBUTE:
break;
case VALUE:
break;
case ELEMENT: // error
case REFERENCE: // error
default:
assert false;
throw new IllegalStateException();
}
}
}
// 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
if(c.isFinal())
if(c.isAbstract())
// these are where we write content model and attributes
// if there is a base class, we need to generate an extension in the schema
if(bc.hasValueProperty()) {
// extending complex type with simple content
contentModel = se;
} else {
contentModel = ce;
// 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
for (PropertyInfo<T,C> p : c.getProperties()) {
// handling for <complexType @mixed='true' ...>
}
Tree t = buildPropertyContentModel(p);
if(t!=null)
}
// write the content model
}
// then attributes
for (PropertyInfo<T,C> p : c.getProperties()) {
if (p instanceof AttributePropertyInfo) {
}
}
if( c.hasAttributeWildcard()) {
}
}
/**
* Writes the name attribute if it's named.
*/
}
for (PropertyInfo p : c.getProperties()) {
if (p instanceof ValuePropertyInfo) return true;
}
return false;
}
/**
* Builds content model writer for the specified property.
*/
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.
*
* @param ep the element property
*/
if (ep.isValueList()) {
e.block(); // we will write occurs later
}
};
}
if ((!t.getTarget().isSimpleType()) && (t.getTarget() instanceof ClassInfo) && collisionChecker.findDuplicate((ClassInfo<T, C>) t.getTarget())) {
} else {
}
} else {
}
} else {
}
}
} else {
writeTypeRef(e,t, "type");
}
if (t.isNillable()) {
e.nillable(true);
}
if(t.getDefaultValue()!=null)
e._default(t.getDefaultValue());
}
});
}
// TODO: we need to generate the corresponding element declaration for this
return;
}
}
if(ep.isCollectionNillable()) {
e.nillable(true);
}
ComplexType p = e.complexType();
}
};
} 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.
*/
// can't put those attributes on <element ref>
return false;
}
}
}
if ((!nsUri.equals(uri) && nsUri.length()>0) && (!((parentInfo instanceof ClassInfo) && (((ClassInfo)parentInfo).getTypeName() == null)))) {
return true;
}
// there's a circular reference from an anonymous subtype to a global element
return true;
}
}
// we have the precise element defined already
}
return false;
}
/**
* 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
*/
// 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)
//
if (attrURI.equals("") /*|| attrURI.equals(uri) --- those are generated as global attributes anyway, so use them.*/) {
} else { // generate an attr ref
}
if(ap.isRequired()) {
// TODO: not type safe
}
}
if( ap.isCollection() )
else
}
/**
* 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.
*/
// fill in content model
boolean local=false;
// scoped. needs to be inlined
if(qualified || unqualified) {
// can be inlined indeed
// write form="..." if necessary
if(unqualified) {
} else {
}
local = true;
// write out type reference
if(e instanceof ClassInfo) {
} else {
}
}
}
if(!local)
}
});
}
}
});
}
final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children).makeRepeated(rp.isCollection()).makeOptional(!rp.isRequired());
if(rp.isCollectionNillable())
e.nillable(true);
writeOccurs(e,true,repeated);
ComplexType p = e.complexType();
}
};
} else { // unwrapped
return choice;
}
}
/**
* Generate the proper schema fragment for the given map property into the
* specified schema compositor.
*
* @param mp the map property
*/
if(mp.isCollectionNillable())
e.nillable(true);
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
}
};
}
}
}
}
return super.toString();
}
/**
* 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 {@link ElementDeclaration}s are representing
* the same schema fragment.
*/
public abstract int hashCode();
/**
* Generates the declaration.
*/
}
/**
* {@link ElementDeclaration} that refers to a {@link NonElement}.
*/
private final boolean nillable;
}
if(nillable)
e.nillable(true);
} else {
e.complexType(); // refer to the nested empty complex type
}
e.commit();
}
if (this == o) return true;
}
public int hashCode() {
}
}
}
/**
* Examine the specified element ref and determine if a swaRef attribute needs to be generated
* @param typeRef
*/
}
/**
* Examine the specified element ref and determine if a swaRef attribute needs to be generated
*/
if (o == null) return false;
}
/**
* Debug information of what's in this {@link XmlSchemaGenerator}.
*/
}
}
/**
* return the string representation of the processContents mode of the
* give wildcard, or null if it is the schema default "strict"
*
*/
switch(wc) {
case LAX:
case SKIP:
case STRICT:
return null;
default:
throw new IllegalStateException();
}
}
/**
* 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
*/
try {
return uri;
return uri;
// normalize base path
}
return ".";
String relPath = calculateRelativePath(uriPath, basePath, fixNull(theUri.getScheme()).equals("file"));
return uri; // recursion found no commonality in the two uris at all
} catch (URISyntaxException e) {
}
}
if(s==null) return "";
else return s;
}
// if this is a file URL (very likely), and if this is on a case-insensitive file system,
// then treat it accordingly.
return null;
}
} else {
}
}
}
/**
* 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.
*/
}
};
}