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 }