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:
040: /**
041: * Generates programs from patterns.
042: */
043: public class InterleaveItem extends Item {
044: protected final static L10N L = new L10N(InterleaveItem.class);
045:
046: private ArrayList<Item> _items = new ArrayList<Item>();
047:
048: public InterleaveItem() {
049: }
050:
051: public static Item create(Item left, Item right) {
052: InterleaveItem item = new InterleaveItem();
053:
054: item.addItem(left);
055: item.addItem(right);
056:
057: return item.getMin();
058: }
059:
060: public void addItem(Item item) {
061: if (item == null)
062: return;
063: else if (item instanceof EmptyItem) {
064: return;
065: } else if (item instanceof InterleaveItem) {
066: InterleaveItem interleave = (InterleaveItem) item;
067:
068: for (int i = 0; i < interleave._items.size(); i++)
069: addItem(interleave._items.get(i));
070:
071: return;
072: }
073:
074: /* XXX: remove for perf?
075: for (int i = 0; i < _items.size(); i++) {
076: Item subItem = _items.get(i);
077:
078: if (item instanceof InElementItem &&
079: subItem instanceof InElementItem) {
080: InElementItem elt1 = (InElementItem) item;
081: InElementItem elt2 = (InElementItem) subItem;
082:
083: if (elt1.getElementItem().equals(elt2.getElementItem())) {
084: subItem = InElementItem.create(elt1.getElementItem(),
085: create(elt1.getContinuationItem(),
086: elt2.getContinuationItem()));
087: _items.remove(i);
088: addItem(subItem);
089: return;
090: }
091: }
092:
093: if (item instanceof GroupItem &&
094: subItem instanceof GroupItem) {
095: GroupItem group1 = (GroupItem) item;
096: GroupItem group2 = (GroupItem) subItem;
097:
098: if (group1.getFirst().equals(group2.getFirst())) {
099: subItem = GroupItem.create(group1.getFirst(),
100: create(group1.getSecond(),
101: group2.getSecond()));
102: _items.remove(i);
103: addItem(subItem);
104: return;
105: }
106: }
107: }
108: */
109:
110: _items.add(item);
111: }
112:
113: public Item getMin() {
114: if (_items.size() == 0)
115: return null;
116: else if (_items.size() == 1)
117: return _items.get(0);
118: else
119: return this ;
120: }
121:
122: /**
123: * Returns the first set, the set of element names possible.
124: */
125: public void firstSet(HashSet<QName> set) {
126: for (int i = 0; i < _items.size(); i++)
127: _items.get(i).firstSet(set);
128: }
129:
130: /**
131: * Returns the first set, the set of element names possible.
132: */
133: public void requiredFirstSet(HashSet<QName> set) {
134: if (allowEmpty())
135: return;
136:
137: for (int i = 0; i < _items.size(); i++)
138: _items.get(i).requiredFirstSet(set);
139: }
140:
141: /**
142: * Only allow empty if all allow empty.
143: */
144: public boolean allowEmpty() {
145: for (int i = 0; i < _items.size(); i++) {
146: if (!_items.get(i).allowEmpty())
147: return false;
148: }
149:
150: return true;
151: }
152:
153: /**
154: * Interleaves a continuation.
155: */
156: public Item interleaveContinuation(Item cont) {
157: InterleaveItem item = new InterleaveItem();
158:
159: for (int i = 0; i < _items.size(); i++)
160: item.addItem(_items.get(i).interleaveContinuation(cont));
161:
162: return item.getMin();
163: }
164:
165: /**
166: * Adds an inElement continuation.
167: */
168: public Item inElementContinuation(Item cont) {
169: InterleaveItem item = new InterleaveItem();
170:
171: for (int i = 0; i < _items.size(); i++)
172: item.addItem(_items.get(i).inElementContinuation(cont));
173:
174: return item.getMin();
175: }
176:
177: /**
178: * Adds a group continuation.
179: */
180: public Item groupContinuation(Item cont) {
181: InterleaveItem item = new InterleaveItem();
182:
183: for (int i = 0; i < _items.size(); i++)
184: item.addItem(_items.get(i).groupContinuation(cont));
185:
186: return item.getMin();
187: }
188:
189: /**
190: * Return all possible child items or null
191: */
192: public Iterator<Item> getItemsIterator() {
193: if (_items.size() == 0)
194: return emptyItemIterator();
195: else
196: return _items.iterator();
197: }
198:
199: /**
200: * Returns the next item on the match.
201: */
202: public Item startElement(QName name) throws RelaxException {
203: Item result = null;
204: ChoiceItem choice = null;
205:
206: for (int i = 0; i < _items.size(); i++) {
207: Item item = _items.get(i);
208:
209: Item nextItem = item.startElement(name);
210:
211: if (nextItem == null)
212: continue;
213:
214: Item resultItem;
215:
216: if (nextItem == item)
217: resultItem = this ;
218: else {
219: InterleaveItem rest = new InterleaveItem();
220: for (int j = 0; j < _items.size(); j++) {
221: if (i != j)
222: rest.addItem(_items.get(j));
223: }
224:
225: resultItem = nextItem.interleaveContinuation(rest);
226: }
227:
228: if (result == null)
229: result = resultItem;
230: else {
231: if (choice == null) {
232: choice = new ChoiceItem();
233: choice.addItem(result);
234: }
235: choice.addItem(resultItem);
236: }
237: }
238:
239: if (choice != null)
240: return choice.getMin();
241: else
242: return result;
243: }
244:
245: /**
246: * Returns true if the attribute is allowed.
247: *
248: * @param name the name of the attribute
249: * @param value the value of the attribute
250: *
251: * @return true if the attribute is allowed
252: */
253: public boolean allowAttribute(QName name, String value)
254: throws RelaxException {
255: for (int i = _items.size() - 1; i >= 0; i--)
256: if (_items.get(i).allowAttribute(name, value))
257: return true;
258:
259: return false;
260: }
261:
262: /**
263: * Returns the first set, the set of attribute names possible.
264: */
265: public void attributeSet(HashSet<QName> set) {
266: for (int i = 0; i < _items.size(); i++)
267: _items.get(i).attributeSet(set);
268: }
269:
270: /**
271: * Sets an attribute.
272: *
273: * @param name the name of the attribute
274: * @param value the value of the attribute
275: *
276: * @return the program for handling the element
277: */
278: public Item setAttribute(QName name, String value)
279: throws RelaxException {
280: if (!allowAttribute(name, value))
281: return this ;
282:
283: InterleaveItem interleave = new InterleaveItem();
284:
285: for (int i = _items.size() - 1; i >= 0; i--) {
286: Item next = _items.get(i).setAttribute(name, value);
287:
288: if (next != null)
289: interleave.addItem(next);
290: }
291:
292: return interleave.getMin();
293: }
294:
295: /**
296: * Returns true if the item can match empty.
297: */
298: public Item attributeEnd() {
299: InterleaveItem interleave = new InterleaveItem();
300:
301: for (int i = _items.size() - 1; i >= 0; i--) {
302: Item next = _items.get(i).attributeEnd();
303:
304: if (next == null)
305: return null;
306:
307: interleave.addItem(next);
308: }
309:
310: if (interleave.equals(this ))
311: return this ;
312: else
313: return interleave.getMin();
314: }
315:
316: /**
317: * Returns the next item on some text
318: */
319: @Override
320: public Item text(CharSequence string) throws RelaxException {
321: Item result = null;
322: ChoiceItem choice = null;
323:
324: for (int i = 0; i < _items.size(); i++) {
325: Item item = _items.get(i);
326:
327: Item nextItem = item.text(string);
328:
329: if (nextItem == null)
330: continue;
331:
332: Item resultItem;
333:
334: if (nextItem == item)
335: resultItem = this ;
336: else {
337: InterleaveItem rest = new InterleaveItem();
338: for (int j = 0; j < _items.size(); j++) {
339: if (i != j)
340: rest.addItem(_items.get(j));
341: }
342:
343: resultItem = nextItem.interleaveContinuation(rest);
344: }
345:
346: if (result == null)
347: result = resultItem;
348: else {
349: if (choice == null) {
350: choice = new ChoiceItem();
351: choice.addItem(result);
352: }
353: choice.addItem(resultItem);
354: }
355: }
356:
357: if (choice != null)
358: return choice.getMin();
359: else
360: return result;
361: }
362:
363: /**
364: * Returns true if the element is allowed somewhere in the item.
365: * allowsElement is used for error messages to give more information
366: * in cases of order dependency.
367: *
368: * @param name the name of the element
369: *
370: * @return true if the element is allowed somewhere
371: */
372: public boolean allowsElement(QName name) {
373: for (int i = 0; i < _items.size(); i++) {
374: Item subItem = _items.get(i);
375:
376: if (subItem.allowsElement(name))
377: return true;
378: }
379:
380: return false;
381: }
382:
383: /**
384: * Returns the pretty printed syntax.
385: */
386: public String toSyntaxDescription(int depth) {
387: if (_items.size() == 1)
388: return _items.get(0).toSyntaxDescription(depth);
389:
390: CharBuffer cb = CharBuffer.allocate();
391:
392: cb.append("(");
393:
394: boolean isSimple = true;
395: for (int i = 0; i < _items.size(); i++) {
396: Item item = _items.get(i);
397:
398: if (!item.isSimpleSyntax())
399: isSimple = false;
400:
401: if (i == 0) {
402: if (!isSimple)
403: cb.append(" ");
404: } else if (isSimple) {
405: cb.append(" & ");
406: } else {
407: addSyntaxNewline(cb, depth);
408: cb.append("& ");
409: }
410:
411: cb.append(item.toSyntaxDescription(depth + 2));
412: }
413:
414: cb.append(')');
415:
416: return cb.close();
417: }
418:
419: /**
420: * Returns the hash code for the empty item.
421: */
422: public int hashCode() {
423: int hash = 37;
424:
425: for (int i = 0; i < _items.size(); i++)
426: hash += _items.get(i).hashCode();
427:
428: return hash;
429: }
430:
431: /**
432: * Returns true if the object is an empty item.
433: */
434: public boolean equals(Object o) {
435: if (this == o)
436: return true;
437:
438: if (!(o instanceof InterleaveItem))
439: return false;
440:
441: InterleaveItem interleave = (InterleaveItem) o;
442:
443: return isSubset(interleave) && interleave.isSubset(this );
444: }
445:
446: private boolean isSubset(InterleaveItem item) {
447: if (_items.size() != item._items.size())
448: return false;
449:
450: for (int i = 0; i < _items.size(); i++) {
451: Item subItem = _items.get(i);
452:
453: if (!item._items.contains(subItem))
454: return false;
455: }
456:
457: return true;
458: }
459: }
|