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
081: .getLineSeparator();
082:
083: /**
084: * The default constructor, does nothing.
085: */
086: public SortedConfigurationWriter() {
087: }
088:
089: /**
090: * Returns a description for the given key. This implementation returns
091: * null to indicate that no description should be written. Subclasses can
092: * overwrite this method to provide comments for every key. These descriptions
093: * will be included as inline comments.
094: *
095: * @param key the key for which a description should be printed.
096: * @return the description or null if no description should be printed.
097: */
098: protected String getDescription(final String key) {
099: return null;
100: }
101:
102: /**
103: * Saves the given configuration into a file specified by the given
104: * filename.
105: *
106: * @param filename the filename
107: * @param config the configuration
108: * @throws IOException if an IOError occurs.
109: */
110: public void save(final String filename, final Configuration config)
111: throws IOException {
112: save(new File(filename), config);
113: }
114:
115: /**
116: * Saves the given configuration into a file specified by the given
117: * file object.
118: *
119: * @param file the target file
120: * @param config the configuration
121: * @throws IOException if an IOError occurs.
122: */
123: public void save(final File file, final Configuration config)
124: throws IOException {
125: final BufferedOutputStream out = new BufferedOutputStream(
126: new FileOutputStream(file));
127: save(out, config);
128: out.close();
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,
139: final Configuration config) 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 = new OutputStreamWriter(
152: 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,
192: writer);
193: writer.write(END_OF_LINE);
194: }
195: }
196:
197: /**
198: * Performs the necessary conversion of an java string into a property
199: * escaped string.
200: *
201: * @param text the text to be escaped
202: * @param escapeMode the mode that should be applied.
203: * @param writer the writer that should receive the content.
204: * @throws IOException if writing fails
205: */
206: private void saveConvert(final String text, final int escapeMode,
207: final Writer writer) 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: if ((escapeMode != ESCAPE_COMMENT)
215: && (x == 0 || escapeMode == ESCAPE_KEY)) {
216: writer.write('\\');
217: }
218: writer.write(' ');
219: break;
220: }
221: case '\\': {
222: writer.write('\\');
223: writer.write('\\');
224: break;
225: }
226: case '\t': {
227: if (escapeMode == ESCAPE_COMMENT) {
228: writer.write(aChar);
229: } else {
230: writer.write('\\');
231: writer.write('t');
232: }
233: break;
234: }
235: case '\n': {
236: writer.write('\\');
237: writer.write('n');
238: break;
239: }
240: case '\r': {
241: writer.write('\\');
242: writer.write('r');
243: break;
244: }
245: case '\f': {
246: if (escapeMode == ESCAPE_COMMENT) {
247: writer.write(aChar);
248: } else {
249: writer.write('\\');
250: writer.write('f');
251: }
252: break;
253: }
254: case '#':
255: case '"':
256: case '!':
257: case '=':
258: case ':': {
259: if (escapeMode == ESCAPE_COMMENT) {
260: writer.write(aChar);
261: } else {
262: writer.write('\\');
263: writer.write(aChar);
264: }
265: break;
266: }
267: default:
268: if ((aChar < 0x0020) || (aChar > 0x007e)) {
269: writer.write('\\');
270: writer.write('u');
271: writer.write(HEX_CHARS[(aChar >> 12) & 0xF]);
272: writer.write(HEX_CHARS[(aChar >> 8) & 0xF]);
273: writer.write(HEX_CHARS[(aChar >> 4) & 0xF]);
274: writer.write(HEX_CHARS[aChar & 0xF]);
275: } else {
276: writer.write(aChar);
277: }
278: }
279: }
280: }
281:
282: /** A lookup-table. */
283: private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4',
284: '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
285: }
|