r787 - in trunk/src: main/java/org/nuiton/io test/java/org/nuiton/io test/resources/org/nuiton/io
Author: tchemit Date: 2010-11-19 16:57:37 +0100 (Fri, 19 Nov 2010) New Revision: 787 Url: http://nuiton.org/repositories/revision/maven-helper-plugin/787 Log: Evolution #1081: improve SortedProperties class Added: trunk/src/test/java/org/nuiton/io/SortedPropertiesTest.java trunk/src/test/resources/org/nuiton/io/unicodeLower.txt trunk/src/test/resources/org/nuiton/io/unicodeUpper.txt Modified: trunk/src/main/java/org/nuiton/io/SortedProperties.java Modified: trunk/src/main/java/org/nuiton/io/SortedProperties.java =================================================================== --- trunk/src/main/java/org/nuiton/io/SortedProperties.java 2010-11-19 11:11:42 UTC (rev 786) +++ trunk/src/main/java/org/nuiton/io/SortedProperties.java 2010-11-19 15:57:37 UTC (rev 787) @@ -25,7 +25,9 @@ package org.nuiton.io; -import org.nuiton.plugin.*; +import org.nuiton.plugin.PluginHelper; + +import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -33,7 +35,9 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.Reader; import java.util.Collections; +import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Properties; @@ -42,37 +46,68 @@ /** * Permet d'avoir les fichiers de proprietes tries. * - * @author julien + * @author ruchaud <ruchaud@codelutin.com> * @author tchemit <chemit@codelutin.com> */ public class SortedProperties extends Properties { private static final long serialVersionUID = -1147150444452577558L; + + /** A table of hex digits in upper case */ + private static final char[] hexDigitUpper = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + /** A table of hex digits in lower case */ + private static final char[] hexDigitLower = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + /** l'encoding a utiliser pour lire et ecrire le properties. */ protected String encoding; + /** un drapeau pour savoir s'il faut enlever l'entete generere */ protected boolean removeHeader; + /** + * A flag to write unicode using the a lower letter. + * <p/> + * Example : {@code \u00e9} instead of {@code \u00E9}. + */ + final protected boolean unicodeLower; + + final protected char[] hexDigit; + + public SortedProperties(String encoding) { - this(encoding, true); + this(encoding, true, false); } public SortedProperties(String encoding, boolean removeHeader) { + this(encoding, removeHeader, false); + } + + public SortedProperties(String encoding, boolean removeHeader, boolean unicodeLower) { this.encoding = encoding; this.removeHeader = removeHeader; + this.unicodeLower = unicodeLower; + hexDigit = unicodeLower ? hexDigitLower : hexDigitUpper; } public SortedProperties(Properties defaults) { super(defaults); + unicodeLower = false; + hexDigit = hexDigitUpper; } @Override - public synchronized Enumeration<Object> keys() { + public Enumeration<Object> keys() { List<Object> objects = Collections.list(super.keys()); Vector<Object> result; try { // Attention, si les clef ne sont pas des string, ca ne marchera pas - List<String> list = PluginHelper.toGenericList(objects, String.class); + List<String> list = + PluginHelper.toGenericList(objects, String.class); Collections.sort(list); result = new Vector<Object>(list); } catch (IllegalArgumentException e) { @@ -91,7 +126,12 @@ * @throws IOException if any io pb */ public SortedProperties load(File src) throws IOException { - super.load(new InputStreamReader(new FileInputStream(src), encoding)); + Reader reader = new InputStreamReader(new FileInputStream(src), encoding); + try { + load(reader); + } finally { + reader.close(); + } return this; } @@ -102,24 +142,180 @@ * @throws IOException if any io pb */ public void store(File dst) throws IOException { - if (removeHeader) { - super.store(new OutputStreamWriter(new PropertiesDateRemoveFilterStream(new FileOutputStream(dst)), encoding), null); - } else { - super.store(new FileOutputStream(dst), null); + OutputStream writer = new FileOutputStream(dst); + try { + store(writer, null); + } finally { + writer.close(); } } /** - * Sauvegarde le properties dans un fichier, sans commentaire en laissant java encode en unicode. + * Sauvegarde le properties dans un fichier, sans commentaire en laissant + * java encode en unicode. * * @param dst le fichier de destination * @throws IOException if any io pb */ public void store(OutputStream dst) throws IOException { - if (removeHeader) { - super.store(new PropertiesDateRemoveFilterStream(dst), null); - } else { - super.store(dst, null); + store(dst, null); + } + + @Override + public void store(OutputStream out, String comments) + throws IOException { + store0(new BufferedWriter(new OutputStreamWriter(out, encoding)), +// store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")), +comments, +true); + } + + protected void store0(BufferedWriter bw, String comments, boolean escUnicode) + throws IOException { + if (comments != null) { + writeComments(bw, comments); } + if (!removeHeader) { + bw.write("#" + new Date().toString()); + bw.newLine(); + } + synchronized (this) { + for (Enumeration e = keys(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + String val = (String) get(key); + key = saveConvert(key, true, escUnicode); + /* No need to escape embedded and trailing spaces for value, hence + * pass false to flag. + */ + val = saveConvert(val, false, escUnicode); + bw.write(key + "=" + val); + bw.newLine(); + } + } + bw.flush(); } + + /* + * Converts unicodes to encoded \uxxxx and escapes + * special characters with a preceding slash + */ + + protected String saveConvert(String theString, + boolean escapeSpace, + boolean escapeUnicode) { + int len = theString.length(); + int bufLen = len * 2; + if (bufLen < 0) { + bufLen = Integer.MAX_VALUE; + } + StringBuffer outBuffer = new StringBuffer(bufLen); + + for (int x = 0; x < len; x++) { + char aChar = theString.charAt(x); + // Handle common case first, selecting largest block that + // avoids the specials below + if ((aChar > 61) && (aChar < 127)) { + if (aChar == '\\') { + outBuffer.append('\\'); + outBuffer.append('\\'); + continue; + } + outBuffer.append(aChar); + continue; + } + switch (aChar) { + case ' ': + if (x == 0 || escapeSpace) + outBuffer.append('\\'); + outBuffer.append(' '); + break; + case '\t': + outBuffer.append('\\'); + outBuffer.append('t'); + break; + case '\n': + outBuffer.append('\\'); + outBuffer.append('n'); + break; + case '\r': + outBuffer.append('\\'); + outBuffer.append('r'); + break; + case '\f': + outBuffer.append('\\'); + outBuffer.append('f'); + break; + case '=': // Fall through + case ':': // Fall through + case '#': // Fall through + case '!': + outBuffer.append('\\'); + outBuffer.append(aChar); + break; + default: + if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) { + outBuffer.append('\\'); + outBuffer.append('u'); + outBuffer.append(toHex((aChar >> 12) & 0xF)); + outBuffer.append(toHex((aChar >> 8) & 0xF)); + outBuffer.append(toHex((aChar >> 4) & 0xF)); + outBuffer.append(toHex(aChar & 0xF)); + } else { + outBuffer.append(aChar); + } + } + } + return outBuffer.toString(); + } + + public void writeComments(BufferedWriter bw, String comments) + throws IOException { + bw.write("#"); + int len = comments.length(); + int current = 0; + int last = 0; + char[] uu = new char[6]; + uu[0] = '\\'; + uu[1] = 'u'; + while (current < len) { + char c = comments.charAt(current); + if (c > '\u00ff' || c == '\n' || c == '\r') { + if (last != current) + bw.write(comments.substring(last, current)); + if (c > '\u00ff') { + uu[2] = toHex((c >> 12) & 0xf); + uu[3] = toHex((c >> 8) & 0xf); + uu[4] = toHex((c >> 4) & 0xf); + uu[5] = toHex(c & 0xf); + bw.write(new String(uu)); + } else { + bw.newLine(); + if (c == '\r' && + current != len - 1 && + comments.charAt(current + 1) == '\n') { + current++; + } + if (current == len - 1 || + (comments.charAt(current + 1) != '#' && + comments.charAt(current + 1) != '!')) + bw.write("#"); + } + last = current + 1; + } + current++; + } + if (last != current) + bw.write(comments.substring(last, current)); + bw.newLine(); + } + + /** + * Convert a nibble to a hex character + * + * @param nibble the nibble to convert. + */ + protected char toHex(int nibble) { + return hexDigit[(nibble & 0xF)]; + } + } Added: trunk/src/test/java/org/nuiton/io/SortedPropertiesTest.java =================================================================== --- trunk/src/test/java/org/nuiton/io/SortedPropertiesTest.java (rev 0) +++ trunk/src/test/java/org/nuiton/io/SortedPropertiesTest.java 2010-11-19 15:57:37 UTC (rev 787) @@ -0,0 +1,121 @@ +/* + * #%L + * Maven helper plugin + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2009 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.io; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.plugin.PluginHelper; +import org.nuiton.plugin.TestHelper; + +import java.io.File; +import java.net.URL; + +/** + * To test {@link SortedProperties}. + * + * @author tchemit <chemit@codelutin.com> + * @since 1.3 + */ +public class SortedPropertiesTest { + + + /** Logger */ + static private Log log = LogFactory.getLog(SortedPropertiesTest.class); + + static File testDir; + + @BeforeClass + public static void init() throws Exception { + testDir = TestHelper.getTestDir(SortedPropertiesTest.class, + "target" + File.separator + "surefire-workdir" + ); + PluginHelper.createDirectoryIfNecessary(testDir); + } + + /** + * To test that a file loaded then stored keep unicode as they were previously. + * <p/> + * In fact we can have some {@code \u00E9} and {@code \u00e9}. + * + * @throws Exception if any error + */ + @Test + public void testUnicodeUpperInvolution() throws Exception { + + URL resource = getClass().getResource("unicodeUpper.txt"); + File in = new File(resource.toURI()); + + log.info("Load file " + in); + SortedProperties p = new SortedProperties("utf-8", true); + + p.load(in); + + File out = new File(testDir, in.getName() + "~"); + + log.info("Store file to " + out); + + p.store(out); + + String inStr = PluginHelper.readAsString(in, "utf-8"); + String outStr = PluginHelper.readAsString(out, "utf-8"); + + Assert.assertEquals(inStr, outStr); + + } + + /** + * To test that a file loaded then stored keep unicode as they were previously. + * <p/> + * In fact we can have some {@code \u00E9} and {@code \u00e9}. + * + * @throws Exception if any error + */ + @Test + public void testUnicodeLowerInvolution() throws Exception { + + URL resource = getClass().getResource("unicodeLower.txt"); + File in = new File(resource.toURI()); + + log.info("Load file " + in); + SortedProperties p = new SortedProperties("utf-8", true, true); + + p.load(in); + + File out = new File(testDir, in.getName() + "~"); + + log.info("Store file to " + out); + + p.store(out); + + String inStr = PluginHelper.readAsString(in, "utf-8"); + String outStr = PluginHelper.readAsString(out, "utf-8"); + + Assert.assertEquals(inStr, outStr); + + } +} Added: trunk/src/test/resources/org/nuiton/io/unicodeLower.txt =================================================================== --- trunk/src/test/resources/org/nuiton/io/unicodeLower.txt (rev 0) +++ trunk/src/test/resources/org/nuiton/io/unicodeLower.txt 2010-11-19 15:57:37 UTC (rev 787) @@ -0,0 +1 @@ +key1=hum\u00e9 Added: trunk/src/test/resources/org/nuiton/io/unicodeUpper.txt =================================================================== --- trunk/src/test/resources/org/nuiton/io/unicodeUpper.txt (rev 0) +++ trunk/src/test/resources/org/nuiton/io/unicodeUpper.txt 2010-11-19 15:57:37 UTC (rev 787) @@ -0,0 +1 @@ +key1=hum\u00E9
participants (1)
-
tchemit@users.nuiton.org