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