svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.i18n / utils / java / src / org / gvsig / i18n / utils / OrderedProperties.java @ 40559
History | View | Annotate | Download (21.6 KB)
1 |
/**
|
---|---|
2 |
* gvSIG. Desktop Geographic Information System.
|
3 |
*
|
4 |
* Copyright (C) 2007-2013 gvSIG Association.
|
5 |
*
|
6 |
* This program is free software; you can redistribute it and/or
|
7 |
* modify it under the terms of the GNU General Public License
|
8 |
* as published by the Free Software Foundation; either version 3
|
9 |
* of the License, or (at your option) any later version.
|
10 |
*
|
11 |
* This program is distributed in the hope that it will be useful,
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 |
* GNU General Public License for more details.
|
15 |
*
|
16 |
* You should have received a copy of the GNU General Public License
|
17 |
* along with this program; if not, write to the Free Software
|
18 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 |
* MA 02110-1301, USA.
|
20 |
*
|
21 |
* For any additional information, do not hesitate to contact us
|
22 |
* at info AT gvsig.com, or visit our website www.gvsig.com.
|
23 |
*/
|
24 |
|
25 |
package org.gvsig.i18n.utils; |
26 |
|
27 |
import java.io.BufferedReader; |
28 |
import java.io.IOException; |
29 |
import java.io.InputStream; |
30 |
import java.io.InputStreamReader; |
31 |
import java.io.OutputStream; |
32 |
import java.io.OutputStreamWriter; |
33 |
import java.io.PrintStream; |
34 |
import java.io.PrintWriter; |
35 |
import java.util.Calendar; |
36 |
import java.util.Collections; |
37 |
import java.util.Comparator; |
38 |
import java.util.Enumeration; |
39 |
import java.util.HashSet; |
40 |
import java.util.Iterator; |
41 |
import java.util.PropertyResourceBundle; |
42 |
import java.util.Set; |
43 |
import java.util.TreeMap; |
44 |
import java.util.Map.Entry; |
45 |
|
46 |
/**
|
47 |
* A set of persistent properties, which can be saved or loaded from a stream.
|
48 |
* A property list may also contain defaults, searched if the main list
|
49 |
* does not contain a property for a given key.
|
50 |
*
|
51 |
* An example of a properties file for the german language is given
|
52 |
* here. This extends the example given in ListResourceBundle.
|
53 |
* Create a file MyResource_de.properties with the following contents
|
54 |
* and put it in the CLASSPATH. (The character
|
55 |
* <code>\</code><code>u00e4</code> is the german umlaut)
|
56 |
*
|
57 |
*
|
58 |
<pre>s1=3
|
59 |
s2=MeineDisk
|
60 |
s3=3. M\<code></code>u00e4rz 96
|
61 |
s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}.
|
62 |
s5=0
|
63 |
s6=keine Dateien
|
64 |
s7=1
|
65 |
s8=eine Datei
|
66 |
s9=2
|
67 |
s10={0,number} Dateien
|
68 |
s11=Das Formatieren schlug fehl mit folgender Exception: {0}
|
69 |
s12=FEHLER
|
70 |
s13=Ergebnis
|
71 |
s14=Dialog
|
72 |
s15=Auswahlkriterium
|
73 |
s16=1,3</pre>
|
74 |
*
|
75 |
* <p>Although this is a sub class of a hash table, you should never
|
76 |
* insert anything other than strings to this property, or several
|
77 |
* methods, that need string keys and values, will fail. To ensure
|
78 |
* this, you should use the <code>get/setProperty</code> method instead
|
79 |
* of <code>get/put</code>.
|
80 |
*
|
81 |
* Properties are saved in the specified encoding. If no encoding is
|
82 |
* specified, then the ISO 8859-1 encoding is used, with Unicode escapes with
|
83 |
* a single <code>u</code> for any character which cannot be represented.
|
84 |
*
|
85 |
* @author Jochen Hoenicke
|
86 |
* @author Eric Blake (ebb9@email.byu.edu)
|
87 |
* @author Cesar Martinez Izquierdo (cesar.martinez@iver.es)
|
88 |
* @see PropertyResourceBundle
|
89 |
* @status updated to 1.4
|
90 |
*/
|
91 |
public class OrderedProperties extends TreeMap |
92 |
{ |
93 |
|
94 |
/**
|
95 |
* The property list that contains default values for any keys not
|
96 |
* in this property list.
|
97 |
*
|
98 |
* @serial the default properties
|
99 |
*/
|
100 |
protected OrderedProperties defaults;
|
101 |
|
102 |
/**
|
103 |
* Compatible with JDK 1.0+.
|
104 |
*/
|
105 |
private static final long serialVersionUID = -5087876565919950510L; |
106 |
|
107 |
/**
|
108 |
* Creates a new empty property list with no default values.
|
109 |
*/
|
110 |
public OrderedProperties()
|
111 |
{ |
112 |
super(new StringComparator()); |
113 |
} |
114 |
|
115 |
/**
|
116 |
* Create a new empty property list with the specified default values.
|
117 |
*
|
118 |
* @param defaults a Properties object containing the default values
|
119 |
*/
|
120 |
public OrderedProperties(OrderedProperties defaults)
|
121 |
{ |
122 |
super(new StringComparator()); |
123 |
this.defaults = defaults;
|
124 |
} |
125 |
|
126 |
/**
|
127 |
* Adds the given key/value pair to this properties. This calls
|
128 |
* the hashtable method put.
|
129 |
*
|
130 |
* @param key the key for this property
|
131 |
* @param value the value for this property
|
132 |
* @return The old value for the given key
|
133 |
* @see #getProperty(String)
|
134 |
* @since 1.2
|
135 |
*/
|
136 |
public Object setProperty(String key, String value) |
137 |
{ |
138 |
return put(key, value);
|
139 |
} |
140 |
|
141 |
/**
|
142 |
* Reads a property list from an input stream. The stream should
|
143 |
* have the following format: <br>
|
144 |
*
|
145 |
* An empty line or a line starting with <code>#</code> or
|
146 |
* <code>!</code> is ignored. An backslash (<code>\</code>) at the
|
147 |
* end of the line makes the line continueing on the next line
|
148 |
* (but make sure there is no whitespace after the backslash).
|
149 |
* Otherwise, each line describes a key/value pair. <br>
|
150 |
*
|
151 |
* The chars up to the first whitespace, = or : are the key. You
|
152 |
* can include these caracters in the key, if you precede them with
|
153 |
* a backslash (<code>\</code>). The key is followed by optional
|
154 |
* whitespaces, optionally one <code>=</code> or <code>:</code>,
|
155 |
* and optionally some more whitespaces. The rest of the line is
|
156 |
* the resource belonging to the key. <br>
|
157 |
*
|
158 |
* Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a
|
159 |
* space), and unicode characters with the
|
160 |
* <code>\\u</code><em>xxxx</em> notation are detected, and
|
161 |
* converted to the corresponding single character. <br>
|
162 |
*
|
163 |
*
|
164 |
<pre># This is a comment
|
165 |
key = value
|
166 |
k\:5 \ a string starting with space and ending with newline\n
|
167 |
# This is a multiline specification; note that the value contains
|
168 |
# no white space.
|
169 |
weekdays: Sunday,Monday,Tuesday,Wednesday,\\
|
170 |
Thursday,Friday,Saturday
|
171 |
# The safest way to include a space at the end of a value:
|
172 |
label = Name:\\u0020</pre>
|
173 |
*
|
174 |
* @param inStream the input stream
|
175 |
* @throws IOException if an error occurred when reading the input
|
176 |
* @throws NullPointerException if in is null
|
177 |
*/
|
178 |
public void load(InputStream inStream) throws IOException |
179 |
{ |
180 |
// The spec says that the file must be encoded using ISO-8859-1.
|
181 |
BufferedReader reader =
|
182 |
new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1")); |
183 |
String line;
|
184 |
|
185 |
while ((line = reader.readLine()) != null) |
186 |
{ |
187 |
char c = 0; |
188 |
int pos = 0; |
189 |
// Leading whitespaces must be deleted first.
|
190 |
while (pos < line.length()
|
191 |
&& Character.isWhitespace(c = line.charAt(pos)))
|
192 |
pos++; |
193 |
|
194 |
// If empty line or begins with a comment character, skip this line.
|
195 |
if ((line.length() - pos) == 0 |
196 |
|| line.charAt(pos) == '#' || line.charAt(pos) == '!') |
197 |
continue;
|
198 |
|
199 |
// The characters up to the next Whitespace, ':', or '='
|
200 |
// describe the key. But look for escape sequences.
|
201 |
// Try to short-circuit when there is no escape char.
|
202 |
int start = pos;
|
203 |
boolean needsEscape = line.indexOf('\\', pos) != -1; |
204 |
StringBuffer key = needsEscape ? new StringBuffer() : null; |
205 |
while (pos < line.length()
|
206 |
&& ! Character.isWhitespace(c = line.charAt(pos++))
|
207 |
&& c != '=' && c != ':') |
208 |
{ |
209 |
if (needsEscape && c == '\\') |
210 |
{ |
211 |
if (pos == line.length())
|
212 |
{ |
213 |
// The line continues on the next line. If there
|
214 |
// is no next line, just treat it as a key with an
|
215 |
// empty value.
|
216 |
line = reader.readLine(); |
217 |
if (line == null) |
218 |
line = "";
|
219 |
pos = 0;
|
220 |
while (pos < line.length()
|
221 |
&& Character.isWhitespace(c = line.charAt(pos)))
|
222 |
pos++; |
223 |
} |
224 |
else
|
225 |
{ |
226 |
c = line.charAt(pos++); |
227 |
switch (c)
|
228 |
{ |
229 |
case 'n': |
230 |
key.append('\n');
|
231 |
break;
|
232 |
case 't': |
233 |
key.append('\t');
|
234 |
break;
|
235 |
case 'r': |
236 |
key.append('\r');
|
237 |
break;
|
238 |
case 'u': |
239 |
if (pos + 4 <= line.length()) |
240 |
{ |
241 |
char uni = (char) Integer.parseInt |
242 |
(line.substring(pos, pos + 4), 16); |
243 |
key.append(uni); |
244 |
pos += 4;
|
245 |
} // else throw exception?
|
246 |
break;
|
247 |
default:
|
248 |
key.append(c); |
249 |
break;
|
250 |
} |
251 |
} |
252 |
} |
253 |
else if (needsEscape) |
254 |
key.append(c); |
255 |
} |
256 |
|
257 |
boolean isDelim = (c == ':' || c == '='); |
258 |
|
259 |
String keyString;
|
260 |
if (needsEscape)
|
261 |
keyString = key.toString(); |
262 |
else if (isDelim || Character.isWhitespace(c)) |
263 |
keyString = line.substring(start, pos - 1);
|
264 |
else
|
265 |
keyString = line.substring(start, pos); |
266 |
|
267 |
while (pos < line.length()
|
268 |
&& Character.isWhitespace(c = line.charAt(pos)))
|
269 |
pos++; |
270 |
|
271 |
if (! isDelim && (c == ':' || c == '=')) |
272 |
{ |
273 |
pos++; |
274 |
while (pos < line.length()
|
275 |
&& Character.isWhitespace(c = line.charAt(pos)))
|
276 |
pos++; |
277 |
} |
278 |
|
279 |
// Short-circuit if no escape chars found.
|
280 |
if (!needsEscape)
|
281 |
{ |
282 |
put(keyString, line.substring(pos)); |
283 |
continue;
|
284 |
} |
285 |
|
286 |
// Escape char found so iterate through the rest of the line.
|
287 |
StringBuffer element = new StringBuffer(line.length() - pos); |
288 |
while (pos < line.length())
|
289 |
{ |
290 |
c = line.charAt(pos++); |
291 |
if (c == '\\') |
292 |
{ |
293 |
if (pos == line.length())
|
294 |
{ |
295 |
// The line continues on the next line.
|
296 |
line = reader.readLine(); |
297 |
|
298 |
// We might have seen a backslash at the end of
|
299 |
// the file. The JDK ignores the backslash in
|
300 |
// this case, so we follow for compatibility.
|
301 |
if (line == null) |
302 |
break;
|
303 |
|
304 |
pos = 0;
|
305 |
while (pos < line.length()
|
306 |
&& Character.isWhitespace(c = line.charAt(pos)))
|
307 |
pos++; |
308 |
element.ensureCapacity(line.length() - pos + |
309 |
element.length()); |
310 |
} |
311 |
else
|
312 |
{ |
313 |
c = line.charAt(pos++); |
314 |
switch (c)
|
315 |
{ |
316 |
case 'n': |
317 |
element.append('\n');
|
318 |
break;
|
319 |
case 't': |
320 |
element.append('\t');
|
321 |
break;
|
322 |
case 'r': |
323 |
element.append('\r');
|
324 |
break;
|
325 |
case 'u': |
326 |
if (pos + 4 <= line.length()) |
327 |
{ |
328 |
char uni = (char) Integer.parseInt |
329 |
(line.substring(pos, pos + 4), 16); |
330 |
element.append(uni); |
331 |
pos += 4;
|
332 |
} // else throw exception?
|
333 |
break;
|
334 |
default:
|
335 |
element.append(c); |
336 |
break;
|
337 |
} |
338 |
} |
339 |
} |
340 |
else
|
341 |
element.append(c); |
342 |
} |
343 |
put(keyString, element.toString()); |
344 |
} |
345 |
} |
346 |
|
347 |
/**
|
348 |
* Reads a property list from an input stream, using the
|
349 |
* provided encoding. The provided stream should be
|
350 |
* correctly encoded as specified, otherwise an IOException
|
351 |
* error will be thrown. No escape sequences are accepted
|
352 |
* to represent any character, and thus this method has some
|
353 |
* limitations and the
|
354 |
* format of the accepted property files is slightly
|
355 |
* different from
|
356 |
* the standard Java property files.
|
357 |
*
|
358 |
* The main differences are:
|
359 |
* <ul><li>whitespaces, = or : cannot be present in
|
360 |
* the key.</li>
|
361 |
* <li>each pair key/value must be contained in a single line</li>
|
362 |
* </ul>
|
363 |
*
|
364 |
* The stream should have the following format: <br>
|
365 |
*
|
366 |
* An empty line or a line starting with <code>#</code> or
|
367 |
* <code>!</code> is ignored.
|
368 |
* Otherwise, each line describes a key/value pair. <br>
|
369 |
*
|
370 |
* The chars up to the first whitespace, = or : are the key. You
|
371 |
* cannot include these caracters in the key. The key is followed by optional
|
372 |
* whitespaces, optionally one <code>=</code> or <code>:</code>,
|
373 |
* and optionally some more whitespaces. The rest of the line is
|
374 |
* the resource belonging to the key. <br>
|
375 |
*
|
376 |
* @param inStream the input stream
|
377 |
* @throws IOException if an error occurred when reading the input
|
378 |
* @throws NullPointerException if in is null
|
379 |
*/
|
380 |
public void load(InputStream inStream, String encoding) throws IOException { |
381 |
BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, encoding)); |
382 |
|
383 |
String line;
|
384 |
while ((line = reader.readLine()) != null) { |
385 |
int pos = 0; |
386 |
// Leading whitespaces must be deleted first.
|
387 |
while (pos < line.length()
|
388 |
&& Character.isWhitespace(line.charAt(pos)))
|
389 |
pos++; |
390 |
|
391 |
// If empty line or begins with a comment character, skip this line.
|
392 |
if ((line.length() - pos) == 0 |
393 |
|| line.charAt(pos) == '#' || line.charAt(pos) == '!') |
394 |
continue;
|
395 |
|
396 |
|
397 |
// do something
|
398 |
String[] entry = line.substring(pos).split(" *[=: ] *", 2); |
399 |
if (entry.length==2) { |
400 |
put(entry[0], entry[1]); |
401 |
} |
402 |
else {
|
403 |
put(entry[0], ""); |
404 |
} |
405 |
} |
406 |
} |
407 |
|
408 |
|
409 |
/**
|
410 |
* Writes the key/value pairs to the given output stream, in a format
|
411 |
* suitable for
|
412 |
* {@link #load(InputStream, String) load(InputStream is, String encoding)}.
|
413 |
* Note that this method does not use escape sequences to represent
|
414 |
* characters outside the encoding range, charset-dependent
|
415 |
* substitution sequence will
|
416 |
* be generated if such character is present in the stream.
|
417 |
* Moreover, because there is no escape sequences, the newline (\n)
|
418 |
* and carriage return (\r) characters must not be used (the
|
419 |
* resulting property file will be incorrect). Because of the
|
420 |
* same reason, whitespaces, :, =, ! and # must not be used in the
|
421 |
* key<br>
|
422 |
*
|
423 |
* If header is not null, this method writes a comment containing
|
424 |
* the header as first line to the stream. The next line (or first
|
425 |
* line if header is null) contains a comment with the current date.
|
426 |
* Afterwards the key/value pairs are written to the stream in the
|
427 |
* following format.<br>
|
428 |
*
|
429 |
* Each line has the form <code>key = value</code>.
|
430 |
*
|
431 |
* Following the listing, the output stream is flushed but left open.
|
432 |
*
|
433 |
* @param out the output stream
|
434 |
* @param header the header written in the first line, may be null
|
435 |
* @throws ClassCastException if this property contains any key or
|
436 |
* value that isn't a string
|
437 |
* @throws IOException if writing to the stream fails
|
438 |
* @throws NullPointerException if out is null
|
439 |
* @since 1.2
|
440 |
*/
|
441 |
public void store(OutputStream out, String header, String encoding) throws IOException { |
442 |
PrintWriter writer
|
443 |
= new PrintWriter(new OutputStreamWriter(out, encoding)); |
444 |
if (header != null) |
445 |
writer.println("#" + header);
|
446 |
writer.println ("#" + Calendar.getInstance ().getTime ()); |
447 |
|
448 |
Iterator iter = entrySet ().iterator ();
|
449 |
int i = size ();
|
450 |
while (--i >= 0) { |
451 |
Entry entry = (Entry) iter.next (); |
452 |
writer.println (entry.getKey()+"="+entry.getValue());
|
453 |
} |
454 |
|
455 |
writer.flush (); |
456 |
} |
457 |
|
458 |
|
459 |
|
460 |
/**
|
461 |
* Writes the key/value pairs to the given output stream, in a format
|
462 |
* suitable for {@link #load(InputStream) load(InputStream)}.<br>
|
463 |
*
|
464 |
* If header is not null, this method writes a comment containing
|
465 |
* the header as first line to the stream. The next line (or first
|
466 |
* line if header is null) contains a comment with the current date.
|
467 |
* Afterwards the key/value pairs are written to the stream in the
|
468 |
* following format.<br>
|
469 |
*
|
470 |
* Each line has the form <code>key = value</code>. Newlines,
|
471 |
* Returns and tabs are written as <code>\n,\t,\r</code> resp.
|
472 |
* The characters <code>\, !, #, =</code> and <code>:</code> are
|
473 |
* preceeded by a backslash. Spaces are preceded with a backslash,
|
474 |
* if and only if they are at the beginning of the key. Characters
|
475 |
* that are not in the ascii range 33 to 127 are written in the
|
476 |
* <code>\</code><code>u</code>xxxx Form.<br>
|
477 |
*
|
478 |
* Following the listing, the output stream is flushed but left open.
|
479 |
*
|
480 |
* @param out the output stream
|
481 |
* @param header the header written in the first line, may be null
|
482 |
* @throws ClassCastException if this property contains any key or
|
483 |
* value that isn't a string
|
484 |
* @throws IOException if writing to the stream fails
|
485 |
* @throws NullPointerException if out is null
|
486 |
* @since 1.2
|
487 |
*/
|
488 |
public void store(OutputStream out, String header) throws IOException |
489 |
{ |
490 |
// The spec says that the file must be encoded using ISO-8859-1.
|
491 |
PrintWriter writer
|
492 |
= new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1")); |
493 |
if (header != null) |
494 |
writer.println("#" + header);
|
495 |
writer.println ("#" + Calendar.getInstance ().getTime ()); |
496 |
|
497 |
Iterator iter = entrySet ().iterator ();
|
498 |
int i = size ();
|
499 |
StringBuffer s = new StringBuffer (); // Reuse the same buffer. |
500 |
while (--i >= 0) |
501 |
{ |
502 |
Entry entry = (Entry) iter.next (); |
503 |
formatForOutput ((String) entry.getKey (), s, true); |
504 |
s.append ('=');
|
505 |
formatForOutput ((String) entry.getValue (), s, false); |
506 |
writer.println (s); |
507 |
} |
508 |
|
509 |
writer.flush (); |
510 |
} |
511 |
|
512 |
/**
|
513 |
* Gets the property with the specified key in this property list.
|
514 |
* If the key is not found, the default property list is searched.
|
515 |
* If the property is not found in the default, null is returned.
|
516 |
*
|
517 |
* @param key The key for this property
|
518 |
* @return the value for the given key, or null if not found
|
519 |
* @throws ClassCastException if this property contains any key or
|
520 |
* value that isn't a string
|
521 |
* @see #defaults
|
522 |
* @see #setProperty(String, String)
|
523 |
* @see #getProperty(String, String)
|
524 |
*/
|
525 |
public String getProperty(String key) |
526 |
{ |
527 |
OrderedProperties prop = this;
|
528 |
// Eliminate tail recursion.
|
529 |
do
|
530 |
{ |
531 |
String value = (String) prop.get(key); |
532 |
if (value != null) |
533 |
return value;
|
534 |
prop = prop.defaults; |
535 |
} |
536 |
while (prop != null); |
537 |
return null; |
538 |
} |
539 |
|
540 |
/**
|
541 |
* Gets the property with the specified key in this property list. If
|
542 |
* the key is not found, the default property list is searched. If the
|
543 |
* property is not found in the default, the specified defaultValue is
|
544 |
* returned.
|
545 |
*
|
546 |
* @param key The key for this property
|
547 |
* @param defaultValue A default value
|
548 |
* @return The value for the given key
|
549 |
* @throws ClassCastException if this property contains any key or
|
550 |
* value that isn't a string
|
551 |
* @see #defaults
|
552 |
* @see #setProperty(String, String)
|
553 |
*/
|
554 |
public String getProperty(String key, String defaultValue) |
555 |
{ |
556 |
String prop = getProperty(key);
|
557 |
if (prop == null) |
558 |
prop = defaultValue; |
559 |
return prop;
|
560 |
} |
561 |
|
562 |
/**
|
563 |
* Returns an enumeration of all keys in this property list, including
|
564 |
* the keys in the default property list.
|
565 |
*
|
566 |
* @return an Enumeration of all defined keys
|
567 |
*/
|
568 |
public Enumeration propertyNames() |
569 |
{ |
570 |
// We make a new Set that holds all the keys, then return an enumeration
|
571 |
// for that. This prevents modifications from ruining the enumeration,
|
572 |
// as well as ignoring duplicates.
|
573 |
OrderedProperties prop = this;
|
574 |
Set s = new HashSet(); |
575 |
// Eliminate tail recursion.
|
576 |
do
|
577 |
{ |
578 |
s.addAll(prop.keySet()); |
579 |
prop = prop.defaults; |
580 |
} |
581 |
while (prop != null); |
582 |
return Collections.enumeration(s); |
583 |
} |
584 |
|
585 |
/**
|
586 |
* Prints the key/value pairs to the given print stream. This is
|
587 |
* mainly useful for debugging purposes.
|
588 |
*
|
589 |
* @param out the print stream, where the key/value pairs are written to
|
590 |
* @throws ClassCastException if this property contains a key or a
|
591 |
* value that isn't a string
|
592 |
* @see #list(PrintWriter)
|
593 |
*/
|
594 |
public void list(PrintStream out) |
595 |
{ |
596 |
PrintWriter writer = new PrintWriter (out); |
597 |
list (writer); |
598 |
} |
599 |
|
600 |
/**
|
601 |
* Prints the key/value pairs to the given print writer. This is
|
602 |
* mainly useful for debugging purposes.
|
603 |
*
|
604 |
* @param out the print writer where the key/value pairs are written to
|
605 |
* @throws ClassCastException if this property contains a key or a
|
606 |
* value that isn't a string
|
607 |
* @see #list(PrintStream)
|
608 |
* @since 1.1
|
609 |
*/
|
610 |
public void list(PrintWriter out) |
611 |
{ |
612 |
out.println ("-- listing properties --");
|
613 |
|
614 |
Iterator iter = entrySet ().iterator ();
|
615 |
int i = size ();
|
616 |
while (--i >= 0) |
617 |
{ |
618 |
Entry entry = (Entry) iter.next (); |
619 |
out.print ((String) entry.getKey () + "="); |
620 |
|
621 |
// JDK 1.3/1.4 restrict the printed value, but not the key,
|
622 |
// to 40 characters, including the truncating ellipsis.
|
623 |
String s = (String ) entry.getValue (); |
624 |
if (s != null && s.length () > 40) |
625 |
out.println (s.substring (0, 37) + "..."); |
626 |
else
|
627 |
out.println (s); |
628 |
} |
629 |
out.flush (); |
630 |
} |
631 |
|
632 |
/**
|
633 |
* Formats a key or value for output in a properties file.
|
634 |
* See store for a description of the format.
|
635 |
*
|
636 |
* @param str the string to format
|
637 |
* @param buffer the buffer to add it to
|
638 |
* @param key true if all ' ' must be escaped for the key, false if only
|
639 |
* leading spaces must be escaped for the value
|
640 |
* @see #store(OutputStream, String)
|
641 |
*/
|
642 |
private void formatForOutput(String str, StringBuffer buffer, boolean key) |
643 |
{ |
644 |
if (key)
|
645 |
{ |
646 |
buffer.setLength(0);
|
647 |
buffer.ensureCapacity(str.length()); |
648 |
} |
649 |
else
|
650 |
buffer.ensureCapacity(buffer.length() + str.length()); |
651 |
boolean head = true; |
652 |
int size = str.length();
|
653 |
for (int i = 0; i < size; i++) |
654 |
{ |
655 |
char c = str.charAt(i);
|
656 |
switch (c)
|
657 |
{ |
658 |
case '\n': |
659 |
buffer.append("\\n");
|
660 |
break;
|
661 |
case '\r': |
662 |
buffer.append("\\r");
|
663 |
break;
|
664 |
case '\t': |
665 |
buffer.append("\\t");
|
666 |
break;
|
667 |
case ' ': |
668 |
buffer.append(head ? "\\ " : " "); |
669 |
break;
|
670 |
case '\\': |
671 |
case '!': |
672 |
case '#': |
673 |
case '=': |
674 |
case ':': |
675 |
buffer.append('\\').append(c);
|
676 |
break;
|
677 |
default:
|
678 |
if (c < ' ' || c > '~') |
679 |
{ |
680 |
String hex = Integer.toHexString(c); |
681 |
buffer.append("\\u0000".substring(0, 6 - hex.length())); |
682 |
buffer.append(hex); |
683 |
} |
684 |
else
|
685 |
buffer.append(c); |
686 |
} |
687 |
if (c != ' ') |
688 |
head = key; |
689 |
} |
690 |
} |
691 |
|
692 |
} // class OrderedProperties
|
693 |
|
694 |
|
695 |
class StringComparator implements Comparator { |
696 |
public int compare(Object o1, Object o2) { |
697 |
String s1 = o1.toString();
|
698 |
String s2 = o2.toString();
|
699 |
|
700 |
if (s1.compareToIgnoreCase(s2)!=0) // we want case insensitive ordenation, but we still need to differenciate 'OK' from 'Ok' |
701 |
return s1.compareToIgnoreCase(s2);
|
702 |
else
|
703 |
return -s1.compareTo(s2); // we want lower case before upper case |
704 |
} |
705 |
} |