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 1999, 2000 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * $Id$
44   */
45  
46  package org.exolab.castor.xml.schema;
47  
48  import java.util.Enumeration;
49  
50  import org.exolab.castor.xml.ValidationException;
51  
52  /**
53   * An XML Schema Group
54   * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
55   * @version $Revision$ $Date: 2006-04-14 04:14:43 -0600 (Fri, 14 Apr 2006) $
56  **/
57  public class Group extends Particle implements ContentModelGroup, Referable {
58      /** SerialVersionUID */
59      private static final long serialVersionUID = 3133443973681261845L;
60  
61      /**
62       * the implementation of ContentModelGroup.
63       **/
64      private ContentModelGroup _contentModel = null;
65  
66      /**
67       * The name of this Group.
68       **/
69      private String _name = null;
70  
71      /**
72       * The Compositor for the Group.
73       **/
74      private Order _order = Order.sequence;
75  
76      /**
77       * ID of this Group (if present at all).
78       */
79      private String _id = null;
80  
81      /**
82       * True if was created for a group tag, false otherwise (all, choice,
83       * sequence).
84       */
85      private boolean _isModelGroupDefinition = false;
86  
87      /**
88       * The parent for this Group (either another Group or a ComplexType).
89       **/
90      private Structure _parent = null;
91  
92      /**
93       * Creates a new {@link Group}, with no name.
94      **/
95      public Group() {
96          this(null);
97      }
98  
99      /**
100      * Creates a new {@link Group} with the given name.
101      * @param name of the {@link Group}
102     **/
103     public Group(final String name) {
104         super();
105         this._name  = name;
106         _contentModel = new ContentModelGroupImpl();
107     }
108 
109     /**
110      * {@inheritDoc}
111      * 
112      * @see org.exolab.castor.xml.schema.ContentModelGroup#addWildcard(org.exolab.castor.xml.schema.Wildcard)
113      */
114     public void addWildcard(final Wildcard wildcard) throws SchemaException {
115          if (wildcard.isAttributeWildcard()) {
116             throw new SchemaException("only <any> should be add in a group.");
117         }
118         _contentModel.addWildcard(wildcard);
119      }
120 
121     /**
122      * {@inheritDoc}
123      * 
124      * @see org.exolab.castor.xml.schema.ContentModelGroup#removeWildcard(org.exolab.castor.xml.schema.Wildcard)
125      */
126     public boolean removeWildcard(final Wildcard wildcard) {
127         if (wildcard == null) {
128             return false;
129         }
130         return _contentModel.removeWildcard(wildcard);
131      }
132 
133     /**
134      * Returns the {@link ContentModelGroup} for this group.
135      * Only used for a <group/> element
136      * @return the ContentModelGroup for this group
137      */
138     public ContentModelGroup getContentModelGroup() {
139         return _contentModel;
140     }
141      
142     /**
143      * Returns the ID for this {@link Group}.
144      * @return the ID for this {@link Group}, or null if no ID is present
145      **/
146     public String getId() {
147         return _id;
148     }
149 
150     /**
151      * Returns the name of this {@link Group}, or null if no name was defined.
152      * @return the name of this {@link Group}, or null if no name was defined
153     **/
154     public String getName() {
155         return _name;
156     }
157 
158     /**
159      * Returns the compositor for this {@link Group}.
160      * @return the compositor for this {@link Group}
161     **/
162     public Order getOrder() {
163 
164         //-- Return proper compositor...
165         //-- according to XML Schema spec 20000407 section 4.3.5
166 
167         //-- Note: it's important not to simply call
168         //-- #getParticleCount or #getParticle because those
169         //-- methods also perform some trickery
170         if (_contentModel.getParticleCount() == 1) {
171             Particle particle = _contentModel.getParticle(0);
172             if (particle.getStructureType() == Structure.GROUP) {
173                 if ((getMinOccurs() == 1) && (getMaxOccurs() == 1)) {
174                     return ((Group) particle).getOrder();
175                 }
176             }
177         }
178         return this._order;
179     }
180 
181 
182     /**
183      * Returns the parent of this Group, this value may be null if
184      * no parent has been set.
185      *
186      * @return the parent Structure of this Group.
187     **/
188     public Structure getParent() {
189         return _parent;
190     }
191 
192     /**
193      * Sets if the group is a model group definition.
194      * @deprecated Since Castor 0.9.2, to handle properly the <group/>
195      * element the class ModelGroup has been created
196      * @see ModelGroup
197      */
198     public void setIsModelGroupDefinition(final boolean isModelGroupDefinition) {
199         _isModelGroupDefinition = isModelGroupDefinition;
200     }
201 
202     /**
203      * Tells if the group is a model group definition.
204      * @return true if the group is a model group definition (<group/> tag), false
205      * otherwise {@literal <all/>}, <choice/>, or <sequence/> tags.
206      * @deprecated Since Castor 0.9.2, to handle properly the <group/>
207      * element the class {@link ModelGroup} has been created
208      * @see ModelGroup
209 
210      */
211     public boolean isModelGroupDefinition() {
212         return _isModelGroupDefinition;
213     }
214 
215 
216     /**
217      * Returns the Id used to refer to this Object.
218      * @return the Id used to refer to this Object
219      * @see Referable
220     **/
221     public String getReferenceId() {
222         if (_name != null) {
223             return "group:" + _name;
224         }
225         return null;
226     }
227 
228     /**
229      * Sets the name of this {@link Group}.
230      * @param name the new name for this {@link Group}
231      **/
232     public void setName(final String name) {
233         this._name = name;
234     }
235 
236     /**
237      * Sets the ID for this {@link Group}.
238      * @param id the ID for this {@link Group}
239      **/
240     public void setId(final String id) {
241         _id = id;
242     }
243 
244     /**
245      * Sets the {@link Order} for this {@link Group}.
246      * @param order the type of {@link Order} that this {@link Group} is restricted to
247     **/
248     public void setOrder(final Order order) {
249         if (order == null) {
250             this._order = Order.all;
251         } else {
252             this._order = order;
253         }
254     }
255 
256     //---------------------------------------/
257     //- Implementation of ContentModelGroup -/
258     //---------------------------------------/
259 
260     /**
261      * {@inheritDoc}
262      * 
263      * @see org.exolab.castor.xml.schema.ContentModelGroup#addElementDecl(org.exolab.castor.xml.schema.ElementDecl)
264      */
265     public void addElementDecl(final ElementDecl elementDecl)
266             throws SchemaException {
267         _contentModel.addElementDecl(elementDecl);
268         // --set the parent
269         elementDecl.setParent(this);
270     }
271 
272     /**
273      * {@inheritDoc}
274      * 
275      * @see org.exolab.castor.xml.schema.ContentModelGroup#removeElementDecl(org.exolab.castor.xml.schema.ElementDecl)
276      */
277     public boolean removeElementDecl(final ElementDecl element) {
278          return _contentModel.removeElementDecl(element);
279      }
280 
281     /**
282      * {@inheritDoc}
283      * 
284      * @see org.exolab.castor.xml.schema.ContentModelGroup#addGroup(org.exolab.castor.xml.schema.Group)
285      */
286     public void addGroup(final Group group) throws SchemaException {
287         _contentModel.addGroup(group);
288 
289         // -- set reference to parent
290         group.setParent(this);
291 
292     }
293 
294     /**
295      * {@inheritDoc}
296      * 
297      * @see org.exolab.castor.xml.schema.ContentModelGroup#removeGroup(org.exolab.castor.xml.schema.Group)
298      */
299     public boolean removeGroup(final Group group){
300         boolean result = _contentModel.removeGroup(group);
301         group.setParent(null);
302         return result;
303     }
304 
305     /**
306      * {@inheritDoc}
307      * 
308      * @see org.exolab.castor.xml.schema.ContentModelGroup#addGroup(org.exolab.castor.xml.schema.ModelGroup)
309      */
310     public void addGroup(final ModelGroup group) throws SchemaException {
311         _contentModel.addGroup(group);
312 
313         // -- set reference to parent
314         group.setParent(this);
315     }
316 
317     /**
318      * {@inheritDoc}
319      * 
320      * @see org.exolab.castor.xml.schema.ContentModelGroup#removeGroup(org.exolab.castor.xml.schema.ModelGroup)
321      */
322     public boolean removeGroup(final ModelGroup group) {
323         boolean result = _contentModel.removeGroup(group);
324         group.setParent(null);
325         return result;
326     }
327 
328     /**
329      * {@inheritDoc}
330      * @see org.exolab.castor.xml.schema.ContentModelGroup#enumerate()
331      */
332     public Enumeration<Annotated> enumerate() {
333         // -- Some trickery to properly handle
334         // -- XML Schema spec 20000407 section 4.3.5
335 
336         if (_contentModel.getParticleCount() == 1) {
337             Particle particle = _contentModel.getParticle(0);
338             if (particle.getStructureType() == Structure.GROUP) {
339                 Group temp = (Group) particle;
340                 if (((getMinOccurs() == 1) && (getMaxOccurs() == 1))
341                         && ((temp.getMinOccurs() == 1) && (temp.getMaxOccurs() == 1))) {
342                     return temp.enumerate();
343                 }
344             }
345         }
346         return _contentModel.enumerate();
347     } //-- enumerate
348     
349      
350     /**
351      * {@inheritDoc}
352      * @see org.exolab.castor.xml.schema.ContentModelGroup#getElementDecl(java.lang.String)
353      */
354     public ElementDecl getElementDecl(final String name) {
355         return _contentModel.getElementDecl(name);
356     }
357 
358     /**
359      * {@inheritDoc}
360      * @see org.exolab.castor.xml.schema.ContentModelGroup#getParticle(int)
361      */
362     public Particle getParticle(final int index) {
363         // -- Some trickery to properly handle
364         // -- XML Schema spec 20000407 section 4.3.5
365         if (_contentModel.getParticleCount() == 1) {
366             Particle particle = _contentModel.getParticle(0);
367             if (particle.getStructureType() == Structure.GROUP) {
368                 if ((getMinOccurs() == 1) && (getMaxOccurs() == 1)) {
369                     return ((Group) particle).getParticle(index);
370                 }
371             }
372         }
373         return _contentModel.getParticle(index);
374     }
375 
376     /**
377      * {@inheritDoc}
378      * 
379      * @see org.exolab.castor.xml.schema.ContentModelGroup#getParticleCount()
380      */
381     public int getParticleCount() {
382         // -- Some trickery to properly handle
383         // -- XML Schema spec 20000407 section 4.3.5
384         if (_contentModel.getParticleCount() == 1) {
385             Particle particle = _contentModel.getParticle(0);
386             if (particle.getStructureType() == Structure.GROUP) {
387                 if ((getMinOccurs() == 1) && (getMaxOccurs() == 1)) {
388                     return ((Group) particle).getParticleCount();
389                 }
390             }
391         }
392         return _contentModel.getParticleCount();
393     }
394 
395     //-------------------------------/
396     //- Implementation of Structure -/
397     //-------------------------------/
398 
399     /**
400      * {@inheritDoc}
401      * 
402      * @see org.exolab.castor.xml.schema.Structure#getStructureType()
403      */
404     public short getStructureType() {
405         return Structure.GROUP;
406     }
407 
408    /**
409     * A helper method that returns true if this group
410     * contains an {@literal <any>} element.
411     * @return  method that returns true if this group
412     * contains an {@literal <any>} element.
413     */
414     public boolean hasAny() {
415         boolean result = false;
416         Enumeration<Structure> enumeration = _contentModel.enumerate();
417         while (enumeration.hasMoreElements() && !result) {
418             Structure struct = enumeration.nextElement();
419             switch (struct.getStructureType()) {
420                 case Structure.ELEMENT:
421                     break;
422                 case Structure.GROUP:
423                 case Structure.MODELGROUP:
424                     result = ((Group) struct).hasAny();
425                     break;
426                 case Structure.WILDCARD:
427                     result = true;
428                     break;
429                 default:
430                     break;
431             }
432         }
433         return result;
434     }
435 
436     /**
437      * Checks the validity of this {@link Group} defintion.
438      *
439      * @throws ValidationException when this {@link Group} definition
440      * is invalid.
441     **/
442     public void validate() throws ValidationException {
443         if (_order == Order.all) {
444             if (getMaxOccurs() != 1) {
445                 String err = "Wrong maxOccurs value for a <all>:" + getMaxOccurs();
446                 err += "\n1 is the only possible value.";
447                 throw new ValidationException(err);
448             }
449             if (getMinOccurs() > 1) {
450                 String err = "Wrong minOccurs value for a <all>:" + getMinOccurs();
451                 err += "\n0 or 1 are the only possible values.";
452                 throw new ValidationException(err);
453             }
454         }
455         Enumeration<Structure> enumeration = _contentModel.enumerate();
456         while (enumeration.hasMoreElements()) {
457             enumeration.nextElement().validate();
458         }
459     }
460 
461     /**
462      * Sets the parent for this {@link Group}.
463      *
464      * @param parent the parent {@link Structure} for this {@link Group}
465      **/
466     protected void setParent(final Structure parent) {
467         if (parent != null) {
468             switch (parent.getStructureType()) {
469                 case Structure.COMPLEX_TYPE:
470                 case Structure.GROUP:
471                 case Structure.MODELGROUP:
472                 case Structure.SCHEMA:
473                     break;
474                 default:
475                     String error = "Invalid parent for group";
476                     throw new IllegalArgumentException(error);
477             }
478         }
479         _parent = parent;
480     }
481 
482     /**
483      * Indicates whether this {@link Particle} is 'emptiable'
484      * @return true if this Particle is 'emptiable'
485      */
486     public boolean isEmptiable() {
487       if (getMinOccurs() == 0) {
488             return true;
489         }
490       
491       boolean result = false;
492       switch (this.getOrder()) {
493       case choice:
494         {
495             result = false;
496             Enumeration<Annotated> enumerate = this.enumerate();
497             while (enumerate.hasMoreElements()) {
498                 Particle p = (Particle) enumerate.nextElement();
499                 if (p.isEmptiable()) {
500                     result = true;
501                     break;
502                 }
503             }
504         }
505         break;
506         
507       case all:
508       case sequence:
509         {
510             result = true;
511             Enumeration<Annotated> enumerate = this.enumerate();
512             while (enumerate.hasMoreElements()) {
513                 Particle p = (Particle) enumerate.nextElement();
514                 if (!p.isEmptiable()) {
515                     result = false;
516                     break;
517                 }
518             }
519         }
520         break;
521       }        
522       return result;
523 
524     }
525 
526 }