You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by da...@apache.org on 2008/01/21 08:10:03 UTC
svn commit: r613782 [1/2] - in
/openejb/trunk/openejb3/container/openejb-core/src:
main/java/org/apache/openejb/config/
main/java/org/apache/openejb/config/sys/ main/java/org/apache/openejb/util/
test/java/org/apache/openejb/util/ test/resources/org/ t...
Author: dain
Date: Sun Jan 20 23:09:58 2008
New Revision: 613782
URL: http://svn.apache.org/viewvc?rev=613782&view=rev
Log:
Added comments perserving properties object
Added:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/SuperProperties.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PropertiesTest.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/SuperPropertiesTest.java
openejb/trunk/openejb3/container/openejb-core/src/test/resources/org/
openejb/trunk/openejb3/container/openejb-core/src/test/resources/org/apache/
openejb/trunk/openejb3/container/openejb-core/src/test/resources/org/apache/openejb/
openejb/trunk/openejb3/container/openejb-core/src/test/resources/org/apache/openejb/util/
openejb/trunk/openejb3/container/openejb-core/src/test/resources/org/apache/openejb/util/test.properties
Removed:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/CaseInsensitiveProperties.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/CaseInsensitivePropertiesTest.java
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/sys/PropertiesAdapter.java
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java?rev=613782&r1=613781&r2=613782&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java Sun Jan 20 23:09:58 2008
@@ -16,7 +16,18 @@
*/
package org.apache.openejb.config;
-import static org.apache.openejb.config.ServiceUtils.implies;
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.assembler.classic.AppInfo;
import org.apache.openejb.assembler.classic.Assembler;
@@ -29,6 +40,8 @@
import org.apache.openejb.assembler.classic.ContainerSystemInfo;
import org.apache.openejb.assembler.classic.EjbJarInfo;
import org.apache.openejb.assembler.classic.FacilitiesInfo;
+import org.apache.openejb.assembler.classic.HandlerChainInfo;
+import org.apache.openejb.assembler.classic.HandlerInfo;
import org.apache.openejb.assembler.classic.JndiContextInfo;
import org.apache.openejb.assembler.classic.MdbContainerInfo;
import org.apache.openejb.assembler.classic.OpenEjbConfiguration;
@@ -41,8 +54,7 @@
import org.apache.openejb.assembler.classic.StatelessSessionContainerInfo;
import org.apache.openejb.assembler.classic.TransactionServiceInfo;
import org.apache.openejb.assembler.classic.WebAppInfo;
-import org.apache.openejb.assembler.classic.HandlerChainInfo;
-import org.apache.openejb.assembler.classic.HandlerInfo;
+import static org.apache.openejb.config.ServiceUtils.implies;
import org.apache.openejb.config.sys.AbstractService;
import org.apache.openejb.config.sys.ConnectionManager;
import org.apache.openejb.config.sys.Container;
@@ -56,28 +68,16 @@
import org.apache.openejb.config.sys.ServiceProvider;
import org.apache.openejb.config.sys.TransactionManager;
import org.apache.openejb.jee.EjbJar;
-import org.apache.openejb.jee.HandlerChains;
-import org.apache.openejb.jee.HandlerChain;
import org.apache.openejb.jee.Handler;
+import org.apache.openejb.jee.HandlerChain;
+import org.apache.openejb.jee.HandlerChains;
import org.apache.openejb.jee.ParamValue;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import org.apache.openejb.util.Messages;
+import org.apache.openejb.util.SuperProperties;
import org.apache.openejb.util.URISupport;
-import org.apache.openejb.util.CaseInsensitiveProperties;
-
-import java.io.File;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Comparator;
public class ConfigurationFactory implements OpenEjbConfigurationFactory {
@@ -527,7 +527,7 @@
logger.info("configureService.configuring", service.getId(), provider.getService(), provider.getId());
- Properties props = new CaseInsensitiveProperties();
+ Properties props = new SuperProperties();
props.putAll(provider.getProperties());
props.putAll(service.getProperties());
props.putAll(getSystemProperties(service.getId(), provider.getService()));
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/sys/PropertiesAdapter.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/sys/PropertiesAdapter.java?rev=613782&r1=613781&r2=613782&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/sys/PropertiesAdapter.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/sys/PropertiesAdapter.java Sun Jan 20 23:09:58 2008
@@ -17,7 +17,7 @@
*/
package org.apache.openejb.config.sys;
-import org.apache.openejb.util.CaseInsensitiveProperties;
+import org.apache.openejb.util.SuperProperties;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.io.ByteArrayInputStream;
@@ -29,7 +29,7 @@
*/
public class PropertiesAdapter extends XmlAdapter<String, Properties> {
public Properties unmarshal(String s) throws Exception {
- Properties properties = new CaseInsensitiveProperties();
+ Properties properties = new SuperProperties();
ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
properties.load(in);
return properties;
Added: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/SuperProperties.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/SuperProperties.java?rev=613782&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/SuperProperties.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/SuperProperties.java Sun Jan 20 23:09:58 2008
@@ -0,0 +1,1233 @@
+/**
+ *
+ * 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.openejb.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.InvalidPropertiesFormatException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Properties is a Hashtable where the keys and values must be Strings. Each Properties can have a default
+ * Properties which specifies the default values which are used if the key is not in this Properties.
+ *
+ * @see Hashtable
+ * @see java.lang.System#getProperties
+ */
+public class SuperProperties extends Properties {
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ private static final String PROP_DTD_NAME = "http://java.sun.com/dtd/properties.dtd";
+
+ private static final String PROP_DTD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + " <!ELEMENT properties (comment?, entry*) >"
+ + " <!ATTLIST properties version CDATA #FIXED \"1.0\" >"
+ + " <!ELEMENT comment (#PCDATA) >"
+ + " <!ELEMENT entry (#PCDATA) >"
+ + " <!ATTLIST entry key CDATA #REQUIRED >";
+
+
+
+ /**
+ * Actual property values.
+ */
+ protected LinkedHashMap<Object,Object> properties = new LinkedHashMap<Object,Object>();
+
+ /**
+ * Comments for the properties.
+ */
+ protected LinkedHashMap<String,String> comments = new LinkedHashMap<String,String>();
+
+ /**
+ * Attributes for the properties.
+ */
+ protected LinkedHashMap<String,LinkedHashMap<String,String>> attributes = new LinkedHashMap<String,LinkedHashMap<String,String>>();
+
+ /**
+ * The default property values.
+ */
+ protected Properties defaults;
+
+ /**
+ * Are lookups case insensitive?
+ */
+ protected boolean caseInsensitive;
+
+ /**
+ * The text between a key and the value.
+ */
+ protected String keyValueSeparator = "=";
+
+ /**
+ * Number of spaces to indent each line of the properties file.
+ */
+ protected String indent = "";
+
+ /**
+ * Number of spaces to indent comment after '#' character.
+ */
+ protected String commentIndent = " ";
+
+ /**
+ * Should there be a blank line between properties.
+ */
+ protected boolean spaceBetweenProperties = true;
+
+ /**
+ * Should there be a blank line between a comment and the property.
+ */
+ protected boolean spaceAfterComment = false;
+
+ /**
+ * Used for loadFromXML.
+ */
+ private DocumentBuilder builder = null;
+
+ /**
+ * Constructs a new Properties object.
+ */
+ public SuperProperties() {
+ super();
+ }
+
+ /**
+ * Constructs a new Properties object using the specified default properties.
+ *
+ * @param properties the default properties
+ */
+ public SuperProperties(Properties properties) {
+ super(properties);
+ defaults = properties;
+ }
+
+ /**
+ * Are lookups case insensitive?
+ * @return true if lookups are insensitive
+ */
+ public boolean isCaseInsensitive() {
+ return caseInsensitive;
+ }
+
+ /**
+ * Sets the sensitive of lookups.
+ * @param caseInsensitive if looks are insensitive
+ */
+ public void setCaseInsensitive(boolean caseInsensitive) {
+ this.caseInsensitive = caseInsensitive;
+ }
+
+ /**
+ * Gets the text that separates keys and values.
+ * The default is "=".
+ * @return the text that separates keys and values
+ */
+ public String getKeyValueSeparator() {
+ return keyValueSeparator;
+ }
+
+ /**
+ * Sets the text that separates keys and values.
+ * @param keyValueSeparator the text that separates keys and values
+ */
+ public void setKeyValueSeparator(String keyValueSeparator) {
+ if (keyValueSeparator == null) throw new NullPointerException("keyValueSeparator is null");
+ if (keyValueSeparator.length() == 0) throw new NullPointerException("keyValueSeparator is empty");
+ this.keyValueSeparator = keyValueSeparator;
+ }
+
+ /**
+ * Gets the number of spaces to indent each line of the properties file.
+ * @return the number of spaces to indent each line of the properties file
+ */
+ public int getIndent() {
+ return indent.length();
+ }
+
+ /**
+ * Sets the number of spaces to indent each line of the properties file.
+ * @param indent the number of spaces to indent each line of the properties file
+ */
+ public void setIndent(int indent) {
+ char[] chars = new char[indent];
+ Arrays.fill(chars, ' ');
+ this.indent = new String(chars);
+ }
+
+ /**
+ * Gets the number of spaces to indent comment after '#' character.
+ * @return the number of spaces to indent comment after '#' character
+ */
+ public int getCommentIndent() {
+ return commentIndent.length();
+ }
+
+ /**
+ * Sets the number of spaces to indent comment after '#' character.
+ * @param commentIndent the number of spaces to indent comment after '#' character
+ */
+ public void setCommentIndent(int commentIndent) {
+ char[] chars = new char[commentIndent];
+ Arrays.fill(chars, ' ');
+ this.commentIndent = new String(chars);
+ }
+
+ /**
+ * Should a blank line be added between properties?
+ * @return true if a blank line should be added between properties; false otherwise
+ */
+ public boolean isSpaceBetweenProperties() {
+ return spaceBetweenProperties;
+ }
+
+ /**
+ * If true a blank line will be added between properties.
+ * @param spaceBetweenProperties if true a blank line will be added between properties
+ */
+ public void setSpaceBetweenProperties(boolean spaceBetweenProperties) {
+ this.spaceBetweenProperties = spaceBetweenProperties;
+ }
+
+ /**
+ * Should there be a blank line between a comment and the property?
+ * @return true if a blank line should be added between a comment and the property
+ */
+ public boolean isSpaceAfterComment() {
+ return spaceAfterComment;
+ }
+
+ /**
+ * If true a blank line will be added between a comment and the property.
+ * @param spaceAfterComment if true a blank line will be added between a comment and the property
+ */
+ public void setSpaceAfterComment(boolean spaceAfterComment) {
+ this.spaceAfterComment = spaceAfterComment;
+ }
+
+ public String getProperty(String name) {
+ Object result = get(name);
+ String property = result instanceof String ? (String) result : null;
+ if (property == null && defaults != null) {
+ property = defaults.getProperty(name);
+ }
+ return property;
+ }
+
+ public String getProperty(String name, String defaultValue) {
+ Object result = get(name);
+ String property = result instanceof String ? (String) result : null;
+ if (property == null && defaults != null) {
+ property = defaults.getProperty(name);
+ }
+ if (property == null) {
+ return defaultValue;
+ }
+ return property;
+ }
+
+ public synchronized Object setProperty(String name, String value) {
+ return put(name, value);
+ }
+
+ /**
+ * Searches for the comment associated with the specified property. If the property is not found, look
+ * in the default properties. If the property is not found in the default properties, answer null.
+ *
+ * @param name the name of the property to find
+ * @return the named property value
+ */
+ public String getComment(String name) {
+ name = normalize(name);
+ String comment = comments.get(name);
+ if (comment == null && defaults instanceof SuperProperties) {
+ comment = ((SuperProperties) defaults).getComment(name);
+ }
+ return comment;
+ }
+
+ /**
+ * Sets the comment associated with a property.
+ * @param name the property name; not null
+ * @param comment the comment; not null
+ */
+ public void setComment(String name, String comment) {
+ if (name == null) throw new NullPointerException("name is null");
+ if (comment == null) throw new NullPointerException("comment is null");
+
+ name = normalize(name);
+ comments.put(name, comment);
+ }
+
+ /**
+ * Searches for the attributes associated with the specified property. If the property is not found, look
+ * in the default properties. If the property is not found in the default properties, answer null.
+ *
+ * @param name the name of the property to find
+ * @return the attributes for an existing property (not null); null for non-existant properties
+ */
+ public Map<String,String> getAttributes(String name) {
+ if (name == null) throw new NullPointerException("name is null");
+
+ name = normalize(name);
+ Map<String, String> attributes = this.attributes.get(name);
+ if (attributes == null && defaults instanceof SuperProperties) {
+ attributes = ((SuperProperties) defaults).getAttributes(name);
+ }
+ return attributes;
+ }
+
+ public void list(PrintStream out) {
+ if (out == null) {
+ throw new NullPointerException();
+ }
+ StringBuffer buffer = new StringBuffer(80);
+ Enumeration<?> keys = propertyNames();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ buffer.append(key);
+ buffer.append('=');
+ String property = (String) get(key);
+ if (property == null) {
+ property = defaults.getProperty(key);
+ }
+ if (property.length() > 40) {
+ buffer.append(property.substring(0, 37));
+ buffer.append("...");
+ } else {
+ buffer.append(property);
+ }
+ out.println(buffer.toString());
+ buffer.setLength(0);
+ }
+ }
+
+ public void list(PrintWriter writer) {
+ if (writer == null) {
+ throw new NullPointerException();
+ }
+ StringBuffer buffer = new StringBuffer(80);
+ Enumeration<?> keys = propertyNames();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ buffer.append(key);
+ buffer.append('=');
+ String property = (String) get(key);
+ while (property == null) {
+ property = defaults.getProperty(key);
+ }
+ if (property.length() > 40) {
+ buffer.append(property.substring(0, 37));
+ buffer.append("...");
+ } else {
+ buffer.append(property);
+ }
+ writer.println(buffer.toString());
+ buffer.setLength(0);
+ }
+ }
+
+ public synchronized void load(InputStream in) throws IOException {
+ // never null, when empty we are processing the white space at the beginning of the line
+ StringBuilder key = new StringBuilder();
+ // null when processing key
+ StringBuilder value = null;
+ // never null, contains the comment for the property or nothing if no comment
+ StringBuilder comment = new StringBuilder();
+ // never null, contains attributes for a property
+ LinkedHashMap<String,String> attributes = new LinkedHashMap<String,String>();
+
+ int indent = 0;
+ boolean globalIndentSet = false;
+
+ int commentIndent = -1;
+ boolean globalCommentIndentSet = false;
+
+ // true when processing the separator between a key and value
+ boolean inSeparator = false;
+
+ while (true) {
+ int nextByte = decodeNextCharacter(in);
+ if (nextByte == EOF) break;
+ char nextChar = (char) (nextByte & 0xff);
+
+ switch (nextByte) {
+ case ' ':
+ case '\t':
+ //
+ // End of key if parsing key
+ //
+ // if parsing the key, this is the end of the key
+ if (key.length() > 0 && value == null) {
+ inSeparator = true;
+ value = new StringBuilder();
+ continue;
+ }
+ break;
+ case ':':
+ case '=':
+ //
+ // End of key
+ //
+ if (inSeparator) {
+ inSeparator = false;
+ continue;
+ }
+ if (value == null) {
+ value = new StringBuilder();
+ continue;
+ }
+ break;
+ case LINE_ENDING:
+ //
+ // End of Line
+ //
+ if (key.length() > 0) {
+ // add property
+ put(key.toString(), value == null ? "" : value.toString());
+ // add comment
+ if (comment.length() > 0) {
+ setComment(key.toString(), comment.toString());
+ comment = new StringBuilder();
+ }
+ // add attributes
+ this.attributes.put(normalize(key.toString()), attributes);
+ attributes = new LinkedHashMap<String,String>();
+ // set line indent
+ if (!globalIndentSet) {
+ setIndent(indent);
+ globalIndentSet = true;
+ }
+ indent = 0;
+ }
+ key = new StringBuilder();
+ value = null;
+ continue;
+ case '#':
+ case '!':
+ //
+ // Comment
+ //
+ if (key.length() == 0) {
+ // set global line indent
+ if (!globalIndentSet) {
+ setIndent(indent);
+ globalIndentSet = true;
+ }
+ indent = 0;
+
+ // read comment Line
+ StringBuilder commentLine = new StringBuilder();
+ int commentLineIndent = 0;
+ boolean inIndent = true;
+ while (true) {
+ nextByte = in.read();
+ if (nextByte < 0) break;
+ nextChar = (char) nextByte; // & 0xff
+
+ if (inIndent && nextChar == ' ') {
+ commentLineIndent++;
+ commentLine.append(' ');
+ } else if (inIndent && nextChar == '\t') {
+ commentLineIndent += 4;
+ commentLine.append(" ");
+ } else if (nextChar == '\r' || nextChar == '\n') {
+ break;
+ } else {
+ inIndent = false;
+ commentLine.append(nextChar);
+ }
+ }
+
+ // Determine indent
+ if (comment.length() == 0) {
+ // if this is a new comment block, the comment indent size for this
+ // block is based the first line of the comment
+ commentIndent = commentLineIndent;
+ if (!globalCommentIndentSet) {
+ setCommentIndent(commentIndent);
+ globalCommentIndentSet = true;
+ }
+ }
+ commentLineIndent = Math.min(commentIndent, commentLineIndent);
+
+ if (commentLine.toString().trim().startsWith("@")) {
+ // process property attribute
+ String attribute = commentLine.toString().trim().substring(1);
+ String[] parts = attribute.split("=", 2);
+ String attributeName = parts[0].trim();
+ String attributeValue = parts.length == 2 ? parts[1].trim() : "";
+ attributes.put(attributeName, attributeValue);
+ } else {
+ // append comment
+ if (comment.length() != 0) {
+ comment.append(LINE_SEPARATOR);
+ }
+ comment.append(commentLine.toString().substring(commentLineIndent));
+ }
+ continue;
+ }
+ break;
+ }
+
+ if (Character.isWhitespace(nextChar)) {
+ // count leading white space
+ if (key.length() == 0) {
+ if (nextChar == '\t') {
+ indent += 4;
+ } else {
+ indent++;
+ }
+ }
+
+ // if key length == 0 or value length == 0
+ if (key.length() == 0 || value == null || value.length() == 0) {
+ continue;
+ }
+ }
+
+ inSeparator = false;
+ if (value == null) {
+ key.append(nextChar);
+ } else {
+ value.append(nextChar);
+ }
+ }
+
+ // if buffer has data, there is a property we still need toadd
+ if (key.length() > 0) {
+ // add property
+ put(key.toString(), value == null ? "" : value.toString());
+ // add comment
+ if (comment.length() > 0) {
+ setComment(key.toString(), comment.toString());
+ }
+ // add attributes
+ this.attributes.put(normalize(key.toString()), attributes);
+ // set line indent
+ if (!globalIndentSet) {
+ setIndent(indent);
+ }
+ }
+ }
+
+ private static final int EOF = -1;
+ private static final int LINE_ENDING = -4200;
+
+ private int decodeNextCharacter(InputStream in) throws IOException {
+ boolean lineContinuation = false;
+ boolean carriageReturnLineContinuation = false;
+ while (true) {
+ // read character
+ int nextByte = in.read();
+ if (nextByte < 0) return EOF;
+ char nextChar = (char) (nextByte & 0xff);
+
+ // if line continuation character was '\r', we need to ignore an optional '\n'
+ // immediately following the \r
+ if (carriageReturnLineContinuation) {
+ carriageReturnLineContinuation = false;
+ if (nextChar == '\n') {
+ continue;
+ }
+ }
+
+ // If escape sequence \x or line continuation, decode it
+ if (nextChar == '\\') {
+ // next character is the escaped character
+ nextByte = in.read();
+ if (nextByte < 0) {
+ // line continuation to end of stream
+ // sun vm returns 0 character for this case
+ nextChar = '\u0000';
+ } else {
+ nextChar = (char) (nextByte & 0xff);
+ }
+
+ switch (nextChar) {
+ case '\r':
+ // line continuation using '\r', which optionally can have a following '\n'
+ carriageReturnLineContinuation = true;
+ // fall through
+ case '\n':
+ // line continuation
+ lineContinuation = true;
+ continue;
+ case 'u':
+ nextChar = readUnicode(in);
+ break;
+ default:
+ nextChar = decodeEscapeChar(nextChar);
+ break;
+ }
+ } else {
+ // if line ending character, we return the special value LINE_ENDING so
+ // caller can differentiate between an encoded "\n" sequence and a real
+ // line ending character in the file
+ if (nextChar == '\n' || nextChar == '\r') {
+ return LINE_ENDING;
+ }
+ }
+
+ // in a line continuation we ignore spaces and tabs until the first real character
+ if (lineContinuation && (nextChar == ' ' || nextChar == '\t')) {
+ continue;
+ }
+
+ return nextChar;
+ }
+ }
+
+ private char decodeEscapeChar(char nextChar) {
+ switch (nextChar) {
+ case 'b':
+ return '\b';
+ case 'f':
+ return'\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'u':
+ throw new IllegalArgumentException("decodeEscapeChar can not decode an unicode sequence");
+ default:
+ return nextChar;
+ }
+ }
+
+ private char readUnicode(InputStream in) throws IOException {
+ char[] buf = new char[4];
+ int unicode = 0;
+ for (int i = 0; i < buf.length; i++) {
+ int nextByte = in.read();
+
+ // we must get exactally 4 bytes
+ if (nextByte < 0) {
+ throw new IllegalArgumentException("Invalid unicode sequence: expected format \\uxxxx, but got \\u" + new String(buf, 0, i));
+ }
+
+ // convert to character
+ char nextChar = (char) (nextByte & 0xff);
+ buf[i] = nextChar;
+
+ // convert to digit
+ int nextDigit = Character.digit(nextChar, 16);
+
+ // all bytes must be valid hex digits
+ if (nextDigit < 0) {
+ throw new IllegalArgumentException("Illegal character " + nextChar + " in unicode sequence \\u" + new String(buf, 0, i + 1));
+ }
+
+
+ unicode = (unicode << 4) + nextDigit;
+ }
+
+ return (char) unicode;
+ }
+
+ public Enumeration<?> propertyNames() {
+ if (defaults == null) {
+ return keys();
+ }
+
+ Hashtable<Object, Object> set = new Hashtable<Object, Object>(defaults.size() + size());
+ Enumeration<?> keys = defaults.propertyNames();
+ while (keys.hasMoreElements()) {
+ set.put(keys.nextElement(), set);
+ }
+ keys = keys();
+ while (keys.hasMoreElements()) {
+ set.put(keys.nextElement(), set);
+ }
+ return set.keys();
+ }
+
+ @SuppressWarnings({"deprecation"})
+ public void save(OutputStream out, String comment) {
+ try {
+ store(out, comment);
+ } catch (IOException e) {
+ }
+ }
+
+ public synchronized void store(OutputStream out, String headComment) throws IOException {
+ OutputStreamWriter writer = new OutputStreamWriter(out, "ISO8859_1");
+ if (headComment != null) {
+ writer.write(indent);
+ writer.write("#");
+ writer.write(commentIndent);
+ writer.write(headComment);
+ writer.write(LINE_SEPARATOR);
+ }
+
+ boolean firstProperty = true;
+ StringBuilder buffer = new StringBuilder(200);
+ for (Map.Entry<Object, Object> entry : entrySet()) {
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+
+ if (!firstProperty && spaceBetweenProperties) {
+ buffer.append(LINE_SEPARATOR);
+ }
+
+ String comment = comments.get(key);
+ Map<String, String> attributes = this.attributes.get(key);
+ if (comment != null || !attributes.isEmpty()) {
+ dumpComment(buffer, comment, attributes, "#");
+ if (spaceAfterComment) {
+ buffer.append(LINE_SEPARATOR);
+ }
+ }
+
+ // ${indent}${key}=${value}
+ buffer.append(indent);
+ dumpString(buffer, key, true);
+ if (value != null && value.length() > 0) {
+ buffer.append(keyValueSeparator);
+ dumpString(buffer, value, false);
+ }
+ buffer.append(LINE_SEPARATOR);
+
+ writer.write(buffer.toString());
+ buffer.setLength(0);
+
+ firstProperty = false;
+ }
+ writer.flush();
+ }
+
+ private void dumpString(StringBuilder buffer, String string, boolean key) {
+ int i = 0;
+ if (!key && i < string.length() && string.charAt(i) == ' ') {
+ buffer.append("\\ ");
+ i++;
+ }
+
+ for (; i < string.length(); i++) {
+ char ch = string.charAt(i);
+ switch (ch) {
+ case '\t':
+ buffer.append("\\t");
+ break;
+ case '\n':
+ buffer.append("\\n");
+ break;
+ case '\f':
+ buffer.append("\\f");
+ break;
+ case '\r':
+ buffer.append("\\r");
+ break;
+ default:
+ if ("\\#!=:".indexOf(ch) >= 0 || (key && ch == ' '))
+ {
+ buffer.append('\\');
+ }
+ if (ch >= ' ' && ch <= '~') {
+ buffer.append(ch);
+ } else {
+ String hex = Integer.toHexString(ch);
+ buffer.append("\\u");
+ for (int j = 0; j < 4 - hex.length(); j++) {
+ buffer.append("0");
+ }
+ buffer.append(hex);
+ }
+ }
+ }
+ }
+
+ private void dumpComment(StringBuilder buffer, String comment, Map<String, String> attributes, String commentToken) {
+ if (comment != null) {
+ boolean startOfLine = true;
+
+ char ch = 0;
+ for (int i = 0; i < comment.length(); i++) {
+ ch = comment.charAt(i);
+
+ if (startOfLine) {
+ buffer.append(indent);
+ buffer.append(commentToken);
+ buffer.append(commentIndent);
+ startOfLine = false;
+ }
+
+ switch (ch) {
+ case '\r':
+ // if next character is not \n, this is the line break
+ if (i+1 < comment.length() && comment.charAt(i+1) != '\n') {
+ buffer.append(LINE_SEPARATOR);
+ startOfLine = true;
+ }
+ break;
+ case '\n':
+ buffer.append(LINE_SEPARATOR);
+ startOfLine = true;
+ break;
+ default:
+ buffer.append(ch);
+ }
+ }
+
+ // if the last character written was not a line break, write one now
+ if (ch != '\r' && ch != '\n'){
+ buffer.append(LINE_SEPARATOR);
+ }
+ }
+
+ // ${indent}#${commentIndent}@${attributeName}=${attributeValue}
+ for (Map.Entry<String, String> entry : attributes.entrySet()) {
+ buffer.append(indent);
+ buffer.append("#");
+ buffer.append(commentIndent);
+ buffer.append("@");
+ buffer.append(entry.getKey());
+ if (entry.getValue() != null && entry.getValue().length() > 0) {
+ buffer.append("=");
+ buffer.append(entry.getValue());
+ }
+ buffer.append(LINE_SEPARATOR);
+ }
+ }
+
+ public synchronized void loadFromXML(InputStream in) throws IOException {
+ if (in == null) {
+ throw new NullPointerException();
+ }
+
+ DocumentBuilder builder = getDocumentBuilder();
+
+ try {
+ Document doc = builder.parse(in);
+ NodeList entries = doc.getElementsByTagName("entry");
+ if (entries == null) {
+ return;
+ }
+
+ int entriesListLength = entries.getLength();
+ for (int i = 0; i < entriesListLength; i++) {
+ Element entry = (Element) entries.item(i);
+ String key = entry.getAttribute("key");
+ String value = entry.getTextContent();
+ put(key, value);
+
+ // search backwards for a comment
+ for (Node node = entry.getPreviousSibling(); node != null && !(node instanceof Element); node = node.getPreviousSibling()) {
+ if (node instanceof Comment) {
+ InputStream cin = new ByteArrayInputStream(((Comment) node).getData().getBytes());
+
+ // read comment line by line
+ StringBuilder comment = new StringBuilder();
+ LinkedHashMap<String,String> attributes = new LinkedHashMap<String,String>();
+
+ int nextByte;
+ char nextChar;
+ boolean firstLine = true;
+ int commentIndent = Integer.MAX_VALUE;
+ do {
+ // read one line
+ StringBuilder commentLine = new StringBuilder();
+ int commentLineIndent = 0;
+ boolean inIndent = true;
+ while (true) {
+ nextByte = cin.read();
+ if (nextByte < 0) break;
+ nextChar = (char) nextByte; // & 0xff
+ if (inIndent && nextChar == ' ') {
+ commentLineIndent++;
+ commentLine.append(' ');
+ } else if (inIndent && nextChar == '\t') {
+ commentLineIndent += 4;
+ commentLine.append(" ");
+ } else if (nextChar == '\r' || nextChar == '\n') {
+ break;
+ } else {
+ inIndent = false;
+ commentLine.append(nextChar);
+ }
+ }
+
+ // Determine indent
+ if (!firstLine && commentIndent == Integer.MAX_VALUE && commentLine.length() > 0) {
+ // if this is a new comment block, the comment indent size for this
+ // block is based the first full line of the comment (ignoring the
+ // line with the <!--
+ commentIndent = commentLineIndent;
+ }
+ commentLineIndent = Math.min(commentIndent, commentLineIndent);
+
+ if (commentLine.toString().trim().startsWith("@")) {
+ // process property attribute
+ String attribute = commentLine.toString().trim().substring(1);
+ String[] parts = attribute.split("=", 2);
+ String attributeName = parts[0].trim();
+ String attributeValue = parts.length == 2 ? parts[1].trim() : "";
+ attributes.put(attributeName, attributeValue);
+ } else {
+ // append comment
+ if (comment.length() != 0) {
+ comment.append(LINE_SEPARATOR);
+ }
+ comment.append(commentLine.toString().substring(commentLineIndent));
+ }
+
+ firstLine = false;
+ } while (nextByte > 0);
+
+ if (comment.length() > 0) {
+ setComment(key, comment.toString());
+ }
+ this.attributes.put(normalize(key), attributes);
+
+ break;
+ }
+ }
+
+ }
+ } catch (SAXException e) {
+ throw new InvalidPropertiesFormatException(e);
+ }
+ }
+
+ private DocumentBuilder getDocumentBuilder() {
+ if (builder == null) {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(true);
+
+ try {
+ builder = factory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new Error(e);
+ }
+
+ builder.setErrorHandler(new ErrorHandler() {
+ public void warning(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ public void error(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ public void fatalError(SAXParseException e) throws SAXException {
+ throw e;
+ }
+ });
+
+ builder.setEntityResolver(new EntityResolver() {
+ public InputSource resolveEntity(String publicId,
+ String systemId) throws SAXException, IOException {
+ if (systemId.equals(PROP_DTD_NAME)) {
+ InputSource result = new InputSource(new StringReader(PROP_DTD));
+ result.setSystemId(PROP_DTD_NAME);
+ return result;
+ }
+ throw new SAXException("Invalid DOCTYPE declaration: " + systemId);
+ }
+ });
+ }
+ return builder;
+ }
+
+ public void storeToXML(OutputStream os, String comment) throws IOException {
+ storeToXML(os, comment, "UTF-8");
+ }
+
+ public synchronized void storeToXML(OutputStream os, String headComment, String encoding) throws IOException {
+ if (os == null || encoding == null) {
+ throw new NullPointerException();
+ }
+
+ // for somereason utf-8 is always used
+ String encodingCanonicalName = "UTF-8";
+
+ // header
+ OutputStreamWriter osw = new OutputStreamWriter(os, encodingCanonicalName);
+ StringBuilder buf = new StringBuilder(200);
+ buf.append("<?xml version=\"1.0\" encoding=\"").append(encodingCanonicalName).append("\"?>").append(LINE_SEPARATOR);
+ buf.append("<!DOCTYPE properties SYSTEM \"" + PROP_DTD_NAME + "\">").append(LINE_SEPARATOR);
+ buf.append("<properties>").append(LINE_SEPARATOR);
+
+ // comment
+ if (headComment != null) {
+ buf.append(indent);
+ buf.append("<comment>");
+ buf.append(substitutePredefinedEntries(headComment));
+ buf.append("</comment>");
+ buf.append(LINE_SEPARATOR);
+
+ if (!isEmpty() && (spaceBetweenProperties || spaceAfterComment)) {
+ buf.append(LINE_SEPARATOR);
+ }
+ }
+
+ // properties
+ boolean firstProperty = true;
+ for (Map.Entry<Object, Object> entry : entrySet()) {
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+
+ if (!firstProperty && spaceBetweenProperties) {
+ buf.append(LINE_SEPARATOR);
+ }
+
+ // property comment
+ String comment = comments.get(key);
+ Map<String, String> attributes = this.attributes.get(key);
+ if (comment != null || !attributes.isEmpty()) {
+ buf.append(indent);
+ buf.append("<!--");
+ buf.append(LINE_SEPARATOR);
+
+ // comments can't contain "--" so we shrink all sequences of them to a single "-"
+ comment = comment.replaceAll("--*", "-");
+ dumpComment(buf, comment, attributes, "");
+
+ buf.append(indent);
+ buf.append("-->");
+ buf.append(LINE_SEPARATOR);
+
+ if (spaceAfterComment) {
+ buf.append(LINE_SEPARATOR);
+ }
+ }
+
+
+ // property
+ buf.append(indent);
+ buf.append("<entry key=\"");
+ buf.append(substitutePredefinedEntries(key));
+ buf.append("\">");
+ buf.append(substitutePredefinedEntries(value));
+ buf.append("</entry>");
+ buf.append(LINE_SEPARATOR);
+
+ firstProperty = false;
+ }
+
+
+ buf.append("</properties>").append(LINE_SEPARATOR);
+
+ osw.write(buf.toString());
+ osw.flush();
+ }
+
+ private String substitutePredefinedEntries(String s) {
+ /*
+ * substitution for predefined character entities
+ * to use them safely in XML
+ */
+ return s.replaceAll("&", "&")
+ .replaceAll("<", "<")
+ .replaceAll(">", ">")
+ .replaceAll("\u0027", "'")
+ .replaceAll("\"", """);
+ }
+
+ //
+ // Delegate all remaining methods to the properties object
+ //
+
+ public boolean isEmpty() {
+ return properties.isEmpty();
+ }
+
+ public int size() {
+ return properties.size();
+ }
+
+ public Object get(Object key) {
+ key = normalize(key);
+ return properties.get(key);
+ }
+
+ public Object put(Object key, Object value) {
+ key = normalize(key);
+ if (key instanceof String) {
+ String name = (String) key;
+ if (!attributes.containsKey(name)) {
+ attributes.put(name, new LinkedHashMap<String,String>());
+ }
+ }
+ return properties.put(key, value);
+ }
+
+ public Object remove(Object key) {
+ key = normalize(key);
+ comments.remove(key);
+ attributes.remove(key);
+ return properties.remove(key);
+ }
+
+ public void putAll(Map<?, ?> t) {
+ for (Map.Entry<?, ?> entry : t.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ if (t instanceof SuperProperties) {
+ SuperProperties superProperties = (SuperProperties) t;
+ for (Map.Entry<String, String> entry : superProperties.comments.entrySet()) {
+ comments.put(normalize(entry.getKey()), entry.getValue());
+ }
+ for (Map.Entry<String, LinkedHashMap<String, String>> entry : superProperties.attributes.entrySet()) {
+ attributes.put(normalize(entry.getKey()), entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * Returns an unmodifiable view of the keys.
+ * @return an unmodifiable view of the keys
+ */
+ public Set<Object> keySet() {
+ return Collections.unmodifiableSet(properties.keySet());
+ }
+
+ public Enumeration<Object> keys() {
+ return Collections.enumeration(properties.keySet());
+ }
+
+ /**
+ * Returns an unmodifiable view of the values.
+ * @return an unmodifiable view of the values
+ */
+ public Collection<Object> values() {
+ return Collections.unmodifiableCollection(properties.values());
+ }
+
+ /**
+ * Returns an unmodifiable view of the entries.
+ * @return an unmodifiable view of the entries
+ */
+ public Set<Map.Entry<Object, Object>> entrySet() {
+ return Collections.unmodifiableSet(properties.entrySet());
+ }
+
+ public Enumeration<Object> elements() {
+ return Collections.enumeration(properties.values());
+ }
+
+ public boolean containsKey(Object key) {
+ key = normalize(key);
+ return properties.containsKey(key);
+ }
+
+ public boolean containsValue(Object value) {
+ return properties.containsValue(value);
+ }
+
+ public boolean contains(Object value) {
+ return properties.containsValue(value);
+ }
+
+ public void clear() {
+ properties.clear();
+ comments.clear();
+ attributes.clear();
+ }
+
+ @SuppressWarnings({"unchecked"})
+ public Object clone() {
+ SuperProperties clone = (SuperProperties) super.clone();
+ clone.properties = (LinkedHashMap<Object,Object>) properties.clone();
+ clone.comments = (LinkedHashMap<String,String>) comments.clone();
+ clone.attributes = (LinkedHashMap<String,LinkedHashMap<String,String>>) attributes.clone();
+ for (Map.Entry<String, LinkedHashMap<String, String>> entry : clone.attributes.entrySet()) {
+ entry.setValue((LinkedHashMap<String, String>) entry.getValue().clone());
+ }
+ return clone;
+ }
+
+ @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
+ public boolean equals(Object o) {
+ return properties.equals(o);
+ }
+
+ public int hashCode() {
+ return properties.hashCode();
+ }
+
+ public String toString() {
+ return properties.toString();
+ }
+
+ protected void rehash() {
+ }
+
+ private Object normalize(Object key){
+ if (key instanceof String) {
+ return normalize((String)key);
+ }
+ return key;
+ }
+
+ private String normalize(String property){
+ if (!caseInsensitive) {
+ return property;
+ }
+
+ if (super.containsKey(property)){
+ return property;
+ }
+
+ for (Object o : keySet()) {
+ if (o instanceof String) {
+ String key = (String) o;
+ if (key.equalsIgnoreCase(property)) return key;
+ }
+ }
+
+ if (defaults != null) {
+ for (Object o : defaults.keySet()) {
+ if (o instanceof String) {
+ String key = (String) o;
+ if (key.equalsIgnoreCase(property)) return key;
+ }
+ }
+ }
+
+ return property;
+ }
+}
Added: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PropertiesTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PropertiesTest.java?rev=613782&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PropertiesTest.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/PropertiesTest.java Sun Jan 20 23:09:58 2008
@@ -0,0 +1,351 @@
+/**
+ *
+ * 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.openejb.util;
+
+import java.io.ByteArrayInputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+public class PropertiesTest extends TestCase {
+
+ public void testProperties() throws Exception {
+ Properties properties = createProperties();
+
+ // empty
+ assertTrue(properties.isEmpty());
+ assertEquals(0, properties.size());
+ assertTrue(properties.keySet().isEmpty());
+ assertEquals(0, properties.keySet().size());
+ assertTrue(properties.values().isEmpty());
+ assertEquals(0, properties.values().size());
+ assertTrue(properties.entrySet().isEmpty());
+ assertEquals(0, properties.entrySet().size());
+ assertFalse(properties.keys().hasMoreElements());
+ assertFalse(properties.propertyNames().hasMoreElements());
+ assertFalse(properties.elements().hasMoreElements());
+ assertNull(properties.get("foo"));
+ assertNull(properties.getProperty("foo"));
+ assertEquals("default", properties.getProperty("foo", "default"));
+
+ // one entry
+ assertNull(properties.put("foo", "bar"));
+ assertFalse(properties.isEmpty());
+ assertEquals(1, properties.size());
+ assertFalse(properties.keySet().isEmpty());
+ assertEquals(1, properties.keySet().size());
+ assertEquals(Collections.singleton("foo"), properties.keySet());
+ assertFalse(properties.values().isEmpty());
+ assertEquals(1, properties.values().size());
+ assertFalse(properties.entrySet().isEmpty());
+ assertEquals(1, properties.entrySet().size());
+ assertTrue(properties.keys().hasMoreElements());
+ assertEquals("foo", properties.keys().nextElement());
+ assertTrue(properties.propertyNames().hasMoreElements());
+ assertEquals("foo", properties.keys().nextElement());
+ assertTrue(properties.elements().hasMoreElements());
+ assertEquals("bar", properties.get("foo"));
+ assertEquals("bar", properties.getProperty("foo"));
+ assertEquals("bar", properties.getProperty("foo", "default"));
+
+ }
+
+ public void testSimpleLoad() throws Exception {
+ Properties properties;
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo:bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo = bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo : bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo\t \t=\t \tbar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo\t \t:\t \tbar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo".getBytes()));
+ assertEquals(singletonProperty("foo", ""), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=".getBytes()));
+ assertEquals(singletonProperty("foo", ""), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo:".getBytes()));
+ assertEquals(singletonProperty("foo", ""), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo =".getBytes()));
+ assertEquals(singletonProperty("foo", ""), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo :".getBytes()));
+ assertEquals(singletonProperty("foo", ""), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo = ".getBytes()));
+ assertEquals(singletonProperty("foo", ""), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo : ".getBytes()));
+ assertEquals(singletonProperty("foo", ""), properties);
+
+ //
+ // Invalid key valid separator (results in separator in value)
+ //
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo := bar".getBytes()));
+ assertEquals(singletonProperty("foo", "= bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo == bar".getBytes()));
+ assertEquals(singletonProperty("foo", "= bar"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo :: bar".getBytes()));
+ assertEquals(singletonProperty("foo", ": bar"), properties);
+
+ }
+
+ public void testUnicode() throws Exception {
+ Properties properties = createProperties();
+ properties.load(new ByteArrayInputStream("a=\\u1234z".getBytes()));
+
+ try {
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("a=\\u123".getBytes()));
+ fail("Expected IllegalArgumentException due to invalid unicode sequence");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("a=\\u123z".getBytes()));
+ fail("Expected IllegalArgumentException due to invalid unicode sequence");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ properties = new SuperProperties();
+ properties.load(new ByteArrayInputStream("a=\\".getBytes()));
+ assertEquals(singletonProperty("a", "\u0000"), properties);
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("a=\\q".getBytes()));
+ assertEquals(singletonProperty("a", "q"), properties);
+ }
+
+ public void testKeyLineContinuation() throws Exception {
+ Properties properties;
+
+ // line continuation (\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\noo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ // line continuation with white space (\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\n oo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\n\t\too=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\n \t oo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ // line continuation (\r)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\roo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ // line continuation (\r) with white space
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\r oo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\r\t\too=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\r \t oo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ // line continuation (\r\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\r\noo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ //
+ // Invalid contunuations (results in a break)
+ //
+
+ // line continuation (\n\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\n\noo=bar".getBytes()));
+ assertEquals(properties("f", "", "oo", "bar"), properties);
+
+ // line continuation (\r\r)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\r\roo=bar".getBytes()));
+ assertEquals(properties("f", "", "oo", "bar"), properties);
+
+ // line continuation (\r\n\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\r\n\noo=bar".getBytes()));
+ assertEquals(properties("f", "", "oo", "bar"), properties);
+
+ // line continuation (\r\n\r)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("f\\\r\n\roo=bar".getBytes()));
+ assertEquals(properties("f", "", "oo", "bar"), properties);
+ }
+
+ public void testValueLineContinuation() throws Exception {
+ Properties properties;
+
+ // line continuation (\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\nar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ // line continuation with white space (\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\n ar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\n\t\tar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\n \t ar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ // line continuation (\r)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\rar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ // line continuation (\r) with white space
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\r ar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\r\t\tar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\r \t ar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ // line continuation (\r\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\r\nar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ //
+ // Invalid contunuations (results in a break)
+ //
+
+ // line continuation (\n\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\n\nar".getBytes()));
+ assertEquals(properties("foo", "b", "ar", ""), properties);
+
+ // line continuation (\r\r)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\r\rar".getBytes()));
+ assertEquals(properties("foo", "b", "ar", ""), properties);
+
+ // line continuation (\r\n\n)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\r\n\nar".getBytes()));
+ assertEquals(properties("foo", "b", "ar", ""), properties);
+
+ // line continuation (\r\n\r)
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("foo=b\\\r\n\rar".getBytes()));
+ assertEquals(properties("foo", "b", "ar", ""), properties);
+ }
+
+ protected Properties createProperties() {
+ return new Properties();
+ }
+
+ protected static Properties singletonProperty(String key, String value) {
+ return properties(key, value);
+ }
+
+ protected static Properties properties(String... keysAndValues) {
+ Properties properties = new Properties();
+ for (int i = 0; i+1 < keysAndValues.length; i += 2) {
+ String key = keysAndValues[i];
+ String value = keysAndValues[i + 1];
+ properties.put(key, value);
+ }
+ return properties;
+ }
+
+ @SuppressWarnings({"unchecked"})
+ protected static void assertProperties(Properties expected, Properties actual) {
+ if (expected.equals(actual)) return;
+
+ StringBuilder message = new StringBuilder().append("\n");
+
+ Set<String> keys = new TreeSet<String>();
+ keys.addAll(new HashSet(expected.keySet()));
+ keys.addAll(new HashSet(actual.keySet()));
+ for (String key : keys) {
+ if (!expected.containsKey(key)) {
+ message.append("A ").append(key).append("=").append(actual.get(key)).append("\n");
+ } else if (!actual.containsKey(key)) {
+ message.append("R ").append(key).append("=").append(expected.get(key)).append("\n");
+ } else {
+ Object expectedValue = expected.get(key);
+ Object actualValue = actual.get(key);
+ if (expectedValue != expectedValue && (expectedValue == null || !expectedValue.equals(actual))) {
+ message.append("C ").append(key).append("=").append(expectedValue).append("\n");
+ message.append(" ").append(key).append("=").append(actualValue).append("\n");
+ }
+
+ }
+ }
+
+ fail(message.toString());
+ }
+}
Added: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/SuperPropertiesTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/SuperPropertiesTest.java?rev=613782&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/SuperPropertiesTest.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/SuperPropertiesTest.java Sun Jan 20 23:09:58 2008
@@ -0,0 +1,389 @@
+/**
+ *
+ * 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.openejb.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+
+public class SuperPropertiesTest extends PropertiesTest {
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public void testCaseSensitivity() throws Exception {
+ SuperProperties properties = createProperties();
+ properties.setProperty("foo", "bar");
+ properties.setComment("foo", "comment");
+ properties.getAttributes("foo").put("name", "value");
+
+ // case sensitive
+ assertTrue(properties.containsKey("foo"));
+ assertEquals("bar", properties.get("foo"));
+ assertEquals("bar", properties.getProperty("foo"));
+ assertEquals("comment", properties.getComment("foo"));
+ assertNotNull(properties.getAttributes("foo"));
+ assertEquals("value", properties.getAttributes("foo").get("name"));
+ assertFalse(properties.containsKey("FOO"));
+ assertNull(properties.get("FOO"));
+ assertNull(properties.getProperty("FOO"));
+ assertNull(properties.getComment("FOO"));
+ assertNull(properties.getAttributes("FOO"));
+
+ // property differing only incase
+ properties.setProperty("FOO", "BAR");
+ properties.setComment("FOO", "COMMENT");
+ properties.getAttributes("FOO").put("NAME", "VALUE");
+ assertTrue(properties.containsKey("foo"));
+ assertEquals("bar", properties.get("foo"));
+ assertEquals("bar", properties.getProperty("foo"));
+ assertEquals("comment", properties.getComment("foo"));
+ assertNotNull(properties.getAttributes("foo"));
+ assertEquals("value", properties.getAttributes("foo").get("name"));
+ assertTrue(properties.containsKey("FOO"));
+ assertEquals("BAR", properties.get("FOO"));
+ assertEquals("BAR", properties.getProperty("FOO"));
+ assertEquals("COMMENT", properties.getComment("FOO"));
+ assertNotNull(properties.getAttributes("FOO"));
+ assertEquals("VALUE", properties.getAttributes("FOO").get("NAME"));
+
+ // case insensitive
+ properties = createProperties();
+ properties.setCaseInsensitive(true);
+ properties.setProperty("foo", "bar");
+ properties.setComment("foo", "comment");
+ properties.getAttributes("foo").put("name", "value");
+ assertTrue(properties.containsKey("foo"));
+ assertEquals("bar", properties.get("foo"));
+ assertEquals("bar", properties.getProperty("foo"));
+ assertEquals("comment", properties.getComment("foo"));
+ assertNotNull(properties.getAttributes("foo"));
+ assertEquals("value", properties.getAttributes("foo").get("name"));
+ assertTrue(properties.containsKey("FOO"));
+ assertEquals("bar", properties.get("FOO"));
+ assertEquals("bar", properties.getProperty("FOO"));
+ assertEquals("comment", properties.getComment("FOO"));
+ assertNotNull(properties.getAttributes("FOO"));
+ assertEquals("value", properties.getAttributes("FOO").get("name"));
+
+ // property differing only incase
+ properties.setProperty("FOO", "BAR");
+ properties.setComment("FOO", "COMMENT");
+ properties.getAttributes("FOO").put("name", "VALUE");
+ assertTrue(properties.containsKey("foo"));
+ assertEquals("BAR", properties.get("foo"));
+ assertEquals("BAR", properties.getProperty("foo"));
+ assertEquals("COMMENT", properties.getComment("foo"));
+ assertNotNull(properties.getAttributes("foo"));
+ assertEquals("VALUE", properties.getAttributes("foo").get("name"));
+ assertTrue(properties.containsKey("FOO"));
+ assertEquals("BAR", properties.get("FOO"));
+ assertEquals("BAR", properties.getProperty("FOO"));
+ assertEquals("COMMENT", properties.getComment("FOO"));
+ assertNotNull(properties.getAttributes("FOO"));
+ assertEquals("VALUE", properties.getAttributes("FOO").get("name"));
+
+ }
+
+ public void testSynchronization() throws Exception {
+ SuperProperties properties = createProperties();
+ properties.setProperty("foo", "bar");
+ properties.setComment("foo", "comment");
+ assertNotNull(properties.getAttributes("foo"));
+ properties.getAttributes("foo").put("name", "value");
+
+ // changing a property value should not effect comments or attributes
+ properties.put("foo", "bar2");
+ assertEquals("comment", properties.getComment("foo"));
+ assertNotNull(properties.getAttributes("foo"));
+ assertEquals("value", properties.getAttributes("foo").get("name"));
+
+ // removing a property should remove comments and attributes
+ properties.remove("foo");
+ assertNull(properties.getComment("foo"));
+ assertNull(properties.getAttributes("foo"));
+ }
+
+ public void testLoadStoreLoad() throws Exception {
+ SuperProperties expected = new SuperProperties();
+ expected.load(getClass().getResourceAsStream("test.properties"));
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ expected.store(out, null);
+
+ SuperProperties actual = createProperties();
+ actual.load(new ByteArrayInputStream(out.toByteArray()));
+
+ assertProperties(expected, actual);
+ }
+
+ public void testLoadStoreLoadXml() throws Exception {
+ SuperProperties expected = new SuperProperties();
+ expected.load(getClass().getResourceAsStream("test.properties"));
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ expected.storeToXML(out, null);
+
+ SuperProperties actual = createProperties();
+ actual.loadFromXML(new ByteArrayInputStream(out.toByteArray()));
+
+ assertProperties(expected, actual);
+ }
+
+ public void testStore() throws Exception {
+ SuperProperties properties = createProperties();
+ assertTrue(properties.isSpaceBetweenProperties());
+ assertFalse(properties.isSpaceAfterComment());
+
+ // one property
+ properties.setProperty("foo", "bar");
+ assertEquals("foo=bar\n", store(properties));
+
+ properties.setIndent(4);
+ assertEquals(" foo=bar\n", store(properties));
+
+ // two properties
+ properties.setProperty("number", "42");
+ assertEquals(" foo=bar\n\n number=42\n", store(properties));
+
+ properties.setSpaceBetweenProperties(false);
+ assertEquals(" foo=bar\n number=42\n", store(properties));
+
+ // one comment
+ properties.setComment("foo", "foo comment");
+ assertEquals(" # foo comment\n foo=bar\n number=42\n", store(properties));
+
+ properties.setCommentIndent(0);
+ assertEquals(" #foo comment\n foo=bar\n number=42\n", store(properties));
+
+ properties.setCommentIndent(2);
+ assertEquals(" # foo comment\n foo=bar\n number=42\n", store(properties));
+
+ properties.setSpaceAfterComment(true);
+ assertEquals(" # foo comment\n\n foo=bar\n number=42\n", store(properties));
+
+ properties.setSpaceBetweenProperties(true);
+ assertEquals(" # foo comment\n\n foo=bar\n\n number=42\n", store(properties));
+
+ // one attribute
+ properties.getAttributes("foo").put("name", "value");
+ assertEquals(" # foo comment\n # @name=value\n\n foo=bar\n\n number=42\n", store(properties));
+
+ properties.getAttributes("foo").put("name", null);
+ assertEquals(" # foo comment\n # @name\n\n foo=bar\n\n number=42\n", store(properties));
+
+ properties.getAttributes("foo").put("name", "");
+ assertEquals(" # foo comment\n # @name\n\n foo=bar\n\n number=42\n", store(properties));
+
+ // two attribute
+ properties.getAttributes("number").put("hidden", "yes");
+ assertEquals(" # foo comment\n # @name\n\n foo=bar\n\n # @hidden=yes\n\n number=42\n", store(properties));
+
+ // key value separator
+ properties = createProperties();
+ properties.setProperty("foo", "bar");
+ properties.setKeyValueSeparator(" ");
+ assertEquals("foo bar\n", store(properties));
+
+ properties.setKeyValueSeparator(":");
+ assertEquals("foo:bar\n", store(properties));
+
+ properties.setKeyValueSeparator(" = ");
+ assertEquals("foo = bar\n", store(properties));
+
+ properties.setKeyValueSeparator("XXXX");
+ assertEquals("fooXXXXbar\n", store(properties));
+ }
+
+ public void testLoadComments() throws Exception {
+ SuperProperties properties;
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("# Comment\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals("Comment", properties.getComment("foo"));
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("# Line1\n# Line2\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals("Line1\nLine2", properties.getComment("foo"));
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("# Comment\n# Indented\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals("Comment\n Indented", properties.getComment("foo"));
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("# Comment\n#Outdented\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals("Comment\nOutdented", properties.getComment("foo"));
+ }
+
+ public void testLoadCommentsXml() throws Exception {
+ SuperProperties properties;
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", null).getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", "\nComment\n").getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals("Comment\n", properties.getComment("foo"));
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", "Line1\n Line2\n").getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals("Line1\nLine2\n", properties.getComment("foo"));
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", "Line1\n Line2\n Indented").getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals("Line1\nLine2\n Indented", properties.getComment("foo"));
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", "Line1\n Line2\nOutdented").getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals("Line1\nLine2\nOutdented", properties.getComment("foo"));
+ }
+
+ public void testLoadAttributes() throws Exception {
+ SuperProperties properties;
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("# @name=value\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(map("name", "value"), properties.getAttributes("foo"));
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("# @name\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(map("name", ""), properties.getAttributes("foo"));
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("# @name=\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(map("name", ""), properties.getAttributes("foo"));
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream("# @a=b\n# @c=d\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(map("a", "b", "c", "d"), properties.getAttributes("foo"));
+ }
+
+ public void testLoadAttributesXml() throws Exception {
+ SuperProperties properties;
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", "@name=value").getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(map("name", "value"), properties.getAttributes("foo"));
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", "@name").getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(map("name", ""), properties.getAttributes("foo"));
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", "@name=").getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(map("name", ""), properties.getAttributes("foo"));
+
+ properties = createProperties();
+ properties.loadFromXML(new ByteArrayInputStream(getXml("foo", "bar", "@a = b \n@ c = d ").getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(map("a", "b", "c", "d"), properties.getAttributes("foo"));
+ }
+
+ public void testIndentDetection() throws Exception {
+ SuperProperties properties;
+ properties = createProperties();
+ assertEquals(0, properties.getIndent());
+ assertEquals(1, properties.getCommentIndent());
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream(" foo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(4, properties.getIndent());
+ assertEquals(1, properties.getCommentIndent());
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream(" # Comment\n foo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(4, properties.getIndent());
+ assertEquals(2, properties.getCommentIndent());
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream(" # Line1\n# Line2\n foo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(4, properties.getIndent());
+ assertEquals(2, properties.getCommentIndent());
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream(" # Comment\n# Indented\n foo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(4, properties.getIndent());
+ assertEquals(2, properties.getCommentIndent());
+
+ properties = createProperties();
+ properties.load(new ByteArrayInputStream(" # Comment\n#Outdented\nfoo=bar".getBytes()));
+ assertEquals(singletonProperty("foo", "bar"), properties);
+ assertEquals(4, properties.getIndent());
+ assertEquals(2, properties.getCommentIndent());
+ }
+
+ protected String store(Properties properties) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ properties.store(out, null);
+ return new String(out.toByteArray());
+ }
+
+ protected SuperProperties createProperties() {
+ return new SuperProperties();
+ }
+
+ protected static Map<String,String> map(String... keysAndValues) {
+ Map<String,String> map = new LinkedHashMap<String,String>();
+ for (int i = 0; i+1 < keysAndValues.length; i += 2) {
+ String key = keysAndValues[i];
+ String value = keysAndValues[i + 1];
+ map.put(key, value);
+ }
+ return map;
+ }
+
+ private String getXml(String key, String value, String comment) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ buf.append("<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">\n");
+ buf.append("<properties>\n");
+
+ if (comment != null) {
+ buf.append(" <!--").append(comment).append("-->\n");
+ }
+ buf.append(" <entry key=\"").append(key).append("\">").append(value).append("</entry>\n");
+
+ buf.append("</properties>\n");
+
+ return buf.toString();
+ }
+}