View Javadoc

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