/*
* xtc - The eXTensible Compiler
* Copyright (C) 2004-2007 Robert Grimm
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.parser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* A sequence of grammar elements.
*
* <p />A sequence may have an optional name. However, this name is
* not considered when comparing sequences for equality.
*
* @author Robert Grimm
* @version $Revision: 1.30 $
*/
public class Sequence extends Element {
/** The optional name. */
public SequenceName name;
/** The ordered list of grammar elements. */
public List<Element> elements;
/** Create a new, empty sequence. */
public Sequence() {
this(null, new ArrayList<Element>());
}
/**
* Create a new, empty sequence with the specified capacity.
*
* @param capacity The capacity.
*/
public Sequence(int capacity) {
this(null, new ArrayList<Element>(capacity));
}
/**
* Create a new sequence.
*
* @param elements The list of elements.
*/
public Sequence(final List<Element> elements) {
this(null, elements);
}
/**
* Create a new sequence.
*
* @param name The name.
* @param elements The list of elements.
*/
public Sequence(final SequenceName name, final List<Element> elements) {
this.name = name;
this.elements = elements;
}
/**
* Create a new sequence with the specified element. If the element
* is another sequence, the new sequence has the same elements but
* is a copy. Otherwise, the new sequence has the specified element
* as its only element. In either case, the new sequence has the
* element's location.
*
* @param element The element.
*/
public Sequence(final Element element) {
if (element instanceof Sequence) {
Sequence s = (Sequence)element;
elements = new ArrayList<Element>(s.elements);
name = s.name;
} else {
elements = new ArrayList<Element>(1);
elements.add(element);
}
setLocation(element);
}
public Tag tag() {
return Tag.SEQUENCE;
}
/**
* Remove all elements from this sequence.
*
* @return This sequence.
*/
public Sequence clear() {
elements.clear();
return this;
}
/**
* Add the specified element to this sequence.
*
* @param e The element to add.
* @return This sequence.
*/
public Sequence add(Element e) {
elements.add(e);
return this;
}
/**
* Add all elements in the specified list to this sequence.
*
* @param l The list of elements.
* @return This sequence.
*/
public Sequence addAll(List<Element> l) {
elements.addAll(l);
return this;
}
/**
* Determine whether this sequence is empty.
*
* @return <code>true</code> if this is an empty sequence.
*/
public boolean isEmpty() {
return elements.isEmpty();
}
/**
* Get the size of this sequence.
*
* @return The size.
*/
public int size() {
return elements.size();
}
/**
* Get the element at the specified index.
*
* @param idx The index.
* @return The element at that position.
* @throws IndexOutOfBoundsException
* Signals that the index is out of range.
*/
public Element get(final int idx) {
return elements.get(idx);
}
/**
* Determine whether this sequence's last element is an ordered
* choice.
*
* @return <code>true</code> if the last element is a choice.
*/
public boolean hasTrailingChoice() {
final int size = elements.size();
return (0 < size) && (elements.get(size-1) instanceof OrderedChoice);
}
/**
* Create a new subsequence from the specified start index. The new
* subsequence ends with the last element of this sequence.
*
* @param start The inclusive start index.
* @return The subsequence.
* @throws IndexOutOfBoundsException
* Signals that the index is out of range.
*/
public Sequence subSequence(final int start) {
return subSequence(start, elements.size());
}
/**
* Create a new subsequence for the specified indices.
*
* @param start The inclusive start index.
* @param end The exclusive end index.
* @return The subsequence.
* @throws IndexOutOfBoundsException
* Signals that the index is out of range.
*/
public Sequence subSequence(final int start, final int end) {
// The subsequence has the original's source location.
Sequence s =
new Sequence(new ArrayList<Element>(elements.subList(start, end)));
s.setLocation(this);
return s;
}
public int hashCode() {
return elements.hashCode();
}
public boolean equals(final Object o) {
if (this == o) return true;
if (! (o instanceof Sequence)) return false;
return elements.equals(((Sequence)o).elements);
}
public void write(Appendable out) throws IOException {
out.append('(');
boolean first = true;
for (Element e : elements) {
if (first) {
first = false;
} else {
out.append(' ');
}
e.write(out);
}
out.append(')');
}
/**
* Ensure that the specified element is a sequence. If the specified
* element is not a sequence, a new sequence with the specified
* element as its only element is returned; the newly created
* sequence has the element's location.
*
* @param e The element.
* @return The element in/as a sequence.
*/
public static Sequence ensure(final Element e) {
if (e instanceof Sequence) {
return (Sequence)e;
} else {
Sequence s = new Sequence(e);
s.setLocation(e);
return s;
}
}
}