/******************************************************************************* * Copyright 2014 Rafael Garcia Moreno. * * Licensed 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.bladecoder.engineeditor.common; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Collections; import java.util.Enumeration; import java.util.Properties; import java.util.TreeSet; /** * Overide Properties methods to allow the next features: * * - The keys are written in order. * - The timestamp comment is not writing when * saving the .properties * * @author rgarcia */ @SuppressWarnings("serial") public class OrderedProperties extends Properties { @Override public synchronized Enumeration<Object> keys() { return Collections.enumeration(new TreeSet<Object>(keySet())); } @Override public void store(Writer writer, String comments) throws IOException { store1((writer instanceof BufferedWriter) ? (BufferedWriter) writer : new BufferedWriter(writer), comments, false); } @Override public void store(OutputStream out, String comments) throws IOException { store1(new BufferedWriter(new OutputStreamWriter(out, "8859_1")), comments, true); } private void store1(BufferedWriter bw, String comments, boolean escUnicode) throws IOException { 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 */ private 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(); } /** * Convert a nibble to a hex character * * @param nibble * the nibble to convert. */ private static char toHex(int nibble) { return hexDigit[(nibble & 0xF)]; } private static final char[] hexDigit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; }