001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.relaxng.program;
031:
032: import com.caucho.relaxng.RelaxException;
033: import com.caucho.util.CharBuffer;
034: import com.caucho.util.L10N;
035: import com.caucho.xml.QName;
036:
037: import java.util.ArrayList;
038: import java.util.HashSet;
039: import java.util.Iterator;
040:
041: /**
042: * Generates programs from patterns.
043: */
044: public class ChoiceItem extends Item {
045: protected final static L10N L = new L10N(ChoiceItem.class);
046:
047: private ArrayList<Item> _items = new ArrayList<Item>();
048:
049: private boolean _allowEmpty = false;
050:
051: public ChoiceItem() {
052: }
053:
054: public static Item create(Item left, Item right) {
055: ChoiceItem choice = new ChoiceItem();
056: choice.addItem(left);
057: choice.addItem(right);
058:
059: return choice.getMin();
060: }
061:
062: public void addItem(Item item) {
063: if (item == null)
064: return;
065: else if (item instanceof EmptyItem) {
066: _allowEmpty = true;
067: return;
068: } else if (item instanceof ChoiceItem) {
069: ChoiceItem choice = (ChoiceItem) item;
070:
071: if (choice._allowEmpty)
072: _allowEmpty = true;
073:
074: for (int i = 0; i < choice._items.size(); i++)
075: addItem(choice._items.get(i));
076:
077: return;
078: }
079:
080: for (int i = 0; i < _items.size(); i++) {
081: Item subItem = _items.get(i);
082:
083: if (item.equals(subItem))
084: return;
085:
086: if (item instanceof InElementItem
087: && subItem instanceof InElementItem) {
088: InElementItem elt1 = (InElementItem) item;
089: InElementItem elt2 = (InElementItem) subItem;
090:
091: if (elt1.getElementItem().equals(elt2.getElementItem())) {
092: subItem = InElementItem.create(elt1
093: .getElementItem(), create(elt1
094: .getContinuationItem(), elt2
095: .getContinuationItem()));
096: _items.remove(i);
097: addItem(subItem);
098: return;
099: }
100: }
101:
102: if (item instanceof GroupItem
103: && subItem instanceof GroupItem) {
104: GroupItem group1 = (GroupItem) item;
105: GroupItem group2 = (GroupItem) subItem;
106:
107: if (group1.getFirst().equals(group2.getFirst())) {
108: subItem = GroupItem.create(group1.getFirst(),
109: create(group1.getSecond(), group2
110: .getSecond()));
111: _items.remove(i);
112: addItem(subItem);
113: return;
114: }
115: }
116: }
117:
118: _items.add(item);
119: }
120:
121: public Item getMin() {
122: if (!_allowEmpty && _items.size() == 0)
123: return null;
124: else if (_allowEmpty && _items.size() == 0)
125: return EmptyItem.create();
126: else if (_items.size() == 1
127: && (!_allowEmpty || _items.get(0).allowEmpty()))
128: return _items.get(0);
129: else
130: return this ;
131: }
132:
133: /**
134: * Returns the first set, the set of element names possible.
135: */
136: public void firstSet(HashSet<QName> set) {
137: for (int i = 0; i < _items.size(); i++)
138: _items.get(i).firstSet(set);
139: }
140:
141: /**
142: * Returns the first set, the set of element names possible.
143: */
144: public void requiredFirstSet(HashSet<QName> set) {
145: if (allowEmpty())
146: return;
147:
148: for (int i = 0; i < _items.size(); i++)
149: _items.get(i).requiredFirstSet(set);
150: }
151:
152: /**
153: * Allows empty if any item allows empty.
154: */
155: public boolean allowEmpty() {
156: if (_allowEmpty)
157: return true;
158:
159: for (int i = 0; i < _items.size(); i++)
160: if (_items.get(i).allowEmpty())
161: return true;
162:
163: return false;
164: }
165:
166: /**
167: * Interleaves a continuation.
168: */
169: public Item interleaveContinuation(Item cont) {
170: ChoiceItem item = new ChoiceItem();
171:
172: for (int i = 0; i < _items.size(); i++)
173: item.addItem(_items.get(i).interleaveContinuation(cont));
174:
175: return item.getMin();
176: }
177:
178: /**
179: * Adds an inElement continuation.
180: */
181: public Item inElementContinuation(Item cont) {
182: ChoiceItem item = new ChoiceItem();
183:
184: for (int i = 0; i < _items.size(); i++)
185: item.addItem(_items.get(i).inElementContinuation(cont));
186:
187: return item.getMin();
188: }
189:
190: /**
191: * Adds a group continuation.
192: */
193: public Item groupContinuation(Item cont) {
194: ChoiceItem item = new ChoiceItem();
195:
196: for (int i = 0; i < _items.size(); i++)
197: item.addItem(_items.get(i).groupContinuation(cont));
198:
199: return item.getMin();
200: }
201:
202: /**
203: * Return all possible child items or null
204: */
205: public Iterator<Item> getItemsIterator() {
206: if (_items.size() == 0)
207: return emptyItemIterator();
208: else
209: return _items.iterator();
210: }
211:
212: /**
213: * Returns the next item on the match.
214: */
215: public Item startElement(QName name) throws RelaxException {
216: Item result = null;
217: ChoiceItem choice = null;
218:
219: for (int i = 0; i < _items.size(); i++) {
220: Item next = _items.get(i).startElement(name);
221:
222: if (next == null) {
223: } else if (result == null)
224: result = next;
225: else {
226: if (choice == null) {
227: choice = new ChoiceItem();
228: choice.addItem(result);
229: }
230:
231: choice.addItem(next);
232: }
233: }
234:
235: if (choice != null)
236: return choice.getMin();
237: else
238: return result;
239: }
240:
241: /**
242: * Returns the first set, the set of attribute names possible.
243: */
244: public void attributeSet(HashSet<QName> set) {
245: for (int i = 0; i < _items.size(); i++)
246: _items.get(i).attributeSet(set);
247: }
248:
249: /**
250: * Returns true if the attribute is allowed.
251: *
252: * @param name the name of the attribute
253: * @param value the value of the attribute
254: *
255: * @return true if the attribute is allowed
256: */
257: public boolean allowAttribute(QName name, String value)
258: throws RelaxException {
259: for (int i = _items.size() - 1; i >= 0; i--)
260: if (_items.get(i).allowAttribute(name, value))
261: return true;
262:
263: return false;
264: }
265:
266: /**
267: * Sets an attribute.
268: *
269: * @param name the name of the attribute
270: * @param value the value of the attribute
271: *
272: * @return the program for handling the element
273: */
274: public Item setAttribute(QName name, String value)
275: throws RelaxException {
276: if (!allowAttribute(name, value))
277: return this ;
278:
279: ChoiceItem choice = new ChoiceItem();
280:
281: if (_allowEmpty)
282: choice.addItem(EmptyItem.create());
283:
284: for (int i = _items.size() - 1; i >= 0; i--) {
285: Item next = _items.get(i).setAttribute(name, value);
286:
287: if (next == null)
288: return null;
289:
290: choice.addItem(next);
291: }
292:
293: return choice.getMin();
294: }
295:
296: /**
297: * Returns true if the item can match empty.
298: */
299: public Item attributeEnd() {
300: ChoiceItem choice = new ChoiceItem();
301:
302: if (_allowEmpty)
303: choice._allowEmpty = true;
304:
305: for (int i = _items.size() - 1; i >= 0; i--) {
306: Item next = _items.get(i).attributeEnd();
307:
308: if (next == null)
309: continue;
310:
311: choice.addItem(next);
312: }
313:
314: if (choice.equals(this ))
315: return this ;
316: else
317: return choice.getMin();
318: }
319:
320: /**
321: * Returns the next item on the match.
322: */
323: @Override
324: public Item text(CharSequence data) throws RelaxException {
325: Item result = null;
326: ChoiceItem choice = null;
327:
328: for (int i = 0; i < _items.size(); i++) {
329: Item next = _items.get(i).text(data);
330:
331: if (next == null) {
332: } else if (result == null)
333: result = next;
334: else {
335: if (choice == null) {
336: choice = new ChoiceItem();
337: choice.addItem(result);
338: }
339:
340: choice.addItem(next);
341: }
342: }
343:
344: if (choice != null)
345: return choice.getMin();
346: else
347: return result;
348: }
349:
350: /**
351: * Returns the next item when the element closes
352: */
353: public Item endElement() throws RelaxException {
354: ChoiceItem choice = new ChoiceItem();
355:
356: if (_allowEmpty)
357: choice._allowEmpty = true;
358:
359: for (int i = _items.size() - 1; i >= 0; i--) {
360: Item next = _items.get(i).endElement();
361:
362: if (next == null)
363: continue;
364:
365: choice.addItem(next);
366: }
367:
368: if (choice.equals(this ))
369: return this ;
370: else
371: return choice.getMin();
372: }
373:
374: /**
375: * Returns the hash code for the empty item.
376: */
377: public int hashCode() {
378: int hash = 37;
379:
380: for (int i = 0; i < _items.size(); i++)
381: hash += _items.get(i).hashCode();
382:
383: return hash;
384: }
385:
386: /**
387: * Returns true if the object is an empty item.
388: */
389: public boolean equals(Object o) {
390: if (this == o)
391: return true;
392:
393: if (!(o instanceof ChoiceItem))
394: return false;
395:
396: ChoiceItem choice = (ChoiceItem) o;
397:
398: return isSubset(choice) && choice.isSubset(this );
399: }
400:
401: private boolean isSubset(ChoiceItem item) {
402: if (_items.size() != item._items.size())
403: return false;
404:
405: for (int i = 0; i < _items.size(); i++) {
406: Item subItem = _items.get(i);
407:
408: if (!item._items.contains(subItem))
409: return false;
410: }
411:
412: return true;
413: }
414:
415: /**
416: * Returns true if the element is allowed somewhere in the item.
417: * allowsElement is used for error messages to give more information
418: * in cases of order dependency.
419: *
420: * @param name the name of the element
421: *
422: * @return true if the element is allowed somewhere
423: */
424: public boolean allowsElement(QName name) {
425: for (int i = 0; i < _items.size(); i++) {
426: Item subItem = _items.get(i);
427:
428: if (subItem.allowsElement(name))
429: return true;
430: }
431:
432: return false;
433: }
434:
435: /**
436: * Returns the pretty printed syntax.
437: */
438: public String toSyntaxDescription(int depth) {
439: CharBuffer cb = CharBuffer.allocate();
440:
441: if (_items.size() > 1)
442: cb.append("(");
443:
444: boolean isSimple = true;
445: for (int i = 0; i < _items.size(); i++) {
446: Item item = _items.get(i);
447: if (!item.isSimpleSyntax())
448: isSimple = false;
449:
450: if (i == 0) {
451: if (!isSimple)
452: cb.append(" ");
453: } else if (isSimple) {
454: cb.append(" | ");
455: } else {
456: addSyntaxNewline(cb, depth);
457: cb.append("| ");
458: }
459:
460: cb.append(item.toSyntaxDescription(depth + 2));
461: }
462:
463: if (_items.size() > 1)
464: cb.append(')');
465:
466: if (_allowEmpty)
467: cb.append('?');
468:
469: return cb.close();
470: }
471:
472: /**
473: * Returns true for an element with simple syntax.
474: */
475: protected boolean isSimpleSyntax() {
476: return (_items.size() == 1) && _items.get(0).isSimpleSyntax();
477: }
478:
479: public String toString() {
480: StringBuffer sb = new StringBuffer();
481:
482: sb.append("ChoiceItem[");
483: for (int i = 0; i < _items.size(); i++) {
484: if (i != 0)
485: sb.append(", ");
486: sb.append(_items.get(i));
487: }
488:
489: sb.append("]");
490:
491: return sb.toString();
492: }
493: }
|