|
|
@@ -79,31 +79,32 @@ import java.util.Properties; |
|
|
|
*/ |
|
|
|
public class LayoutPreservingProperties extends Properties { |
|
|
|
private static final String LS = System.getProperty("line.separator"); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Logical lines have escaping and line continuation taken care of. Comments |
|
|
|
* and blank lines are logical lines; they are not removed. |
|
|
|
* Logical lines have escaping and line continuation taken care |
|
|
|
* of. Comments and blank lines are logical lines; they are not |
|
|
|
* removed. |
|
|
|
*/ |
|
|
|
private ArrayList logicalLines = new ArrayList(); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Position in the <code>logicalLines</code> list, keyed by property name. |
|
|
|
*/ |
|
|
|
private HashMap keyedPairLines = new HashMap(); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Flag to indicate that, when we remove a property from the file, we |
|
|
|
* also want to remove the comments that precede it. |
|
|
|
*/ |
|
|
|
private boolean removeComments; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Create a new, empty, Properties collection, with no defaults. |
|
|
|
*/ |
|
|
|
public LayoutPreservingProperties() { |
|
|
|
super(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Create a new, empty, Properties collection, with the specified defaults. |
|
|
|
* @param defaults the default property values |
|
|
@@ -113,58 +114,63 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Returns <code>true</code> if comments are removed along with properties, or |
|
|
|
* <code>false</code> otherwise. If <code>true</code>, then when a property is |
|
|
|
* removed, the comment preceding it in the original file is removed also. |
|
|
|
* @return <code>true</code> if leading comments are removed when a property is |
|
|
|
* removed; <code>false</code> otherwise |
|
|
|
* Returns <code>true</code> if comments are removed along with |
|
|
|
* properties, or <code>false</code> otherwise. If |
|
|
|
* <code>true</code>, then when a property is removed, the comment |
|
|
|
* preceding it in the original file is removed also. |
|
|
|
* @return <code>true</code> if leading comments are removed when |
|
|
|
* a property is removed; <code>false</code> otherwise |
|
|
|
*/ |
|
|
|
public boolean isRemoveComments() { |
|
|
|
return removeComments; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Sets the behaviour for comments accompanying properties that are being |
|
|
|
* removed. If <code>true</code>, then when a property is removed, the comment |
|
|
|
* preceding it in the original file is removed also. |
|
|
|
* @param val <code>true</code> if leading comments are to be removed when a property is |
|
|
|
* removed; <code>false</code> otherwise |
|
|
|
* Sets the behaviour for comments accompanying properties that |
|
|
|
* are being removed. If <code>true</code>, then when a property |
|
|
|
* is removed, the comment preceding it in the original file is |
|
|
|
* removed also. |
|
|
|
* @param val <code>true</code> if leading comments are to be |
|
|
|
* removed when a property is removed; <code>false</code> |
|
|
|
* otherwise |
|
|
|
*/ |
|
|
|
public void setRemoveComments(boolean val) { |
|
|
|
removeComments = val; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void load(InputStream inStream) throws IOException { |
|
|
|
String s = readLines(inStream); |
|
|
|
byte[] ba = s.getBytes("ISO-8859-1"); |
|
|
|
ByteArrayInputStream bais = new ByteArrayInputStream(ba); |
|
|
|
super.load(bais); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Object put(Object key, Object value) throws NullPointerException { |
|
|
|
Object obj = super.put(key, value); |
|
|
|
// the above call will have failed if key or value are null |
|
|
|
innerSetProperty(key.toString(), value.toString()); |
|
|
|
return obj; |
|
|
|
} |
|
|
|
|
|
|
|
public Object setProperty(String key, String value) throws NullPointerException { |
|
|
|
|
|
|
|
public Object setProperty(String key, String value) |
|
|
|
throws NullPointerException { |
|
|
|
Object obj = super.setProperty(key, value); |
|
|
|
// the above call will have failed if key or value are null |
|
|
|
innerSetProperty(key, value); |
|
|
|
return obj; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Store a new key-value pair, or add a new one. The normal functionality is |
|
|
|
* taken care of by the superclass in the call to {@link #setProperty}; this |
|
|
|
* method takes care of this classes extensions. |
|
|
|
* Store a new key-value pair, or add a new one. The normal |
|
|
|
* functionality is taken care of by the superclass in the call to |
|
|
|
* {@link #setProperty}; this method takes care of this classes |
|
|
|
* extensions. |
|
|
|
* @param key the key of the property to be stored |
|
|
|
* @param value the value to be stored |
|
|
|
*/ |
|
|
|
private void innerSetProperty(String key, String value) { |
|
|
|
value = escapeValue(value); |
|
|
|
|
|
|
|
|
|
|
|
if (keyedPairLines.containsKey(key)) { |
|
|
|
Integer i = (Integer) keyedPairLines.get(key); |
|
|
|
Pair p = (Pair) logicalLines.get(i.intValue()); |
|
|
@@ -177,13 +183,13 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
logicalLines.add(p); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void clear() { |
|
|
|
super.clear(); |
|
|
|
keyedPairLines.clear(); |
|
|
|
logicalLines.clear(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Object remove(Object key) { |
|
|
|
Object obj = super.remove(key); |
|
|
|
Integer i = (Integer) keyedPairLines.remove(key); |
|
|
@@ -195,9 +201,10 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
return obj; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Object clone() { |
|
|
|
LayoutPreservingProperties dolly = (LayoutPreservingProperties) super.clone(); |
|
|
|
LayoutPreservingProperties dolly = |
|
|
|
(LayoutPreservingProperties) super.clone(); |
|
|
|
dolly.keyedPairLines = (HashMap) this.keyedPairLines.clone(); |
|
|
|
dolly.logicalLines = (ArrayList) this.logicalLines.clone(); |
|
|
|
for (int j = 0; j < dolly.logicalLines.size(); j++) { |
|
|
@@ -210,7 +217,7 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
return dolly; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Echo the lines of the properties (including blanks and comments) to the |
|
|
|
* stream. |
|
|
@@ -232,7 +239,7 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Save the properties to a file. |
|
|
|
* @param dest the file to write to |
|
|
@@ -242,15 +249,15 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
store(fos, null); |
|
|
|
fos.close(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void store(OutputStream out, String header) throws IOException { |
|
|
|
OutputStreamWriter osw = new OutputStreamWriter(out, "ISO-8859-1"); |
|
|
|
|
|
|
|
|
|
|
|
if (header != null) { |
|
|
|
osw.write("#" + header + LS); |
|
|
|
} |
|
|
|
osw.write("#" + (new Date()).toString() + LS); |
|
|
|
|
|
|
|
|
|
|
|
boolean writtenSep = false; |
|
|
|
for (Iterator i = logicalLines.iterator();i.hasNext();) { |
|
|
|
LogicalLine line = (LogicalLine) i.next(); |
|
|
@@ -268,46 +275,48 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
osw.close(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Reads a properties file into an internally maintained collection of logical |
|
|
|
* lines (possibly spanning physcial lines), which make up the comments, blank |
|
|
|
* lines and properties of the file. |
|
|
|
* Reads a properties file into an internally maintained |
|
|
|
* collection of logical lines (possibly spanning physcial lines), |
|
|
|
* which make up the comments, blank lines and properties of the |
|
|
|
* file. |
|
|
|
* @param is the stream from which to read the data |
|
|
|
*/ |
|
|
|
private String readLines(InputStream is) throws IOException { |
|
|
|
InputStreamReader isr = new InputStreamReader(is, "ISO-8859-1"); |
|
|
|
BufferedReader br = new BufferedReader(isr); |
|
|
|
|
|
|
|
|
|
|
|
if (logicalLines.size() > 0) { |
|
|
|
// we add a blank line for spacing |
|
|
|
logicalLines.add(new Blank()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
String s = br.readLine(); |
|
|
|
|
|
|
|
|
|
|
|
boolean continuation = false; |
|
|
|
boolean comment = false; |
|
|
|
StringBuffer fileBuffer = new StringBuffer(); |
|
|
|
StringBuffer logicalLineBuffer = new StringBuffer(); |
|
|
|
while (s != null) { |
|
|
|
fileBuffer.append(s).append(LS); |
|
|
|
|
|
|
|
|
|
|
|
if (continuation) { |
|
|
|
// put in the line feed that was removed |
|
|
|
s = "\n" + s; |
|
|
|
} else { |
|
|
|
// could be a comment, if first non-whitespace is a # or ! |
|
|
|
// could be a comment, if first non-whitespace is a # or ! |
|
|
|
comment = s.matches("^( |\t|\f)*(#|!).*"); |
|
|
|
} |
|
|
|
|
|
|
|
// continuation if not a comment and the line ends is an odd number of backslashes |
|
|
|
|
|
|
|
// continuation if not a comment and the line ends is an |
|
|
|
// odd number of backslashes |
|
|
|
if (!comment) { |
|
|
|
continuation = requiresContinuation(s); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
logicalLineBuffer.append(s); |
|
|
|
|
|
|
|
|
|
|
|
if (!continuation) { |
|
|
|
LogicalLine line = null; |
|
|
|
if (comment) { |
|
|
@@ -331,12 +340,14 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
return fileBuffer.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Returns <code>true</code> if the line represented by <code>s</code> is to be continued |
|
|
|
* on the next line of the file, or <code>false</code> otherwise. |
|
|
|
* Returns <code>true</code> if the line represented by |
|
|
|
* <code>s</code> is to be continued on the next line of the file, |
|
|
|
* or <code>false</code> otherwise. |
|
|
|
* @param s the contents of the line to examine |
|
|
|
* @return <code>true</code> if the line is to be continued, <code>false</code> otherwise |
|
|
|
* @return <code>true</code> if the line is to be continued, |
|
|
|
* <code>false</code> otherwise |
|
|
|
*/ |
|
|
|
private boolean requiresContinuation(String s) { |
|
|
|
char[] ca = s.toCharArray(); |
|
|
@@ -348,10 +359,11 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
int tb = ca.length - i - 1; |
|
|
|
return tb % 2 == 1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Unescape the string according to the rules for a Properites file, as laid out in |
|
|
|
* the docs for <a href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>. |
|
|
|
* Unescape the string according to the rules for a Properites |
|
|
|
* file, as laid out in the docs for <a |
|
|
|
* href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>. |
|
|
|
* @param s the string to unescape (coming from the source file) |
|
|
|
* @return the unescaped string |
|
|
|
*/ |
|
|
@@ -367,7 +379,7 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
* Any other slash is ignored, so |
|
|
|
* \b becomes 'b'. |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
char[] ch = new char[s.length() + 1]; |
|
|
|
s.getChars(0, s.length(), ch, 0); |
|
|
|
ch[s.length()] = '\n'; |
|
|
@@ -404,10 +416,10 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
return buffy.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Retrieve the unicode character whose code is listed at position <code>i</code> |
|
|
|
* in the character array <code>ch</code>. |
|
|
|
* Retrieve the unicode character whose code is listed at position |
|
|
|
* <code>i</code> in the character array <code>ch</code>. |
|
|
|
* @param ch the character array containing the unicode character code |
|
|
|
* @return the character extracted |
|
|
|
*/ |
|
|
@@ -415,41 +427,46 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
String s = new String(ch, i, 4); |
|
|
|
return (char) Integer.parseInt(s, 16); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Escape the string <code>s</code> according to the rules in the docs for |
|
|
|
* <a href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>. |
|
|
|
* Escape the string <code>s</code> according to the rules in the |
|
|
|
* docs for <a |
|
|
|
* href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>. |
|
|
|
* @param s the string to escape |
|
|
|
* @return the escaped string |
|
|
|
*/ |
|
|
|
private String escapeValue(String s) { |
|
|
|
return escape(s, false); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Escape the string <code>s</code> according to the rules in the docs for |
|
|
|
* <a href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>. |
|
|
|
* This method escapes all the whitespace, not just the stuff at the beginning. |
|
|
|
* Escape the string <code>s</code> according to the rules in the |
|
|
|
* docs for <a |
|
|
|
* href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>. |
|
|
|
* This method escapes all the whitespace, not just the stuff at |
|
|
|
* the beginning. |
|
|
|
* @param s the string to escape |
|
|
|
* @return the escaped string |
|
|
|
*/ |
|
|
|
private String escapeName(String s) { |
|
|
|
return escape(s, true); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Escape the string <code>s</code> according to the rules in the docs for |
|
|
|
* <a href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>. |
|
|
|
* Escape the string <code>s</code> according to the rules in the |
|
|
|
* docs for <a |
|
|
|
* href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a>. |
|
|
|
* @param s the string to escape |
|
|
|
* @param escapeAllSpaces if <code>true</code> the method escapes all the spaces, |
|
|
|
* if <code>false</code>, it escapes only the leading whitespace |
|
|
|
* @param escapeAllSpaces if <code>true</code> the method escapes |
|
|
|
* all the spaces, if <code>false</code>, it escapes only the |
|
|
|
* leading whitespace |
|
|
|
* @return the escaped string |
|
|
|
*/ |
|
|
|
private String escape(String s, boolean escapeAllSpaces) { |
|
|
|
if (s == null) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
char[] ch = new char[s.length()]; |
|
|
|
s.getChars(0, s.length(), ch, 0); |
|
|
|
String forEscaping = "\t\f\r\n\\:=#!"; |
|
|
@@ -476,9 +493,10 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
return buffy.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Return the unicode escape sequence for a character, in the form \u005CuNNNN. |
|
|
|
* Return the unicode escape sequence for a character, in the form |
|
|
|
* \u005CuNNNN. |
|
|
|
* @param ch the character to encode |
|
|
|
* @return the unicode escape sequence |
|
|
|
*/ |
|
|
@@ -489,66 +507,68 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
buffy.append(hex); |
|
|
|
return buffy.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Remove the comments in the leading up the {@link logicalLines} list leading |
|
|
|
* up to line <code>pos</code>. |
|
|
|
* Remove the comments in the leading up the {@link logicalLines} |
|
|
|
* list leading up to line <code>pos</code>. |
|
|
|
* @param pos the line number to which the comments lead |
|
|
|
*/ |
|
|
|
private void removeCommentsEndingAt(int pos) { |
|
|
|
/* We want to remove comments preceding this position. Step back counting |
|
|
|
* blank lines (call this range B1) until we hit something non-blank. If |
|
|
|
* what we hit is not a comment, then exit. If what we hit is a comment, |
|
|
|
* then step back counting comment lines (call this range C1). Nullify |
|
|
|
* lines in C1 and B1. |
|
|
|
/* We want to remove comments preceding this position. Step |
|
|
|
* back counting blank lines (call this range B1) until we hit |
|
|
|
* something non-blank. If what we hit is not a comment, then |
|
|
|
* exit. If what we hit is a comment, then step back counting |
|
|
|
* comment lines (call this range C1). Nullify lines in C1 and |
|
|
|
* B1. |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
int end = pos - 1; |
|
|
|
|
|
|
|
|
|
|
|
// step pos back until it hits something non-blank |
|
|
|
for (pos = end; pos > 0; pos--) { |
|
|
|
if (!(logicalLines.get(pos) instanceof Blank)) { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// if the thing it hits is not a comment, then we have nothing to remove |
|
|
|
|
|
|
|
// if the thing it hits is not a comment, then we have nothing |
|
|
|
// to remove |
|
|
|
if (!(logicalLines.get(pos) instanceof Comment)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// step back until we hit the start of the comment |
|
|
|
for (; pos >= 0; pos--) { |
|
|
|
if (!(logicalLines.get(pos) instanceof Comment)) { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// now we want to delete from pos+1 to end |
|
|
|
for (pos++ ;pos <= end; pos++) { |
|
|
|
logicalLines.set(pos, null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* A logical line of the properties input stream. |
|
|
|
*/ |
|
|
|
private static abstract class LogicalLine { |
|
|
|
private String text; |
|
|
|
|
|
|
|
|
|
|
|
public LogicalLine(String text) { |
|
|
|
this.text = text; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setText(String text) { |
|
|
|
this.text = text; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public String toString() { |
|
|
|
return text; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* A blank line of the input stream. |
|
|
|
*/ |
|
|
@@ -557,7 +577,7 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
super(""); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* A comment line of the input stream. |
|
|
|
*/ |
|
|
@@ -566,48 +586,49 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
super(text); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* A key-value pair from the input stream. This may span more than one physical |
|
|
|
* line, but it is constitutes as a single logical line. |
|
|
|
* A key-value pair from the input stream. This may span more than |
|
|
|
* one physical line, but it is constitutes as a single logical |
|
|
|
* line. |
|
|
|
*/ |
|
|
|
private static class Pair extends LogicalLine implements Cloneable { |
|
|
|
private String name; |
|
|
|
private String value; |
|
|
|
private boolean added; |
|
|
|
|
|
|
|
|
|
|
|
public Pair(String text) { |
|
|
|
super(text); |
|
|
|
parsePair(text); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Pair(String name, String value) { |
|
|
|
this(name + "=" + value); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public String getName() { |
|
|
|
return name; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public String getValue() { |
|
|
|
return value; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setValue(String value) { |
|
|
|
this.value = value; |
|
|
|
setText(name + "=" + value); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public boolean isNew() { |
|
|
|
return added; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setNew(boolean val) { |
|
|
|
added = val; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Object clone() { |
|
|
|
Object dolly = null; |
|
|
|
Object dolly = null; |
|
|
|
try { |
|
|
|
dolly = super.clone(); |
|
|
|
} |
|
|
@@ -617,7 +638,7 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
return dolly; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void parsePair(String text) { |
|
|
|
// need to find first non-escaped '=', ':', '\t' or ' '. |
|
|
|
int pos = findFirstSeparator(text); |
|
|
@@ -633,12 +654,12 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
// trim leading whitespace only |
|
|
|
name = stripStart(name, " \t\f"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String stripStart(String s, String chars) { |
|
|
|
if (s == null) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int i = 0; |
|
|
|
for (;i < s.length(); i++) { |
|
|
|
if (chars.indexOf(s.charAt(i)) == -1) { |
|
|
@@ -650,29 +671,29 @@ public class LayoutPreservingProperties extends Properties { |
|
|
|
} |
|
|
|
return s.substring(i); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private int findFirstSeparator(String s) { |
|
|
|
// Replace double backslashes with underscores so that they don't |
|
|
|
// confuse us looking for '\t' or '\=', for example, but they also |
|
|
|
// don't change the position of other characters |
|
|
|
s = s.replaceAll("\\\\\\\\", "__"); |
|
|
|
|
|
|
|
|
|
|
|
// Replace single backslashes followed by separators, so we don't |
|
|
|
// pick them up |
|
|
|
s = s.replaceAll("\\\\=", "__"); |
|
|
|
s = s.replaceAll("\\\\:", "__"); |
|
|
|
s = s.replaceAll("\\\\ ", "__"); |
|
|
|
s = s.replaceAll("\\\\t", "__"); |
|
|
|
|
|
|
|
|
|
|
|
// Now only the unescaped separators are left |
|
|
|
return indexOfAny(s, " :=\t"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private int indexOfAny(String s, String chars) { |
|
|
|
if (s == null || chars == null) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int p = s.length() + 1; |
|
|
|
for (int i = 0; i < chars.length(); i++) { |
|
|
|
int x = s.indexOf(chars.charAt(i)); |
|
|
|