diff options
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java')
-rw-r--r-- | framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java | 775 |
1 files changed, 0 insertions, 775 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java deleted file mode 100644 index aed6f371..00000000 --- a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/LayoutPreservingProperties.java +++ /dev/null @@ -1,775 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.apache.tools.ant.util; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintStream; -import java.io.PushbackReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Properties; - -/** - * <p>A Properties collection which preserves comments and whitespace - * present in the input stream from which it was loaded.</p> - * <p>The class defers the usual work of the <a href="http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html">java.util.Properties</a> - * class to there, but it also keeps track of the contents of the - * input stream from which it was loaded (if applicable), so that it can - * write out the properties in as close a form as possible to the input.</p> - * <p>If no changes occur to property values, the output should be the same - * as the input, except for the leading date stamp, as normal for a - * properties file. Properties added are appended to the file. Properties - * whose values are changed are changed in place. Properties that are - * removed are excised. If the <code>removeComments</code> flag is set, - * then the comments immediately preceding the property are also removed.</p> - * <p>If a second set of properties is loaded into an existing set, the - * lines of the second set are added to the end. Note however, that if a - * property already stored is present in a stream subsequently loaded, then - * that property is removed before the new value is set. For example, - * consider the file</p> - * <pre> # the first line - * alpha=one - * - * # the second line - * beta=two</pre> - * <p>This file is loaded, and then the following is also loaded into the - * same <code>LayoutPreservingProperties</code> object</p> - * <pre> # association - * beta=band - * - * # and finally - * gamma=rays</pre> - * <p>The resulting collection sequence of logical lines depends on whether - * or not <code>removeComments</code> was set at the time the second stream - * is loaded. If it is set, then the resulting list of lines is</p> - * <pre> # the first line - * alpha=one - * - * # association - * beta=band - * - * # and finally - * gamma=rays</pre> - * <p>If the flag is not set, then the comment "the second line" is retained, - * although the key-value pair <code>beta=two</code> is removed.</p> - */ -public class LayoutPreservingProperties extends Properties { - private String LS = StringUtils.LINE_SEP; - - /** - * 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 - */ - public LayoutPreservingProperties(final Properties defaults) { - super(defaults); - } - - /** - * 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 - */ - public void setRemoveComments(final boolean val) { - removeComments = val; - } - - @Override - public void load(final InputStream inStream) throws IOException { - final String s = readLines(inStream); - final byte[] ba = s.getBytes(ResourceUtils.ISO_8859_1); - final ByteArrayInputStream bais = new ByteArrayInputStream(ba); - super.load(bais); - } - - @Override - public Object put(final Object key, final Object value) throws NullPointerException { - final 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; - } - - @Override - public Object setProperty(final String key, final String value) - throws NullPointerException { - final 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. - * @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)) { - final Integer i = (Integer) keyedPairLines.get(key); - final Pair p = (Pair) logicalLines.get(i.intValue()); - p.setValue(value); - } else { - key = escapeName(key); - final Pair p = new Pair(key, value); - p.setNew(true); - keyedPairLines.put(key, new Integer(logicalLines.size())); - logicalLines.add(p); - } - } - - @Override - public void clear() { - super.clear(); - keyedPairLines.clear(); - logicalLines.clear(); - } - - @Override - public Object remove(final Object key) { - final Object obj = super.remove(key); - final Integer i = (Integer) keyedPairLines.remove(key); - if (null != i) { - if (removeComments) { - removeCommentsEndingAt(i.intValue()); - } - logicalLines.set(i.intValue(), null); - } - return obj; - } - - @Override - public Object clone() { - final LayoutPreservingProperties dolly = - (LayoutPreservingProperties) super.clone(); - dolly.keyedPairLines = (HashMap) this.keyedPairLines.clone(); - dolly.logicalLines = (ArrayList) this.logicalLines.clone(); - final int size = dolly.logicalLines.size(); - for (int j = 0; j < size; j++) { - final LogicalLine line = (LogicalLine) dolly.logicalLines.get(j); - if (line instanceof Pair) { - final Pair p = (Pair) line; - dolly.logicalLines.set(j, p.clone()); - } - // no reason to clone other lines are they are immutable - } - return dolly; - } - - /** - * Echo the lines of the properties (including blanks and comments) to the - * stream. - * @param out the stream to write to - */ - public void listLines(final PrintStream out) { - out.println("-- logical lines --"); - final Iterator i = logicalLines.iterator(); - while (i.hasNext()) { - final LogicalLine line = (LogicalLine) i.next(); - if (line instanceof Blank) { - out.println("blank: \"" + line + "\""); - } else if (line instanceof Comment) { - out.println("comment: \"" + line + "\""); - } else if (line instanceof Pair) { - out.println("pair: \"" + line + "\""); - } - } - } - - /** - * Save the properties to a file. - * @param dest the file to write to - */ - public void saveAs(final File dest) throws IOException { - final FileOutputStream fos = new FileOutputStream(dest); - store(fos, null); - fos.close(); - } - - @Override - public void store(final OutputStream out, final String header) throws IOException { - final OutputStreamWriter osw = new OutputStreamWriter(out, ResourceUtils.ISO_8859_1); - - int skipLines = 0; - final int totalLines = logicalLines.size(); - - if (header != null) { - osw.write("#" + header + LS); - if (totalLines > 0 - && logicalLines.get(0) instanceof Comment - && header.equals(logicalLines.get(0).toString().substring(1))) { - skipLines = 1; - } - } - - // we may be updatiung a file written by this class, replace - // the date comment instead of adding a new one and preserving - // the one written last time - if (totalLines > skipLines - && logicalLines.get(skipLines) instanceof Comment) { - try { - DateUtils.parseDateFromHeader(logicalLines - .get(skipLines) - .toString().substring(1)); - skipLines++; - } catch (final java.text.ParseException pe) { - // not an existing date comment - } - } - osw.write("#" + DateUtils.getDateForHeader() + LS); - - boolean writtenSep = false; - for (final Iterator i = logicalLines.subList(skipLines, totalLines).iterator(); - i.hasNext();) { - final LogicalLine line = (LogicalLine) i.next(); - if (line instanceof Pair) { - if (((Pair)line).isNew()) { - if (!writtenSep) { - osw.write(LS); - writtenSep = true; - } - } - osw.write(line.toString() + LS); - } else if (line != null) { - osw.write(line.toString() + LS); - } - } - 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. - * @param is the stream from which to read the data - */ - private String readLines(final InputStream is) throws IOException { - final InputStreamReader isr = new InputStreamReader(is, ResourceUtils.ISO_8859_1); - final PushbackReader pbr = new PushbackReader(isr, 1); - - if (logicalLines.size() > 0) { - // we add a blank line for spacing - logicalLines.add(new Blank()); - } - - String s = readFirstLine(pbr); - final BufferedReader br = new BufferedReader(pbr); - - boolean continuation = false; - boolean comment = false; - final StringBuffer fileBuffer = new StringBuffer(); - final 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 ! - comment = s.matches("^( |\t|\f)*(#|!).*"); - } - - // 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) { - line = new Comment(logicalLineBuffer.toString()); - } else if (logicalLineBuffer.toString().trim().length() == 0) { - line = new Blank(); - } else { - line = new Pair(logicalLineBuffer.toString()); - final String key = unescape(((Pair)line).getName()); - if (keyedPairLines.containsKey(key)) { - // this key is already present, so we remove it and add - // the new one - remove(key); - } - keyedPairLines.put(key, new Integer(logicalLines.size())); - } - logicalLines.add(line); - logicalLineBuffer.setLength(0); - } - s = br.readLine(); - } - return fileBuffer.toString(); - } - - /** - * Reads the first line and determines the EOL-style of the file - * (relies on the style to be consistent, of course). - * - * <p>Sets LS as a side-effect.</p> - * - * @return the first line without any line separator, leaves the - * reader positioned after the first line separator - * - * @since Ant 1.8.2 - */ - private String readFirstLine(final PushbackReader r) throws IOException { - final StringBuffer sb = new StringBuffer(80); - int ch = r.read(); - boolean hasCR = false; - // when reaching EOF before the first EOL, assume native line - // feeds - LS = StringUtils.LINE_SEP; - - while (ch >= 0) { - if (hasCR && ch != '\n') { - // line feed is sole CR - r.unread(ch); - break; - } - - if (ch == '\r') { - LS = "\r"; - hasCR = true; - } else if (ch == '\n') { - LS = hasCR ? "\r\n" : "\n"; - break; - } else { - sb.append((char) ch); - } - ch = r.read(); - } - return sb.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. - * @param s the contents of the line to examine - * @return <code>true</code> if the line is to be continued, - * <code>false</code> otherwise - */ - private boolean requiresContinuation(final String s) { - final char[] ca = s.toCharArray(); - int i = ca.length - 1; - while (i > 0 && ca[i] == '\\') { - i--; - } - // trailing backslashes - final 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>. - * @param s the string to unescape (coming from the source file) - * @return the unescaped string - */ - private String unescape(final String s) { - /* - * The following combinations are converted: - * \n newline - * \r carraige return - * \f form feed - * \t tab - * \\ backslash - * \u0000 unicode character - * Any other slash is ignored, so - * \b becomes 'b'. - */ - - final char[] ch = new char[s.length() + 1]; - s.getChars(0, s.length(), ch, 0); - ch[s.length()] = '\n'; - final StringBuffer buffy = new StringBuffer(s.length()); - for (int i = 0; i < ch.length; i++) { - char c = ch[i]; - if (c == '\n') { - // we have hit out end-of-string marker - break; - } else if (c == '\\') { - // possibly an escape sequence - c = ch[++i]; - if (c == 'n') { - buffy.append('\n'); - } else if (c == 'r') { - buffy.append('\r'); - } else if (c == 'f') { - buffy.append('\f'); - } else if (c == 't') { - buffy.append('\t'); - } else if (c == 'u') { - // handle unicode escapes - c = unescapeUnicode(ch, i+1); - i += 4; - buffy.append(c); - } else { - buffy.append(c); - } - } else { - buffy.append(c); - } - } - return buffy.toString(); - } - - /** - * 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 - */ - private char unescapeUnicode(final char[] ch, final int i) { - final 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>. - * @param s the string to escape - * @return the escaped string - */ - private String escapeValue(final 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. - * @param s the string to escape - * @return the escaped string - */ - private String escapeName(final 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>. - * @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 - * @return the escaped string - */ - private String escape(final String s, final boolean escapeAllSpaces) { - if (s == null) { - return null; - } - - final char[] ch = new char[s.length()]; - s.getChars(0, s.length(), ch, 0); - final String forEscaping = "\t\f\r\n\\:=#!"; - final String escaped = "tfrn\\:=#!"; - final StringBuffer buffy = new StringBuffer(s.length()); - boolean leadingSpace = true; - for (int i = 0; i < ch.length; i++) { - final char c = ch[i]; - if (c == ' ') { - if (escapeAllSpaces || leadingSpace) { - buffy.append("\\"); - } - } else { - leadingSpace = false; - } - final int p = forEscaping.indexOf(c); - if (p != -1) { - buffy.append("\\").append(escaped.substring(p,p+1)); - } else if (c < 0x0020 || c > 0x007e) { - buffy.append(escapeUnicode(c)); - } else { - buffy.append(c); - } - } - return buffy.toString(); - } - - /** - * Return the unicode escape sequence for a character, in the form - * \u005CuNNNN. - * @param ch the character to encode - * @return the unicode escape sequence - */ - private String escapeUnicode(final char ch) { - return "\\" + UnicodeUtil.EscapeUnicode(ch); - } - - /** - * 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. - */ - - final 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 (!(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 abstract static class LogicalLine { - private String text; - - public LogicalLine(final String text) { - this.text = text; - } - - public void setText(final String text) { - this.text = text; - } - - @Override - public String toString() { - return text; - } - } - - /** - * A blank line of the input stream. - */ - private static class Blank extends LogicalLine { - public Blank() { - super(""); - } - } - - /** - * A comment line of the input stream. - */ - private class Comment extends LogicalLine { - public Comment(final String text) { - 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. - */ - private static class Pair extends LogicalLine implements Cloneable { - private String name; - private String value; - private boolean added; - - public Pair(final String text) { - super(text); - parsePair(text); - } - - public Pair(final String name, final String value) { - this(name + "=" + value); - } - - public String getName() { - return name; - } - - public String getValue() { - return value; - } - - public void setValue(final String value) { - this.value = value; - setText(name + "=" + value); - } - - public boolean isNew() { - return added; - } - - public void setNew(final boolean val) { - added = val; - } - - @Override - public Object clone() { - Object dolly = null; - try { - dolly = super.clone(); - } catch (final CloneNotSupportedException e) { - // should be fine - e.printStackTrace(); - } - return dolly; - } - - private void parsePair(final String text) { - // need to find first non-escaped '=', ':', '\t' or ' '. - final int pos = findFirstSeparator(text); - if (pos == -1) { - // trim leading whitespace only - name = text; - value = null; - } else { - name = text.substring(0, pos); - value = text.substring(pos+1, text.length()); - } - // trim leading whitespace only - name = stripStart(name, " \t\f"); - } - - private String stripStart(final String s, final String chars) { - if (s == null) { - return null; - } - - int i = 0; - for (;i < s.length(); i++) { - if (chars.indexOf(s.charAt(i)) == -1) { - break; - } - } - if (i == s.length()) { - return ""; - } - 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(final String s, final String chars) { - if (s == null || chars == null) { - return -1; - } - - int p = s.length() + 1; - for (int i = 0; i < chars.length(); i++) { - final int x = s.indexOf(chars.charAt(i)); - if (x != -1 && x < p) { - p = x; - } - } - if (p == s.length() + 1) { - return -1; - } - return p; - } - } -} |