2727import java .util .Map ;
2828import java .util .Scanner ;
2929import java .util .Set ;
30+ import java .util .Stack ;
3031import java .util .regex .Pattern ;
3132
3233/**
7778 * @since 2.0
7879 */
7980public class Rule {
80- private static final String DOUBLE_QUOTE = "\" " ;
81-
8281 public static final String ALL = "ALL" ;
8382
83+ private static final String DOUBLE_QUOTE = "\" " ;
84+
8485 private static final String HASH_INCLUDE = "#include" ;
8586
8687 private static final Map <NameType , Map <RuleType , Map <String , List <Rule >>>> RULES = new EnumMap <NameType , Map <RuleType , Map <String , List <Rule >>>>(
@@ -95,10 +96,14 @@ public class Rule {
9596
9697 Languages ls = Languages .instance (s );
9798 for (String l : ls .getLanguages ()) {
98- rs .put (l , parseRules (mkScanner (s , rt , l )));
99+ try {
100+ rs .put (l , parseRules (createScanner (s , rt , l )));
101+ } catch (IllegalStateException e ) {
102+ throw new IllegalStateException ("Problem processing " + createResourceName (s , rt , l ), e );
103+ }
99104 }
100105 if (!rt .equals (RuleType .RULES )) {
101- rs .put ("common" , parseRules (mkScanner (s , rt , "common" )));
106+ rs .put ("common" , parseRules (createScanner (s , rt , "common" )));
102107 }
103108
104109 rts .put (rt , Collections .unmodifiableMap (rs ));
@@ -108,6 +113,32 @@ public class Rule {
108113 }
109114 }
110115
116+ private static String createResourceName (NameType nameType , RuleType rt , String lang ) {
117+ return String .format ("org/apache/commons/codec/language/bm/%s_%s_%s.txt" , nameType .getName (), rt .getName (), lang );
118+ }
119+
120+ private static Scanner createScanner (NameType nameType , RuleType rt , String lang ) {
121+ String resName = createResourceName (nameType , rt , lang );
122+ InputStream rulesIS = Languages .class .getClassLoader ().getResourceAsStream (resName );
123+
124+ if (rulesIS == null ) {
125+ throw new IllegalArgumentException ("Unable to load resource: " + resName );
126+ }
127+
128+ return new Scanner (rulesIS , ResourceConstants .ENCODING );
129+ }
130+
131+ private static Scanner createScanner (String lang ) {
132+ String resName = String .format ("org/apache/commons/codec/language/bm/%s.txt" , lang );
133+ InputStream rulesIS = Languages .class .getClassLoader ().getResourceAsStream (resName );
134+
135+ if (rulesIS == null ) {
136+ throw new IllegalArgumentException ("Unable to load resource: " + resName );
137+ }
138+
139+ return new Scanner (rulesIS , ResourceConstants .ENCODING );
140+ }
141+
111142 /**
112143 * Gets rules for a combination of name type, rule type and languages.
113144 *
@@ -148,33 +179,13 @@ public static List<Rule> instance(NameType nameType, RuleType rt, String lang) {
148179 return rules ;
149180 }
150181
151- private static Scanner mkScanner (NameType nameType , RuleType rt , String lang ) {
152- String resName = String .format ("org/apache/commons/codec/language/bm/%s_%s_%s.txt" , nameType .getName (), rt .getName (), lang );
153- InputStream rulesIS = Languages .class .getClassLoader ().getResourceAsStream (resName );
154-
155- if (rulesIS == null ) {
156- throw new IllegalArgumentException ("Unable to load resource: " + resName );
157- }
158-
159- return new Scanner (rulesIS , ResourceConstants .ENCODING );
160- }
161-
162- private static Scanner mkScanner (String lang ) {
163- String resName = String .format ("org/apache/commons/codec/language/bm/%s.txt" , lang );
164- InputStream rulesIS = Languages .class .getClassLoader ().getResourceAsStream (resName );
165-
166- if (rulesIS == null ) {
167- throw new IllegalArgumentException ("Unable to load resource: " + resName );
168- }
169-
170- return new Scanner (rulesIS , ResourceConstants .ENCODING );
171- }
172-
173182 private static List <Rule > parseRules (Scanner scanner ) {
174183 List <Rule > lines = new ArrayList <Rule >();
184+ int currentLine = 0 ;
175185
176186 boolean inMultilineComment = false ;
177187 while (scanner .hasNextLine ()) {
188+ currentLine ++;
178189 String rawLine = scanner .nextLine ();
179190 String line = rawLine ;
180191
@@ -206,7 +217,7 @@ private static List<Rule> parseRules(Scanner scanner) {
206217 if (incl .contains (" " )) {
207218 System .err .println ("Warining: malformed import statement: " + rawLine );
208219 } else {
209- lines .addAll (parseRules (mkScanner (incl )));
220+ lines .addAll (parseRules (createScanner (incl )));
210221 }
211222 } else {
212223 // rule
@@ -218,6 +229,11 @@ private static List<Rule> parseRules(Scanner scanner) {
218229 String lCon = stripQuotes (parts [1 ]);
219230 String rCon = stripQuotes (parts [2 ]);
220231 String ph = stripQuotes (parts [3 ]);
232+ try {
233+ validatePhenome (ph );
234+ } catch (IllegalArgumentException e ) {
235+ throw new IllegalStateException ("Problem parsing line " + currentLine , e );
236+ }
221237 Rule r = new Rule (pat , lCon , rCon , ph , Collections .<String > emptySet (), "" ); // guessing last 2 parameters
222238 lines .add (r );
223239 }
@@ -241,6 +257,40 @@ private static String stripQuotes(String str) {
241257 return str ;
242258 }
243259
260+ private static void validatePhenome (CharSequence ph ) {
261+ Stack <Character > stack = new Stack <Character >();
262+ for (int i = 0 ; i < ph .length (); i ++) {
263+ switch (ph .charAt (i )) {
264+ case '(' :
265+ stack .push ('(' );
266+ break ;
267+ case '[' :
268+ stack .push ('[' );
269+ break ;
270+ case ')' : {
271+ if (stack .isEmpty ())
272+ throw new IllegalArgumentException ("Closing ')' at " + i + " without an opening '('" + " in " + ph );
273+ char c = stack .pop ();
274+ if (c != '(' )
275+ throw new IllegalArgumentException ("Closing ')' does not pair with opening '" + c + "' at " + i + " in " + ph );
276+ break ;
277+ }
278+ case ']' : {
279+ if (stack .isEmpty ())
280+ throw new IllegalArgumentException ("Closing ']' at " + i + " without an opening '['" + " in " + ph );
281+ char c = stack .pop ();
282+ if (c != '[' )
283+ throw new IllegalArgumentException ("Closing ']' does not pair with opening '" + c + "' at " + i + " in " + ph );
284+ break ;
285+ }
286+ default :
287+ break ;
288+ }
289+ }
290+ if (!stack .isEmpty ())
291+ throw new IllegalArgumentException ("Bracket(s) opened without corresponding closes: " + stack + " in " + ph );
292+ }
293+
244294 private final Set <String > languages ;
245295
246296 private final Pattern lContext ;
0 commit comments