View Javadoc

1   package io.github.reggert.reb4j;
2   
3   import fj.F2;
4   import fj.data.LazyString;
5   import fj.data.List;
6   
7   /**
8    * Expression representing a set of alternatives that may be matched.
9    */
10  public final class Alternation extends AbstractExpression
11  	implements Alternative
12  {
13  	private static final long serialVersionUID = 1L;
14  	public final List<Alternative> alternatives;
15  	
16  	private Alternation(final List<Alternative> alternatives)
17  	{
18  		if (alternatives == null) throw new NullPointerException("alternatives");
19  		this.alternatives = alternatives;
20  	}
21  	
22  	/**
23  	 * Constructs a new alternation representing the union of two existing
24  	 * alternations.
25  	 * 
26  	 * @param left
27  	 * 	an alternation; must not be <code>null</code>.
28  	 * @param right
29  	 *  an alternation; must not be <code>null</code>.
30  	 * @throws NullPointerException
31  	 * 	if either argument is <code>null</code>.
32  	 */
33  	Alternation(final Alternation left, final Alternation right)
34  	{
35  		if (left == null) throw new NullPointerException("left");
36  		if (right == null) throw new NullPointerException("right");
37  		this.alternatives = left.alternatives.append(right.alternatives);
38  	}
39  	
40  	/**
41  	 * Constructs a new alternation representing the union of an existing
42  	 * alternation and an alternative to be appended to the end.
43  	 * 
44  	 * @param left
45  	 * 	an alternation; must not be <code>null</code>.
46  	 * @param right
47  	 *  an alternative to be appended; must not be <code>null</code>.
48  	 * @throws NullPointerException
49  	 * 	if either argument is <code>null</code>.
50  	 */
51  	Alternation(final Alternation left, final Alternative right)
52  	{
53  		if (left == null) throw new NullPointerException("left");
54  		if (right == null) throw new NullPointerException("right");
55  		this.alternatives = left.alternatives.append(List.single(right));
56  	}
57  	
58  	/**
59  	 * Constructs a new alternation representing the union of an existing
60  	 * alternation and an alternative to be prepended to the beginning.
61  	 * 
62  	 * @param left
63  	 * 	an alternative to be prepended; must not be <code>null</code>.
64  	 * @param right
65  	 *  an alternation; must not be <code>null</code>.
66  	 * @throws NullPointerException
67  	 * 	if either argument is <code>null</code>.
68  	 */
69  	Alternation(final Alternative left, final Alternation right)
70  	{
71  		if (left == null) throw new NullPointerException("left");
72  		if (right == null) throw new NullPointerException("right");
73  		this.alternatives = right.alternatives.cons(left);
74  	}
75  	
76  	/**
77  	 * Constructs a new alternation that may match either of two alternatives.
78  	 * 
79  	 * @param left
80  	 * 	the first alternative; must not be <code>null</code>.
81  	 * @param right
82  	 *  the second alternative; must not be <code>null</code>.
83  	 * @throws NullPointerException
84  	 * 	if either argument is <code>null</code>.
85  	 */
86  	Alternation(final Alternative left, final Alternative right)
87  	{
88  		if (left == null) throw new NullPointerException("left");
89  		if (right == null) throw new NullPointerException("right");
90  		this.alternatives = List.list(left, right);
91  	}
92  	
93  	/**
94  	 * Constructs a new alternation that may match any of several alternatives.
95  	 * 
96  	 * @param first
97  	 * 	the first alternative; must not <code>null</code>.
98  	 * @param second
99  	 * 	the second alternative; must not <code>null</code>.
100 	 * @param rest
101 	 * 	the remaining alternatives; must not <code>null</code>.
102 	 * @return a new Alternation.
103 	 * @throws NullPointerException
104 	 * 	if any argument is <code>null</code>.
105 	 */
106 	public static Alternation alternatives(
107 			final Alternative first, 
108 			final Alternative second, 
109 			final Alternative... rest
110 		)
111 	{
112 		if (first == null) throw new NullPointerException("first");
113 		if (second == null) throw new NullPointerException("second");
114 		if (rest == null) throw new NullPointerException("rest");
115 		return new Alternation(List.list(rest).cons(second).cons(first));
116 	}
117 	
118 	@Override
119 	public LazyString expression() 
120 	{
121 		return alternatives.tail().foldLeft(
122 				new F2<LazyString, Alternative, LazyString>() 
123 				{
124 					@Override
125 					public LazyString f(final LazyString a, final Alternative b)
126 					{return a.append("|").append(b.expression());}
127 				}, 
128 				alternatives.head().expression()
129 			);
130 	}
131 
132 	@Override
133 	public Alternation or(final Alternation right) 
134 	{return new Alternation(this, right);}
135 
136 	@Override
137 	public Alternation or(final Alternative right) 
138 	{return new Alternation(this, right);}
139 	
140 	@Override
141 	public int hashCode()
142 	{
143 		final int prime = 31;
144 		int result = 1;
145 		result = prime * result + alternatives.hashCode();
146 		return result;
147 	}
148 
149 	@Override
150 	public boolean equals(final Object obj)
151 	{
152 		if (this == obj)
153 			return true;
154 		if (obj == null)
155 			return false;
156 		if (getClass() != obj.getClass())
157 			return false;
158 		final Alternation other = (Alternation) obj;
159 		return alternatives.equals(other.alternatives);
160 	}
161 
162 	@Override
163 	public Integer boundedLength() 
164 	{
165 		int maximumLength = 0;
166 		for (final Alternative alternative : alternatives)
167 		{
168 			final Integer alternativeLength = alternative.boundedLength();
169 			if (alternativeLength == null)
170 				return null;
171 			if (alternativeLength > maximumLength)
172 				maximumLength = alternativeLength;
173 		}
174 		return maximumLength;
175 	}
176 
177 	@Override
178 	public boolean repetitionInvalidatesBounds() 
179 	{
180 		return true;
181 	}
182 
183 	@Override
184 	public boolean possiblyZeroLength() 
185 	{
186 		for (final Alternative a : alternatives)
187 			if (a.possiblyZeroLength())
188 				return true;
189 		return false;
190 	}
191 }
192