KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > tomcat5 > util > EditableProperties


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.tomcat5.util;
21
22 import java.io.BufferedReader JavaDoc;
23 import java.io.BufferedWriter JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.InputStreamReader JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.io.OutputStreamWriter JavaDoc;
29 import java.util.AbstractMap JavaDoc;
30 import java.util.AbstractSet JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Arrays JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.LinkedList JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Map JavaDoc;
38 import java.util.NoSuchElementException JavaDoc;
39 import java.util.Set JavaDoc;
40
41 // TODO: remove this class once the issue #65229 gets fixed.
42

43 /**
44  * Similar to {@link Properties} but designed to retain additional
45  * information needed for safe hand-editing.
46  * Useful for various <samp>*.properties</samp> in a project:
47  * <ol>
48  * <li>Can associate comments with particular entries.
49  * <li>Order of entries preserved during modifications whenever possible.
50  * <li>VCS-friendly: lines which are not semantically modified are not textually modified.
51  * <li>Can automatically insert line breaks in new or modified values at positions
52  * that are likely to be semantically meaningful, e.g. between path components
53  * </ol>
54  * The file format (including encoding etc.) is compatible with the regular JRE implementation.
55  * Only (non-null) String is supported for keys and values.
56  * This class is not thread-safe; use only from a single thread, or use {@link Collections#synchronizedMap}.
57  * @author Jesse Glick, David Konecny
58  */

59 public final class EditableProperties extends AbstractMap JavaDoc/*<String,String>*/ implements Cloneable JavaDoc {
60     
61     /** List of Item instances as read from the properties file. Order is important.
62      * Saving properties will save then in this order. */

63     private List JavaDoc/*<Item>*/ items;
64
65     /** Map of [property key, Item instance] for faster access. */
66     private Map JavaDoc itemIndex;
67
68     private boolean alphabetize = true;
69     
70     private static final String JavaDoc keyValueSeparators = "=: \t\r\n\f";
71
72     private static final String JavaDoc strictKeyValueSeparators = "=:";
73
74     private static final String JavaDoc whiteSpaceChars = " \t\r\n\f";
75
76     private static final String JavaDoc commentChars = "#!";
77     
78     private static final String JavaDoc INDENT = " ";
79
80     // parse states:
81
private static final int WAITING_FOR_KEY_VALUE = 1;
82     private static final int READING_KEY_VALUE = 2;
83     
84     /**
85      * Creates empty instance which items will not be sorted by default.
86      */

87     public EditableProperties() {
88         items = new ArrayList JavaDoc();
89         itemIndex = new HashMap JavaDoc();
90     }
91
92     /**
93      * Creates empty instance.
94      * @param alphabetize alphabetize new items according to key or not
95      */

96     public EditableProperties(boolean alphabetize) {
97         this();
98         this.alphabetize = alphabetize;
99     }
100     
101     /**
102      * Creates instance from an existing map. No comments will be defined.
103      * Any order from the existing map will be retained.
104      * @param map a map from String to String
105      */

106     public EditableProperties(Map JavaDoc map) {
107         this();
108         putAll(map);
109     }
110     
111     /**
112      * Creates new instance from an existing one.
113      * @param ep an instance of EditableProperties
114      */

115     EditableProperties(EditableProperties ep) {
116         this();
117         Iterator JavaDoc it = ep.items.iterator();
118         while (it.hasNext()) {
119             Item item = (Item)it.next();
120             addItem((Item)item.clone(), false);
121         }
122     }
123     
124     /**
125      * Returns a set view of the mappings ordered according to their file
126      * position. Each element in this set is a Map.Entry. See
127      * {@link AbstractMap#entrySet} for more dertails.
128      * @return set with Map.Entry instances.
129      */

130     public Set JavaDoc entrySet() {
131         return new SetImpl(this);
132     }
133     
134     /**
135      * Load properties from a stream.
136      * @param stream an input stream
137      * @throws IOException if the contents are malformed or the stream could not be read
138      */

139     public void load(InputStream JavaDoc stream) throws IOException JavaDoc {
140         int state = WAITING_FOR_KEY_VALUE;
141         BufferedReader JavaDoc input = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(stream, "ISO-8859-1"));
142         LinkedList JavaDoc tempList = new LinkedList JavaDoc();
143         String JavaDoc line;
144         int commentLinesCount = 0;
145         // Read block of lines and create instance of Item for each.
146
// Separator is: either empty line or valid end of proeprty declaration
147
while (null != (line = input.readLine())) {
148             tempList.add(line);
149             boolean empty = isEmpty(line);
150             boolean comment = isComment(line);
151             if (state == WAITING_FOR_KEY_VALUE) {
152                 if (empty) {
153                     // empty line: create Item without any key
154
createNonKeyItem(tempList);
155                     commentLinesCount = 0;
156                 } else {
157                     if (comment) {
158                         commentLinesCount++;
159                     } else {
160                         state = READING_KEY_VALUE;
161                     }
162                 }
163             }
164             if (state == READING_KEY_VALUE && !isContinue(line)) {
165                 // valid end of property declaration: create Item for it
166
createKeyItem(tempList, commentLinesCount);
167                 state = WAITING_FOR_KEY_VALUE;
168                 commentLinesCount = 0;
169             }
170         }
171         if (tempList.size() > 0) {
172             if (state == READING_KEY_VALUE) {
173                 // value was not ended correctly? ignore.
174
createKeyItem(tempList, commentLinesCount);
175             } else {
176                 createNonKeyItem(tempList);
177             }
178         }
179     }
180
181     /**
182      * Store properties to a stream.
183      * @param stream an output stream
184      * @throws IOException if the stream could not be written to
185      */

186     public void store(OutputStream JavaDoc stream) throws IOException JavaDoc {
187         boolean previousLineWasEmpty = true;
188         BufferedWriter JavaDoc output = new BufferedWriter JavaDoc(new OutputStreamWriter JavaDoc(stream, "ISO-8859-1"));
189         Iterator JavaDoc it = items.iterator();
190         while (it.hasNext()) {
191             Item item = (Item)it.next();
192             if (item.isSeparate() && !previousLineWasEmpty) {
193                 output.newLine();
194             }
195             Iterator JavaDoc it2 = item.getRawData().iterator();
196             String JavaDoc line = null;
197             while (it2.hasNext()) {
198                 line = (String JavaDoc)it2.next();
199                 output.write(line);
200                 output.newLine();
201             }
202             if (line != null) {
203                 previousLineWasEmpty = isEmpty(line);
204             }
205         }
206         output.flush();
207     }
208     
209     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
210         if (key == null || value == null) {
211             throw new NullPointerException JavaDoc();
212         }
213         Item item = (Item)itemIndex.get((String JavaDoc) key);
214         String JavaDoc result = null;
215         if (item != null) {
216             result = item.getValue();
217             item.setValue((String JavaDoc) value);
218         } else {
219             item = new Item((String JavaDoc) key, (String JavaDoc) value);
220             addItem(item, alphabetize);
221         }
222         return result;
223     }
224
225     /**
226      * Convenience method to get a property as a string.
227      * Same behavior as {@link #get} but has the correct return type.
228      * @param key a property name; cannot be null nor empty
229      * @return the property value, or null if it was not defined
230      */

231     public String JavaDoc getProperty(String JavaDoc key) {
232         return (String JavaDoc)get(key);
233     }
234     
235     /**
236      * Convenience method to set a property.
237      * Same behavior as {@link #put} but has the correct argument types.
238      * (Slightly slower however.)
239      * @param key a property name; cannot be null nor empty
240      * @param value the desired value; cannot be null
241      * @return previous value of the property or null if there was not any
242      */

243     public String JavaDoc setProperty(String JavaDoc key, String JavaDoc value) {
244         String JavaDoc result = getProperty(key);
245         put(key, value);
246         return result;
247     }
248
249     /**
250      * Sets a property to a value broken into segments for readability.
251      * Same behavior as {@link #setProperty(String,String)} with the difference that each item
252      * will be stored on its own line of text. {@link #getProperty} will simply concatenate
253      * all the items into one string, so generally separators
254      * (such as <samp>:</samp> for path-like properties) must be included in
255      * the items (for example, at the end of all but the last item).
256      * @param key a property name; cannot be null nor empty
257      * @param value the desired value; cannot be null; can be empty array
258      * @return previous value of the property or null if there was not any
259      */

260     public String JavaDoc setProperty(String JavaDoc key, String JavaDoc[] value) {
261         String JavaDoc result = getProperty(key);
262         if (key == null || value == null) {
263             throw new NullPointerException JavaDoc();
264         }
265         List JavaDoc/*<String>*/ valueList = Arrays.asList(value);
266         Item item = (Item) itemIndex.get(key);
267         if (item != null) {
268             item.setValue(valueList);
269         } else {
270             addItem(new Item(key, valueList), alphabetize);
271         }
272         return result;
273     }
274
275     /**
276      * Returns comment associated with the property. The comment lines are
277      * returned as defined in properties file, that is comment delimiter is
278      * included. Comment for property is defined as: continuous block of lines
279      * starting with comment delimiter which are followed by property
280      * declaration (no empty line separator allowed).
281      * @param key a property name; cannot be null nor empty
282      * @return array of String lines as specified in properties file; comment
283      * delimiter character is included
284      */

285     public String JavaDoc[] getComment(String JavaDoc key) {
286         Item item = (Item)itemIndex.get(key);
287         if (item == null) {
288             return new String JavaDoc[0];
289         }
290         return item.getComment();
291     }
292
293     /**
294      * Create comment for the property.
295      * <p>Note: if a comment includes non-ISO-8859-1 characters, they will be written
296      * to disk using Unicode escapes (and {@link #getComment} will interpret
297      * such escapes), but of course they will be unreadable for humans.
298      * @param key a property name; cannot be null nor empty
299      * @param comment lines of comment which will be written just above
300      * the property; no reformatting; comment lines must start with
301      * comment delimiter; cannot be null; cannot be emty array
302      * @param separate whether the comment should be separated from previous
303      * item by empty line
304      */

305     public void setComment(String JavaDoc key, String JavaDoc[] comment, boolean separate) {
306         // XXX: check validity of comment parameter
307
Item item = (Item)itemIndex.get(key);
308         if (item == null) {
309             throw new IllegalArgumentException JavaDoc("Cannot set comment for non-existing property "+key);
310         }
311         item.setComment(comment, separate);
312     }
313     
314     public Object JavaDoc clone() {
315         return cloneProperties();
316     }
317     
318     /**
319      * Create an exact copy of this properties object.
320      * @return a clone of this object
321      */

322     public EditableProperties cloneProperties() {
323         return new EditableProperties(this);
324     }
325
326     // non-key item is block of empty lines/comment not associated with any property
327
private void createNonKeyItem(List JavaDoc/*<String>*/ lines) {
328         // First check that previous item is not non-key item.
329
if (items.size() > 0) {
330             Item item = (Item)items.get(items.size()-1);
331             if (item.getKey() == null) {
332                 // it is non-key item: merge them
333
item.addCommentLines(lines);
334                 lines.clear();
335                 return;
336             }
337         }
338         // create new non-key item
339
Item item = new Item(lines);
340         addItem(item, false);
341         lines.clear();
342     }
343
344     // opposite to non-key item: item with valid property declaration and
345
// perhaps some comment lines
346
private void createKeyItem(List JavaDoc/*<String>*/ lines, int commentLinesCount) {
347         Item item = new Item(lines.subList(0, commentLinesCount), lines.subList(commentLinesCount, lines.size()));
348         addItem(item, false);
349         lines.clear();
350     }
351     
352     private void addItem(Item item, boolean sort) {
353         String JavaDoc key = item.getKey();
354         if (sort) {
355             assert key != null;
356             for (int i=0; i<items.size(); i++) {
357                 String JavaDoc k = ((Item)items.get(i)).getKey();
358                 if (k != null && k.compareToIgnoreCase(key) > 0) {
359                     items.add(i, item);
360                     itemIndex.put(key, item);
361                     return;
362                 }
363             }
364         }
365         items.add(item);
366         if (key != null) {
367             itemIndex.put(key, item);
368         }
369     }
370     
371     private void removeItem(Item item) {
372         items.remove(item);
373         if (item.getKey() != null) {
374             itemIndex.remove(item.getKey());
375         }
376     }
377     
378     // does property declaration continue on next line?
379
private boolean isContinue(String JavaDoc line) {
380         int index = line.length() - 1;
381         int slashCount = 0;
382         while (index >= 0 && line.charAt(index) == '\\') {
383             slashCount++;
384             index--;
385         }
386         // if line ends with odd number of backslash then property definition
387
// continues on next line
388
return (slashCount % 2 != 0);
389     }
390     
391     // does line start with comment delimiter? (whitespaces are ignored)
392
private static boolean isComment(String JavaDoc line) {
393         line = trimLeft(line);
394         if (line.length() != 0 && commentChars.indexOf(line.charAt(0)) != -1) {
395             return true;
396         } else {
397             return false;
398         }
399     }
400
401     // is line empty? (whitespaces are ignored)
402
private static boolean isEmpty(String JavaDoc line) {
403         return trimLeft(line).length() == 0;
404     }
405
406     // remove all whitespaces from left
407
private static String JavaDoc trimLeft(String JavaDoc line) {
408         int start = 0;
409         while (start < line.length()) {
410             if (whiteSpaceChars.indexOf(line.charAt(start)) == -1) {
411                 break;
412             }
413             start++;
414         }
415         return line.substring(start);
416     }
417     
418     /**
419      * Representation of one item read from properties file. It can be either
420      * valid property declaration with associated comment or chunk of empty
421      * lines or lines with comment which are not associated with any property.
422      */

423     private static class Item implements Cloneable JavaDoc {
424
425         /** Lines of comment as read from properties file and as they will be
426          * written back to properties file. */

427         private List JavaDoc/*<String>*/ commentLines;
428
429         /** Lines with property name and value declaration as read from
430          * properties file and as they will be written back to properties file. */

431         private List JavaDoc/*<String>*/ keyValueLines;
432
433         /** Property key */
434         private String JavaDoc key;
435         
436         /** Property value */
437         private String JavaDoc value;
438
439         /** Should this property be separated from previous one by at least
440          * one empty line. */

441         private boolean separate;
442         
443         // constructor only for cloning
444
private Item() {
445         }
446         
447         /**
448          * Create instance which does not have any key and value - just
449          * some empty or comment lines. This item is READ-ONLY.
450          */

451         public Item(List JavaDoc/*<String>*/ commentLines) {
452             this.commentLines = new ArrayList JavaDoc(commentLines);
453         }
454
455         /**
456          * Create instance from the lines of comment and property declaration.
457          * Property name and value will be split.
458          */

459         public Item(List JavaDoc/*<String>*/ commentLines, List JavaDoc/*<String>*/ keyValueLines) {
460             this.commentLines = new ArrayList JavaDoc(commentLines);
461             this.keyValueLines = new ArrayList JavaDoc(keyValueLines);
462             parse(keyValueLines);
463         }
464
465         /**
466          * Create new instance with key and value.
467          */

468         public Item(String JavaDoc key, String JavaDoc value) {
469             this.key = key;
470             this.value = value;
471         }
472
473         /**
474          * Create new instance with key and value.
475          */

476         public Item(String JavaDoc key, List JavaDoc/*<String>*/ value) {
477             this.key = key;
478             setValue(value);
479         }
480
481         // backdoor for merging non-key items
482
void addCommentLines(List JavaDoc/*<String>*/ lines) {
483             assert key == null;
484             commentLines.addAll(lines);
485         }
486         
487         public String JavaDoc[] getComment() {
488             String JavaDoc[] res = new String JavaDoc[commentLines.size()];
489             for (int i = 0; i < res.length; i++) {
490                 // #60249: the comment might have Unicode chars in escapes.
491
res[i] = decodeUnicode((String JavaDoc) commentLines.get(i));
492             }
493             return res;
494         }
495         
496         public void setComment(String JavaDoc[] commentLines, boolean separate) {
497             this.separate = separate;
498             this.commentLines = new ArrayList JavaDoc(commentLines.length);
499             for (int i = 0; i < commentLines.length; i++) {
500                 // #60249 again - write only ISO-8859-1.
501
this.commentLines.add(encodeUnicode(commentLines[i]));
502             }
503         }
504         
505         public String JavaDoc getKey() {
506             return key;
507         }
508         
509         public String JavaDoc getValue() {
510             return value;
511         }
512
513         public void setValue(String JavaDoc value) {
514             this.value = value;
515             keyValueLines = null;
516         }
517
518         public void setValue(List JavaDoc/*<String>*/ value) {
519             StringBuffer JavaDoc val = new StringBuffer JavaDoc();
520             List JavaDoc/*<String>*/ l = new ArrayList JavaDoc();
521             if (!value.isEmpty()) {
522                 l.add(encode(key, true) + "=\\"); // NOI18N
523
Iterator JavaDoc it = value.iterator();
524                 while (it.hasNext()) {
525                     String JavaDoc s = (String JavaDoc)it.next();
526                     val.append(s);
527                     s = encode(s, false);
528                     l.add(it.hasNext() ? INDENT + s + '\\' : INDENT + s); // NOI18N
529
}
530             } else {
531                 // #45061: for no vals, use just "prop="
532
l.add(encode(key, true) + '='); // NOI18N
533
}
534             this.value = val.toString();
535             keyValueLines = l;
536         }
537
538         public boolean isSeparate() {
539             return separate;
540         }
541
542         /**
543          * Returns persistent image of this property.
544          */

545         public List JavaDoc/*<String>*/ getRawData() {
546             ArrayList JavaDoc l = new ArrayList JavaDoc();
547             if (commentLines != null) {
548                 l.addAll(commentLines);
549             }
550             if (keyValueLines != null) {
551                 l.addAll(keyValueLines);
552             } else {
553                 keyValueLines = new ArrayList JavaDoc();
554                 if (key != null && value != null) {
555                     keyValueLines.add(encode(key, true)+"="+encode(value, false));
556                 }
557                 l.addAll(keyValueLines);
558             }
559             return l;
560         }
561         
562         private void parse(List JavaDoc/*<String>*/ keyValueLines) {
563             // merge lines into one:
564
String JavaDoc line = mergeLines(keyValueLines);
565             // split key and value
566
splitKeyValue(line);
567         }
568         
569         private String JavaDoc mergeLines(List JavaDoc/*<String>*/ lines) {
570             String JavaDoc line = "";
571             Iterator JavaDoc it = lines.iterator();
572             while (it.hasNext()) {
573                 String JavaDoc l = trimLeft((String JavaDoc)it.next());
574                 // if this is not the last line then remove last backslash
575
if (it.hasNext()) {
576                     assert l.endsWith("\\") : lines;
577                     l = l.substring(0, l.length()-1);
578                 }
579                 line += l;
580             }
581             return line;
582         }
583         
584         private void splitKeyValue(String JavaDoc line) {
585             int separatorIndex = 0;
586             while (separatorIndex < line.length()) {
587                 char ch = line.charAt(separatorIndex);
588                 if (ch == '\\') {
589                     // ignore next one character
590
separatorIndex++;
591                 } else {
592                     if (keyValueSeparators.indexOf(ch) != -1) {
593                         break;
594                     }
595                 }
596                 separatorIndex++;
597             }
598             key = decode(line.substring(0, separatorIndex));
599             line = trimLeft(line.substring(separatorIndex));
600             if (line.length() == 0) {
601                 value = "";
602                 return;
603             }
604             if (strictKeyValueSeparators.indexOf(line.charAt(0)) != -1) {
605                 line = trimLeft(line.substring(1));
606             }
607             value = decode(line);
608         }
609         
610         private static String JavaDoc decode(String JavaDoc input) {
611             char ch;
612             int len = input.length();
613             StringBuffer JavaDoc output = new StringBuffer JavaDoc(len);
614             for (int x=0; x<len; x++) {
615                 ch = input.charAt(x);
616                 if (ch != '\\') {
617                     output.append(ch);
618                     continue;
619                 }
620                 x++;
621                 if (x==len) {
622                     // backslash at the end? syntax error: ignore it
623
continue;
624                 }
625                 ch = input.charAt(x);
626                 if (ch == 'u') {
627                     if (x+5>len) {
628                         // unicode character not finished? syntax error: ignore
629
output.append(input.substring(x-1));
630                         x += 4;
631                         continue;
632                     }
633                     String JavaDoc val = input.substring(x+1, x+5);
634                     try {
635                         output.append((char)Integer.parseInt(val, 16));
636                     } catch (NumberFormatException JavaDoc e) {
637                         // #46234: handle gracefully
638
output.append(input.substring(x - 1, x + 5));
639                     }
640                     x += 4;
641                 } else {
642                     if (ch == 't') {
643                         ch = '\t';
644                     } else if (ch == 'r') {
645                         ch = '\r';
646                     } else if (ch == 'n') {
647                         ch = '\n';
648                     } else if (ch == 'f') {
649                         ch = '\f';
650                     }
651                     output.append(ch);
652                 }
653             }
654             return output.toString();
655         }
656
657         private static String JavaDoc encode(String JavaDoc input, boolean escapeSpace) {
658             int len = input.length();
659             StringBuffer JavaDoc output = new StringBuffer JavaDoc(len*2);
660
661             for(int x=0; x<len; x++) {
662                 char ch = input.charAt(x);
663                 switch(ch) {
664                     case ' ':
665                         if (x == 0 || escapeSpace) {
666                             output.append('\\');
667                         }
668                         output.append(' ');
669                         break;
670                     case '\\':
671                         output.append("\\\\");
672                         break;
673                     case '\t':
674                         output.append("\\t");
675                         break;
676                     case '\n':
677                         output.append("\\n");
678                         break;
679                     case '\r':
680                         output.append("\\r");
681                         break;
682                     case '\f':
683                         output.append("\\f");
684                         break;
685                     default:
686                         if ((ch < 0x0020) || (ch > 0x007e)) {
687                             output.append("\\u");
688                             String JavaDoc hex = Integer.toHexString(ch);
689                             for (int i = 0; i < 4 - hex.length(); i++) {
690                                 output.append('0');
691                             }
692                             output.append(hex);
693                         } else {
694                             output.append(ch);
695                         }
696                 }
697             }
698             return output.toString();
699         }
700         
701         private static String JavaDoc decodeUnicode(String JavaDoc input) {
702             char ch;
703             int len = input.length();
704             StringBuffer JavaDoc output = new StringBuffer JavaDoc(len);
705             for (int x = 0; x < len; x++) {
706                 ch = input.charAt(x);
707                 if (ch != '\\') {
708                     output.append(ch);
709                     continue;
710                 }
711                 x++;
712                 if (x==len) {
713                     // backslash at the end? syntax error: ignore it
714
continue;
715                 }
716                 ch = input.charAt(x);
717                 if (ch == 'u') {
718                     if (x+5>len) {
719                         // unicode character not finished? syntax error: ignore
720
output.append(input.substring(x-1));
721                         x += 4;
722                         continue;
723                     }
724                     String JavaDoc val = input.substring(x+1, x+5);
725                     try {
726                         output.append((char)Integer.parseInt(val, 16));
727                     } catch (NumberFormatException JavaDoc e) {
728                         // #46234: handle gracefully
729
output.append(input.substring(x - 1, x + 5));
730                     }
731                     x += 4;
732                 } else {
733                     output.append(ch);
734                 }
735             }
736             return output.toString();
737         }
738
739         private static String JavaDoc encodeUnicode(String JavaDoc input) {
740             int len = input.length();
741             StringBuffer JavaDoc output = new StringBuffer JavaDoc(len * 2);
742             for (int x = 0; x < len; x++) {
743                 char ch = input.charAt(x);
744                 if ((ch < 0x0020) || (ch > 0x007e)) {
745                     output.append("\\u"); // NOI18N
746
String JavaDoc hex = Integer.toHexString(ch);
747                     for (int i = 0; i < 4 - hex.length(); i++) {
748                         output.append('0');
749                     }
750                     output.append(hex);
751                 } else {
752                     output.append(ch);
753                 }
754             }
755             return output.toString();
756         }
757         
758         public Object JavaDoc clone() {
759             Item item = new Item();
760             if (keyValueLines != null) {
761                 item.keyValueLines = new ArrayList JavaDoc(keyValueLines);
762             }
763             if (commentLines != null) {
764                 item.commentLines = new ArrayList JavaDoc(commentLines);
765             }
766             item.key = key;
767             item.value = value;
768             return item;
769         }
770     
771     }
772     
773     private static class SetImpl extends AbstractSet JavaDoc {
774
775         private EditableProperties props;
776         
777         public SetImpl(EditableProperties props) {
778             this.props = props;
779         }
780         
781         public Iterator JavaDoc iterator() {
782             return new IteratorImpl(props);
783         }
784         
785         public int size() {
786             return props.items.size();
787         }
788         
789     }
790     
791     private static class IteratorImpl implements Iterator JavaDoc {
792
793         private EditableProperties props;
794         private int index = -1;
795         private Item item;
796         
797         public IteratorImpl(EditableProperties props) {
798             this.props = props;
799         }
800         
801         public boolean hasNext() {
802             return findNext() != -1;
803         }
804         
805         public Object JavaDoc next() {
806             index = findNext();
807             if (index == -1) {
808                 throw new NoSuchElementException JavaDoc("There is no more items");
809             }
810             item = (Item)props.items.get(index);
811             return new MapEntryImpl(item);
812         }
813         
814         public void remove() {
815             if (item == null) {
816                 throw new IllegalStateException JavaDoc();
817             }
818             props.removeItem(item);
819             index--;
820             item = null;
821         }
822         
823         private int findNext() {
824             int res = index+1;
825             while (res < props.size()) {
826                 Item i = (Item)props.items.get(res);
827                 if (i.getKey() != null && i.getValue() != null) {
828                     return res;
829                 }
830                 res++;
831             }
832             return -1;
833         }
834         
835     }
836     
837     private static class MapEntryImpl implements Map.Entry JavaDoc {
838         
839         private Item item;
840         
841         public MapEntryImpl(Item item) {
842             this.item = item;
843         }
844         
845         public Object JavaDoc getKey() {
846             return item.getKey();
847         }
848         
849         public Object JavaDoc getValue() {
850             return item.getValue();
851         }
852         
853         public Object JavaDoc setValue(Object JavaDoc value) {
854             String JavaDoc result = item.getValue();
855             item.setValue((String JavaDoc)value);
856             return result;
857         }
858         
859     }
860     
861 }
862
Popular Tags