View Javadoc
1   /*******************************************************************************
2    * Gisgraphy Project 
3    *  
4    *    This library is free software; you can redistribute it and/or
5    *    modify it under the terms of the GNU Lesser General Public
6    *    License as published by the Free Software Foundation; either
7    *    version 2.1 of the License, or (at your option) any later version.
8    *  
9    *    This library is distributed in the hope that it will be useful,
10   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   *    Lesser General Public License for more details.
13   *  
14   *    You should have received a copy of the GNU Lesser General Public
15   *    License along with this library; if not, write to the Free Software
16   *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
17   *  
18   *   Copyright 2008  Gisgraphy project 
19   * 
20   *   David Masclet <davidmasclet@gisgraphy.com>
21   ******************************************************************************/
22  package com.gisgraphy.helper;
23  
24  import java.io.BufferedReader;
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileNotFoundException;
28  import java.io.FileOutputStream;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.InputStreamReader;
32  import java.io.OutputStream;
33  import java.io.OutputStreamWriter;
34  import java.io.PrintWriter;
35  import java.net.URL;
36  import java.util.Properties;
37  import java.util.Vector;
38  
39  /**
40   * The CommentedProperties class is an extension of java.util.Properties
41   * to allow retention of comment lines and blank (whitespace only) lines
42   * in the properties file.
43   * 
44   * Written for Java version 1.4
45   */
46  public class CommentedProperties extends java.util.Properties {
47  
48      String filePath;
49      
50      /**
51       * 
52       */
53      public CommentedProperties() {
54  	super();
55      }
56  
57      /**
58       * @param defaults
59       */
60      public CommentedProperties(Properties defaults) {
61  	super(defaults);
62      }
63  
64      public static void editProperty(String filePath,String key,String value) {
65  	InputStream inputStream;
66  	try {
67  	     inputStream = new FileInputStream(filePath);
68  	} catch (FileNotFoundException e) {
69  	    throw new IllegalArgumentException(e);
70  	}
71  	CommentedProperties instance = new CommentedProperties();
72  	try {
73  	    instance.load(inputStream);
74  	    instance.setProperty(key, value);
75  	    OutputStream outputStream = new FileOutputStream(filePath);
76  	    instance.store(outputStream, "");
77  	} catch (Exception e) {
78  	    throw new RuntimeException(e);
79  	}
80      }
81      
82      public static void editPropertyFromClassPathRessource(String classPathRessource,String key,String value) {
83  	CommentedProperties instance = new CommentedProperties();
84  	InputStream inputStream;
85  	File file ;
86  	try {
87  	    URL resourceUrl = instance.getClass().getResource(classPathRessource);
88  	    if (resourceUrl==null){
89  		throw new IllegalArgumentException(classPathRessource+ " can not be found in classpath");
90  	    }
91  	    file = new File(resourceUrl.toURI());
92  
93  	     inputStream =  new FileInputStream(file);
94  
95  	} catch (Exception e) {
96  	    throw new IllegalArgumentException(e);
97  	}
98  	try {
99  	    instance.load(inputStream);
100 	    instance.setProperty(key, value);
101 	    OutputStream outputStream = new FileOutputStream(file);
102 	    instance.store(outputStream, "");
103 	} catch (Exception e) {
104 	    throw new RuntimeException(e);
105 	}
106     }
107     
108 	/**
109      * 
110      */
111     private static final long serialVersionUID = 4729607947460578219L;
112 
113 	/**
114 	 * Use a Vector to keep a copy of lines that are a comment or 'blank'
115 	 */
116 	public Vector lineData = new Vector(0, 1);
117 
118 	/**
119 	 * Use a Vector to keep a copy of lines containing a key, i.e. they are a property.
120 	 */
121 	public Vector keyData = new Vector(0, 1);
122 
123 	/**
124 	 * Load properties from the specified InputStream. 
125 	 * Overload the load method in Properties so we can keep comment and blank lines.
126 	 * @param   inStream   The InputStream to read.
127 	 */
128 	public void load(InputStream inStream) throws IOException
129 	{
130 		// The spec says that the file must be encoded using ISO-8859-1.
131 		BufferedReader reader =
132 		new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1"));
133 		String line;
134 
135 		while ((line = reader.readLine()) != null) {
136 			char c = 0;
137 			int pos = 0;
138 			// Leading whitespaces must be deleted first.
139 			while ( pos < line.length()
140 					&& Character.isWhitespace(c = line.charAt(pos))) {
141 				pos++;
142 			}
143 
144 			// If empty line or begins with a comment character, save this line
145 			// in lineData and save a "" in keyData.
146 			if (	(line.length() - pos) == 0
147 					|| line.charAt(pos) == '#' || line.charAt(pos) == '!') {
148 				lineData.add(line);
149 				keyData.add("");
150 				continue;
151 			}
152 
153 			// The characters up to the next Whitespace, ':', or '='
154 			// describe the key.  But look for escape sequences.
155 			// Try to short-circuit when there is no escape char.
156 			int start = pos;
157 			boolean needsEscape = line.indexOf('\\', pos) != -1;
158 			StringBuffer key = needsEscape ? new StringBuffer() : null;
159 
160 			while ( pos < line.length()
161 					&& ! Character.isWhitespace(c = line.charAt(pos++))
162 					&& c != '=' && c != ':') {
163 				if (needsEscape && c == '\\') {
164 					if (pos == line.length()) {
165 						// The line continues on the next line.  If there
166 						// is no next line, just treat it as a key with an
167 						// empty value.
168 						line = reader.readLine();
169 						if (line == null)
170 							line = "";
171 						pos = 0;
172 						while ( pos < line.length()
173 								&& Character.isWhitespace(c = line.charAt(pos)))
174 							pos++;
175 					} else {
176 						c = line.charAt(pos++);
177 						switch (c) {
178 							case 'n':
179 								key.append('\n');
180 								break;
181 							case 't':
182 								key.append('\t');
183 								break;
184 							case 'r':
185 								key.append('\r');
186 								break;
187 							case 'u':
188 								if (pos + 4 <= line.length()) {
189 									char uni = (char) Integer.parseInt
190 											   (line.substring(pos, pos + 4), 16);
191 									key.append(uni);
192 									pos += 4;
193 								}   // else throw exception?
194 								break;
195 							default:
196 								key.append(c);
197 								break;
198 						}
199 					}
200 				} else if (needsEscape)
201 					key.append(c);
202 			}
203 
204 			boolean isDelim = (c == ':' || c == '=');
205 
206 			String keyString;
207 			if (needsEscape)
208 				keyString = key.toString();
209 			else if (isDelim || Character.isWhitespace(c))
210 				keyString = line.substring(start, pos - 1);
211 			else
212 				keyString = line.substring(start, pos);
213 
214 			while ( pos < line.length()
215 					&& Character.isWhitespace(c = line.charAt(pos)))
216 				pos++;
217 
218 			if (! isDelim && (c == ':' || c == '=')) {
219 				pos++;
220 				while ( pos < line.length()
221 						&& Character.isWhitespace(c = line.charAt(pos)))
222 					pos++;
223 			}
224 
225 			// Short-circuit if no escape chars found.
226 			if (!needsEscape) {
227 				put(keyString, line.substring(pos));
228 				// Save a "" in lineData and save this
229 				// keyString in keyData.
230 				lineData.add("");
231 				keyData.add(keyString);
232 				continue;
233 			}
234 
235 			// Escape char found so iterate through the rest of the line.
236 			StringBuffer element = new StringBuffer(line.length() - pos);
237 			while (pos < line.length()) {
238 				c = line.charAt(pos++);
239 				if (c == '\\') {
240 					if (pos == line.length()) {
241 						// The line continues on the next line.
242 						line = reader.readLine();
243 
244 						// We might have seen a backslash at the end of
245 						// the file.  The JDK ignores the backslash in
246 						// this case, so we follow for compatibility.
247 						if (line == null)
248 							break;
249 
250 						pos = 0;
251 						while ( pos < line.length()
252 								&& Character.isWhitespace(c = line.charAt(pos)))
253 							pos++;
254 						element.ensureCapacity(line.length() - pos +
255 											   element.length());
256 					} else {
257 						c = line.charAt(pos++);
258 						switch (c) {
259 							case 'n':
260 								element.append('\n');
261 								break;
262 							case 't':
263 								element.append('\t');
264 								break;
265 							case 'r':
266 								element.append('\r');
267 								break;
268 							case 'u':
269 								if (pos + 4 <= line.length()) {
270 									char uni = (char) Integer.parseInt
271 											   (line.substring(pos, pos + 4), 16);
272 									element.append(uni);
273 									pos += 4;
274 								}   // else throw exception?
275 								break;
276 							default:
277 								element.append(c);
278 								break;
279 						}
280 					}
281 				} else
282 					element.append(c);
283 			}
284 			put(keyString, element.toString());
285 			// Save a "" in lineData and save this
286 			// keyString in keyData.
287 			lineData.add("");
288 			keyData.add(keyString);
289 		}
290 	}
291 
292 	/**
293 	 * Write the properties to the specified OutputStream.
294 	 * 
295 	 * Overloads the store method in Properties so we can put back comment	
296 	 * and blank lines.													  
297 	 * 
298 	 * @param out	The OutputStream to write to.
299 	 * @param header Ignored, here for compatability w/ Properties.
300 	 * 
301 	 * @exception IOException
302 	 */
303 	public void store(OutputStream out, String header) throws IOException
304 	{
305 		// The spec says that the file must be encoded using ISO-8859-1.
306 		PrintWriter writer
307 		= new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
308 
309 		// We ignore the header, because if we prepend a commented header
310 		// then read it back in it is now a comment, which will be saved
311 		// and then when we write again we would prepend Another header...
312 
313 		String line;
314 		String key;
315 		StringBuffer s = new StringBuffer ();
316 
317 		for (int i=0; i<lineData.size(); i++) {
318 			line = (String) lineData.get(i);
319 			key = (String) keyData.get(i);
320 			if (key.length() > 0) {  // This is a 'property' line, so rebuild it
321 				formatForOutput (key, s, true);
322 				s.append ('=');
323 				formatForOutput ((String) get(key), s, false);
324 				writer.println (s);
325 			} else {  // was a blank or comment line, so just restore it
326 				writer.println (line);
327 			}
328 		} 
329 		writer.flush ();
330 	}
331 
332 	/**
333 	 * Need this method from Properties because original code has StringBuilder,
334 	 * which is an element of Java 1.5, used StringBuffer instead (because
335 	 * this code was written for Java 1.4)
336 	 * 
337 	 * @param str	- the string to format
338 	 * @param buffer - buffer to hold the string
339 	 * @param key	- true if str the key is formatted, false if the value is formatted
340 	 */
341 	private void formatForOutput(String str, StringBuffer buffer, boolean key)
342 	{
343 		if (key) {
344 			buffer.setLength(0);
345 			buffer.ensureCapacity(str.length());
346 		} else
347 			buffer.ensureCapacity(buffer.length() + str.length());
348 		boolean head = true;
349 		int size = str.length();
350 		for (int i = 0; i < size; i++) {
351 			char c = str.charAt(i);
352 			switch (c) {
353 				case '\n':
354 					buffer.append("\\n");
355 					break;
356 				case '\r':
357 					buffer.append("\\r");
358 					break;
359 				case '\t':
360 					buffer.append("\\t");
361 					break;
362 				case ' ':
363 					buffer.append(head ? "\\ " : " ");
364 					break;
365 				case '\\':
366 				case '!':
367 				case '#':
368 				case '=':
369 				case ':':
370 					buffer.append('\\').append(c);
371 					break;
372 				default:
373 					if (c < ' ' || c > '~') {
374 						String hex = Integer.toHexString(c);
375 						buffer.append("\\u0000".substring(0, 6 - hex.length()));
376 						buffer.append(hex);
377 					} else
378 						buffer.append(c);
379 			}
380 			if (c != ' ')
381 				head = key;
382 		}
383 	}
384 
385 	/**
386 	 * Add a Property to the end of the CommentedProperties. 
387 	 * 
388 	 * @param   keyString	 The Property key.
389 	 * @param   value		 The value of this Property.
390 	 */
391 	public void add(String keyString, String value)
392 	{
393 		put(keyString, value);
394 		lineData.add("");
395 		keyData.add(keyString);
396 	}
397 
398 	/**
399 	 * Add a comment or blank line or comment to the end of the CommentedProperties. 
400 	 * 
401 	 * @param   line The string to add to the end, make sure this is a comment
402 	 *			   or a 'whitespace' line.
403 	 */
404 	public void addLine(String line)
405 	{
406 		lineData.add(line);
407 		keyData.add("");
408 	}
409 }
410