1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.millscript.millscript.datatypes;
23
24 import org.millscript.commons.util.list.IArrayList;
25 import org.millscript.millscript.alert.Alerts;
26
27 import java.io.Serializable;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 /**
32 * This class represents a MillScript Binding, which itself reprsents the set
33 * of match variables for a given regular expression and string.
34 */
35 public final class Binding extends IArrayList< String > implements Serializable {
36
37 /**
38 * This is the ID from the release 10.2.0 for future compatibility.
39 */
40 private static final long serialVersionUID = 3690755102965314870L;
41
42 /**
43 * Returns a new binding for the specified matcher.
44 *
45 * @param s the original string that was matched against
46 * @param m the matcher to make a binding from
47 */
48 public static final Binding newBinding( final String s, final Matcher m ) {
49
50 final String[] matchVars = new String[ m.groupCount() ];
51
52 final int[] starts = new int[ m.groupCount() + 1 ];
53
54 final int[] ends = new int[ m.groupCount() + 1 ];
55
56
57
58
59 starts[ 0 ] = m.start( 0 ) + 1;
60 ends[ 0 ] = m.end( 0 );
61 for ( int i = 1; i <= m.groupCount(); i++ ) {
62 starts[ i ] = m.start( i ) + 1;
63 ends[ i ] = m.end( i );
64 matchVars[ i - 1 ] = s.substring( m.start( i ), m.end( i ) );
65 }
66 return new Binding( m.group(), s, m.pattern(), matchVars, starts, ends );
67 }
68
69 /**
70 * The section of the original string matched by the regular expression
71 * used to make this binding.
72 */
73 private final String matchedString;
74
75 /**
76 * The original string the regex was matched against.
77 */
78 private final String originalString;
79
80 /**
81 * The pattern that was interpreted to get this binding.
82 */
83 private final Pattern pattern;
84
85 /**
86 * An array of the match variable start indices. Each element is the index
87 * of the start of the relevant match variable within the original string
88 * we matched against. Note the indices are 1 based, not 0.
89 */
90 private final int[] starts;
91
92 /**
93 * An array of the match variable end indices. Each element is the index
94 * of the end of the relevant match variable within the original string we
95 * matched against. Note the indices are 1 based, not 0.
96 */
97 private final int[] ends;
98
99 /**
100 * Constructs a new binding with the specified match variables. The correct
101 * way to construct a new binding is through the static
102 * {@link #newBinding(String, Matcher)} method.
103 *
104 * @param ms the whole matched string(i.e. usually considered group
105 * zero)
106 * @param os the original string that was matched against
107 * @param p the pattern matched against the original string
108 * @param matches an array of match variable strings(i.e. groups within
109 * the pattern)
110 * @param st an array of match variable string start indicies(one based)
111 * within the original string, the first position in the array should hold
112 * the index for the matched string
113 * @param en an array of match variable string end indicies(one based)
114 * within the original string, the first position in the array should hold
115 * the index for the matched string
116 */
117 private Binding( final String ms, final String os, final Pattern p, final String[] matches, final int[] st, final int[] en ) {
118 super( matches, true );
119 this.matchedString = ms;
120 this.originalString = os;
121 this.pattern = p;
122 this.starts = st;
123 this.ends = en;
124 }
125
126 /**
127 * Returns the section of the original string matched by the regular
128 * expression used to make this binding.
129 *
130 * @return a String which is this bindings section of the original string
131 */
132 public String getMatchedString() {
133 return matchedString;
134 }
135
136 /**
137 * Returns the index in the original string of the last character of the
138 * matched section. Note the index is 1 based, not 0.
139 *
140 * @return the index in the original string of the last character of the
141 * matched section
142 */
143 public Integer getMatchedStringEnd() {
144 return new Integer( this.ends[ 0 ] );
145 }
146
147 /**
148 * Returns the index in the original string of the first character of the
149 * matched section. Note the index is 1 based, not 0.
150 *
151 * @return the index in the original string of the first character of the
152 * matched section
153 */
154 public Integer getMatchedStringStart() {
155 return new Integer( this.starts[ 0 ] );
156 }
157
158 /**
159 * Returns the binding for the specified match variable.
160 *
161 * @param n the match variable to get
162 * @return a String holding the binding for the specified match variable
163 */
164 public String getMatchVar( final int n ) {
165
166 this.validateMatchVarReference( n );
167 return this.originalString.substring( this.starts[n] - 1, this.ends[n] );
168 }
169
170 /**
171 * Returns the index in the original string of the last character of the
172 * specified match variable. Note the index is 1 based, not 0.
173 *
174 * @return the index in the original string of the last character of the
175 * specified match variable
176 */
177 public Integer getMatchVarEnd( final int n ) {
178
179 this.validateMatchVarReference( n );
180 return new Integer( this.ends[n] );
181 }
182
183 /**
184 * Returns the index in the original string of the first character of the
185 * specified match variable. Note the index is 1 based, not 0.
186 *
187 * @return the index in the original string of the first character of the
188 * specified match variable
189 */
190 public Integer getMatchVarStart( final int n ) {
191
192 this.validateMatchVarReference( n );
193 return new Integer( this.starts[n] );
194 }
195
196 /**
197 * Returns the original string the regex was matched against.
198 *
199 * @return the original String the regex was matched against
200 */
201 public String getOriginalString() {
202 return originalString;
203 }
204
205 /**
206 * Returns the pattern used to make this binding.
207 *
208 * @return the Pattern used to make this binding.
209 */
210 public Pattern getPattern() {
211 return this.pattern;
212 }
213
214 /**
215 * Validates the specified integer as a match variable reference,
216 * generating an alert if the refernce is not valid. A valid refernce is a
217 * non-zero positive integer that is less than or equal to the number of
218 * match variables in this binding.
219 *
220 * @param n the match variable reference to validate
221 */
222 public void validateMatchVarReference( final int n ) {
223 if ( n < 1 ) {
224
225 throw Alerts.eval(
226 "Illegal match variable reference",
227 "Match variable references must be non-zero positive integers"
228 ).culprit( "match variable reference", new Integer( n ) ).mishap();
229 } else if ( n > this.size() ) {
230 throw Alerts.eval(
231 "Invalid match variable reference",
232 "There aren't that many match variables in this binding"
233 ).
234 culprit( "match variable reference", new Integer( n ) ).
235 culprit( "match count", this.size() ).
236 mishap();
237 }
238 }
239
240 }