All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opensymphony.xwork2.util.PropertiesReader Maven / Gradle / Ivy

There is a newer version: 6.4.0
Show newest version
/*
 * 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 com.opensymphony.xwork2.util;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 

* This class is used to read properties lines. These lines do * not terminate with new-line chars but rather when there is no * backslash sign a the end of the line. This is used to * concatenate multiple lines for readability. *

* *

* This class was pulled out of Jakarta Commons Configuration and * Jakarta Commons Lang trunk revision 476093 *

*/ public class PropertiesReader extends LineNumberReader { /** * Stores the comment lines for the currently processed property. */ private List commentLines; /** * Stores the name of the last read property. */ private String propertyName; /** * Stores the value of the last read property. */ private String propertyValue; /** * Stores the list delimiter character. */ private char delimiter; /** * Constant for the supported comment characters. */ static final String COMMENT_CHARS = "#!"; /** * Constant for the radix of hex numbers. */ private static final int HEX_RADIX = 16; /** * Constant for the length of a unicode literal. */ private static final int UNICODE_LEN = 4; /** * The list of possible key/value separators */ private static final char[] SEPARATORS = new char[]{'=', ':'}; /** * The white space characters used as key/value separators. */ private static final char[] WHITE_SPACE = new char[]{' ', '\t', '\f'}; /** * Constructor. * * @param reader A Reader. */ public PropertiesReader(Reader reader) { this(reader, ','); } /** * Creates a new instance of PropertiesReader and sets * the underlaying reader and the list delimiter. * * @param reader the reader * @param listDelimiter the list delimiter character * @since 1.3 */ public PropertiesReader(Reader reader, char listDelimiter) { super(reader); commentLines = new ArrayList(); delimiter = listDelimiter; } /** * Tests whether a line is a comment, i.e. whether it starts with a comment * character. * * @param line the line * @return a flag if this is a comment line * @since 1.3 */ boolean isCommentLine(String line) { String s = line.trim(); // blanc lines are also treated as comment lines return s.length() < 1 || COMMENT_CHARS.indexOf(s.charAt(0)) >= 0; } /** * Reads a property line. Returns null if Stream is * at EOF. Concatenates lines ending with "\". * Skips lines beginning with "#" or "!" and empty lines. * The return value is a property definition (<name> * = <value>) * * @return A string containing a property value or null * @throws IOException in case of an I/O error */ public String readProperty() throws IOException { commentLines.clear(); StringBuilder buffer = new StringBuilder(); while (true) { String line = readLine(); if (line == null) { // EOF return null; } if (isCommentLine(line)) { commentLines.add(line); continue; } line = line.trim(); if (checkCombineLines(line)) { line = line.substring(0, line.length() - 1); buffer.append(line); } else { buffer.append(line); break; } } return buffer.toString(); } /** * Parses the next property from the input stream and stores the found * name and value in internal fields. These fields can be obtained using * the provided getter methods. The return value indicates whether EOF * was reached (false) or whether further properties are * available (true). * * @return a flag if further properties are available * @throws IOException if an error occurs * @since 1.3 */ public boolean nextProperty() throws IOException { String line = readProperty(); if (line == null) { return false; // EOF } // parse the line String[] property = parseProperty(line); propertyName = unescapeJava(property[0]); propertyValue = unescapeJava(property[1], delimiter); return true; } /** * Returns the comment lines that have been read for the last property. * * @return the comment lines for the last property returned by * readProperty() * @since 1.3 */ public List getCommentLines() { return commentLines; } /** * Returns the name of the last read property. This method can be called * after {@link #nextProperty()} was invoked and its * return value was true. * * @return the name of the last read property * @since 1.3 */ public String getPropertyName() { return propertyName; } /** * Returns the value of the last read property. This method can be * called after {@link #nextProperty()} was invoked and * its return value was true. * * @return the value of the last read property * @since 1.3 */ public String getPropertyValue() { return propertyValue; } /** * Checks if the passed in line should be combined with the following. * This is true, if the line ends with an odd number of backslashes. * * @param line the line * @return a flag if the lines should be combined */ private boolean checkCombineLines(String line) { int bsCount = 0; for (int idx = line.length() - 1; idx >= 0 && line.charAt(idx) == '\\'; idx--) { bsCount++; } return bsCount % 2 == 1; } /** * Parse a property line and return the key and the value in an array. * * @param line the line to parse * @return an array with the property's key and value * @since 1.2 */ private String[] parseProperty(String line) { // sorry for this spaghetti code, please replace it as soon as // possible with a regexp when the Java 1.3 requirement is dropped String[] result = new String[2]; StringBuilder key = new StringBuilder(); StringBuilder value = new StringBuilder(); // state of the automaton: // 0: key parsing // 1: antislash found while parsing the key // 2: separator crossing // 3: value parsing int state = 0; for (int pos = 0; pos < line.length(); pos++) { char c = line.charAt(pos); switch (state) { case 0: if (c == '\\') { state = 1; } else if (contains(WHITE_SPACE, c)) { // switch to the separator crossing state state = 2; } else if (contains(SEPARATORS, c)) { // switch to the value parsing state state = 3; } else { key.append(c); } break; case 1: if (contains(SEPARATORS, c) || contains(WHITE_SPACE, c)) { // this is an escaped separator or white space key.append(c); } else { // another escaped character, the '\' is preserved key.append('\\'); key.append(c); } // return to the key parsing state state = 0; break; case 2: if (contains(WHITE_SPACE, c)) { // do nothing, eat all white spaces state = 2; } else if (contains(SEPARATORS, c)) { // switch to the value parsing state state = 3; } else { // any other character indicates we encoutered the beginning of the value value.append(c); // switch to the value parsing state state = 3; } break; case 3: value.append(c); break; } } result[0] = key.toString().trim(); result[1] = value.toString().trim(); return result; } /** *

Unescapes any Java literals found in the String to a * Writer.

This is a slightly modified version of the * StringEscapeUtils.unescapeJava() function in commons-lang that doesn't * drop escaped separators (i.e '\,'). * * @param str the String to unescape, may be null * @param delimiter the delimiter for multi-valued properties * @return the processed string * @throws IllegalArgumentException if the Writer is null */ protected static String unescapeJava(String str, char delimiter) { if (str == null) { return null; } int sz = str.length(); StringBuilder out = new StringBuilder(sz); StringBuffer unicode = new StringBuffer(UNICODE_LEN); boolean hadSlash = false; boolean inUnicode = false; for (int i = 0; i < sz; i++) { char ch = str.charAt(i); if (inUnicode) { // if in unicode, then we're reading unicode // values in somehow unicode.append(ch); if (unicode.length() == UNICODE_LEN) { // unicode now contains the four hex digits // which represents our unicode character try { int value = Integer.parseInt(unicode.toString(), HEX_RADIX); out.append((char) value); unicode.setLength(0); inUnicode = false; hadSlash = false; } catch (NumberFormatException nfe) { throw new RuntimeException("Unable to parse unicode value: " + unicode, nfe); } } continue; } if (hadSlash) { // handle an escaped value hadSlash = false; if (ch == '\\') { out.append('\\'); } else if (ch == '\'') { out.append('\''); } else if (ch == '\"') { out.append('"'); } else if (ch == 'r') { out.append('\r'); } else if (ch == 'f') { out.append('\f'); } else if (ch == 't') { out.append('\t'); } else if (ch == 'n') { out.append('\n'); } else if (ch == 'b') { out.append('\b'); } else if (ch == delimiter) { out.append('\\'); out.append(delimiter); } else if (ch == 'u') { // uh-oh, we're in unicode country.... inUnicode = true; } else { out.append(ch); } continue; } else if (ch == '\\') { hadSlash = true; continue; } out.append(ch); } if (hadSlash) { // then we're in the weird case of a \ at the end of the // string, let's output it anyway. out.append('\\'); } return out.toString(); } /** *

Checks if the object is in the given array.

* *

The method returns false if a null array is passed in.

* * @param array the array to search through * @param objectToFind the object to find * @return true if the array contains the object */ public boolean contains(char[] array, char objectToFind) { if (array == null) { return false; } for (char anArray : array) { if (objectToFind == anArray) { return true; } } return false; } /** *

Unescapes any Java literals found in the String. * For example, it will turn a sequence of '\' and * 'n' into a newline character, unless the '\' * is preceded by another '\'.

* * @param str the String to unescape, may be null * @return a new unescaped String, null if null string input */ public static String unescapeJava(String str) { if (str == null) { return null; } try { StringWriter writer = new StringWriter(str.length()); unescapeJava(writer, str); return writer.toString(); } catch (IOException ioe) { // this should never ever happen while writing to a StringWriter ioe.printStackTrace(); return null; } } /** *

Unescapes any Java literals found in the String to a * Writer.

* *

For example, it will turn a sequence of '\' and * 'n' into a newline character, unless the '\' * is preceded by another '\'.

* *

A null string input has no effect.

* * @param out the Writer used to output unescaped characters * @param str the String to unescape, may be null * @throws IllegalArgumentException if the Writer is null * @throws IOException if error occurs on underlying Writer */ public static void unescapeJava(Writer out, String str) throws IOException { if (out == null) { throw new IllegalArgumentException("The Writer must not be null"); } if (str == null) { return; } int sz = str.length(); StringBuffer unicode = new StringBuffer(4); boolean hadSlash = false; boolean inUnicode = false; for (int i = 0; i < sz; i++) { char ch = str.charAt(i); if (inUnicode) { // if in unicode, then we're reading unicode // values in somehow unicode.append(ch); if (unicode.length() == 4) { // unicode now contains the four hex digits // which represents our unicode character try { int value = Integer.parseInt(unicode.toString(), 16); out.write((char) value); unicode.setLength(0); inUnicode = false; hadSlash = false; } catch (NumberFormatException nfe) { throw new RuntimeException("Unable to parse unicode value: " + unicode, nfe); } } continue; } if (hadSlash) { // handle an escaped value hadSlash = false; switch (ch) { case '\\': out.write('\\'); break; case '\'': out.write('\''); break; case '\"': out.write('"'); break; case 'r': out.write('\r'); break; case 'f': out.write('\f'); break; case 't': out.write('\t'); break; case 'n': out.write('\n'); break; case 'b': out.write('\b'); break; case 'u': { // uh-oh, we're in unicode country.... inUnicode = true; break; } default: out.write(ch); break; } continue; } else if (ch == '\\') { hadSlash = true; continue; } out.write(ch); } if (hadSlash) { // then we're in the weird case of a \ at the end of the // string, let's output it anyway. out.write('\\'); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy