FELIX-4844 : Store configuration data in a diff-tool friendly way. Apply patch from Balazs Zsoldos
This closes #16
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1678871 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/changelog.txt b/configadmin/changelog.txt
index 622da53..e17efbe 100644
--- a/configadmin/changelog.txt
+++ b/configadmin/changelog.txt
@@ -4,6 +4,9 @@
** Bug
* [FELIX-4884] - listConfigurations should return null if no configuration is found
+** Improvement
+ * [FELIX-4844] - Store configuration data in a diff-tool friendly way
+
Changes from 1.8.2 to 1.8.4
---------------------------
diff --git a/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java b/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java
index 1d53ad2..d1a54a9 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java
@@ -30,8 +30,10 @@
import java.io.Writer;
import java.lang.reflect.Array;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
+import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
@@ -95,6 +97,8 @@
protected static final int TOKEN_PRIMITIVE_BOOLEAN = 'b';
protected static final String CRLF = "\r\n";
+ protected static final String INDENT = " ";
+ protected static final String COLLECTION_LINE_BREAK = " \\\r\n";
protected static final Map code2Type;
protected static final Map type2Code;
@@ -200,7 +204,7 @@
{
BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( out, ENCODING ) );
- for ( Enumeration ce = properties.keys(); ce.hasMoreElements(); )
+ for ( Enumeration ce = orderedKeys(properties); ce.hasMoreElements(); )
{
String key = ( String ) ce.nextElement();
@@ -214,6 +218,28 @@
bw.flush();
}
+ /**
+ * Generates an <code>Enumeration</code> for the given
+ * <code>Dictionary</code> where the keys of the <code>Dictionary</code>
+ * are provided in sorted order.
+ *
+ * @param properties
+ * The <code>Dictionary</code> that keys are sorted.
+ * @return An <code>Enumeration</code> that provides the keys of
+ * properties in an ordered manner.
+ */
+ private static Enumeration orderedKeys(Dictionary properties) {
+ String[] keyArray = new String[properties.size()];
+ int i = 0;
+ for ( Enumeration ce = properties.keys(); ce.hasMoreElements(); )
+ {
+ keyArray[i] = ( String ) ce.nextElement();
+ i++;
+ }
+ Arrays.sort(keyArray);
+ return Collections.enumeration( Arrays.asList( keyArray ) );
+ }
+
/**
* Reads configuration data from the given <code>InputStream</code> and
@@ -336,7 +362,7 @@
List list = new ArrayList();
for ( ;; )
{
- int c = read(pr);
+ int c = ignorablePageBreakAndWhiteSpace( pr );
if ( c == TOKEN_VAL_OPEN )
{
Object value = readSimple( typeCode, pr );
@@ -350,7 +376,7 @@
list.add( value );
- c = read( pr );
+ c = ignorablePageBreakAndWhiteSpace( pr );
}
if ( c == TOKEN_ARR_CLOS )
@@ -380,7 +406,7 @@
Collection collection = new ArrayList();
for ( ;; )
{
- int c = read( pr );
+ int c = ignorablePageBreakAndWhiteSpace( pr );
if ( c == TOKEN_VAL_OPEN )
{
Object value = readSimple( typeCode, pr );
@@ -394,7 +420,7 @@
collection.add( value );
- c = read( pr );
+ c = ignorablePageBreakAndWhiteSpace( pr );
}
if ( c == TOKEN_VEC_CLOS )
@@ -480,23 +506,6 @@
}
- private boolean checkNext( PushbackReader pr, int expected ) throws IOException
- {
- int next = read( pr );
- if ( next < 0 )
- {
- return false;
- }
-
- if ( next == expected )
- {
- return true;
- }
-
- return false;
- }
-
-
private String readQuoted( PushbackReader pr ) throws IOException
{
StringBuffer buf = new StringBuffer();
@@ -599,6 +608,28 @@
}
+ private int ignorablePageBreakAndWhiteSpace( PushbackReader pr ) throws IOException
+ {
+ int c = ignorableWhiteSpace( pr );
+ for ( ;; )
+ {
+ if ( c != '\\' )
+ {
+ break;
+ }
+ int c1 = pr.read();
+ if ( c1 == '\r' || c1 == '\n' )
+ {
+ c = ignorableWhiteSpace( pr );
+ } else {
+ pr.unread(c1);
+ break;
+ }
+ }
+ return c;
+ }
+
+
private int read( PushbackReader pr ) throws IOException
{
int c = pr.read();
@@ -678,12 +709,12 @@
int size = Array.getLength( arrayValue );
writeType( out, arrayValue.getClass().getComponentType() );
out.write( TOKEN_ARR_OPEN );
+ out.write( COLLECTION_LINE_BREAK );
for ( int i = 0; i < size; i++ )
{
- if ( i > 0 )
- out.write( TOKEN_COMMA );
- writeSimple( out, Array.get( arrayValue, i ) );
+ writeCollectionElement(out, Array.get( arrayValue, i ));
}
+ out.write( INDENT );
out.write( TOKEN_ARR_CLOS );
}
@@ -693,6 +724,7 @@
if ( collection.isEmpty() )
{
out.write( TOKEN_VEC_OPEN );
+ out.write( COLLECTION_LINE_BREAK );
out.write( TOKEN_VEC_CLOS );
}
else
@@ -702,18 +734,27 @@
writeType( out, firstElement.getClass() );
out.write( TOKEN_VEC_OPEN );
- writeSimple( out, firstElement );
+ out.write( COLLECTION_LINE_BREAK );
+
+ writeCollectionElement( out, firstElement );
while ( ci.hasNext() )
{
- out.write( TOKEN_COMMA );
- writeSimple( out, ci.next() );
+ writeCollectionElement( out, ci.next() );
}
out.write( TOKEN_VEC_CLOS );
}
}
+ private static void writeCollectionElement(Writer out, Object element) throws IOException {
+ out.write( INDENT );
+ writeSimple( out, element );
+ out.write( TOKEN_COMMA );
+ out.write(COLLECTION_LINE_BREAK);
+ }
+
+
private static void writeType( Writer out, Class valueType ) throws IOException
{
Integer code = ( Integer ) type2Code.get( valueType );
diff --git a/configadmin/src/test/java/org/apache/felix/cm/file/FilePersistenceManagerTest.java b/configadmin/src/test/java/org/apache/felix/cm/file/FilePersistenceManagerTest.java
index b8a03a1..ad41462 100644
--- a/configadmin/src/test/java/org/apache/felix/cm/file/FilePersistenceManagerTest.java
+++ b/configadmin/src/test/java/org/apache/felix/cm/file/FilePersistenceManagerTest.java
@@ -19,7 +19,9 @@
package org.apache.felix.cm.file;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
@@ -29,6 +31,7 @@
import java.util.Hashtable;
import java.util.Vector;
+import junit.framework.Assert;
import junit.framework.TestCase;
@@ -253,6 +256,37 @@
check( "LPT1", "lpt1" );
}
+ public void testKeyOrderInFile() throws IOException
+ {
+ Dictionary props = new Hashtable();
+ // The following keys are stored as "c, a, b" in HashTable based
+ // due to their hash code
+ props.put( "a_first", "a" );
+ props.put( "b_second", "b" );
+ props.put( "c_third", "c" );
+
+ String pid = "keyOrderInFile";
+ fpm.store( pid, props );
+ File configFile = new File( file, fpm.encodePid( pid ) + ".config" );
+ FileReader reader = new FileReader( configFile );
+ BufferedReader breader = new BufferedReader(reader);
+ try
+ {
+ String previousLine = breader.readLine();
+ while ( previousLine != null)
+ {
+ String line = breader.readLine();
+ if (line != null) {
+ Assert.assertTrue( previousLine.compareTo( line ) < 0 );
+ }
+ previousLine = line;
+ }
+ }
+ finally
+ {
+ breader.close();
+ }
+ }
private void check( String name, Object value ) throws IOException
{