    001    /* ========================================================================
    002     * JCommon : a free general purpose class library for the Java(tm) platform
    003     * ========================================================================
    004     *
    005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jcommon/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * ------------------------------
    028     * SortedConfigurationWriter.java
    029     * ------------------------------
    030     * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
    031     *
    032     * Original Author:  Thomas Morgner;
    033     * Contributor(s):   -;
    034     *
    035     * $Id: SortedConfigurationWriter.java,v 1.4 2005/11/03 09:55:27 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     *
    040     */
    041
    042    package org.jfree.util;
    043
    044    import java.io.BufferedOutputStream;
    045    import java.io.File;
    046    import java.io.FileOutputStream;
    047    import java.io.IOException;
    048    import java.io.OutputStream;
    049    import java.io.OutputStreamWriter;
    050    import java.io.Writer;
    051    import java.util.ArrayList;
    052    import java.util.Collections;
    053    import java.util.Iterator;
    054
    055    /**
    056     * Writes a <code>Configuration</code> instance into a property file, where
    057     * the keys are sorted by their name. Writing sorted keys make it easier for
    058     * users to find and change properties in the file.
    059     *
    060     * @author Thomas Morgner
    061     */
    062    public class SortedConfigurationWriter {
    063        /**
    064         * A constant defining that text should be escaped in a way
    065         * which is suitable for property keys.
    066         */
    067        private static final int ESCAPE_KEY = 0;
    068        /**
    069         * A constant defining that text should be escaped in a way
    070         * which is suitable for property values.
    071         */
    072        private static final int ESCAPE_VALUE = 1;
    073        /**
    074         * A constant defining that text should be escaped in a way
    075         * which is suitable for property comments.
    076         */
    077        private static final int ESCAPE_COMMENT = 2;
    078
    079        /** The system-dependent End-Of-Line separator. */
    080        private static final String END_OF_LINE = StringUtils.getLineSeparator();
    081
    082        /**
    083         * The default constructor, does nothing.
    084         */
    085        public SortedConfigurationWriter() {
    086        }
    087
    088        /**
    089         * Returns a description for the given key. This implementation returns
    090         * null to indicate that no description should be written. Subclasses can
    091         * overwrite this method to provide comments for every key. These descriptions
    092         * will be included as inline comments.
    093         *
    094         * @param key the key for which a description should be printed.
    095         * @return the description or null if no description should be printed.
    096         */
    097        protected String getDescription(final String key) {
    098            return null;
    099        }
    100
    101        /**
    102         * Saves the given configuration into a file specified by the given
    103         * filename.
    104         *
    105         * @param filename the filename
    106         * @param config the configuration
    107         * @throws IOException if an IOError occurs.
    108         */
    109        public void save(final String filename, final Configuration config)
    110            throws IOException {
    111            save(new File(filename), config);
    112        }
    113
    114        /**
    115         * Saves the given configuration into a file specified by the given
    116         * file object.
    117         *
    118         * @param file the target file
    119         * @param config the configuration
    120         * @throws IOException if an IOError occurs.
    121         */
    122        public void save(final File file, final Configuration config)
    123            throws IOException {
    124            final BufferedOutputStream out =
    125                new BufferedOutputStream(new FileOutputStream(file));
    126            save(out, config);
    127            out.close();
    128        }
    129
    130
    131        /**
    132         * Writes the configuration into the given output stream.
    133         *
    134         * @param outStream the target output stream
    135         * @param config the configuration
    136         * @throws IOException if writing fails.
    137         */
    138        public void save(final OutputStream outStream, final Configuration config)
    139            throws IOException {
    140            final ArrayList names = new ArrayList();
    141
    142            // clear all previously set configuration settings ...
    143            final Iterator defaults = config.findPropertyKeys("");
    144            while (defaults.hasNext()) {
    145                final String key = (String) defaults.next();
    146                names.add(key);
    147            }
    148
    149            Collections.sort(names);
    150
    151            final OutputStreamWriter out =
    152                new OutputStreamWriter(outStream, "iso-8859-1");
    153
    154            for (int i = 0; i < names.size(); i++) {
    155                final String key = (String) names.get(i);
    156                final String value = config.getConfigProperty(key);
    157
    158                final String description = getDescription(key);
    159                if (description != null) {
    160                    writeDescription(description, out);
    161                }
    162                saveConvert(key, ESCAPE_KEY, out);
    163                out.write("=");
    164                saveConvert(value, ESCAPE_VALUE, out);
    165                out.write(END_OF_LINE);
    166            }
    167            out.flush();
    168
    169        }
    170
    171        /**
    172         * Writes a descriptive comment into the given print writer.
    173         *
    174         * @param text   the text to be written. If it contains more than
    175         *               one line, every line will be prepended by the comment character.
    176         * @param writer the writer that should receive the content.
    177         * @throws IOException if writing fails
    178         */
    179        private void writeDescription(final String text, final Writer writer)
    180            throws IOException {
    181            // check if empty content ... this case is easy ...
    182            if (text.length() == 0) {
    183                return;
    184            }
    185
    186            writer.write("# ");
    187            writer.write(END_OF_LINE);
    188            final LineBreakIterator iterator = new LineBreakIterator(text);
    189            while (iterator.hasNext()) {
    190                writer.write("# ");
    191                saveConvert((String) iterator.next(), ESCAPE_COMMENT, writer);
    192                writer.write(END_OF_LINE);
    193            }
    194        }
    195
    196        /**
    197         * Performs the necessary conversion of an java string into a property
    198         * escaped string.
    199         *
    200         * @param text       the text to be escaped
    201         * @param escapeMode the mode that should be applied.
    202         * @param writer     the writer that should receive the content.
    203         * @throws IOException if writing fails
    204         */
    205        private void saveConvert(final String text, final int escapeMode,
    206                                 final Writer writer)
    207            throws IOException {
    208            final char[] string = text.toCharArray();
    209
    210            for (int x = 0; x < string.length; x++) {
    211                final char aChar = string[x];
    212                switch (aChar) {
    213                    case ' ':
    214                        {
    215                            if ((escapeMode != ESCAPE_COMMENT)
    216                                    && (x == 0 || escapeMode == ESCAPE_KEY)) {
    217                                writer.write('\\');
    218                            }
    219                            writer.write(' ');
    220                            break;
    221                        }
    222                    case '\\':
    223                        {
    224                            writer.write('\\');
    225                            writer.write('\\');
    226                            break;
    227                        }
    228                    case '\t':
    229                        {
    230                            if (escapeMode == ESCAPE_COMMENT) {
    231                                writer.write(aChar);
    232                            }
    233                            else {
    234                                writer.write('\\');
    235                                writer.write('t');
    236                            }
    237                            break;
    238                        }
    239                    case '\n':
    240                        {
    241                            writer.write('\\');
    242                            writer.write('n');
    243                            break;
    244                        }
    245                    case '\r':
    246                        {
    247                            writer.write('\\');
    248                            writer.write('r');
    249                            break;
    250                        }
    251                    case '\f':
    252                        {
    253                            if (escapeMode == ESCAPE_COMMENT) {
    254                                writer.write(aChar);
    255                            }
    256                            else {
    257                                writer.write('\\');
    258                                writer.write('f');
    259                            }
    260                            break;
    261                        }
    262                    case '#':
    263                    case '"':
    264                    case '!':
    265                    case '=':
    266                    case ':':
    267                        {
    268                            if (escapeMode == ESCAPE_COMMENT) {
    269                                writer.write(aChar);
    270                            }
    271                            else {
    272                                writer.write('\\');
    273                                writer.write(aChar);
    274                            }
    275                            break;
    276                        }
    277                    default:
    278                        if ((aChar < 0x0020) || (aChar > 0x007e)) {
    279                            writer.write('\\');
    280                            writer.write('u');
    281                            writer.write(HEX_CHARS[(aChar >> 12) & 0xF]);
    282                            writer.write(HEX_CHARS[(aChar >> 8) & 0xF]);
    283                            writer.write(HEX_CHARS[(aChar >> 4) & 0xF]);
    284                            writer.write(HEX_CHARS[aChar & 0xF]);
    285                        }
    286                        else {
    287                            writer.write(aChar);
    288                        }
    289                }
    290            }
    291        }
    292
    293        /** A lookup-table. */
    294        private static final char[] HEX_CHARS =
    295            {'0', '1', '2', '3', '4', '5', '6', '7',
    296             '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    297    }