001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: */
019: package org.enhydra.zeus.binder;
020:
021: import java.io.IOException;
022: import java.util.Iterator;
023: import java.util.LinkedList;
024: import java.util.List;
025:
026: // Zeus imports
027: import org.enhydra.zeus.Binder;
028: import org.enhydra.zeus.Binding;
029: import org.enhydra.zeus.Source;
030: import org.enhydra.zeus.ZeusDefaults;
031: import org.enhydra.zeus.binding.AtomicProperty;
032: import org.enhydra.zeus.binding.ContainerProperty;
033: import org.enhydra.zeus.util.NamingUtils;
034: import org.enhydra.zeus.util.SchemaUtils;
035:
036: // JDOM imports
037: import org.jdom.Attribute;
038: import org.jdom.Document;
039: import org.jdom.Element;
040: import org.jdom.Namespace;
041:
042: /**
043: * <p>
044: * <code>SchemaBinder</code> implements the <code>{@link Binder}</code>
045: * interface and allows generation of Zeus
046: * <code>{@link Binding}</code>s from an XML Schema.
047: * </p>
048: *
049: * @author Brett McLaughlin
050: * @version 1.0
051: */
052: public class SchemaBinder extends BaseBinder {
053:
054: /** The JDOM <code>Namespace</code> object for the schema namespace */
055: private Namespace schemaNamespace;
056:
057: /**
058: * <p>
059: * This constructor takes in a <code>{@link Source}</code>
060: * to read an XML Schema from and allow generation of the
061: * <code>{@link Binding}</code>s from it.
062: * </p>
063: *
064: * @param source <code>Source</code> to read input from.
065: */
066: public SchemaBinder(Source source) {
067: super (source);
068:
069: // Set up default XML Schema namespace
070: schemaNamespace = Namespace
071: .getNamespace(ZeusDefaults.SCHEMA_NAMESPACE_URI);
072: }
073:
074: /**
075: * <p>
076: * This returns the current namespace URI being used for XML Schema.
077: * </p>
078: *
079: * @return <code>String</code> - XML Schema namespace URI being used.
080: */
081: public String getSchemaNamespaceURI() {
082: return schemaNamespace.getURI();
083: }
084:
085: /**
086: * <p>
087: * This will set the URI to use for XML Schema. This is useful for
088: * documents that have an older schema namespace URI. Note that this does
089: * <i>not</i> imply that the <code>SchemaBinder</code> can deal with
090: * older schema features and syntax; this is purely for incorrect or old
091: * namespace URIs.
092: * </p>
093: *
094: * @param schemaNamespaceURI the URI to use for XML Schema
095: */
096: public void setSchemaNamespaceURI(String schemaNamespaceURI) {
097: schemaNamespace = Namespace.getNamespace(schemaNamespaceURI);
098: }
099:
100: /**
101: * <p>
102: * This is integral portion of the <code>Binder</code>. It
103: * is responsible for returning a Zeus representation of
104: * the set of constraints that this binding represents,
105: * resulting from the supplied XML Schema.
106: * </p>
107: *
108: * @return <code>List</code> - the resultant
109: * <code>Bindings</code> from conversion of
110: * constraints.
111: * @throws <code>IOException</code> when errors in reading
112: * input occur.
113: */
114: public List getBindings() throws IOException {
115: Document schema = source.getDocument();
116: Element root = schema.getRootElement();
117:
118: return convertToBindings(root);
119: }
120:
121: /**
122: * <p>
123: * This takes a single <code>Element</code> (in JDOM form), and
124: * converts it to a <code>Binding</code>.
125: * </p>
126: *
127: * @param element <code>Element</code> to convert to a Zeus
128: * <code>Binding</code>.
129: * @return <code>List</code> - the <code>Binding</code>s from
130: * the supplied <code>Element</code>.
131: */
132: private List convertToBindings(Element element) {
133:
134: // Create the list to work with
135: List bindings = new LinkedList();
136:
137: // Get all the elements named "element"
138: List elements = element.getChildren("element", schemaNamespace);
139: for (Iterator i = elements.iterator(); i.hasNext();) {
140: Element nextElement = (Element) i.next();
141: Attribute elementName = nextElement.getAttribute("name");
142: if (elementName == null) {
143: // All root-level elements should be named types
144: throw new IllegalArgumentException(
145: "All root-level elements "
146: + "should be named types (with a 'name' attribute).");
147: }
148:
149: // Deal with the element definition
150: bindings.add(convertDefinitionToBinding(nextElement));
151: }
152:
153: /**
154: * Currently, anything other than root-level elements are ignored.
155: * Eventually, complexTypes defined here should be handled at this
156: * point.
157: */
158:
159: // Deal with the element itself
160: return bindings;
161: }
162:
163: /**
164: * <p>
165: * This will take an element that defines a structure (already determined
166: * by the time that this method is invoked) and convert the definition
167: * to a <code>{@link Binding}</code>.
168: * </p>
169: *
170: * @param element the element to convert
171: * @return <code>Binding</code> - the converted Zeus <code>Binding</code>.
172: */
173: private Binding convertDefinitionToBinding(Element element) {
174:
175: // Create base container
176: String xmlName = element.getAttribute("name").getValue();
177: String xmlType = xmlName;
178:
179: ContainerProperty container = new ContainerProperty(xmlName,
180: xmlType);
181:
182: // See if this is a simple type or complex type
183: Attribute type = element.getAttribute("type");
184: if (type != null) {
185: // Simple type: deal with allowed values
186: String schemaType = type.getValue();
187: // XXX: I am sure I broke this! (Sean)
188: AtomicProperty property = new AtomicProperty("value",
189: schemaType);
190: container.addProperty(property);
191: } else {
192: // This should be a complex type
193: Element complexType = element.getChild("complexType",
194: schemaNamespace);
195: if (complexType == null) {
196: // XXX: This should be a Zeus-specific exception
197: throw new IllegalArgumentException("Illegal schema: "
198: + "No complexType defined for this element.");
199: }
200:
201: // Determine if this extends a base type
202: Attribute baseType = complexType.getAttribute("baseType");
203: if (baseType != null) {
204: // Set up the base type as a property
205: String schemaType = baseType.getValue();
206:
207: // XXX: I am sure I broke this! (Sean)
208: AtomicProperty property = new AtomicProperty("value",
209: schemaType);
210: container.addProperty(property);
211: }
212:
213: // If there is a "sequence" element, it doesn't affect anything
214: Element parent = complexType.getChild("sequence",
215: schemaNamespace);
216: if (parent == null) {
217: parent = complexType;
218: }
219:
220: // Deal with the attributes
221: List attributes = parent.getChildren("attribute",
222: schemaNamespace);
223: for (Iterator i = attributes.iterator(); i.hasNext();) {
224: Element theAttribute = (Element) i.next();
225: Attribute propertyName = theAttribute
226: .getAttribute("name");
227: if (propertyName == null) {
228: // XXX: This should be a Zeus-specific exception
229: throw new IllegalArgumentException(
230: "Attributes must have "
231: + "a name (specified by the 'name' attribute).");
232: }
233:
234: Attribute schemaType = theAttribute
235: .getAttribute("type");
236: if (schemaType == null) {
237: // XXX: This should be a Zeus-specific exception
238: throw new IllegalArgumentException(
239: "Attributes must have "
240: + "a type (specified by the 'type' attribute).");
241: }
242:
243: // XXX: I am sure I broke this! (Sean)
244: AtomicProperty property = new AtomicProperty(
245: propertyName.getValue(), "", "string",
246: ZeusDefaults.SCHEMA_NAMESPACE_URI);
247: container.addProperty(property);
248: }
249:
250: // Deal with the elements
251: List elements = parent.getChildren("element",
252: schemaNamespace);
253: for (Iterator i = elements.iterator(); i.hasNext();) {
254: Element theElement = (Element) i.next();
255:
256: // If an element ref, add as a property; otherwise recurse
257: Attribute ref = theElement.getAttribute("ref");
258: if (ref == null) {
259: } else {
260: // Since this is a reference, add the property
261: String propertyName = ref.getValue();
262: String propertyType = NamingUtils
263: .getJavaClassName(propertyName);
264:
265: // XXX: I am sure I broke this! (Sean)
266: AtomicProperty property = new AtomicProperty(
267: propertyName, propertyType);
268:
269: // Determine if a list is needed
270: Attribute maxOccurs = theElement
271: .getAttribute("maxOccurs");
272: if (maxOccurs != null) {
273: String value = maxOccurs.getValue();
274:
275: // The value should be either 'unbounded' or an int
276: if (value.equals("unbounded")) {
277: property.setIsCollection(true);
278: } else {
279: try {
280: int maxOccursInt = Integer
281: .parseInt(value);
282: if (maxOccursInt > 1) {
283: property.setIsCollection(true);
284: }
285: } catch (NumberFormatException ignored) {
286: // Not a collection, so don't worry about it
287: }
288: }
289: }
290:
291: container.addProperty(property);
292: }
293: }
294: }
295:
296: return container;
297: }
298: }
|