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 042package org.jfree.util; 043 044import java.io.BufferedOutputStream; 045import java.io.File; 046import java.io.FileOutputStream; 047import java.io.IOException; 048import java.io.OutputStream; 049import java.io.OutputStreamWriter; 050import java.io.Writer; 051import java.util.ArrayList; 052import java.util.Collections; 053import 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 */ 062public 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}