-//##header\r
-//#if defined(FOUNDATION10) || defined(J2SE13) || defined(J2SE14)\r
-//#else\r
-/*\r
- *******************************************************************************\r
- * Copyright (C) 2009, Google, International Business Machines Corporation and *\r
- * others. All Rights Reserved. *\r
- *******************************************************************************\r
- */\r
-package com.ibm.icu.impl;\r
-\r
-import java.io.BufferedReader;\r
-import java.io.FileInputStream;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.InputStreamReader;\r
-import java.io.UnsupportedEncodingException;\r
-import java.text.ParsePosition;\r
-import java.util.Arrays;\r
-import java.util.Comparator;\r
-import java.util.Iterator;\r
-import java.util.LinkedHashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.TreeMap;\r
-import java.util.regex.Pattern;\r
-\r
-import com.ibm.icu.text.StringTransform;\r
-import com.ibm.icu.text.UnicodeSet;\r
-import com.ibm.icu.util.Freezable;\r
-\r
-/**\r
- * Contains utilities to supplement the JDK Regex, since it doesn't handle\r
- * Unicode well.\r
- * \r
- * @author markdavis\r
- */\r
-public class UnicodeRegex implements Cloneable, Freezable, StringTransform {\r
- // Note: we don't currently have any state, but intend to in the future,\r
- // particularly for the regex style supported.\r
-\r
- /**\r
- * Adds full Unicode property support, with the latest version of Unicode,\r
- * to Java Regex, bringing it up to Level 1 (see\r
- * http://www.unicode.org/reports/tr18/). It does this by preprocessing the\r
- * regex pattern string and interpreting the character classes (\p{...},\r
- * \P{...}, [...]) according to their syntax and meaning in UnicodeSet. With\r
- * this utility, Java regex expressions can be updated to work with the\r
- * latest version of Unicode, and with all Unicode properties. Note that the\r
- * UnicodeSet syntax has not yet, however, been updated to be completely\r
- * consistent with Java regex, so be careful of the differences.\r
- * <p>Not thread-safe; create a separate copy for different threads.\r
- * <p>In the future, we may extend this to support other regex packages.\r
- * \r
- * @regex A modified Java regex pattern, as in the input to\r
- * Pattern.compile(), except that all "character classes" are\r
- * processed as if they were UnicodeSet patterns. Example:\r
- * "abc[:bc=N:]. See UnicodeSet for the differences in syntax.\r
- * @return A processed Java regex pattern, suitable for input to\r
- * Pattern.compile().\r
- */\r
- public String transform(String regex) {\r
- StringBuffer result = new StringBuffer();\r
- UnicodeSet temp = new UnicodeSet();\r
- ParsePosition pos = new ParsePosition(0);\r
- int state = 0; // 1 = after \\r
-\r
- // We add each character unmodified to the output, unless we have a\r
- // UnicodeSet. Note that we don't worry about supplementary characters,\r
- // since none of the syntax uses them.\r
-\r
- for (int i = 0; i < regex.length(); ++i) {\r
- // look for UnicodeSets, allowing for quoting with \ and \Q\r
- char ch = regex.charAt(i);\r
- switch (state) {\r
- case 0: // we only care about \, and '['.\r
- if (ch == '\\') {\r
- if (UnicodeSet.resemblesPattern(regex, i)) {\r
- // should only happen with \p\r
- i = processSet(regex, i, result, temp, pos);\r
- continue;\r
- }\r
- state = 1;\r
- } else if (ch == '[') {\r
- // if we have what looks like a UnicodeSet\r
- if (UnicodeSet.resemblesPattern(regex, i)) {\r
- i = processSet(regex, i, result, temp, pos);\r
- continue;\r
- }\r
- }\r
- break;\r
-\r
- case 1: // we are after a \\r
- if (ch == 'Q') {\r
- state = 1;\r
- } else {\r
- state = 0;\r
- }\r
- break;\r
-\r
- case 2: // we are in a \Q...\r
- if (ch == '\\') {\r
- state = 3;\r
- }\r
- break;\r
-\r
- case 3: // we are in at \Q...\\r
- if (ch == 'E') {\r
- state = 0;\r
- }\r
- state = 2;\r
- break;\r
- }\r
- result.append(ch);\r
- }\r
- return result.toString();\r
- }\r
-\r
- /**\r
- * Convenience static function, using standard parameters.\r
- * @param regex as in process()\r
- * @return processed regex pattern, as in process()\r
- */\r
- public static String fix(String regex) {\r
- return STANDARD.transform(regex);\r
- }\r
-\r
- /**\r
- * Compile a regex string, after processing by fix(...).\r
- * \r
- * @param regex\r
- * Raw regex pattern, as in fix(...).\r
- * @return Pattern\r
- */\r
- public static Pattern compile(String regex) {\r
- return Pattern.compile(STANDARD.transform(regex));\r
- }\r
-\r
- /**\r
- * Compile a regex string, after processing by fix(...).\r
- * \r
- * @param regex\r
- * Raw regex pattern, as in fix(...).\r
- * @return Pattern\r
- */\r
- public static Pattern compile(String regex, int options) {\r
- return Pattern.compile(STANDARD.transform(regex), options);\r
- }\r
-\r
- /**\r
- * Compile a composed string from a set of BNF lines; see the List version for more information.\r
- * \r
- * @param bnfLines Series of BNF lines.\r
- * @return Pattern\r
- */\r
- public String compileBnf(String bnfLines) {\r
- return compileBnf(Arrays.asList(bnfLines.split("\\r\\n?|\\n")));\r
- }\r
-\r
- /**\r
- * Compile a composed string from a set of BNF lines, such as for composing a regex\r
- * expression. The lines can be in any order, but there must not be any\r
- * cycles. The result can be used as input for fix().\r
- * <p>\r
- * Example:\r
- * <pre>\r
- * uri = (?: (scheme) \\:)? (host) (?: \\? (query))? (?: \\u0023 (fragment))?;\r
- * scheme = reserved+;\r
- * host = // reserved+;\r
- * query = [\\=reserved]+;\r
- * fragment = reserved+;\r
- * reserved = [[:ascii:][:alphabetic:]];\r
- * </pre>\r
- * <p>\r
- * Caveats: at this point the parsing is simple; for example, # cannot be\r
- * quoted (use \\u0023); you can set it to null to disable. \r
- * The equality sign and a few others can be reset with\r
- * setBnfX().\r
- * \r
- * @param bnfLines\r
- * Series of lines that represent a BNF expression. The lines contain\r
- * a series of statements that of the form x=y;. A statement can take\r
- * multiple lines, but there can't be multiple statements on a line.\r
- * A hash quotes to the end of the line.\r
- * @return Pattern\r
- */\r
- public String compileBnf(List lines) {\r
- Map variables = getVariables(lines);\r
- Set unused = new LinkedHashSet(variables.keySet());\r
- // brute force replacement; do twice to allow for different order\r
- // later on can optimize\r
- for (int i = 0; i < 2; ++i) {\r
- for (Iterator it = variables.keySet().iterator(); it.hasNext();) {\r
- String variable = (String) it.next();\r
- String definition = (String) variables.get(variable);\r
- for (Iterator it2 = variables.keySet().iterator(); it2.hasNext();) {\r
- String variable2 = (String) it2.next();\r
- if (variable.equals(variable2)) continue;\r
- String definition2 = (String) variables.get(variable2);\r
- String altered2 = definition2.replace(variable, definition);\r
- if (!altered2.equals(definition2)) {\r
- unused.remove(variable);\r
- variables.put(variable2, altered2);\r
- if (log != null) {\r
- try {\r
- log.append(variable2 + "=" + altered2 + ";");\r
- } catch (IOException e) {\r
- throw (IllegalArgumentException) new IllegalArgumentException().initCause(e);\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
- if (unused.size() != 1) {\r
- throw new IllegalArgumentException("Not a single root: " + unused);\r
- }\r
- return (String) variables.get(unused.iterator().next());\r
- }\r
-\r
- public String getBnfCommentString() {\r
- return bnfCommentString;\r
- }\r
-\r
- public void setBnfCommentString(String bnfCommentString) {\r
- this.bnfCommentString = bnfCommentString;\r
- }\r
-\r
- public String getBnfVariableInfix() {\r
- return bnfVariableInfix;\r
- }\r
-\r
- public void setBnfVariableInfix(String bnfVariableInfix) {\r
- this.bnfVariableInfix = bnfVariableInfix;\r
- }\r
-\r
- public String getBnfLineSeparator() {\r
- return bnfLineSeparator;\r
- }\r
-\r
- public void setBnfLineSeparator(String bnfLineSeparator) {\r
- this.bnfLineSeparator = bnfLineSeparator;\r
- }\r
-\r
- /**\r
- * Utility for loading lines from a file.\r
- * @param result\r
- * @param file\r
- * @param encoding if null, then UTF-8\r
- * @return filled list\r
- * @throws IOException\r
- */\r
- public static List appendLines(List result, String file, String encoding) throws IOException {\r
- return appendLines(result, new FileInputStream(file), encoding);\r
- }\r
-\r
- /**\r
- * Utility for loading lines from a UTF8 file.\r
- * @param result\r
- * @param inputStream\r
- * @param encoding if null, then UTF-8\r
- * @return filled list\r
- * @throws IOException\r
- */\r
- public static List appendLines(List result, InputStream inputStream, String encoding)\r
- throws UnsupportedEncodingException, IOException {\r
- BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, encoding == null ? "UTF-8" : encoding));\r
- while (true) {\r
- String line = in.readLine();\r
- if (line == null) break;\r
- result.add(line);\r
- }\r
- return result;\r
- }\r
- \r
- \r
-\r
- /* (non-Javadoc)\r
- * @see com.ibm.icu.util.Freezable#cloneAsThawed()\r
- */\r
- public Object cloneAsThawed() {\r
- // TODO Auto-generated method stub\r
- try {\r
- return this.clone();\r
- } catch (CloneNotSupportedException e) {\r
- throw new IllegalArgumentException(); // should never happen\r
- }\r
- }\r
-\r
- /* (non-Javadoc)\r
- * @see com.ibm.icu.util.Freezable#freeze()\r
- */\r
- public Object freeze() {\r
- // no action needed now.\r
- return this;\r
- }\r
-\r
- /* (non-Javadoc)\r
- * @see com.ibm.icu.util.Freezable#isFrozen()\r
- */\r
- public boolean isFrozen() {\r
- // at this point, always true\r
- return true;\r
- }\r
-\r
- // ===== PRIVATES =====\r
-\r
- private int processSet(String regex, int i, StringBuffer result, UnicodeSet temp, ParsePosition pos) {\r
- try {\r
- pos.setIndex(i);\r
- UnicodeSet x = temp.clear().applyPattern(regex, pos, null, 0);\r
- x.complement().complement(); // hack to fix toPattern\r
- result.append(x.toPattern(false));\r
- i = pos.getIndex() - 1; // allow for the loop increment\r
- return i;\r
- } catch (Exception e) {\r
- throw (IllegalArgumentException) new IllegalArgumentException("Error in " + regex).initCause(e);\r
- }\r
- }\r
-\r
- private static UnicodeRegex STANDARD = new UnicodeRegex();\r
- private String bnfCommentString = "#";\r
- private String bnfVariableInfix = "=";\r
- private String bnfLineSeparator = "\n";\r
- private Appendable log = null;\r
-\r
- private Comparator LongestFirst = new Comparator () {\r
- public int compare(Object obj0, Object obj1) {\r
- String arg0 = obj0.toString();\r
- String arg1 = obj1.toString();\r
- int len0 = arg0.length();\r
- int len1 = arg1.length();\r
- if (len0 != len1) return len1 - len0;\r
- return arg0.compareTo(arg1);\r
- }\r
- };\r
-\r
- private Map getVariables(List lines) {\r
- Map variables = new TreeMap(LongestFirst);\r
- String variable = null;\r
- StringBuffer definition = new StringBuffer();\r
- int count = 0;\r
- for (Iterator it = lines.iterator(); it.hasNext();) {\r
- String line = (String)it.next();\r
- ++count;\r
- // remove initial bom, comments\r
- if (line.length() == 0) continue;\r
- if (line.charAt(0) == '\uFEFF') line = line.substring(1);\r
-\r
- if (bnfCommentString != null) {\r
- int hashPos = line.indexOf(bnfCommentString);\r
- if (hashPos >= 0) line = line.substring(0, hashPos);\r
- }\r
- String trimline = line.trim();\r
- if (trimline.length() == 0) continue;\r
-\r
- // String[] lineParts = line.split(";");\r
- String linePart = line; // lineParts[i]; // .trim().replace("\\s+", " ");\r
- if (linePart.trim().length() == 0) continue;\r
- boolean terminated = trimline.endsWith(";");\r
- if (terminated) {\r
- linePart = linePart.substring(0,linePart.lastIndexOf(';'));\r
- }\r
- int equalsPos = linePart.indexOf(bnfVariableInfix);\r
- if (equalsPos >= 0) {\r
- if (variable != null) {\r
- throw new IllegalArgumentException("Missing ';' before " + count + ") " + line);\r
- }\r
- variable = linePart.substring(0,equalsPos).trim();\r
- if (variables.containsKey(variable)) {\r
- throw new IllegalArgumentException("Duplicate variable definition in " + line);\r
- }\r
- definition.append(linePart.substring(equalsPos+1).trim());\r
- } else { // no equals, so\r
- if (variable == null) {\r
- throw new IllegalArgumentException("Missing '=' at " + count + ") " + line);\r
- }\r
- definition.append(bnfLineSeparator).append(linePart);\r
- }\r
- // we are terminated if i is not at the end, or the line ends with a ;\r
- if (terminated) {\r
- variables.put(variable, definition.toString());\r
- variable = null; // signal we have no variable\r
- definition.setLength(0);\r
- }\r
- }\r
- if (variable != null) {\r
- throw new IllegalArgumentException("Missing ';' at end");\r
- }\r
- return variables;\r
- }\r
-}\r
-//#endif\r
-\r
+//##header J2SE15
+//#if defined(FOUNDATION10) || defined(J2SE13) || defined(J2SE14)
+//#else
+/*
+ *******************************************************************************
+ * Copyright (C) 2009, Google, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+ */
+package com.ibm.icu.impl;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.text.ParsePosition;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+import com.ibm.icu.text.StringTransform;
+import com.ibm.icu.text.UnicodeSet;
+import com.ibm.icu.util.Freezable;
+
+/**
+ * Contains utilities to supplement the JDK Regex, since it doesn't handle
+ * Unicode well.
+ *
+ * @author markdavis
+ */
+public class UnicodeRegex implements Cloneable, Freezable, StringTransform {
+ // Note: we don't currently have any state, but intend to in the future,
+ // particularly for the regex style supported.
+
+ /**
+ * Adds full Unicode property support, with the latest version of Unicode,
+ * to Java Regex, bringing it up to Level 1 (see
+ * http://www.unicode.org/reports/tr18/). It does this by preprocessing the
+ * regex pattern string and interpreting the character classes (\p{...},
+ * \P{...}, [...]) according to their syntax and meaning in UnicodeSet. With
+ * this utility, Java regex expressions can be updated to work with the
+ * latest version of Unicode, and with all Unicode properties. Note that the
+ * UnicodeSet syntax has not yet, however, been updated to be completely
+ * consistent with Java regex, so be careful of the differences.
+ * <p>Not thread-safe; create a separate copy for different threads.
+ * <p>In the future, we may extend this to support other regex packages.
+ *
+ * @regex A modified Java regex pattern, as in the input to
+ * Pattern.compile(), except that all "character classes" are
+ * processed as if they were UnicodeSet patterns. Example:
+ * "abc[:bc=N:]. See UnicodeSet for the differences in syntax.
+ * @return A processed Java regex pattern, suitable for input to
+ * Pattern.compile().
+ */
+ public String transform(String regex) {
+ StringBuffer result = new StringBuffer();
+ UnicodeSet temp = new UnicodeSet();
+ ParsePosition pos = new ParsePosition(0);
+ int state = 0; // 1 = after \
+
+ // We add each character unmodified to the output, unless we have a
+ // UnicodeSet. Note that we don't worry about supplementary characters,
+ // since none of the syntax uses them.
+
+ for (int i = 0; i < regex.length(); ++i) {
+ // look for UnicodeSets, allowing for quoting with \ and \Q
+ char ch = regex.charAt(i);
+ switch (state) {
+ case 0: // we only care about \, and '['.
+ if (ch == '\\') {
+ if (UnicodeSet.resemblesPattern(regex, i)) {
+ // should only happen with \p
+ i = processSet(regex, i, result, temp, pos);
+ continue;
+ }
+ state = 1;
+ } else if (ch == '[') {
+ // if we have what looks like a UnicodeSet
+ if (UnicodeSet.resemblesPattern(regex, i)) {
+ i = processSet(regex, i, result, temp, pos);
+ continue;
+ }
+ }
+ break;
+
+ case 1: // we are after a \
+ if (ch == 'Q') {
+ state = 1;
+ } else {
+ state = 0;
+ }
+ break;
+
+ case 2: // we are in a \Q...
+ if (ch == '\\') {
+ state = 3;
+ }
+ break;
+
+ case 3: // we are in at \Q...\
+ if (ch == 'E') {
+ state = 0;
+ }
+ state = 2;
+ break;
+ }
+ result.append(ch);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Convenience static function, using standard parameters.
+ * @param regex as in process()
+ * @return processed regex pattern, as in process()
+ */
+ public static String fix(String regex) {
+ return STANDARD.transform(regex);
+ }
+
+ /**
+ * Compile a regex string, after processing by fix(...).
+ *
+ * @param regex
+ * Raw regex pattern, as in fix(...).
+ * @return Pattern
+ */
+ public static Pattern compile(String regex) {
+ return Pattern.compile(STANDARD.transform(regex));
+ }
+
+ /**
+ * Compile a regex string, after processing by fix(...).
+ *
+ * @param regex
+ * Raw regex pattern, as in fix(...).
+ * @return Pattern
+ */
+ public static Pattern compile(String regex, int options) {
+ return Pattern.compile(STANDARD.transform(regex), options);
+ }
+
+ /**
+ * Compile a composed string from a set of BNF lines; see the List version for more information.
+ *
+ * @param bnfLines Series of BNF lines.
+ * @return Pattern
+ */
+ public String compileBnf(String bnfLines) {
+ return compileBnf(Arrays.asList(bnfLines.split("\\r\\n?|\\n")));
+ }
+
+ /**
+ * Compile a composed string from a set of BNF lines, such as for composing a regex
+ * expression. The lines can be in any order, but there must not be any
+ * cycles. The result can be used as input for fix().
+ * <p>
+ * Example:
+ * <pre>
+ * uri = (?: (scheme) \\:)? (host) (?: \\? (query))? (?: \\u0023 (fragment))?;
+ * scheme = reserved+;
+ * host = // reserved+;
+ * query = [\\=reserved]+;
+ * fragment = reserved+;
+ * reserved = [[:ascii:][:alphabetic:]];
+ * </pre>
+ * <p>
+ * Caveats: at this point the parsing is simple; for example, # cannot be
+ * quoted (use \\u0023); you can set it to null to disable.
+ * The equality sign and a few others can be reset with
+ * setBnfX().
+ *
+ * @param bnfLines
+ * Series of lines that represent a BNF expression. The lines contain
+ * a series of statements that of the form x=y;. A statement can take
+ * multiple lines, but there can't be multiple statements on a line.
+ * A hash quotes to the end of the line.
+ * @return Pattern
+ */
+ public String compileBnf(List lines) {
+ Map variables = getVariables(lines);
+ Set unused = new LinkedHashSet(variables.keySet());
+ // brute force replacement; do twice to allow for different order
+ // later on can optimize
+ for (int i = 0; i < 2; ++i) {
+ for (Iterator it = variables.keySet().iterator(); it.hasNext();) {
+ String variable = (String) it.next();
+ String definition = (String) variables.get(variable);
+ for (Iterator it2 = variables.keySet().iterator(); it2.hasNext();) {
+ String variable2 = (String) it2.next();
+ if (variable.equals(variable2)) continue;
+ String definition2 = (String) variables.get(variable2);
+ String altered2 = definition2.replace(variable, definition);
+ if (!altered2.equals(definition2)) {
+ unused.remove(variable);
+ variables.put(variable2, altered2);
+ if (log != null) {
+ try {
+ log.append(variable2 + "=" + altered2 + ";");
+ } catch (IOException e) {
+ throw (IllegalArgumentException) new IllegalArgumentException().initCause(e);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (unused.size() != 1) {
+ throw new IllegalArgumentException("Not a single root: " + unused);
+ }
+ return (String) variables.get(unused.iterator().next());
+ }
+
+ public String getBnfCommentString() {
+ return bnfCommentString;
+ }
+
+ public void setBnfCommentString(String bnfCommentString) {
+ this.bnfCommentString = bnfCommentString;
+ }
+
+ public String getBnfVariableInfix() {
+ return bnfVariableInfix;
+ }
+
+ public void setBnfVariableInfix(String bnfVariableInfix) {
+ this.bnfVariableInfix = bnfVariableInfix;
+ }
+
+ public String getBnfLineSeparator() {
+ return bnfLineSeparator;
+ }
+
+ public void setBnfLineSeparator(String bnfLineSeparator) {
+ this.bnfLineSeparator = bnfLineSeparator;
+ }
+
+ /**
+ * Utility for loading lines from a file.
+ * @param result
+ * @param file
+ * @param encoding if null, then UTF-8
+ * @return filled list
+ * @throws IOException
+ */
+ public static List appendLines(List result, String file, String encoding) throws IOException {
+ return appendLines(result, new FileInputStream(file), encoding);
+ }
+
+ /**
+ * Utility for loading lines from a UTF8 file.
+ * @param result
+ * @param inputStream
+ * @param encoding if null, then UTF-8
+ * @return filled list
+ * @throws IOException
+ */
+ public static List appendLines(List result, InputStream inputStream, String encoding)
+ throws UnsupportedEncodingException, IOException {
+ BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, encoding == null ? "UTF-8" : encoding));
+ while (true) {
+ String line = in.readLine();
+ if (line == null) break;
+ result.add(line);
+ }
+ return result;
+ }
+
+
+
+ /* (non-Javadoc)
+ * @see com.ibm.icu.util.Freezable#cloneAsThawed()
+ */
+ public Object cloneAsThawed() {
+ // TODO Auto-generated method stub
+ try {
+ return this.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalArgumentException(); // should never happen
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.ibm.icu.util.Freezable#freeze()
+ */
+ public Object freeze() {
+ // no action needed now.
+ return this;
+ }
+
+ /* (non-Javadoc)
+ * @see com.ibm.icu.util.Freezable#isFrozen()
+ */
+ public boolean isFrozen() {
+ // at this point, always true
+ return true;
+ }
+
+ // ===== PRIVATES =====
+
+ private int processSet(String regex, int i, StringBuffer result, UnicodeSet temp, ParsePosition pos) {
+ try {
+ pos.setIndex(i);
+ UnicodeSet x = temp.clear().applyPattern(regex, pos, null, 0);
+ x.complement().complement(); // hack to fix toPattern
+ result.append(x.toPattern(false));
+ i = pos.getIndex() - 1; // allow for the loop increment
+ return i;
+ } catch (Exception e) {
+ throw (IllegalArgumentException) new IllegalArgumentException("Error in " + regex).initCause(e);
+ }
+ }
+
+ private static UnicodeRegex STANDARD = new UnicodeRegex();
+ private String bnfCommentString = "#";
+ private String bnfVariableInfix = "=";
+ private String bnfLineSeparator = "\n";
+ private Appendable log = null;
+
+ private Comparator LongestFirst = new Comparator () {
+ public int compare(Object obj0, Object obj1) {
+ String arg0 = obj0.toString();
+ String arg1 = obj1.toString();
+ int len0 = arg0.length();
+ int len1 = arg1.length();
+ if (len0 != len1) return len1 - len0;
+ return arg0.compareTo(arg1);
+ }
+ };
+
+ private Map getVariables(List lines) {
+ Map variables = new TreeMap(LongestFirst);
+ String variable = null;
+ StringBuffer definition = new StringBuffer();
+ int count = 0;
+ for (Iterator it = lines.iterator(); it.hasNext();) {
+ String line = (String)it.next();
+ ++count;
+ // remove initial bom, comments
+ if (line.length() == 0) continue;
+ if (line.charAt(0) == '\uFEFF') line = line.substring(1);
+
+ if (bnfCommentString != null) {
+ int hashPos = line.indexOf(bnfCommentString);
+ if (hashPos >= 0) line = line.substring(0, hashPos);
+ }
+ String trimline = line.trim();
+ if (trimline.length() == 0) continue;
+
+ // String[] lineParts = line.split(";");
+ String linePart = line; // lineParts[i]; // .trim().replace("\\s+", " ");
+ if (linePart.trim().length() == 0) continue;
+ boolean terminated = trimline.endsWith(";");
+ if (terminated) {
+ linePart = linePart.substring(0,linePart.lastIndexOf(';'));
+ }
+ int equalsPos = linePart.indexOf(bnfVariableInfix);
+ if (equalsPos >= 0) {
+ if (variable != null) {
+ throw new IllegalArgumentException("Missing ';' before " + count + ") " + line);
+ }
+ variable = linePart.substring(0,equalsPos).trim();
+ if (variables.containsKey(variable)) {
+ throw new IllegalArgumentException("Duplicate variable definition in " + line);
+ }
+ definition.append(linePart.substring(equalsPos+1).trim());
+ } else { // no equals, so
+ if (variable == null) {
+ throw new IllegalArgumentException("Missing '=' at " + count + ") " + line);
+ }
+ definition.append(bnfLineSeparator).append(linePart);
+ }
+ // we are terminated if i is not at the end, or the line ends with a ;
+ if (terminated) {
+ variables.put(variable, definition.toString());
+ variable = null; // signal we have no variable
+ definition.setLength(0);
+ }
+ }
+ if (variable != null) {
+ throw new IllegalArgumentException("Missing ';' at end");
+ }
+ return variables;
+ }
+}
+//#endif
+