View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 2002 (C) Intalio Inc. All Rights Reserved.
32   *
33   * $Id$
34   */
35  package org.exolab.castor.builder.binding;
36  
37  // --Castor imports
38  import java.util.HashMap;
39  import java.util.HashSet;
40  import java.util.Hashtable;
41  import java.util.Map;
42  import java.util.Set;
43  
44  import org.exolab.castor.builder.binding.xml.AutomaticNamingType;
45  import org.exolab.castor.builder.binding.xml.Binding;
46  import org.exolab.castor.builder.binding.xml.ComponentBindingType;
47  import org.exolab.castor.builder.binding.xml.Exclude;
48  import org.exolab.castor.builder.binding.xml.Excludes;
49  import org.exolab.castor.builder.binding.xml.Forces;
50  import org.exolab.castor.xml.schema.Annotated;
51  import org.exolab.castor.xml.schema.AttributeDecl;
52  import org.exolab.castor.xml.schema.ElementDecl;
53  import org.exolab.castor.xml.schema.Structure;
54  
55  /**
56   * This class adds the necessary logic to a Binding Object to bring the gap between the XML Schema
57   * Object Model and the Binding File. It queries the Binding Object to retrieve the the associated
58   * ComponentBinding.
59   * <p>
60   * An "XPath like" representation of an XML Schema structure is built to lookup the component
61   * bindings in their storage structure. The algorithm used to build the "XPath like" representation
62   * is summarized in the following example: Given the XML schema declaration:
63   *
64   * <pre>
65   *        &lt;xsd:element name=&quot;foo&quot;&gt;
66   *            &lt;xsd:complextype&gt;
67   *                &lt;xsd:attribute name=&quot;bar&quot; type=&quot;xsd:string&quot;/&gt;
68   *            &lt;/xsd:complextype&gt;
69   *        &lt;/xsd:element&gt;
70   * </pre>
71   *
72   * The path to identify the attribute 'bar' will be:
73   *
74   * <pre>
75   *        /foo/@bar
76   * </pre>
77   *
78   * The keywords <tt>complexType</tt> and <tt>group</tt> are used to identify respectively an XML
79   * Schema ComplexType and a Model Group <b>definition</b>.
80   *
81   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
82   * @version $Revision$ $Date: 2005-03-05 06:42:06 -0700 (Sat, 05 Mar 2005) $
83   */
84  public final class ExtendedBinding extends Binding {
85  
86    /**
87     * Constants needed to create the XPath.
88     */
89    protected static final String PATH_SEPARATOR = "/";
90    /**
91     * Prefix used to identify an attribute.
92     */
93    protected static final String ATTRIBUTE_PREFIX = "@";
94    /**
95     * Prefix used to identify a complexType.
96     */
97    public static final String COMPLEXTYPE_ID = "complexType:";
98    /**
99     * Prefix used to identity a simplyType.
100    */
101   public static final String SIMPLETYPE_ID = "simpleType:";
102   /**
103    * Prefix used to identify an enumeration.
104    */
105   public static final String ENUMTYPE_ID = "enumType:";
106   /**
107    * Prefix used to identify a model group.
108    */
109   public static final String GROUP_ID = "group:";
110 
111   private static final short ATTRIBUTE = 10;
112   private static final short ELEMENT = 11;
113   private static final short COMPLEXTYPE = 12;
114   private static final short GROUP = 13;
115   private static final short ENUM_TYPE = 14;
116   private static final short SIMPLETYPE = 15;
117 
118   /**
119    * The hashtables that contain the different componentBindings.
120    */
121   private Hashtable<String, ComponentBindingType> _componentBindings;
122 
123   /**
124    * A flag that indicates if the component bindings of that Binding have been processed.
125    */
126   private boolean _bindingProcessed = false;
127 
128   /**
129    * Maintains a list of element names where automatic name conflict resolution should be used all
130    * times, incl. the first one.
131    */
132   private Set<String> _automaticNameResolutionForced = new HashSet<String>();
133 
134   /**
135    * Maintains a map of exclusions from the automatic name conflict.
136    */
137   private Map<String, Exclude> _automaticNameResolutionExcludes = new HashMap<String, Exclude>();
138 
139   /**
140    * Default constructor.
141    * 
142    * @see java.lang.Object#Object()
143    */
144   public ExtendedBinding() {
145     super();
146     _componentBindings = new Hashtable<String, ComponentBindingType>();
147   }
148 
149   /**
150    * Returns the ComponentBinding that corresponds to the given Annotated XML Schema structure An
151    * Schema location will be built for the given Annotated XML schema structure.
152    *
153    * @param annotated the XML Schema annotated structure for which to query the Binding object for a
154    *        ComponentBinding.
155    *
156    * @return the ComponentBinding that corresponds to the given Annotated XML Schema structure.
157    */
158   public ComponentBindingType getComponentBindingType(final Annotated annotated) {
159     if (annotated == null) {
160       return null;
161     }
162 
163     // --no binding can be defined for a GROUP
164     if (annotated.getStructureType() == Structure.GROUP) {
165       return null;
166     }
167 
168     if (!_bindingProcessed) {
169       processBindingComponents();
170     }
171 
172     String xPath = XPathHelper.getSchemaLocation(annotated);
173     ComponentBindingType result = lookupComponentBindingType(xPath);
174     if (result == null) {
175       // --handle reference
176       switch (annotated.getStructureType()) {
177 
178         case Structure.ELEMENT:
179           // --handle reference: if the element referred a
180           // --global element then we use the global binding
181           if (result == null) {
182             ElementDecl element = (ElementDecl) annotated;
183             if (element.isReference()) {
184               xPath = XPathHelper.getSchemaLocation(element.getReference());
185               result = lookupComponentBindingType(xPath);
186             }
187             // --discard the element
188             element = null;
189           }
190           break;
191 
192         case Structure.ATTRIBUTE:
193           if (result == null) {
194             // --handle reference: if the element referred a
195             // --global element then we use the global binding
196             AttributeDecl attribute = (AttributeDecl) annotated;
197             if (attribute.isReference()) {
198               xPath = XPathHelper.getSchemaLocation(attribute.getReference());
199               result = lookupComponentBindingType(xPath);
200             }
201             attribute = null;
202           }
203           break;
204 
205         default:
206           break;
207       }
208     } // --result == null
209 
210     return result;
211   }
212 
213   /**
214    * Returns the ComponentBindingType that corresponds to the given Schema location XPath. This is a
215    * direct lookup in the hashtable, null is returned if no ComponentBindingType corresponds to the
216    * given Schema Location XPath.
217    *
218    * @param xPath the schema location xpath
219    * @return The ComponentBindingType that correspond to the given Schema Location XPath, Null is
220    *         returned when no ComponentBindingType is found.
221    * @see org.exolab.castor.builder.binding.XPathHelper#getSchemaLocation(Structure)
222    */
223   private ComponentBindingType lookupComponentBindingType(final String xPath) {
224     if (xPath == null) {
225       return null;
226     }
227     ComponentBindingType componentBinding = _componentBindings.get(xPath);
228 
229     // if no component binding has been found, retry with xpath without namespaces
230     // this is to ensure backwards compatibility
231     if (componentBinding == null) {
232       int occurence = xPath.indexOf('{');
233       String xPathNoNamespaces = xPath;
234       if (occurence > 0) {
235         while (occurence > 0) {
236           String xPathOld = xPathNoNamespaces;
237           xPathNoNamespaces = xPathOld.substring(0, occurence);
238           int closingOccurence = xPathOld.indexOf('}');
239           xPathNoNamespaces += xPathOld.substring(closingOccurence + 1);
240           occurence = xPathNoNamespaces.indexOf('{');
241         }
242         componentBinding = lookupComponentBindingType(xPathNoNamespaces);
243       }
244     }
245     return componentBinding;
246   }
247 
248   /**
249    * Process the top-level Binding Component definitions and their children. Processing a binding
250    * component is a 2-step process:
251    * <ul>
252    * <li>Create a key for the component for direct lookup.</li>
253    * <li>Process its children</li>
254    * </ul>
255    */
256   private void processBindingComponents() {
257     ComponentBindingType temp;
258     ComponentBindingType[] tempBindings = getAttributeBinding();
259 
260     // 1--attributes
261     for (int i = 0; i < tempBindings.length; i++) {
262       temp = tempBindings[i];
263       // --top-level attribute --> no location computation
264       handleComponent(temp, null, ATTRIBUTE);
265     }
266 
267     // 2--complexTypes
268     tempBindings = getComplexTypeBinding();
269     for (int i = 0; i < tempBindings.length; i++) {
270       temp = tempBindings[i];
271       handleComponent(temp, null, COMPLEXTYPE);
272     }
273 
274     // 3--elements
275     tempBindings = getElementBinding();
276     for (int i = 0; i < tempBindings.length; i++) {
277       temp = tempBindings[i];
278       handleComponent(temp, null, ELEMENT);
279     }
280 
281     // 4--groups
282     tempBindings = getGroupBinding();
283     for (int i = 0; i < tempBindings.length; i++) {
284       temp = tempBindings[i];
285       handleComponent(temp, null, GROUP);
286     }
287 
288     // 5--enums
289     tempBindings = getEnumBinding();
290     for (int i = 0; i < tempBindings.length; i++) {
291       temp = tempBindings[i];
292       handleComponent(temp, null, ENUM_TYPE);
293     }
294 
295     // 6--simpleTypes
296     tempBindings = getSimpleTypeBinding();
297     for (int i = 0; i < tempBindings.length; i++) {
298       temp = tempBindings[i];
299       handleComponent(temp, null, SIMPLETYPE);
300     }
301 
302     temp = null;
303     tempBindings = null;
304 
305     _bindingProcessed = true;
306   }
307 
308   /**
309    * Process automatic name conflict resolution section, and memorize definitions.
310    * 
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(), exclude);
330       }
331     }
332   }
333 
334   /**
335    * Processes the given ComponentBindingType given its type.
336    *
337    * @param binding the ComponentBindingType for which we want to process the children.
338    * @param xPath the current XPath location that points to the parent of the given
339    *        ComponentBindingType.
340    * @param type an integer that indicates the type of the given ComponentBindingType
341    */
342   private void handleComponent(final ComponentBindingType binding, final String xPath,
343       final int type) {
344     if (binding == null) {
345       return;
346     }
347 
348     String currentPath = xPath;
349     if (currentPath == null) {
350       currentPath = new String();
351     }
352 
353     String name = binding.getName();
354     boolean xpathUsed = (name.indexOf("/") != -1);
355 
356     switch (type) {
357       case ATTRIBUTE:
358         // --handle attributes
359         if (!xpathUsed) {
360           currentPath = currentPath + PATH_SEPARATOR + ATTRIBUTE_PREFIX;
361         }
362         currentPath += name;
363         _componentBindings.put(currentPath, binding);
364         break;
365 
366       case SIMPLETYPE:
367         // --handle simpleType
368         if (!xpathUsed) {
369           currentPath += SIMPLETYPE_ID;
370         }
371         currentPath += name;
372         _componentBindings.put(currentPath, binding);
373         break;
374 
375       case ELEMENT:
376         // --handle element
377         if (!xpathUsed) {
378           currentPath += PATH_SEPARATOR;
379         }
380         currentPath += name;
381         _componentBindings.put(currentPath, binding);
382         break;
383 
384       case COMPLEXTYPE:
385         // --handle complexType
386         if (!xpathUsed) {
387           currentPath += COMPLEXTYPE_ID;
388         } else {
389           if (!name.substring(1, 12).equals("complexType")) {
390             currentPath += PATH_SEPARATOR + COMPLEXTYPE_ID;
391             currentPath += name.substring(1);
392           } else {
393             currentPath += name;
394           }
395         }
396         _componentBindings.put(currentPath, binding);
397         break;
398 
399       case ENUM_TYPE:
400         // --handle enum
401         if (!xpathUsed) {
402           currentPath += ENUMTYPE_ID;
403         }
404         currentPath += name;
405         _componentBindings.put(currentPath, binding);
406         break;
407 
408       case GROUP:
409         // --handle group
410         if (!xpathUsed) {
411           currentPath += GROUP_ID;
412         }
413         currentPath += name;
414         _componentBindings.put(currentPath, binding);
415         break;
416 
417       default:
418         // --there's a problem somewhere
419         throw new IllegalStateException("Invalid ComponentBindingType: the"
420             + " type (attribute, element, complextype or group) is unknown");
421     }
422 
423     // --process children
424     ComponentBindingType temp;
425     ComponentBindingType[] tempBindings = binding.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 &lt;exclude&gt; element has been specified in a binding file for the given
476    * 'local name' of an element definition.
477    * 
478    * @param localName 'local name' of an element definition
479    * @return True if an &lt;exclude&gt; element has been specified
480    */
481   public boolean existsExclusion(final String localName) {
482     return _automaticNameResolutionExcludes.containsKey(localName);
483   }
484 
485   /**
486    * Returns the {@link Exclude} instance for the element identified by the given local name.
487    * 
488    * @param localName Local name for an element (definition).
489    * @return The {@link Exclude} instance.
490    */
491   public Exclude getExclusion(final String localName) {
492     return _automaticNameResolutionExcludes.get(localName);
493   }
494 
495   /**
496    * Indicates whether an &lt;force&gt; element has been specified in a binding file for the given
497    * 'local name' of an element definition.
498    * 
499    * @param localName 'local name' of an element definition
500    * @return True if an &lt;force&gt; element has been specified
501    */
502   public boolean existsForce(final String localName) {
503     return _automaticNameResolutionForced.contains(localName);
504   }
505 
506   /**
507    * Returns all &lt;force&gt; elements defined in the binding file.
508    * 
509    * @return all &lt;force&gt; elements defined in the binding file
510    */
511   public Set<String> getForces() {
512     return _automaticNameResolutionForced;
513   }
514 
515 }