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: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.relaxng.program;
030:
031: import com.caucho.relaxng.RelaxException;
032: import com.caucho.util.CharBuffer;
033: import com.caucho.util.L10N;
034: import com.caucho.xml.QName;
035:
036: import java.util.ArrayList;
037: import java.util.HashSet;
038: import java.util.Iterator;
039: import java.util.NoSuchElementException;
040:
041: /**
042: * Generates programs from patterns.
043: */
044: public class GroupItem extends Item {
045: protected final static L10N L = new L10N(GroupItem.class);
046:
047: private Item _first;
048: private Item _second;
049:
050: private GroupItem(Item first, Item second) {
051: _first = first;
052: _second = second;
053: }
054:
055: public static Item create(Item first, Item second) {
056: if (first == null || second == null)
057: return null;
058: else if (first instanceof EmptyItem)
059: return second;
060: else if (second instanceof EmptyItem)
061: return first;
062: else if (first instanceof GroupItem) {
063: GroupItem firstSeq = (GroupItem) first;
064:
065: return create(firstSeq.getFirst(), create(firstSeq
066: .getSecond(), second));
067: } else if (first instanceof InElementItem) {
068: InElementItem firstElt = (InElementItem) first;
069:
070: return InElementItem.create(firstElt.getFirst(), create(
071: firstElt.getSecond(), second));
072: } else
073: return new GroupItem(first, second);
074: }
075:
076: Item getFirst() {
077: return _first;
078: }
079:
080: Item getSecond() {
081: return _second;
082: }
083:
084: /**
085: * Returns the first set, the set of element names possible.
086: */
087: public void firstSet(HashSet<QName> set) {
088: _first.firstSet(set);
089: if (_first.allowEmpty())
090: _second.firstSet(set);
091: }
092:
093: /**
094: * Adds to the first set the set of element names required.
095: */
096: public void requiredFirstSet(HashSet<QName> set) {
097: if (!_first.allowEmpty())
098: _first.requiredFirstSet(set);
099: else
100: _second.requiredFirstSet(set);
101: }
102:
103: /**
104: * Return all possible child items or null
105: */
106: public Iterator<Item> getItemsIterator() {
107: if (_first == null && _second == null)
108: return emptyItemIterator();
109:
110: return new Iterator<Item>() {
111: private int _cnt;
112:
113: public boolean hasNext() {
114: if (_cnt == 0)
115: return _first != null || _second != null;
116: else if (_cnt == 1)
117: return _first != null && _second != null;
118: else
119: return false;
120: }
121:
122: public Item next() {
123: if (!hasNext())
124: throw new NoSuchElementException();
125:
126: if (_cnt++ == 0)
127: return _first != null ? _first : _second;
128: else
129: return _second;
130: }
131:
132: public void remove() {
133: throw new UnsupportedOperationException();
134: }
135: };
136: }
137:
138: /**
139: * Returns the next item when an element of the given name is returned
140: *
141: * @param name the name of the element
142: * @param contItem the continuation item
143: *
144: * @return the program for handling the element
145: */
146: public Item startElement(QName name) throws RelaxException {
147: Item nextHead = _first.startElement(name);
148:
149: Item tail = GroupItem.create(nextHead, _second);
150:
151: if (_first.allowEmpty())
152: return ChoiceItem.create(tail, _second.startElement(name));
153: else
154: return tail;
155: }
156:
157: /**
158: * Returns the next item when some text data is available.
159: *
160: * @param string the text data
161: *
162: * @return the program for handling the element
163: */
164: @Override
165: public Item text(CharSequence string) throws RelaxException {
166: Item nextHead = _first.text(string);
167:
168: Item tail = GroupItem.create(nextHead, _second);
169:
170: if (_first.allowEmpty())
171: return ChoiceItem.create(tail, _second.text(string));
172: else
173: return tail;
174: }
175:
176: /**
177: * Returns the attribute set, the set of attribute names possible.
178: */
179: public void attributeSet(HashSet<QName> set) {
180: _first.attributeSet(set);
181: _second.attributeSet(set);
182: }
183:
184: /**
185: * Returns true if the attribute is allowed.
186: *
187: * @param name the name of the attribute
188: * @param value the value of the attribute
189: *
190: * @return true if the attribute is allowed
191: */
192: public boolean allowAttribute(QName name, String value)
193: throws RelaxException {
194: return (_first.allowAttribute(name, value) || _second
195: .allowAttribute(name, value));
196: }
197:
198: /**
199: * Sets an attribute.
200: *
201: * @param name the name of the attribute
202: * @param value the value of the attribute
203: *
204: * @return the program for handling the element
205: */
206: public Item setAttribute(QName name, String value)
207: throws RelaxException {
208: Item first = _first.setAttribute(name, value);
209: Item second = _second.setAttribute(name, value);
210:
211: if (first == _first && second == _second)
212: return this ;
213: else if (first == null)
214: return second;
215: else if (second == null)
216: return first;
217: else
218: return create(first, second);
219: }
220:
221: /**
222: * Returns the next item after the attributes end.
223: */
224: public Item attributeEnd() {
225: Item first = _first.attributeEnd();
226: Item second = _second.attributeEnd();
227:
228: if (first == null || second == null)
229: return null;
230: else if (first == _first && second == _second)
231: return this ;
232: else
233: return create(first, second);
234: }
235:
236: /**
237: * Allows empty if both allow empty.
238: */
239: public boolean allowEmpty() {
240: return _first.allowEmpty() && _second.allowEmpty();
241: }
242:
243: /**
244: * Returns true if the element is allowed somewhere in the item.
245: * allowsElement is used for error messages to give more information
246: * in cases of order dependency.
247: *
248: * @param name the name of the element
249: *
250: * @return true if the element is allowed somewhere
251: */
252: public boolean allowsElement(QName name) {
253: return _first.allowsElement(name)
254: || _second.allowsElement(name);
255: }
256:
257: /**
258: * Returns the pretty printed syntax.
259: */
260: public String toSyntaxDescription(int depth) {
261: if (_second instanceof EmptyItem)
262: return _first.toSyntaxDescription(depth);
263:
264: ArrayList<Item> items = new ArrayList<Item>();
265:
266: Item item = this ;
267: while (item instanceof GroupItem) {
268: GroupItem groupItem = (GroupItem) item;
269:
270: items.add(groupItem._first);
271:
272: item = groupItem._second;
273: }
274:
275: if (item != null && !(item instanceof EmptyItem))
276: items.add(item);
277:
278: CharBuffer cb = CharBuffer.allocate();
279:
280: cb.append('(');
281:
282: boolean isSimple = true;
283: for (int i = 0; i < items.size(); i++) {
284: item = items.get(i);
285:
286: if (i == 0) {
287: cb.append(item.toSyntaxDescription(depth + 1));
288: isSimple = item.isSimpleSyntax();
289: } else
290: isSimple = addSyntaxItem(cb, item, depth, isSimple);
291:
292: if (i + 1 < items.size()) {
293: Item next = items.get(i + 1);
294:
295: if (next instanceof ZeroOrMoreItem) {
296: ZeroOrMoreItem starItem = (ZeroOrMoreItem) next;
297:
298: if (starItem.getItem().equals(item)) {
299: cb.append("+");
300: i++;
301:
302: if (i == 1 && i == items.size() - 1) {
303: cb.delete(0, 1);
304:
305: return cb.close();
306: }
307: }
308: }
309: }
310: }
311:
312: cb.append(')');
313:
314: return cb.close();
315: }
316:
317: /**
318: * Adds an item to the description.
319: */
320: private boolean addSyntaxItem(CharBuffer cb, Item item, int depth,
321: boolean isSimple) {
322: if (!item.isSimpleSyntax())
323: isSimple = false;
324:
325: if (isSimple) {
326: cb.append(", ");
327: } else {
328: cb.append(",");
329: addSyntaxNewline(cb, depth + 1);
330: }
331:
332: cb.append(item.toSyntaxDescription(depth + 1));
333:
334: return isSimple;
335: }
336:
337: /**
338: * Returns true for an element with simple syntax.
339: */
340: protected boolean isSimpleSyntax() {
341: return (_second instanceof EmptyItem)
342: && _first.isSimpleSyntax();
343: }
344:
345: /**
346: * Returns the hash code for the empty item.
347: */
348: public int hashCode() {
349: return _first.hashCode() * 65521 + _second.hashCode();
350: }
351:
352: /**
353: * Returns true if the object is an empty item.
354: */
355: public boolean equals(Object o) {
356: if (!(o instanceof GroupItem))
357: return false;
358:
359: GroupItem seq = (GroupItem) o;
360:
361: return _first.equals(seq._first) && _second.equals(seq._second);
362: }
363:
364: public String toString() {
365: return "GroupItem[" + _first + ", " + _second + "]";
366: }
367: }
|