/* ======================================================================== * JCommon : a free general purpose class library for the Java(tm) platform * ======================================================================== * * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jcommon/index.html * * This library 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 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Java is a trademark or registered trademark of Sun Microsystems, Inc. * in the United States and other countries.] * * ------------------------------ * SortedConfigurationWriter.java * ------------------------------ * (C)opyright 2003, 2004, by Thomas Morgner and Contributors. * * Original Author: Thomas Morgner; * Contributor(s): -; * * $Id: SortedConfigurationWriter.java,v 1.4 2005/11/03 09:55:27 mungady Exp $ * * Changes * ------- * */ package org.jfree.util; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; /** * Writes a <code>Configuration</code> instance into a property file, where * the keys are sorted by their name. Writing sorted keys make it easier for * users to find and change properties in the file. * * @author Thomas Morgner */ public class SortedConfigurationWriter { /** * A constant defining that text should be escaped in a way * which is suitable for property keys. */ private static final int ESCAPE_KEY = 0; /** * A constant defining that text should be escaped in a way * which is suitable for property values. */ private static final int ESCAPE_VALUE = 1; /** * A constant defining that text should be escaped in a way * which is suitable for property comments. */ private static final int ESCAPE_COMMENT = 2; /** The system-dependent End-Of-Line separator. */ private static final String END_OF_LINE = StringUtils.getLineSeparator(); /** * The default constructor, does nothing. */ public SortedConfigurationWriter() { } /** * Returns a description for the given key. This implementation returns * null to indicate that no description should be written. Subclasses can * overwrite this method to provide comments for every key. These descriptions * will be included as inline comments. * * @param key the key for which a description should be printed. * @return the description or null if no description should be printed. */ protected String getDescription(final String key) { return null; } /** * Saves the given configuration into a file specified by the given * filename. * * @param filename the filename * @param config the configuration * @throws IOException if an IOError occurs. */ public void save(final String filename, final Configuration config) throws IOException { save(new File(filename), config); } /** * Saves the given configuration into a file specified by the given * file object. * * @param file the target file * @param config the configuration * @throws IOException if an IOError occurs. */ public void save(final File file, final Configuration config) throws IOException { final BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)); save(out, config); out.close(); } /** * Writes the configuration into the given output stream. * * @param outStream the target output stream * @param config the configuration * @throws IOException if writing fails. */ public void save(final OutputStream outStream, final Configuration config) throws IOException { final ArrayList names = new ArrayList(); // clear all previously set configuration settings ... final Iterator defaults = config.findPropertyKeys(""); while (defaults.hasNext()) { final String key = (String) defaults.next(); names.add(key); } Collections.sort(names); final OutputStreamWriter out = new OutputStreamWriter(outStream, "iso-8859-1"); for (int i = 0; i < names.size(); i++) { final String key = (String) names.get(i); final String value = config.getConfigProperty(key); final String description = getDescription(key); if (description != null) { writeDescription(description, out); } saveConvert(key, ESCAPE_KEY, out); out.write("="); saveConvert(value, ESCAPE_VALUE, out); out.write(END_OF_LINE); } out.flush(); } /** * Writes a descriptive comment into the given print writer. * * @param text the text to be written. If it contains more than * one line, every line will be prepended by the comment character. * @param writer the writer that should receive the content. * @throws IOException if writing fails */ private void writeDescription(final String text, final Writer writer) throws IOException { // check if empty content ... this case is easy ... if (text.length() == 0) { return; } writer.write("# "); writer.write(END_OF_LINE); final LineBreakIterator iterator = new LineBreakIterator(text); while (iterator.hasNext()) { writer.write("# "); saveConvert((String) iterator.next(), ESCAPE_COMMENT, writer); writer.write(END_OF_LINE); } } /** * Performs the necessary conversion of an java string into a property * escaped string. * * @param text the text to be escaped * @param escapeMode the mode that should be applied. * @param writer the writer that should receive the content. * @throws IOException if writing fails */ private void saveConvert(final String text, final int escapeMode, final Writer writer) throws IOException { final char[] string = text.toCharArray(); for (int x = 0; x < string.length; x++) { final char aChar = string[x]; switch (aChar) { case ' ': { if ((escapeMode != ESCAPE_COMMENT) && (x == 0 || escapeMode == ESCAPE_KEY)) { writer.write('\\'); } writer.write(' '); break; } case '\\': { writer.write('\\'); writer.write('\\'); break; } case '\t': { if (escapeMode == ESCAPE_COMMENT) { writer.write(aChar); } else { writer.write('\\'); writer.write('t'); } break; } case '\n': { writer.write('\\'); writer.write('n'); break; } case '\r': { writer.write('\\'); writer.write('r'); break; } case '\f': { if (escapeMode == ESCAPE_COMMENT) { writer.write(aChar); } else { writer.write('\\'); writer.write('f'); } break; } case '#': case '"': case '!': case '=': case ':': { if (escapeMode == ESCAPE_COMMENT) { writer.write(aChar); } else { writer.write('\\'); writer.write(aChar); } break; } default: if ((aChar < 0x0020) || (aChar > 0x007e)) { writer.write('\\'); writer.write('u'); writer.write(HEX_CHARS[(aChar >> 12) & 0xF]); writer.write(HEX_CHARS[(aChar >> 8) & 0xF]); writer.write(HEX_CHARS[(aChar >> 4) & 0xF]); writer.write(HEX_CHARS[aChar & 0xF]); } else { writer.write(aChar); } } } } /** A lookup-table. */ private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; }