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 }