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