001: /*
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 2002 (C) Intalio Inc. All Rights Reserved.
042: *
043: * $Id: ExtendedBinding.java 6936 2007-04-09 12:40:02Z wguttmn $
044: */
045: package org.exolab.castor.builder.binding;
046:
047: //--Castor imports
048: import java.util.HashMap;
049: import java.util.HashSet;
050: import java.util.Hashtable;
051: import java.util.Map;
052: import java.util.Set;
053:
054: import org.exolab.castor.builder.binding.xml.AutomaticNamingType;
055: import org.exolab.castor.builder.binding.xml.Binding;
056: import org.exolab.castor.builder.binding.xml.ComponentBindingType;
057: import org.exolab.castor.builder.binding.xml.Exclude;
058: import org.exolab.castor.builder.binding.xml.Excludes;
059: import org.exolab.castor.builder.binding.xml.Forces;
060: import org.exolab.castor.xml.schema.Annotated;
061: import org.exolab.castor.xml.schema.AttributeDecl;
062: import org.exolab.castor.xml.schema.ElementDecl;
063: import org.exolab.castor.xml.schema.Structure;
064:
065: /**
066: * This class adds the necessary logic to a Binding Object to bring the gap
067: * between the XML Schema Object Model and the Binding File. It queries the
068: * Binding Object to retrieve the the associated ComponentBinding.
069: * <p>
070: * An "XPath like" representation of an XML Schema structure is built to lookup
071: * the component bindings in their storage structure. The algorithm used to
072: * build the "XPath like" representation is summarized in the following example:
073: * Given the XML schema declaration:
074: *
075: * <pre>
076: * <xsd:element name="foo">
077: * <xsd:complextype>
078: * <xsd:attribute name="bar" type="xsd:string"/>
079: * </xsd:complextype>
080: * </xsd:element>
081: * </pre>
082: *
083: * The path to identify the attribute 'bar' will be:
084: *
085: * <pre>
086: * /foo/@bar
087: * </pre>
088: *
089: * The keywords <tt>complexType</tt> and <tt>group</tt> are used to identify
090: * respectively an XML Schema ComplexType and a Model Group <b>definition</b>.
091: *
092: * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
093: * @version $Revision: 6936 $ $Date: 2005-03-05 06:42:06 -0700 (Sat, 05 Mar 2005) $
094: */
095: public final class ExtendedBinding extends Binding {
096:
097: /**
098: * Constants needed to create the XPath.
099: */
100: protected static final String PATH_SEPARATOR = "/";
101: /**
102: * Prefix used to identify an attribute.
103: */
104: protected static final String ATTRIBUTE_PREFIX = "@";
105: /**
106: * Prefix used to identify a complexType.
107: */
108: public static final String COMPLEXTYPE_ID = "complexType:";
109: /**
110: * Prefix used to identity a simplyType.
111: */
112: public static final String SIMPLETYPE_ID = "simpleType:";
113: /**
114: * Prefix used to identify an enumeration.
115: */
116: public static final String ENUMTYPE_ID = "enumType:";
117: /**
118: * Prefix used to identify a model group.
119: */
120: public static final String GROUP_ID = "group:";
121:
122: private static final short ATTRIBUTE = 10;
123: private static final short ELEMENT = 11;
124: private static final short COMPLEXTYPE = 12;
125: private static final short GROUP = 13;
126: private static final short ENUM_TYPE = 14;
127: private static final short SIMPLETYPE = 15;
128:
129: /**
130: * The hashtables that contain the different componentBindings.
131: */
132: private Hashtable _componentBindings;
133:
134: /**
135: * A flag that indicates if the component bindings of that Binding have been
136: * processed.
137: */
138: private boolean _bindingProcessed = false;
139:
140: /**
141: * Maintains a list of element names where automatic name conflict resolution should be
142: * used all times, incl. the first one.
143: */
144: private Set _automaticNameResolutionForced = new HashSet();
145:
146: /**
147: * Maintains a map of exclusions from the automatic name conflict.
148: */
149: private Map _automaticNameResolutionExcludes = new HashMap();
150:
151: /**
152: * Default constructor.
153: * @see java.lang.Object#Object()
154: */
155: public ExtendedBinding() {
156: super ();
157: _componentBindings = new Hashtable();
158: }
159:
160: /**
161: * Returns the ComponentBinding that corresponds to the given Annotated XML
162: * Schema structure An Schema location will be built for the given Annotated
163: * XML schema structure.
164: *
165: * @param annotated the XML Schema annotated structure for which to query
166: * the Binding object for a ComponentBinding.
167: *
168: * @return the ComponentBinding that corresponds to the given Annotated XML
169: * Schema structure.
170: */
171: public ComponentBindingType getComponentBindingType(
172: final Annotated annotated) {
173: if (annotated == null) {
174: return null;
175: }
176:
177: //--no binding can be defined for a GROUP
178: if (annotated.getStructureType() == Structure.GROUP) {
179: return null;
180: }
181:
182: if (!_bindingProcessed) {
183: processBindingComponents();
184: }
185:
186: String xPath = XPathHelper.getSchemaLocation(annotated);
187: ComponentBindingType result = lookupComponentBindingType(xPath);
188: if (result == null) {
189: //--handle reference
190: switch (annotated.getStructureType()) {
191:
192: case Structure.ELEMENT:
193: //--handle reference: if the element referred a
194: //--global element then we use the global binding
195: if (result == null) {
196: ElementDecl element = (ElementDecl) annotated;
197: if (element.isReference()) {
198: xPath = XPathHelper.getSchemaLocation(element
199: .getReference());
200: result = lookupComponentBindingType(xPath);
201: }
202: //--discard the element
203: element = null;
204: }
205: break;
206:
207: case Structure.ATTRIBUTE:
208: if (result == null) {
209: //--handle reference: if the element referred a
210: //--global element then we use the global binding
211: AttributeDecl attribute = (AttributeDecl) annotated;
212: if (attribute.isReference()) {
213: xPath = XPathHelper.getSchemaLocation(attribute
214: .getReference());
215: result = lookupComponentBindingType(xPath);
216: }
217: attribute = null;
218: }
219: break;
220:
221: default:
222: break;
223: }
224: } //--result == null
225:
226: return result;
227: }
228:
229: /**
230: * Returns the ComponentBindingType that corresponds to the given Schema
231: * location XPath. This is a direct lookup in the hashtable, null is
232: * returned if no ComponentBindingType corresponds to the given Schema
233: * Location XPath.
234: *
235: * @param xPath the schema location xpath
236: * @return The ComponentBindingType that correspond to the given Schema
237: * Location XPath, Null is returned when no ComponentBindingType is
238: * found.
239: * @see org.exolab.castor.builder.binding.XPathHelper#getSchemaLocation(Structure)
240: */
241: private ComponentBindingType lookupComponentBindingType(
242: final String xPath) {
243: if (xPath == null) {
244: return null;
245: }
246: return (ComponentBindingType) _componentBindings.get(xPath);
247: }
248:
249: /**
250: * Process the top-level Binding Component definitions and their children.
251: * Processing a binding component is a 2-step process:
252: * <ul>
253: * <li>Create a key for the component for direct lookup.</li>
254: * <li>Process its children</li>
255: * </ul>
256: */
257: private void processBindingComponents() {
258: ComponentBindingType temp;
259: ComponentBindingType[] tempBindings = getAttributeBinding();
260:
261: //1--attributes
262: for (int i = 0; i < tempBindings.length; i++) {
263: temp = tempBindings[i];
264: //--top-level attribute --> no location computation
265: handleComponent(temp, null, ATTRIBUTE);
266: }
267:
268: //2--complexTypes
269: tempBindings = getComplexTypeBinding();
270: for (int i = 0; i < tempBindings.length; i++) {
271: temp = tempBindings[i];
272: handleComponent(temp, null, COMPLEXTYPE);
273: }
274:
275: //3--elements
276: tempBindings = getElementBinding();
277: for (int i = 0; i < tempBindings.length; i++) {
278: temp = tempBindings[i];
279: handleComponent(temp, null, ELEMENT);
280: }
281:
282: //4--groups
283: tempBindings = getGroupBinding();
284: for (int i = 0; i < tempBindings.length; i++) {
285: temp = tempBindings[i];
286: handleComponent(temp, null, GROUP);
287: }
288:
289: //5--enums
290: tempBindings = getEnumBinding();
291: for (int i = 0; i < tempBindings.length; i++) {
292: temp = tempBindings[i];
293: handleComponent(temp, null, ENUM_TYPE);
294: }
295:
296: //6--simpleTypes
297: tempBindings = getSimpleTypeBinding();
298: for (int i = 0; i < tempBindings.length; i++) {
299: temp = tempBindings[i];
300: handleComponent(temp, null, SIMPLETYPE);
301: }
302:
303: temp = null;
304: tempBindings = null;
305:
306: _bindingProcessed = true;
307: }
308:
309: /**
310: * Process automatic name conflict resolution section, and memorize definitions.
311: * @param type {@link AutomaticNamingType} instance
312: */
313: void handleAutomaticNaming(final AutomaticNamingType type) {
314: Forces forcesOuter = type.getForces();
315: if (forcesOuter != null) {
316: String[] forces = forcesOuter.getForce();
317: for (int i = 0; i < forces.length; i++) {
318: String elementName = forces[i];
319: _automaticNameResolutionForced.add(elementName);
320:
321: }
322: }
323:
324: Excludes excludesOuter = type.getExcludes();
325: if (excludesOuter != null) {
326: Exclude[] excludes = excludesOuter.getExclude();
327: for (int i = 0; i < excludes.length; i++) {
328: Exclude exclude = excludes[i];
329: _automaticNameResolutionExcludes.put(exclude.getName(),
330: exclude);
331: }
332: }
333: }
334:
335: /**
336: * Processes the given ComponentBindingType given its type.
337: *
338: * @param binding the ComponentBindingType for which we want to process the
339: * children.
340: * @param xPath the current XPath location that points to the parent of the
341: * given ComponentBindingType.
342: * @param type an integer that indicates the type of the given
343: * ComponentBindingType
344: */
345: private void handleComponent(final ComponentBindingType binding,
346: final String xPath, final int type) {
347: if (binding == null) {
348: return;
349: }
350:
351: String currentPath = xPath;
352: if (currentPath == null) {
353: currentPath = new String();
354: }
355:
356: String name = binding.getName();
357: boolean xpathUsed = (name.indexOf("/") != -1);
358:
359: switch (type) {
360: case ATTRIBUTE:
361: //--handle attributes
362: if (!xpathUsed) {
363: currentPath = currentPath + PATH_SEPARATOR
364: + ATTRIBUTE_PREFIX;
365: }
366: currentPath += name;
367: _componentBindings.put(currentPath, binding);
368: break;
369:
370: case SIMPLETYPE:
371: //--handle simpleType
372: if (!xpathUsed) {
373: currentPath += SIMPLETYPE_ID;
374: }
375: currentPath += name;
376: _componentBindings.put(currentPath, binding);
377: break;
378:
379: case ELEMENT:
380: //--handle element
381: if (!xpathUsed) {
382: currentPath += PATH_SEPARATOR;
383: }
384: currentPath += name;
385: _componentBindings.put(currentPath, binding);
386: break;
387:
388: case COMPLEXTYPE:
389: //--handle complexType
390: if (!xpathUsed) {
391: currentPath += COMPLEXTYPE_ID;
392: }
393: currentPath += name;
394: _componentBindings.put(currentPath, binding);
395: break;
396:
397: case ENUM_TYPE:
398: //--handle enum
399: if (!xpathUsed) {
400: currentPath += ENUMTYPE_ID;
401: }
402: currentPath += name;
403: _componentBindings.put(currentPath, binding);
404: break;
405:
406: case GROUP:
407: //--handle group
408: if (!xpathUsed) {
409: currentPath += GROUP_ID;
410: }
411: currentPath += name;
412: _componentBindings.put(currentPath, binding);
413: break;
414:
415: default:
416: //--there's a problem somewhere
417: throw new IllegalStateException(
418: "Invalid ComponentBindingType: the"
419: + " type (attribute, element, complextype or group) is unknown");
420: }
421:
422: //--process children
423: ComponentBindingType temp;
424: ComponentBindingType[] tempBindings = binding
425: .getAttributeBinding();
426:
427: //1--attributes
428: for (int i = 0; i < tempBindings.length; i++) {
429: temp = tempBindings[i];
430: //--top-level attribute --> no location computation
431: handleComponent(temp, currentPath, ATTRIBUTE);
432: }
433:
434: //2--complexTypes
435: tempBindings = binding.getComplexTypeBinding();
436: for (int i = 0; i < tempBindings.length; i++) {
437: temp = tempBindings[i];
438: handleComponent(temp, currentPath, COMPLEXTYPE);
439: }
440:
441: //X--simpleTypes
442: tempBindings = binding.getSimpleTypeBinding();
443: for (int i = 0; i < tempBindings.length; i++) {
444: temp = tempBindings[i];
445: handleComponent(temp, currentPath, SIMPLETYPE);
446: }
447:
448: //3--elements
449: tempBindings = binding.getElementBinding();
450: for (int i = 0; i < tempBindings.length; i++) {
451: temp = tempBindings[i];
452: handleComponent(temp, currentPath, ELEMENT);
453: }
454:
455: //4--groups
456: tempBindings = binding.getGroupBinding();
457: for (int i = 0; i < tempBindings.length; i++) {
458: temp = tempBindings[i];
459: handleComponent(temp, currentPath, GROUP);
460: }
461:
462: //5--enums
463: tempBindings = binding.getEnumBinding();
464: for (int i = 0; i < tempBindings.length; i++) {
465: temp = tempBindings[i];
466: handleComponent(temp, currentPath, ENUM_TYPE);
467: }
468:
469: //
470: temp = null;
471: tempBindings = null;
472: }
473:
474: /**
475: * Indicates whether an <exclude> element has been specified in a binding
476: * file for the given 'local name' of an element definition.
477: * @param localName 'local name' of an element definition
478: * @return True if an <exclude> element has been specified
479: */
480: public boolean existsExclusion(final String localName) {
481: return _automaticNameResolutionExcludes.containsKey(localName);
482: }
483:
484: /**
485: * Returns the {@link Exclude} instance for the element identified by the given local name.
486: * @param localName Local name for an element (definition).
487: * @return The {@link Exclude} instance.
488: */
489: public Exclude getExclusion(final String localName) {
490: return (Exclude) _automaticNameResolutionExcludes
491: .get(localName);
492: }
493:
494: /**
495: * Indicates whether an <force> element has been specified in a binding
496: * file for the given 'local name' of an element definition.
497: * @param localName 'local name' of an element definition
498: * @return True if an <force> element has been specified
499: */
500: public boolean existsForce(final String localName) {
501: return _automaticNameResolutionForced.contains(localName);
502: }
503:
504: /**
505: * Returns all <force> elements defined in the binding file.
506: * @return all <force> elements defined in the binding file
507: */
508: public Set getForces() {
509: return _automaticNameResolutionForced;
510: }
511:
512: }
|