This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 66577
Collapse All | Expand All

(-)a/core.startup/src/org/netbeans/core/startup/preferences/NbPreferences.java (-6 / +7 lines)
Lines 42-52 Link Here
42
package org.netbeans.core.startup.preferences;
42
package org.netbeans.core.startup.preferences;
43
43
44
import java.io.IOException;
44
import java.io.IOException;
45
import java.util.Properties;
46
import java.util.prefs.AbstractPreferences;
45
import java.util.prefs.AbstractPreferences;
47
import java.util.prefs.BackingStoreException;
46
import java.util.prefs.BackingStoreException;
48
import java.util.prefs.Preferences;
47
import java.util.prefs.Preferences;
49
import org.openide.ErrorManager;
48
import org.openide.ErrorManager;
49
import org.openide.util.EditableProperties;
50
import org.openide.util.RequestProcessor;
50
import org.openide.util.RequestProcessor;
51
51
52
/**
52
/**
Lines 57-63 Link Here
57
    private static Preferences USER_ROOT;
57
    private static Preferences USER_ROOT;
58
    private static Preferences SYSTEM_ROOT;
58
    private static Preferences SYSTEM_ROOT;
59
    
59
    
60
    /*private*/Properties properties;
60
    /*private*/EditableProperties properties;
61
    /*private*/FileStorage fileStorage;
61
    /*private*/FileStorage fileStorage;
62
    
62
    
63
    private static final RequestProcessor RP = new RequestProcessor();
63
    private static final RequestProcessor RP = new RequestProcessor();
Lines 181-189 Link Here
181
        }
181
        }
182
    }
182
    }
183
    
183
    
184
    Properties properties()  {
184
    EditableProperties properties()  {
185
        if (properties == null) {
185
        if (properties == null) {
186
            properties = new Properties(/*loadDefaultProperties()*/);
186
            properties = new EditableProperties(true);
187
            //properties.putAll(loadDefaultProperties());
187
            try {
188
            try {
188
                properties().putAll(fileStorage.load());
189
                properties().putAll(fileStorage.load());
189
            } catch (IOException ex) {
190
            } catch (IOException ex) {
Lines 264-271 Link Here
264
        boolean existsNode();
265
        boolean existsNode();
265
        void removeNode() throws IOException;
266
        void removeNode() throws IOException;
266
        void markModified();
267
        void markModified();
267
        Properties load() throws IOException;
268
        EditableProperties load() throws IOException;
268
        void save(final Properties properties) throws IOException;
269
        void save(final EditableProperties properties) throws IOException;
269
        void runAtomic(Runnable run);
270
        void runAtomic(Runnable run);
270
    }
271
    }
271
}
272
}
(-)a/core.startup/src/org/netbeans/core/startup/preferences/PropertiesStorage.java (-7 / +7 lines)
Lines 49-61 Link Here
49
import java.util.Collections;
49
import java.util.Collections;
50
import java.util.Enumeration;
50
import java.util.Enumeration;
51
import java.util.List;
51
import java.util.List;
52
import java.util.Properties;
53
import java.util.logging.Level;
52
import java.util.logging.Level;
54
import java.util.logging.Logger;
53
import java.util.logging.Logger;
55
import org.openide.filesystems.FileLock;
54
import org.openide.filesystems.FileLock;
56
import org.openide.filesystems.FileObject;
55
import org.openide.filesystems.FileObject;
57
import org.openide.filesystems.FileSystem.AtomicAction;
56
import org.openide.filesystems.FileSystem.AtomicAction;
58
import org.openide.filesystems.FileUtil;
57
import org.openide.filesystems.FileUtil;
58
import org.openide.util.EditableProperties;
59
import org.openide.util.Exceptions;
59
import org.openide.util.Exceptions;
60
60
61
/**
61
/**
Lines 91-98 Link Here
91
                return new String[0];
91
                return new String[0];
92
            }
92
            }
93
            
93
            
94
            public @Override final Properties load() throws IOException {
94
            public @Override final EditableProperties load() throws IOException {
95
                return new Properties();
95
                return new EditableProperties(true);
96
            }
96
            }
97
            
97
            
98
            protected @Override FileObject toPropertiesFile(boolean create) throws IOException {
98
            protected @Override FileObject toPropertiesFile(boolean create) throws IOException {
Lines 191-200 Link Here
191
        }
191
        }
192
    }
192
    }
193
    
193
    
194
    public Properties load() throws IOException {
194
    public EditableProperties load() throws IOException {
195
        Statistics.StopWatch sw = Statistics.getStopWatch(Statistics.LOAD, true);
195
        Statistics.StopWatch sw = Statistics.getStopWatch(Statistics.LOAD, true);
196
        try {
196
        try {
197
            Properties retval = new Properties();
197
            EditableProperties retval = new EditableProperties(true);
198
            FileObject file = toPropertiesFile(false);
198
            FileObject file = toPropertiesFile(false);
199
            if (file != null) {
199
            if (file != null) {
200
                try {
200
                try {
Lines 215-221 Link Here
215
        }
215
        }
216
    }
216
    }
217
    
217
    
218
    public void save(final Properties properties) throws IOException {
218
    public void save(final EditableProperties properties) throws IOException {
219
        if (isModified) {
219
        if (isModified) {
220
            Statistics.StopWatch sw = Statistics.getStopWatch(Statistics.FLUSH, true);
220
            Statistics.StopWatch sw = Statistics.getStopWatch(Statistics.FLUSH, true);
221
            try {
221
            try {
Lines 224-230 Link Here
224
                    OutputStream os = null;
224
                    OutputStream os = null;
225
                    try {
225
                    try {
226
                        os = outputStream();
226
                        os = outputStream();
227
                        properties.store(os, null);
227
                        properties.store(os);
228
                    } finally {
228
                    } finally {
229
                        if (os != null) os.close();
229
                        if (os != null) os.close();
230
                    }
230
                    }
(-)a/core.startup/test/unit/src/org/netbeans/core/startup/preferences/TestFileStorage.java (-2 / +2 lines)
Lines 42-48 Link Here
42
package org.netbeans.core.startup.preferences;
42
package org.netbeans.core.startup.preferences;
43
43
44
import java.io.IOException;
44
import java.io.IOException;
45
import java.util.Properties;
45
import org.openide.util.EditableProperties;
46
46
47
/**
47
/**
48
 *
48
 *
Lines 70-76 Link Here
70
        noFileRepresentationAssertion();
70
        noFileRepresentationAssertion();
71
        
71
        
72
        //load doesn't change file layout
72
        //load doesn't change file layout
73
        Properties p = instance.load();
73
        EditableProperties p = instance.load();
74
        p.put("key", "value");//NOI18N
74
        p.put("key", "value");//NOI18N
75
        noFileRepresentationAssertion();
75
        noFileRepresentationAssertion();
76
        
76
        
(-)a/openide.util/apichanges.xml (+16 lines)
Lines 49-54 Link Here
49
    <apidef name="actions">Actions API</apidef>
49
    <apidef name="actions">Actions API</apidef>
50
</apidefs>
50
</apidefs>
51
<changes>
51
<changes>
52
    <change id="EditableProperties">
53
        <api name="util"/>
54
        <summary>Copied API from <code>project.ant</code> for <code>EditableProperties</code>.</summary>
55
        <version major="7" minor="26"/>
56
        <date day="29" month="7" year="2009"/>
57
        <author login="jglick"/>
58
        <compatibility addition="yes"/>
59
        <description>
60
            <p>
61
                 <code>EditableProperties</code> now available in Utilities API
62
                 so it can be used more broadly.
63
            </p>
64
        </description>
65
        <class package="org.openide.util" name="EditableProperties"/>
66
        <issue number="66577"/>
67
    </change>
52
    <change id="LifecycleManager.markForRestart">
68
    <change id="LifecycleManager.markForRestart">
53
        <api name="util"/>
69
        <api name="util"/>
54
        <summary>Added way to request that the platform restart.</summary>
70
        <summary>Added way to request that the platform restart.</summary>
(-)a/openide.util/nbproject/project.properties (-1 / +1 lines)
Lines 42-48 Link Here
42
module.jar.dir=lib
42
module.jar.dir=lib
43
cp.extra=${nb_all}/apisupport.harness/external/openjdk-javac-6-b12.jar
43
cp.extra=${nb_all}/apisupport.harness/external/openjdk-javac-6-b12.jar
44
44
45
spec.version.base=7.25.0
45
spec.version.base=7.26.0
46
46
47
# For XMLSerializer, needed for XMLUtil.write to work w/ namespaces under JDK 1.4:
47
# For XMLSerializer, needed for XMLUtil.write to work w/ namespaces under JDK 1.4:
48
48
(-)a/project.ant/src/org/netbeans/spi/project/support/ant/EditableProperties.java (-21 / +2 lines)
Lines 39-45 Link Here
39
 * made subject to such option by the copyright holder.
39
 * made subject to such option by the copyright holder.
40
 */
40
 */
41
41
42
package org.netbeans.spi.project.support.ant;
42
package org.openide.util;
43
43
44
import java.io.BufferedReader;
44
import java.io.BufferedReader;
45
import java.io.BufferedWriter;
45
import java.io.BufferedWriter;
Lines 79-84 Link Here
79
 * Only (non-null) String is supported for keys and values.
79
 * Only (non-null) String is supported for keys and values.
80
 * This class is not thread-safe; use only from a single thread, or use {@link java.util.Collections#synchronizedMap}.
80
 * This class is not thread-safe; use only from a single thread, or use {@link java.util.Collections#synchronizedMap}.
81
 * @author Jesse Glick, David Konecny
81
 * @author Jesse Glick, David Konecny
82
 * @since org.openide.util 7.26
82
 */
83
 */
83
public final class EditableProperties extends AbstractMap<String,String> implements Cloneable {
84
public final class EditableProperties extends AbstractMap<String,String> implements Cloneable {
84
    
85
    
Lines 106-120 Link Here
106
    private static final int READING_KEY_VALUE = 2;
107
    private static final int READING_KEY_VALUE = 2;
107
    
108
    
108
    /**
109
    /**
109
     * Creates empty instance whose items will not be alphabetized.
110
     * @deprecated Use {@link #EditableProperties(boolean)} for clarity instead.
111
     */
112
    @Deprecated
113
    public EditableProperties() {
114
        this(/* mentioned in #64174 - documented default */false);
115
    }
116
117
    /**
118
     * Creates empty instance.
110
     * Creates empty instance.
119
     * @param alphabetize alphabetize new items according to key or not
111
     * @param alphabetize alphabetize new items according to key or not
120
     */
112
     */
Lines 125-141 Link Here
125
    }
117
    }
126
    
118
    
127
    /**
119
    /**
128
     * Creates instance from an existing map. No comments will be defined.
129
     * Any order from the existing map will be retained,
130
     * and further additions will not be alphabetized.
131
     * @param map a map from String to String
132
     */
133
    public EditableProperties(Map<String,String> map) {
134
        this(false);
135
        putAll(map);
136
    }
137
    
138
    /**
139
     * Creates new instance from an existing one.
120
     * Creates new instance from an existing one.
140
     * @param ep an instance of EditableProperties
121
     * @param ep an instance of EditableProperties
141
     */
122
     */
(-)a/project.ant/test/unit/src/org/netbeans/spi/project/support/ant/EditablePropertiesTest.java (-134 / +1 lines)
Lines 39-56 Link Here
39
 * made subject to such option by the copyright holder.
39
 * made subject to such option by the copyright holder.
40
 */
40
 */
41
41
42
package org.netbeans.spi.project.support.ant;
42
package org.openide.util;
43
43
44
import java.io.ByteArrayInputStream;
44
import java.io.ByteArrayInputStream;
45
import java.io.ByteArrayOutputStream;
45
import java.io.ByteArrayOutputStream;
46
import java.io.File;
46
import java.io.File;
47
import java.io.FileInputStream;
48
import java.io.FileOutputStream;
47
import java.io.FileOutputStream;
49
import java.io.IOException;
48
import java.io.IOException;
50
import java.io.InputStream;
49
import java.io.InputStream;
51
import java.io.InputStreamReader;
52
import java.io.OutputStream;
50
import java.io.OutputStream;
53
import java.io.Reader;
54
import java.net.URI;
51
import java.net.URI;
55
import java.net.URL;
52
import java.net.URL;
56
import java.util.Arrays;
53
import java.util.Arrays;
Lines 138-204 Link Here
138
        assertFile("Saved cloned properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
135
        assertFile("Saved cloned properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
139
    }
136
    }
140
137
141
    // test that modifications changes only necessary parts
142
    public void testVersionability() throws Exception {
143
        clearWorkDir();
144
        
145
        EditableProperties ep = loadTestProperties();
146
        
147
        EditableProperties ep2 = ep.cloneProperties();
148
        ep2.setProperty("key24", "new value of key 24");
149
        String dest = getWorkDirPath()+File.separatorChar+"mod1.properties";
150
        saveProperties(ep2, dest);
151
        int res[] = compare(filenameOfTestProperties(), dest);
152
        assertEquals("One line modified", 1, res[0]);
153
        assertEquals("No lines added", 0, res[1]);
154
        assertEquals("No lines removed", 0, res[2]);
155
        
156
        ep2 = ep.cloneProperties();
157
        ep2.setProperty("key23", "new value of key23");
158
        dest = getWorkDirPath()+File.separatorChar+"mod2.properties";
159
        saveProperties(ep2, dest);
160
        res = compare(filenameOfTestProperties(), dest);
161
        assertEquals("Four lines modified", 4, res[0]);
162
        assertEquals("No lines added", 0, res[1]);
163
        assertEquals("No lines removed", 0, res[2]);
164
        
165
        ep2 = ep.cloneProperties();
166
        ep2.put("newkey", "new value");
167
        dest = getWorkDirPath()+File.separatorChar+"mod3.properties";
168
        saveProperties(ep2, dest);
169
        res = compare(filenameOfTestProperties(), dest);
170
        assertEquals("No lines modified", 0, res[0]);
171
        assertEquals("One line added", 1, res[1]);
172
        assertEquals("No lines removed", 0, res[2]);
173
        
174
        ep2 = ep.cloneProperties();
175
        assertNotNull(ep2.get("key14"));
176
        ep2.remove("key14");
177
        assertNull(ep2.get("key14"));
178
        dest = getWorkDirPath()+File.separatorChar+"mod4.properties";
179
        saveProperties(ep2, dest);
180
        res = compare(filenameOfTestProperties(), dest);
181
        assertEquals("No lines modified", 0, res[0]);
182
        assertEquals("No lines added", 0, res[1]);
183
        assertEquals("Two lines removed", 2, res[2]);
184
        
185
        ep2 = ep.cloneProperties();
186
        ep2.setProperty("key21", new String[]{"first line;", "second line;", "third line"});
187
        dest = getWorkDirPath()+File.separatorChar+"mod5.properties";
188
        saveProperties(ep2, dest);
189
        res = compare(filenameOfTestProperties(), dest);
190
        assertEquals("Four lines modified", 4, res[0]);
191
        assertEquals("No lines added", 0, res[1]);
192
        assertEquals("No lines removed", 0, res[2]);
193
        ep2.setProperty("key21", "first line;second line;third line");
194
        String dest2 = getWorkDirPath()+File.separatorChar+"mod6.properties";
195
        saveProperties(ep2, dest2);
196
        res = compare(dest, dest2);
197
        assertEquals("Four lines modified", 4, res[0]);
198
        assertEquals("No lines added", 0, res[1]);
199
        assertEquals("No lines removed", 0, res[2]);
200
    }
201
202
    // test that array values are stored correctly
138
    // test that array values are stored correctly
203
    public void testArrayValues() throws Exception {
139
    public void testArrayValues() throws Exception {
204
        EditableProperties ep = new EditableProperties(false);
140
        EditableProperties ep = new EditableProperties(false);
Lines 274-331 Link Here
274
    }
210
    }
275
211
276
    // test that changing comments work and modify only comments
212
    // test that changing comments work and modify only comments
277
    public void testComment() throws Exception {
278
        clearWorkDir();
279
        
280
        EditableProperties ep = loadTestProperties();
281
        
282
        EditableProperties ep2 = ep.cloneProperties();
283
        ep2.setComment("key10", new String[]{"# this is new comment for property key 10"}, false);
284
        String dest = getWorkDirPath()+File.separatorChar+"comment1.properties";
285
        saveProperties(ep2, dest);
286
        int res[] = compare(filenameOfTestProperties(), dest);
287
        assertEquals("No lines modified", 0, res[0]);
288
        assertEquals("One line added", 1, res[1]);
289
        assertEquals("No lines removed", 0, res[2]);
290
        
291
        ep2 = ep.cloneProperties();
292
        ep2.setComment("key1", new String[]{"# new comment", "# new comment second line"}, true);
293
        dest = getWorkDirPath()+File.separatorChar+"comment2.properties";
294
        saveProperties(ep2, dest);
295
        res = compare(filenameOfTestProperties(), dest);
296
        assertEquals("No lines modified", 0, res[0]);
297
        assertEquals("Two lines added", 2, res[1]);
298
        assertEquals("No lines removed", 0, res[2]);
299
        
300
        ep2 = ep.cloneProperties();
301
        ep2.setComment("key26", new String[]{"# changed comment"}, false);
302
        dest = getWorkDirPath()+File.separatorChar+"comment3.properties";
303
        saveProperties(ep2, dest);
304
        res = compare(filenameOfTestProperties(), dest);
305
        assertEquals("One line modified", 1, res[0]);
306
        assertEquals("No lines added", 0, res[1]);
307
        assertEquals("No lines removed", 0, res[2]);
308
        
309
        ep2 = ep.cloneProperties();
310
        ep2.setComment("key25", new String[]{"# one line comment"}, false);
311
        dest = getWorkDirPath()+File.separatorChar+"comment4.properties";
312
        saveProperties(ep2, dest);
313
        res = compare(filenameOfTestProperties(), dest);
314
        assertEquals("Two lines modified", 2, res[0]);
315
        assertEquals("No lines added", 0, res[1]);
316
        assertEquals("No lines removed", 0, res[2]);
317
        
318
        ep2 = ep.cloneProperties();
319
        ep2.setComment("key26", ep2.getComment("key26"), true);
320
        dest = getWorkDirPath()+File.separatorChar+"comment5.properties";
321
        saveProperties(ep2, dest);
322
        res = compare(filenameOfTestProperties(), dest);
323
        assertEquals("No line modified", 0, res[0]);
324
        assertEquals("One line added", 1, res[1]);
325
        assertEquals("No lines removed", 0, res[2]);
326
        
327
    }
328
329
    // test that misc chars are correctly escaped, unicode encoded, etc.
213
    // test that misc chars are correctly escaped, unicode encoded, etc.
330
    public void testEscaping() throws Exception {
214
    public void testEscaping() throws Exception {
331
        String umlaut = "" + (char)252;
215
        String umlaut = "" + (char)252;
Lines 472-494 Link Here
472
        }
356
        }
473
    }
357
    }
474
358
475
    private int[] compare(String f1, String f2) throws Exception {
476
        Reader r1 = null;
477
        Reader r2 = null;
478
        try {
479
            r1 = new InputStreamReader(new FileInputStream(f1), "ISO-8859-1");
480
            r2 = new InputStreamReader(new FileInputStream(f2), "ISO-8859-1");
481
            return AntBasedTestUtil.countTextDiffs(r1, r2);
482
        } finally {
483
            if (r1 != null) {
484
                r1.close();
485
            }
486
            if (r2 != null) {
487
                r2.close();
488
            }
489
        }
490
    }
491
    
492
    private String getAsString(EditableProperties ep) throws Exception {
359
    private String getAsString(EditableProperties ep) throws Exception {
493
        ByteArrayOutputStream os = new ByteArrayOutputStream();
360
        ByteArrayOutputStream os = new ByteArrayOutputStream();
494
        ep.store(os);
361
        ep.store(os);
(-)a/project.ant/nbproject/project.xml (-1 / +1 lines)
Lines 166-172 Link Here
166
                    <build-prerequisite/>
166
                    <build-prerequisite/>
167
                    <compile-dependency/>
167
                    <compile-dependency/>
168
                    <run-dependency>
168
                    <run-dependency>
169
                        <specification-version>7.15</specification-version>
169
                        <specification-version>7.26</specification-version>
170
                    </run-dependency>
170
                    </run-dependency>
171
                </dependency>
171
                </dependency>
172
                <dependency>
172
                <dependency>
(-)a/project.ant/src/org/netbeans/spi/project/support/ant/EditableProperties.java (-682 / +16 lines)
Lines 41-69 Link Here
41
41
42
package org.netbeans.spi.project.support.ant;
42
package org.netbeans.spi.project.support.ant;
43
43
44
import java.io.BufferedReader;
45
import java.io.BufferedWriter;
46
import java.io.IOException;
44
import java.io.IOException;
47
import java.io.InputStream;
45
import java.io.InputStream;
48
import java.io.InputStreamReader;
49
import java.io.OutputStream;
46
import java.io.OutputStream;
50
import java.io.OutputStreamWriter;
51
import java.util.AbstractMap;
47
import java.util.AbstractMap;
52
import java.util.AbstractSet;
53
import java.util.ArrayList;
54
import java.util.Arrays;
55
import java.util.HashMap;
56
import java.util.Iterator;
57
import java.util.LinkedList;
58
import java.util.List;
59
import java.util.ListIterator;
60
import java.util.Map;
48
import java.util.Map;
61
import java.util.NoSuchElementException;
62
import java.util.Set;
49
import java.util.Set;
63
50
64
// XXX: consider adding getInitialComment() and setInitialComment() methods
65
// (useful e.g. for GeneratedFilesHelper)
66
67
/**
51
/**
68
 * Similar to {@link java.util.Properties} but designed to retain additional
52
 * Similar to {@link java.util.Properties} but designed to retain additional
69
 * information needed for safe hand-editing.
53
 * information needed for safe hand-editing.
Lines 78-109 Link Here
78
 * The file format (including encoding etc.) is compatible with the regular JRE implementation.
62
 * The file format (including encoding etc.) is compatible with the regular JRE implementation.
79
 * Only (non-null) String is supported for keys and values.
63
 * Only (non-null) String is supported for keys and values.
80
 * This class is not thread-safe; use only from a single thread, or use {@link java.util.Collections#synchronizedMap}.
64
 * This class is not thread-safe; use only from a single thread, or use {@link java.util.Collections#synchronizedMap}.
81
 * @author Jesse Glick, David Konecny
65
 * <p>This class exists here only for historical reasons. It delegates to {@link org.openide.util.EditableProperties}.
82
 */
66
 */
83
public final class EditableProperties extends AbstractMap<String,String> implements Cloneable {
67
public final class EditableProperties extends AbstractMap<String,String> implements Cloneable {
84
    
85
    /** List of Item instances as read from the properties file. Order is important.
86
     * Saving properties will save then in this order. */
87
    private final LinkedList<Item> items;
88
68
89
    /** Map of [property key, Item instance] for faster access. */
69
    private final org.openide.util.EditableProperties delegate;
90
    private final Map<String,Item> itemIndex;
91
70
92
    private final boolean alphabetize;
71
    private EditableProperties(org.openide.util.EditableProperties delegate) {
93
    
72
        this.delegate = delegate;
94
    private static final String keyValueSeparators = "=: \t\r\n\f";
73
    }
95
96
    private static final String strictKeyValueSeparators = "=:";
97
98
    private static final String whiteSpaceChars = " \t\r\n\f";
99
100
    private static final String commentChars = "#!";
101
    
102
    private static final String INDENT = "    ";
103
104
    // parse states:
105
    private static final int WAITING_FOR_KEY_VALUE = 1;
106
    private static final int READING_KEY_VALUE = 2;
107
    
74
    
108
    /**
75
    /**
109
     * Creates empty instance whose items will not be alphabetized.
76
     * Creates empty instance whose items will not be alphabetized.
Lines 119-127 Link Here
119
     * @param alphabetize alphabetize new items according to key or not
86
     * @param alphabetize alphabetize new items according to key or not
120
     */
87
     */
121
    public EditableProperties(boolean alphabetize) {
88
    public EditableProperties(boolean alphabetize) {
122
        this.alphabetize = alphabetize;
89
        this(new org.openide.util.EditableProperties(alphabetize));
123
        items = new LinkedList<Item>();
124
        itemIndex = new HashMap<String,Item>();
125
    }
90
    }
126
    
91
    
127
    /**
92
    /**
Lines 136-164 Link Here
136
    }
101
    }
137
    
102
    
138
    /**
103
    /**
139
     * Creates new instance from an existing one.
140
     * @param ep an instance of EditableProperties
141
     */
142
    private EditableProperties(EditableProperties ep) {
143
        // #64174: use a simple deep copy for speed
144
        alphabetize = ep.alphabetize;
145
        items = new LinkedList<Item>();
146
        itemIndex = new HashMap<String,Item>(ep.items.size() * 4 / 3 + 1);
147
        for (Item _i : ep.items) {
148
            Item i = (Item) _i.clone();
149
            items.add(i);
150
            itemIndex.put(i.getKey(), i);
151
        }
152
    }
153
    
154
    /**
155
     * Returns a set view of the mappings ordered according to their file 
104
     * Returns a set view of the mappings ordered according to their file 
156
     * position.  Each element in this set is a Map.Entry. See
105
     * position.  Each element in this set is a Map.Entry. See
157
     * {@link AbstractMap#entrySet} for more details.
106
     * {@link AbstractMap#entrySet} for more details.
158
     * @return set with Map.Entry instances.
107
     * @return set with Map.Entry instances.
159
     */
108
     */
160
    public Set<Map.Entry<String,String>> entrySet() {
109
    public Set<Map.Entry<String,String>> entrySet() {
161
        return new SetImpl(this);
110
        return delegate.entrySet();
162
    }
111
    }
163
    
112
    
164
    /**
113
    /**
Lines 167-211 Link Here
167
     * @throws IOException if the contents are malformed or the stream could not be read
116
     * @throws IOException if the contents are malformed or the stream could not be read
168
     */
117
     */
169
    public void load(InputStream stream) throws IOException {
118
    public void load(InputStream stream) throws IOException {
170
        int state = WAITING_FOR_KEY_VALUE;
119
        delegate.load(stream);
171
        BufferedReader input = new BufferedReader(new InputStreamReader(stream, "ISO-8859-1"));
172
        List<String> tempList = new LinkedList<String>();
173
        String line;
174
        int commentLinesCount = 0;
175
        // Read block of lines and create instance of Item for each.
176
        // Separator is: either empty line or valid end of proeprty declaration
177
        while (null != (line = input.readLine())) {
178
            tempList.add(line);
179
            boolean empty = isEmpty(line);
180
            boolean comment = isComment(line);
181
            if (state == WAITING_FOR_KEY_VALUE) {
182
                if (empty) {
183
                    // empty line: create Item without any key
184
                    createNonKeyItem(tempList);
185
                    commentLinesCount = 0;
186
                } else {
187
                    if (comment) {
188
                        commentLinesCount++;
189
                    } else {
190
                        state = READING_KEY_VALUE;
191
                    }
192
                }
193
            }
194
            if (state == READING_KEY_VALUE && !isContinue(line)) {
195
                // valid end of property declaration: create Item for it
196
                createKeyItem(tempList, commentLinesCount);
197
                state = WAITING_FOR_KEY_VALUE;
198
                commentLinesCount = 0;
199
            }
200
        }
201
        if (tempList.size() > 0) {
202
            if (state == READING_KEY_VALUE) {
203
                // value was not ended correctly? ignore.
204
                createKeyItem(tempList, commentLinesCount);
205
            } else {
206
                createNonKeyItem(tempList);
207
            }
208
        }
209
    }
120
    }
210
121
211
    /**
122
    /**
Lines 214-254 Link Here
214
     * @throws IOException if the stream could not be written to
125
     * @throws IOException if the stream could not be written to
215
     */
126
     */
216
    public void store(OutputStream stream) throws IOException {
127
    public void store(OutputStream stream) throws IOException {
217
        boolean previousLineWasEmpty = true;
128
        delegate.store(stream);
218
        BufferedWriter output = new BufferedWriter(new OutputStreamWriter(stream, "ISO-8859-1"));
219
        for (Item item : items) {
220
            if (item.isSeparate() && !previousLineWasEmpty) {
221
                output.newLine();
222
            }
223
            String line = null;
224
            Iterator<String> it = item.getRawData().iterator();
225
            while (it.hasNext()) {
226
                line = it.next();
227
                output.write(line);
228
                output.newLine();
229
            }
230
            if (line != null) {
231
                previousLineWasEmpty = isEmpty(line);
232
            }
233
        }
234
        output.flush();
235
    }
129
    }
236
130
237
    @Override
131
    @Override
238
    public String put(String key, String value) {
132
    public String put(String key, String value) {
239
        if (key == null || value == null) {
133
        return delegate.put(key, value);
240
            throw new NullPointerException();
241
        }
242
        Item item = itemIndex.get(key);
243
        String result = null;
244
        if (item != null) {
245
            result = item.getValue();
246
            item.setValue(value);
247
        } else {
248
            item = new Item(key, value);
249
            addItem(item, alphabetize);
250
        }
251
        return result;
252
    }
134
    }
253
135
254
    /**
136
    /**
Lines 258-264 Link Here
258
     * @return the property value, or null if it was not defined
140
     * @return the property value, or null if it was not defined
259
     */
141
     */
260
    public String getProperty(String key) {
142
    public String getProperty(String key) {
261
        return get(key);
143
        return delegate.getProperty(key);
262
    }
144
    }
263
    
145
    
264
    /**
146
    /**
Lines 269-275 Link Here
269
     * @return previous value of the property or null if there was not any
151
     * @return previous value of the property or null if there was not any
270
     */
152
     */
271
    public String setProperty(String key, String value) {
153
    public String setProperty(String key, String value) {
272
        return put(key, value);
154
        return delegate.setProperty(key, value);
273
    }
155
    }
274
156
275
    /**
157
    /**
Lines 284-301 Link Here
284
     * @return previous value of the property or null if there was not any
166
     * @return previous value of the property or null if there was not any
285
     */
167
     */
286
    public String setProperty(String key, String[] value) {
168
    public String setProperty(String key, String[] value) {
287
        String result = get(key);
169
        return delegate.setProperty(key, value);
288
        if (key == null || value == null) {
289
            throw new NullPointerException();
290
        }
291
        List<String> valueList = Arrays.asList(value);
292
        Item item = itemIndex.get(key);
293
        if (item != null) {
294
            item.setValue(valueList);
295
        } else {
296
            addItem(new Item(key, valueList), alphabetize);
297
        }
298
        return result;
299
    }
170
    }
300
171
301
    /**
172
    /**
Lines 309-319 Link Here
309
     *    delimiter character is included
180
     *    delimiter character is included
310
     */
181
     */
311
    public String[] getComment(String key) {
182
    public String[] getComment(String key) {
312
        Item item = itemIndex.get(key);
183
        return delegate.getComment(key);
313
        if (item == null) {
314
            return new String[0];
315
        }
316
        return item.getComment();
317
    }
184
    }
318
185
319
    /**
186
    /**
Lines 329-340 Link Here
329
     *    item by empty line
196
     *    item by empty line
330
     */
197
     */
331
    public void setComment(String key, String[] comment, boolean separate) {
198
    public void setComment(String key, String[] comment, boolean separate) {
332
        // XXX: check validity of comment parameter
199
        delegate.setComment(key, comment, separate);
333
        Item item = itemIndex.get(key);
334
        if (item == null) {
335
            throw new IllegalArgumentException("Cannot set comment for non-existing property "+key);
336
        }
337
        item.setComment(comment, separate);
338
    }
200
    }
339
201
340
    @Override
202
    @Override
Lines 347-881 Link Here
347
     * @return a clone of this object
209
     * @return a clone of this object
348
     */
210
     */
349
    public EditableProperties cloneProperties() {
211
    public EditableProperties cloneProperties() {
350
        return new EditableProperties(this);
212
        return new EditableProperties(delegate.cloneProperties());
351
    }
213
    }
352
214
353
    // non-key item is block of empty lines/comment not associated with any property
354
    private void createNonKeyItem(List<String> lines) {
355
        // First check that previous item is not non-key item.
356
        if (!items.isEmpty()) {
357
            Item item = items.getLast();
358
            if (item.getKey() == null) {
359
                // it is non-key item:  merge them
360
                item.addCommentLines(lines);
361
                lines.clear();
362
                return;
363
            }
364
        }
365
        // create new non-key item
366
        Item item = new Item(lines);
367
        addItem(item, false);
368
        lines.clear();
369
    }
370
371
    // opposite to non-key item: item with valid property declaration and 
372
    // perhaps some comment lines
373
    private void createKeyItem(List<String> lines, int commentLinesCount) {
374
        Item item = new Item(lines.subList(0, commentLinesCount), lines.subList(commentLinesCount, lines.size()));
375
        addItem(item, false);
376
        lines.clear();
377
    }
378
    
379
    private void addItem(Item item, boolean sort) {
380
        String key = item.getKey();
381
        if (sort) {
382
            assert key != null;
383
            ListIterator<Item> it = items.listIterator();
384
            while (it.hasNext()) {
385
                String k = it.next().getKey();
386
                if (k != null && k.compareToIgnoreCase(key) > 0) {
387
                    it.previous();
388
                    it.add(item);
389
                    itemIndex.put(key, item);
390
                    return;
391
                }
392
            }
393
        }
394
        items.add(item);
395
        if (key != null) {
396
            itemIndex.put(key, item);
397
        }
398
    }
399
    
400
    // does property declaration continue on next line?
401
    private boolean isContinue(String line) {
402
        int index = line.length() - 1;
403
        int slashCount = 0;
404
        while (index >= 0 && line.charAt(index) == '\\') {
405
            slashCount++;
406
            index--;
407
        }
408
        // if line ends with odd number of backslash then property definition 
409
        // continues on next line
410
        return (slashCount % 2 != 0);
411
    }
412
    
413
    // does line start with comment delimiter? (whitespaces are ignored)
414
    private static boolean isComment(String line) {
415
        line = trimLeft(line);
416
        if (line.length() != 0 && commentChars.indexOf(line.charAt(0)) != -1) {
417
            return true;
418
        } else {
419
            return false;
420
        }
421
    }
422
423
    // is line empty? (whitespaces are ignored)
424
    private static boolean isEmpty(String line) {
425
        return trimLeft(line).length() == 0;
426
    }
427
428
    // remove all whitespaces from left
429
    private static String trimLeft(String line) {
430
        int start = 0;
431
        while (start < line.length()) {
432
            if (whiteSpaceChars.indexOf(line.charAt(start)) == -1) {
433
                break;
434
            }
435
            start++;
436
        }
437
        return line.substring(start);
438
    }
439
    
440
    /**
441
     * Representation of one item read from properties file. It can be either
442
     * valid property declaration with associated comment or chunk of empty
443
     * lines or lines with comment which are not associated with any property.
444
     */
445
    private static class Item implements Cloneable {
446
447
        /** Lines of comment as read from properties file and as they will be
448
         * written back to properties file. */
449
        private List<String> commentLines;
450
451
        /** Lines with property name and value declaration as read from 
452
         * properties file and as they will be written back to properties file. */
453
        private List<String> keyValueLines;
454
455
        /** Property key */
456
        private String key;
457
        
458
        /** Property value */
459
        private String value;
460
461
        /** Should this property be separated from previous one by at least
462
         * one empty line. */
463
        private boolean separate;
464
        
465
        // constructor only for cloning
466
        private Item() {
467
        }
468
        
469
        /**
470
         * Create instance which does not have any key and value - just 
471
         * some empty or comment lines. This item is READ-ONLY.
472
         */
473
        public Item(List<String> commentLines) {
474
            this.commentLines = new ArrayList<String>(commentLines);
475
        }
476
477
        /**
478
         * Create instance from the lines of comment and property declaration.
479
         * Property name and value will be split.
480
         */
481
        public Item(List<String> commentLines, List<String> keyValueLines) {
482
            this.commentLines = new ArrayList<String>(commentLines);
483
            this.keyValueLines = new ArrayList<String>(keyValueLines);
484
            parse(keyValueLines);
485
        }
486
487
        /**
488
         * Create new instance with key and value.
489
         */
490
        public Item(String key, String value) {
491
            this.key = key;
492
            this.value = value;
493
        }
494
495
        /**
496
         * Create new instance with key and value.
497
         */
498
        public Item(String key, List<String> value) {
499
            this.key = key;
500
            setValue(value);
501
        }
502
503
        // backdoor for merging non-key items
504
        void addCommentLines(List<String> lines) {
505
            assert key == null;
506
            commentLines.addAll(lines);
507
        }
508
        
509
        public String[] getComment() {
510
            String[] res = new String[commentLines.size()];
511
            for (int i = 0; i < res.length; i++) {
512
                // #60249: the comment might have Unicode chars in escapes.
513
                res[i] = decodeUnicode(commentLines.get(i));
514
            }
515
            return res;
516
        }
517
        
518
        public void setComment(String[] commentLines, boolean separate) {
519
            this.separate = separate;
520
            this.commentLines = new ArrayList<String>(commentLines.length);
521
            for (int i = 0; i < commentLines.length; i++) {
522
                // #60249 again - write only ISO-8859-1.
523
                this.commentLines.add(encodeUnicode(commentLines[i]));
524
            }
525
        }
526
        
527
        public String getKey() {
528
            return key;
529
        }
530
        
531
        public String getValue() {
532
            return value;
533
        }
534
535
        public void setValue(String value) {
536
            this.value = value;
537
            keyValueLines = null;
538
        }
539
540
        public void setValue(List<String> value) {
541
            StringBuffer val = new StringBuffer();
542
            List<String> l = new ArrayList<String>();
543
            if (!value.isEmpty()) {
544
                l.add(encode(key, true) + "=\\"); // NOI18N
545
                Iterator<String> it = value.iterator();
546
                while (it.hasNext()) {
547
                    String s = it.next();
548
                    val.append(s);
549
                    s = encode(s, false);
550
                    l.add(it.hasNext() ? INDENT + s + '\\' : INDENT + s); // NOI18N
551
                }
552
            } else {
553
                // #45061: for no vals, use just "prop="
554
                l.add(encode(key, true) + '='); // NOI18N
555
            }
556
            this.value = val.toString();
557
            keyValueLines = l;
558
        }
559
560
        public boolean isSeparate() {
561
            return separate;
562
        }
563
564
        /**
565
         * Returns persistent image of this property.
566
         */
567
        public List<String> getRawData() {
568
            List<String> l = new ArrayList<String>();
569
            if (commentLines != null) {
570
                l.addAll(commentLines);
571
            }
572
            if (keyValueLines == null) {
573
                keyValueLines = new ArrayList<String>();
574
                if (key != null && value != null) {
575
                    keyValueLines.add(encode(key, true)+"="+encode(value, false));
576
                }
577
            }
578
            l.addAll(keyValueLines);
579
            return l;
580
        }
581
        
582
        private void parse(List<String> keyValueLines) {
583
            // merge lines into one:
584
            String line = mergeLines(keyValueLines);
585
            // split key and value
586
            splitKeyValue(line);
587
        }
588
        
589
        private String mergeLines(List<String> lines) {
590
            String line = ""; // XXX use StringBuilder instead
591
            Iterator<String> it = lines.iterator();
592
            while (it.hasNext()) {
593
                String l = trimLeft(it.next());
594
                // if this is not the last line then remove last backslash
595
                if (it.hasNext()) {
596
                    assert l.endsWith("\\") : lines;
597
                    l = l.substring(0, l.length()-1);
598
                }
599
                line += l;
600
            }
601
            return line;
602
        }
603
        
604
        private void splitKeyValue(String line) {
605
            int separatorIndex = 0;
606
            while (separatorIndex < line.length()) {
607
                char ch = line.charAt(separatorIndex);
608
                if (ch == '\\') {
609
                    // ignore next one character
610
                    separatorIndex++;
611
                } else {
612
                    if (keyValueSeparators.indexOf(ch) != -1) {
613
                        break;
614
                    }
615
                }
616
                separatorIndex++;
617
            }
618
            key = decode(line.substring(0, separatorIndex));
619
            line = trimLeft(line.substring(separatorIndex));
620
            if (line.length() == 0) {
621
                value = "";
622
                return;
623
            }
624
            if (strictKeyValueSeparators.indexOf(line.charAt(0)) != -1) {
625
                line = trimLeft(line.substring(1));
626
            }
627
            value = decode(line);
628
        }
629
        
630
        private static String decode(String input) {
631
            char ch;
632
            int len = input.length();
633
            StringBuffer output = new StringBuffer(len);
634
            for (int x=0; x<len; x++) {
635
                ch = input.charAt(x);
636
                if (ch != '\\') {
637
                    output.append(ch);
638
                    continue;
639
                }
640
                x++;
641
                if (x==len) {
642
                    // backslash at the end? syntax error: ignore it
643
                    continue;
644
                }
645
                ch = input.charAt(x);
646
                if (ch == 'u') {
647
                    if (x+5>len) {
648
                        // unicode character not finished? syntax error: ignore
649
                        output.append(input.substring(x-1));
650
                        x += 4;
651
                        continue;
652
                    }
653
                    String val = input.substring(x+1, x+5);
654
                    try {
655
                        output.append((char)Integer.parseInt(val, 16));
656
                    } catch (NumberFormatException e) {
657
                        // #46234: handle gracefully
658
                        output.append(input.substring(x - 1, x + 5));
659
                    }
660
                    x += 4;
661
                } else {
662
                    if (ch == 't') ch = '\t';
663
                    else if (ch == 'r') ch = '\r';
664
                    else if (ch == 'n') ch = '\n';
665
                    else if (ch == 'f') ch = '\f';
666
                    output.append(ch);
667
                }
668
            }
669
            return output.toString();
670
        }
671
672
        private static String encode(String input, boolean escapeSpace) {
673
            int len = input.length();
674
            StringBuffer output = new StringBuffer(len*2);
675
676
            for(int x=0; x<len; x++) {
677
                char ch = input.charAt(x);
678
                switch(ch) {
679
                    case ' ':
680
                        if (x == 0 || escapeSpace)  {
681
                            output.append('\\');
682
                        }
683
                        output.append(' ');
684
                        break;
685
                    case '\\':
686
                        output.append("\\\\");
687
                        break;
688
                    case '\t':
689
                        output.append("\\t");
690
                        break;
691
                    case '\n':
692
                        output.append("\\n");
693
                        break;
694
                    case '\r':
695
                        output.append("\\r");
696
                        break;
697
                    case '\f':
698
                        output.append("\\f");
699
                        break;
700
                    default:
701
                        if ((ch < 0x0020) || (ch > 0x007e)) {
702
                            output.append("\\u");
703
                            String hex = Integer.toHexString(ch);
704
                            for (int i = 0; i < 4 - hex.length(); i++) {
705
                                output.append('0');
706
                            }
707
                            output.append(hex);
708
                        } else {
709
                            output.append(ch);
710
                        }
711
                }
712
            }
713
            return output.toString();
714
        }
715
        
716
        private static String decodeUnicode(String input) {
717
            char ch;
718
            int len = input.length();
719
            StringBuffer output = new StringBuffer(len);
720
            for (int x = 0; x < len; x++) {
721
                ch = input.charAt(x);
722
                if (ch != '\\') {
723
                    output.append(ch);
724
                    continue;
725
                }
726
                x++;
727
                if (x==len) {
728
                    // backslash at the end? syntax error: ignore it
729
                    continue;
730
                }
731
                ch = input.charAt(x);
732
                if (ch == 'u') {
733
                    if (x+5>len) {
734
                        // unicode character not finished? syntax error: ignore
735
                        output.append(input.substring(x-1));
736
                        x += 4;
737
                        continue;
738
                    }
739
                    String val = input.substring(x+1, x+5);
740
                    try {
741
                        output.append((char)Integer.parseInt(val, 16));
742
                    } catch (NumberFormatException e) {
743
                        // #46234: handle gracefully
744
                        output.append(input.substring(x - 1, x + 5));
745
                    }
746
                    x += 4;
747
                } else {
748
                    output.append(ch);
749
                }
750
            }
751
            return output.toString();
752
        }
753
754
        private static String encodeUnicode(String input) {
755
            int len = input.length();
756
            StringBuffer output = new StringBuffer(len * 2);
757
            for (int x = 0; x < len; x++) {
758
                char ch = input.charAt(x);
759
                if ((ch < 0x0020) || (ch > 0x007e)) {
760
                    output.append("\\u"); // NOI18N
761
                    String hex = Integer.toHexString(ch);
762
                    for (int i = 0; i < 4 - hex.length(); i++) {
763
                        output.append('0');
764
                    }
765
                    output.append(hex);
766
                } else {
767
                    output.append(ch);
768
                }
769
            }
770
            return output.toString();
771
        }
772
773
        @Override
774
        public Object clone() {
775
            Item item = new Item();
776
            if (keyValueLines != null) {
777
                item.keyValueLines = new ArrayList<String>(keyValueLines);
778
            }
779
            if (commentLines != null) {
780
                item.commentLines = new ArrayList<String>(commentLines);
781
            }
782
            item.key = key;
783
            item.value = value;
784
            item.separate = separate;
785
            return item;
786
        }
787
    
788
    }
789
    
790
    private static class SetImpl extends AbstractSet<Map.Entry<String,String>> {
791
792
        private EditableProperties props;
793
        
794
        public SetImpl(EditableProperties props) {
795
            this.props = props;
796
        }
797
        
798
        public Iterator<Map.Entry<String,String>> iterator() {
799
            return new IteratorImpl(props);
800
        }
801
        
802
        public int size() {
803
            return props.items.size();
804
        }
805
        
806
    }
807
    
808
    private static class IteratorImpl implements Iterator<Map.Entry<String,String>> {
809
810
        private final EditableProperties props;
811
        private ListIterator<Item> delegate;
812
        
813
        public IteratorImpl(EditableProperties props) {
814
            this.props = props;
815
            delegate = props.items.listIterator();
816
        }
817
        
818
        public boolean hasNext() {
819
            return findNext() != null;
820
        }
821
        
822
        public Map.Entry<String,String> next() {
823
            Item item = findNext();
824
            if (item == null) {
825
                throw new NoSuchElementException();
826
            }
827
            delegate.next();
828
            return new MapEntryImpl(item);
829
        }
830
        
831
        public void remove() {
832
            delegate.previous();
833
            Item item = findNext();
834
            if (item == null) {
835
                throw new IllegalStateException();
836
            }
837
            int index = delegate.nextIndex();
838
            props.items.remove(item);
839
            props.itemIndex.remove(item.getKey());
840
            delegate = props.items.listIterator(index);
841
        }
842
        
843
        private Item findNext() {
844
            while (delegate.hasNext()) {
845
                Item item = delegate.next();
846
                if (item.getKey() != null && item.getValue() != null) {
847
                    // Found one. Back up!
848
                    delegate.previous();
849
                    return item;
850
                }
851
            }
852
            return null;
853
        }
854
        
855
    }
856
    
857
    private static class MapEntryImpl implements Map.Entry<String,String> {
858
        
859
        private Item item;
860
        
861
        public MapEntryImpl(Item item) {
862
            this.item = item;
863
        }
864
        
865
        public String getKey() {
866
            return item.getKey();
867
        }
868
        
869
        public String getValue() {
870
            return item.getValue();
871
        }
872
        
873
        public String setValue(String value) {
874
            String result = item.getValue();
875
            item.setValue(value);
876
            return result;
877
        }
878
        
879
    }
880
    
881
}
215
}
(-)a/project.ant/test/unit/src/org/netbeans/spi/project/support/ant/EditablePropertiesTest.java (-276 / +1 lines)
Lines 41-48 Link Here
41
41
42
package org.netbeans.spi.project.support.ant;
42
package org.netbeans.spi.project.support.ant;
43
43
44
import java.io.ByteArrayInputStream;
45
import java.io.ByteArrayOutputStream;
46
import java.io.File;
44
import java.io.File;
47
import java.io.FileInputStream;
45
import java.io.FileInputStream;
48
import java.io.FileOutputStream;
46
import java.io.FileOutputStream;
Lines 53-143 Link Here
53
import java.io.Reader;
51
import java.io.Reader;
54
import java.net.URI;
52
import java.net.URI;
55
import java.net.URL;
53
import java.net.URL;
56
import java.util.Arrays;
57
import java.util.Collections;
58
import java.util.HashMap;
59
import java.util.Iterator;
60
import java.util.Map;
61
import org.netbeans.junit.NbTestCase;
54
import org.netbeans.junit.NbTestCase;
62
55
56
// here because AntBasedTestUtil.countTextDiffs cannot be moved to openide.util
63
public class EditablePropertiesTest extends NbTestCase {
57
public class EditablePropertiesTest extends NbTestCase {
64
58
65
    public EditablePropertiesTest(String name) {
59
    public EditablePropertiesTest(String name) {
66
        super(name);
60
        super(name);
67
    }
61
    }
68
    
62
    
69
    public void testLoad() throws Exception {
70
        Map<String,String> content = new HashMap<String,String>();
71
        for (int i=1; i<=26; i++) {
72
            content.put("key"+i, "value"+i);
73
        }
74
        content.put("@!#$%^keyA", "valueA!@#$%^&*(){}");
75
        content.put(" =:keyB", "valueB =:");
76
        content.put(""+(char)0x1234+"keyC", "valueC"+(char)0x9876);
77
        content.put("keyD", "");
78
        content.put("keyE", "");
79
        content.put("keyF", "");
80
        content.put("keyG", "");
81
        content.put("keyH", "value#this is not comment");
82
        content.put("keyI", "incorrect end: \\u123");
83
        // #46234: does not handle bad Unicode escapes well
84
        content.put("keyJ", "malformed Unicode escape: \\uabyz");
85
        
86
        EditableProperties ep = loadTestProperties();
87
        
88
        for (Map.Entry<String,String> entry : content.entrySet()) {
89
            String key = entry.getKey();
90
            String value = entry.getValue();
91
            String epValue = ep.getProperty(key);
92
            assertEquals("Expected value for key "+key+" is different", value, epValue);
93
        }
94
        int count = 0;
95
        for (Map.Entry<String,String> entry : ep.entrySet()) {
96
            if (entry.getKey() != null) {
97
                count++;
98
            }
99
        }
100
        assertEquals("Number of items in property file", content.keySet().size(), count);
101
    }
102
    
103
    /* Doesn't work; java.util.Properties throws IAE for malformed Unicode escapes:
104
    public void testJavaUtilPropertiesEquivalence() throws Exception {
105
        Properties p = loadTestJavaUtilProperties();
106
        EditableProperties ep = loadTestProperties();
107
        Iterator it = p.entrySet().iterator();
108
        while (it.hasNext()) {
109
            Map.Entry entry = (Map.Entry) it.next();
110
            String key = (String) entry.getKey();
111
            String val = (String) entry.getValue();
112
            assertEquals("right value for " + key, val, ep.getProperty(key));
113
        }
114
        assertEquals("right number of items", p.size(), ep.size());
115
    }
116
     */
117
118
    public void testSave() throws Exception {
119
        clearWorkDir();
120
        EditableProperties ep = loadTestProperties();
121
        String dest = getWorkDirPath()+File.separatorChar+"new.properties";
122
        saveProperties(ep, dest);
123
        assertFile("Saved properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
124
    }
125
    
126
    public void testClonability() throws Exception {
127
        clearWorkDir();
128
        EditableProperties ep = loadTestProperties();
129
        
130
        EditableProperties ep2 = ep.cloneProperties();
131
        String dest = getWorkDirPath()+File.separatorChar+"new2.properties";
132
        saveProperties(ep2, dest);
133
        assertFile("Saved cloned properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
134
        
135
        EditableProperties ep3 = (EditableProperties)ep.clone();
136
        dest = getWorkDirPath()+File.separatorChar+"new3.properties";
137
        saveProperties(ep3, dest);
138
        assertFile("Saved cloned properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
139
    }
140
141
    // test that modifications changes only necessary parts
63
    // test that modifications changes only necessary parts
142
    public void testVersionability() throws Exception {
64
    public void testVersionability() throws Exception {
143
        clearWorkDir();
65
        clearWorkDir();
Lines 199-278 Link Here
199
        assertEquals("No lines removed", 0, res[2]);
121
        assertEquals("No lines removed", 0, res[2]);
200
    }
122
    }
201
123
202
    // test that array values are stored correctly
203
    public void testArrayValues() throws Exception {
204
        EditableProperties ep = new EditableProperties(false);
205
        ep.setProperty("key1", new String[]{"1. line;", "2. line;", "3. line"});
206
        ep.setProperty("key2", "1. line;2. line;3. line");
207
        String output = getAsString(ep);
208
        String expected = 
209
            "key1=\\"+System.getProperty("line.separator")+
210
            "    1. line;\\"+System.getProperty("line.separator")+
211
            "    2. line;\\"+System.getProperty("line.separator")+
212
            "    3. line"+System.getProperty("line.separator")+
213
            "key2=1. line;2. line;3. line"+System.getProperty("line.separator");
214
        assertEquals(expected, output);
215
        assertEquals(ep.getProperty("key1"), "1. line;2. line;3. line");
216
        assertEquals(ep.getProperty("key2"), "1. line;2. line;3. line");
217
        ep.setProperty("key1", "one; two; three");
218
        output = getAsString(ep);
219
        expected = 
220
            "key1=one; two; three"+System.getProperty("line.separator")+
221
            "key2=1. line;2. line;3. line"+System.getProperty("line.separator");
222
        assertEquals(expected, output);
223
        assertEquals(ep.getProperty("key1"), "one; two; three");
224
        assertEquals(ep.getProperty("key2"), "1. line;2. line;3. line");
225
        ep.setProperty("key2", new String[]{"1. line;", "2. line;", "3. line", "one;", "more;", "line;"});
226
        ep.setProperty("key", new String[0]);
227
        output = getAsString(ep);
228
        expected = 
229
            "key1=one; two; three"+System.getProperty("line.separator")+
230
            "key2=\\"+System.getProperty("line.separator")+
231
            "    1. line;\\"+System.getProperty("line.separator")+
232
            "    2. line;\\"+System.getProperty("line.separator")+
233
            "    3. line\\"+System.getProperty("line.separator")+
234
            "    one;\\"+System.getProperty("line.separator")+
235
            "    more;\\"+System.getProperty("line.separator")+
236
            "    line;"+System.getProperty("line.separator")+
237
            "key="+System.getProperty("line.separator"); // #45061
238
        assertEquals(expected, output);
239
        assertEquals(ep.getProperty("key1"), "one; two; three");
240
        assertEquals(ep.getProperty("key2"), "1. line;2. line;3. lineone;more;line;");
241
        assertEquals(ep.getProperty("key"), "");
242
    }
243
        
244
    public void testSorting() throws Exception {
245
        EditableProperties ep = new EditableProperties(false);
246
        ep.setProperty("a", "val-a");
247
        ep.setProperty("c", "val-c");
248
        ep.put("b", "val-b");
249
        String output = getAsString(ep);
250
        String expected = "a=val-a"+System.getProperty("line.separator")+"c=val-c"+
251
                System.getProperty("line.separator")+"b=val-b"+
252
                System.getProperty("line.separator");
253
        assertEquals(expected, output);
254
        
255
        ep = new EditableProperties(false);
256
        ep.setProperty("a", "val-a");
257
        ep.setProperty("c", "val-c");
258
        ep.put("b", "val-b");
259
        output = getAsString(ep);
260
        expected = "a=val-a"+System.getProperty("line.separator")+"c=val-c"+
261
                System.getProperty("line.separator")+"b=val-b"+
262
                System.getProperty("line.separator");
263
        assertEquals(expected, output);
264
        
265
        ep = new EditableProperties(true);
266
        ep.setProperty("a", "val-a");
267
        ep.setProperty("c", "val-c");
268
        ep.put("b", "val-b");
269
        output = getAsString(ep);
270
        expected = "a=val-a"+System.getProperty("line.separator")+"b=val-b"+
271
                System.getProperty("line.separator")+"c=val-c"+
272
                System.getProperty("line.separator");
273
        assertEquals(expected, output);
274
    }
275
276
    // test that changing comments work and modify only comments
124
    // test that changing comments work and modify only comments
277
    public void testComment() throws Exception {
125
    public void testComment() throws Exception {
278
        clearWorkDir();
126
        clearWorkDir();
Lines 326-433 Link Here
326
        
174
        
327
    }
175
    }
328
176
329
    // test that misc chars are correctly escaped, unicode encoded, etc.
330
    public void testEscaping() throws Exception {
331
        String umlaut = "" + (char)252;
332
        EditableProperties ep = new EditableProperties(false);
333
        ep.setProperty("a a", "a space a");
334
        ep.setProperty("b"+(char)0x4567, "val"+(char)0x1234);
335
        ep.setProperty("@!#$%^\\", "!@#$%^&*(){}\\");
336
        ep.setProperty("d\nd", "d\nnewline\nd");
337
        ep.setProperty("umlaut", umlaut);
338
        ep.setProperty("_a a", new String[]{"a space a"});
339
        ep.setProperty("_b"+(char)0x4567, new String[]{"val"+(char)0x1234});
340
        ep.setProperty("_@!#$%^\\", new String[]{"!@#$%^&*\\", "(){}\\"});
341
        ep.setProperty("_d\nd", new String[]{"d\nnew","line\nd", "\n", "end"});
342
        ep.setProperty("_umlaut", new String[]{umlaut, umlaut});
343
        String output = getAsString(ep);
344
        String expected = "a\\ a=a space a"+System.getProperty("line.separator")+
345
                "b\\u4567=val\\u1234"+System.getProperty("line.separator")+
346
                "@!#$%^\\\\=!@#$%^&*(){}\\\\"+System.getProperty("line.separator")+
347
                "d\\nd=d\\nnewline\\nd"+System.getProperty("line.separator")+
348
                "umlaut=\\u00fc"+System.getProperty("line.separator")+
349
                "_a\\ a=\\"+System.getProperty("line.separator")+"    a space a"+System.getProperty("line.separator")+
350
                "_b\\u4567=\\"+System.getProperty("line.separator")+"    val\\u1234"+System.getProperty("line.separator")+
351
                "_@!#$%^\\\\=\\"+System.getProperty("line.separator")+"    !@#$%^&*\\\\\\"+System.getProperty("line.separator")+
352
                    "    (){}\\\\"+System.getProperty("line.separator")+
353
                "_d\\nd=\\"+System.getProperty("line.separator")+"    d\\nnew\\"+System.getProperty("line.separator")+
354
                    "    line\\nd\\"+System.getProperty("line.separator")+
355
                    "    \\n\\"+System.getProperty("line.separator")+
356
                    "    end"+System.getProperty("line.separator")+
357
                "_umlaut=\\" +System.getProperty("line.separator")+"    \\u00fc\\"+System.getProperty("line.separator")+
358
                    "    \\u00fc"+System.getProperty("line.separator");
359
        assertEquals(expected, output);
360
        assertEquals("a space a", ep.getProperty("a a"));
361
        assertEquals("val"+(char)0x1234, ep.getProperty("b"+(char)0x4567));
362
        assertEquals("!@#$%^&*(){}\\", ep.getProperty("@!#$%^\\"));
363
        assertEquals("d\nnewline\nd", ep.getProperty("d\nd"));
364
        assertEquals(umlaut, ep.getProperty("umlaut"));
365
        assertEquals("a space a", ep.getProperty("_a a"));
366
        assertEquals("val"+(char)0x1234, ep.getProperty("_b"+(char)0x4567));
367
        assertEquals("!@#$%^&*\\(){}\\", ep.getProperty("_@!#$%^\\"));
368
        assertEquals("d\nnewline\nd\nend", ep.getProperty("_d\nd"));
369
        assertEquals(umlaut+umlaut, ep.getProperty("_umlaut"));
370
    }
371
    
372
    // test that iterator implementation is OK
373
    public void testIterator() throws Exception {
374
        EditableProperties ep = loadTestProperties();
375
        Iterator<Map.Entry<String,String>> it1 = ep.entrySet().iterator();
376
        while (it1.hasNext()) {
377
            it1.next();
378
        }
379
        Iterator<String> it2 = ep.keySet().iterator();
380
        while (it2.hasNext()) {
381
            it2.next();
382
        }
383
        it2 = ep.keySet().iterator();
384
        while (it2.hasNext()) {
385
            it2.next();
386
            it2.remove();
387
        }
388
        ep.put("a", "aval");
389
        ep.remove("a");
390
        ep = loadTestProperties();
391
        it1 = ep.entrySet().iterator();
392
        while (it1.hasNext()) {
393
            Map.Entry<String,String> entry = it1.next();
394
            assertNotNull("Property key cannot be null", entry.getKey());
395
            assertNotNull("Property value cannot be null", entry.getValue());
396
            entry.setValue(entry.getValue()+"-something-new");
397
        }
398
        it1 = ep.entrySet().iterator();
399
        while (it1.hasNext()) {
400
            it1.next();
401
            it1.remove();
402
        }
403
    }
404
    
405
    // test that syntax errors are survived
406
    public void testInvalidPropertiesFile() throws Exception {
407
        String invalidProperty = "key=value without correct end\\";
408
        ByteArrayInputStream is = new ByteArrayInputStream(invalidProperty.getBytes());
409
        EditableProperties ep = new EditableProperties(false);
410
        ep.load(is);
411
        assertEquals("Syntax error should be resolved", 1, ep.keySet().size());
412
        assertEquals("value without correct end", ep.getProperty("key"));
413
    }
414
    
415
    public void testNonLatinComments() throws Exception {
416
        // #60249.
417
        String lsep = System.getProperty("line.separator");
418
        EditableProperties p = new EditableProperties(false);
419
        p.setProperty("k", "v");
420
        p.setComment("k", new String[] {"# \u0158ekni koment teda!"}, false);
421
        String expected = "# \\u0158ekni koment teda!" + lsep + "k=v" + lsep;
422
        assertEquals("Storing non-Latin chars in comments works", expected, getAsString(p));
423
        p = new EditableProperties(false);
424
        p.load(new ByteArrayInputStream(expected.getBytes("ISO-8859-1")));
425
        assertEquals("Reading non-Latin chars in comments works", Collections.singletonList("# \u0158ekni koment teda!"), Arrays.asList(p.getComment("k")));
426
        p.setProperty("k", "v2");
427
        expected = "# \\u0158ekni koment teda!" + lsep + "k=v2" + lsep;
428
        assertEquals("Reading and re-writing non-Latin chars in comments works", expected, getAsString(p));
429
    }
430
431
    
177
    
432
    // helper methods:
178
    // helper methods:
433
    
179
    
Lines 449-468 Link Here
449
        return ep;
195
        return ep;
450
    }
196
    }
451
    
197
    
452
    /*
453
    private Properties loadTestJavaUtilProperties() throws IOException {
454
        URL u = EditablePropertiesTest.class.getResource("data/test.properties");
455
        Properties p = new Properties();
456
        InputStream is = u.openStream();
457
        try {
458
            p.load(is);
459
        } finally {
460
            is.close();
461
        }
462
        return p;
463
    }
464
     */
465
    
466
    private void saveProperties(EditableProperties ep, String path) throws Exception {
198
    private void saveProperties(EditableProperties ep, String path) throws Exception {
467
        OutputStream os = new FileOutputStream(path);
199
        OutputStream os = new FileOutputStream(path);
468
        try {
200
        try {
Lines 489-499 Link Here
489
        }
221
        }
490
    }
222
    }
491
    
223
    
492
    private String getAsString(EditableProperties ep) throws Exception {
493
        ByteArrayOutputStream os = new ByteArrayOutputStream();
494
        ep.store(os);
495
        os.close();
496
        return os.toString("ISO-8859-1");
497
    }
498
    
499
}
224
}

Return to bug 66577