Issue #66577: make EditableProperties available even without a dep on project.ant; generally useful.
1.1 --- a/openide.util/apichanges.xml Tue Jul 28 10:13:45 2009 -0400
1.2 +++ b/openide.util/apichanges.xml Wed Jul 29 11:49:18 2009 -0400
1.3 @@ -49,6 +49,22 @@
1.4 <apidef name="actions">Actions API</apidef>
1.5 </apidefs>
1.6 <changes>
1.7 + <change id="EditableProperties">
1.8 + <api name="util"/>
1.9 + <summary>Copied API from <code>project.ant</code> for <code>EditableProperties</code>.</summary>
1.10 + <version major="7" minor="26"/>
1.11 + <date day="29" month="7" year="2009"/>
1.12 + <author login="jglick"/>
1.13 + <compatibility addition="yes"/>
1.14 + <description>
1.15 + <p>
1.16 + <code>EditableProperties</code> now available in Utilities API
1.17 + so it can be used more broadly.
1.18 + </p>
1.19 + </description>
1.20 + <class package="org.openide.util" name="EditableProperties"/>
1.21 + <issue number="66577"/>
1.22 + </change>
1.23 <change id="LifecycleManager.markForRestart">
1.24 <api name="util"/>
1.25 <summary>Added way to request that the platform restart.</summary>
2.1 --- a/openide.util/nbproject/project.properties Tue Jul 28 10:13:45 2009 -0400
2.2 +++ b/openide.util/nbproject/project.properties Wed Jul 29 11:49:18 2009 -0400
2.3 @@ -42,7 +42,7 @@
2.4 module.jar.dir=lib
2.5 cp.extra=${nb_all}/apisupport.harness/external/openjdk-javac-6-b12.jar
2.6
2.7 -spec.version.base=7.25.0
2.8 +spec.version.base=7.26.0
2.9
2.10 # For XMLSerializer, needed for XMLUtil.write to work w/ namespaces under JDK 1.4:
2.11
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/openide.util/src/org/openide/util/EditableProperties.java Wed Jul 29 11:49:18 2009 -0400
3.3 @@ -0,0 +1,861 @@
3.4 +/*
3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3.6 + *
3.7 + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
3.8 + *
3.9 + * The contents of this file are subject to the terms of either the GNU
3.10 + * General Public License Version 2 only ("GPL") or the Common
3.11 + * Development and Distribution License("CDDL") (collectively, the
3.12 + * "License"). You may not use this file except in compliance with the
3.13 + * License. You can obtain a copy of the License at
3.14 + * http://www.netbeans.org/cddl-gplv2.html
3.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
3.16 + * specific language governing permissions and limitations under the
3.17 + * License. When distributing the software, include this License Header
3.18 + * Notice in each file and include the License file at
3.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
3.20 + * particular file as subject to the "Classpath" exception as provided
3.21 + * by Sun in the GPL Version 2 section of the License file that
3.22 + * accompanied this code. If applicable, add the following below the
3.23 + * License Header, with the fields enclosed by brackets [] replaced by
3.24 + * your own identifying information:
3.25 + * "Portions Copyrighted [year] [name of copyright owner]"
3.26 + *
3.27 + * Contributor(s):
3.28 + *
3.29 + * The Original Software is NetBeans. The Initial Developer of the Original
3.30 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
3.31 + * Microsystems, Inc. All Rights Reserved.
3.32 + *
3.33 + * If you wish your version of this file to be governed by only the CDDL
3.34 + * or only the GPL Version 2, indicate your decision by adding
3.35 + * "[Contributor] elects to include this software in this distribution
3.36 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
3.37 + * single choice of license, a recipient has the option to distribute
3.38 + * your version of this file under either the CDDL, the GPL Version 2 or
3.39 + * to extend the choice of license to its licensees as provided above.
3.40 + * However, if you add GPL Version 2 code and therefore, elected the GPL
3.41 + * Version 2 license, then the option applies only if the new code is
3.42 + * made subject to such option by the copyright holder.
3.43 + */
3.44 +
3.45 +package org.openide.util;
3.46 +
3.47 +import java.io.BufferedReader;
3.48 +import java.io.BufferedWriter;
3.49 +import java.io.IOException;
3.50 +import java.io.InputStream;
3.51 +import java.io.InputStreamReader;
3.52 +import java.io.OutputStream;
3.53 +import java.io.OutputStreamWriter;
3.54 +import java.util.AbstractMap;
3.55 +import java.util.AbstractSet;
3.56 +import java.util.ArrayList;
3.57 +import java.util.Arrays;
3.58 +import java.util.HashMap;
3.59 +import java.util.Iterator;
3.60 +import java.util.LinkedList;
3.61 +import java.util.List;
3.62 +import java.util.ListIterator;
3.63 +import java.util.Map;
3.64 +import java.util.NoSuchElementException;
3.65 +import java.util.Set;
3.66 +
3.67 +// XXX: consider adding getInitialComment() and setInitialComment() methods
3.68 +// (useful e.g. for GeneratedFilesHelper)
3.69 +
3.70 +/**
3.71 + * Similar to {@link java.util.Properties} but designed to retain additional
3.72 + * information needed for safe hand-editing.
3.73 + * Useful for various <samp>*.properties</samp> in a project:
3.74 + * <ol>
3.75 + * <li>Can associate comments with particular entries.
3.76 + * <li>Order of entries preserved during modifications whenever possible.
3.77 + * <li>VCS-friendly: lines which are not semantically modified are not textually modified.
3.78 + * <li>Can automatically insert line breaks in new or modified values at positions
3.79 + * that are likely to be semantically meaningful, e.g. between path components
3.80 + * </ol>
3.81 + * The file format (including encoding etc.) is compatible with the regular JRE implementation.
3.82 + * Only (non-null) String is supported for keys and values.
3.83 + * This class is not thread-safe; use only from a single thread, or use {@link java.util.Collections#synchronizedMap}.
3.84 + * @author Jesse Glick, David Konecny
3.85 + * @since org.openide.util 7.26
3.86 + */
3.87 +public final class EditableProperties extends AbstractMap<String,String> implements Cloneable {
3.88 +
3.89 + /** List of Item instances as read from the properties file. Order is important.
3.90 + * Saving properties will save then in this order. */
3.91 + private final LinkedList<Item> items;
3.92 +
3.93 + /** Map of [property key, Item instance] for faster access. */
3.94 + private final Map<String,Item> itemIndex;
3.95 +
3.96 + private final boolean alphabetize;
3.97 +
3.98 + private static final String keyValueSeparators = "=: \t\r\n\f";
3.99 +
3.100 + private static final String strictKeyValueSeparators = "=:";
3.101 +
3.102 + private static final String whiteSpaceChars = " \t\r\n\f";
3.103 +
3.104 + private static final String commentChars = "#!";
3.105 +
3.106 + private static final String INDENT = " ";
3.107 +
3.108 + // parse states:
3.109 + private static final int WAITING_FOR_KEY_VALUE = 1;
3.110 + private static final int READING_KEY_VALUE = 2;
3.111 +
3.112 + /**
3.113 + * Creates empty instance.
3.114 + * @param alphabetize alphabetize new items according to key or not
3.115 + */
3.116 + public EditableProperties(boolean alphabetize) {
3.117 + this.alphabetize = alphabetize;
3.118 + items = new LinkedList<Item>();
3.119 + itemIndex = new HashMap<String,Item>();
3.120 + }
3.121 +
3.122 + /**
3.123 + * Creates new instance from an existing one.
3.124 + * @param ep an instance of EditableProperties
3.125 + */
3.126 + private EditableProperties(EditableProperties ep) {
3.127 + // #64174: use a simple deep copy for speed
3.128 + alphabetize = ep.alphabetize;
3.129 + items = new LinkedList<Item>();
3.130 + itemIndex = new HashMap<String,Item>(ep.items.size() * 4 / 3 + 1);
3.131 + for (Item _i : ep.items) {
3.132 + Item i = (Item) _i.clone();
3.133 + items.add(i);
3.134 + itemIndex.put(i.getKey(), i);
3.135 + }
3.136 + }
3.137 +
3.138 + /**
3.139 + * Returns a set view of the mappings ordered according to their file
3.140 + * position. Each element in this set is a Map.Entry. See
3.141 + * {@link AbstractMap#entrySet} for more details.
3.142 + * @return set with Map.Entry instances.
3.143 + */
3.144 + public Set<Map.Entry<String,String>> entrySet() {
3.145 + return new SetImpl(this);
3.146 + }
3.147 +
3.148 + /**
3.149 + * Load properties from a stream.
3.150 + * @param stream an input stream
3.151 + * @throws IOException if the contents are malformed or the stream could not be read
3.152 + */
3.153 + public void load(InputStream stream) throws IOException {
3.154 + int state = WAITING_FOR_KEY_VALUE;
3.155 + BufferedReader input = new BufferedReader(new InputStreamReader(stream, "ISO-8859-1"));
3.156 + List<String> tempList = new LinkedList<String>();
3.157 + String line;
3.158 + int commentLinesCount = 0;
3.159 + // Read block of lines and create instance of Item for each.
3.160 + // Separator is: either empty line or valid end of proeprty declaration
3.161 + while (null != (line = input.readLine())) {
3.162 + tempList.add(line);
3.163 + boolean empty = isEmpty(line);
3.164 + boolean comment = isComment(line);
3.165 + if (state == WAITING_FOR_KEY_VALUE) {
3.166 + if (empty) {
3.167 + // empty line: create Item without any key
3.168 + createNonKeyItem(tempList);
3.169 + commentLinesCount = 0;
3.170 + } else {
3.171 + if (comment) {
3.172 + commentLinesCount++;
3.173 + } else {
3.174 + state = READING_KEY_VALUE;
3.175 + }
3.176 + }
3.177 + }
3.178 + if (state == READING_KEY_VALUE && !isContinue(line)) {
3.179 + // valid end of property declaration: create Item for it
3.180 + createKeyItem(tempList, commentLinesCount);
3.181 + state = WAITING_FOR_KEY_VALUE;
3.182 + commentLinesCount = 0;
3.183 + }
3.184 + }
3.185 + if (tempList.size() > 0) {
3.186 + if (state == READING_KEY_VALUE) {
3.187 + // value was not ended correctly? ignore.
3.188 + createKeyItem(tempList, commentLinesCount);
3.189 + } else {
3.190 + createNonKeyItem(tempList);
3.191 + }
3.192 + }
3.193 + }
3.194 +
3.195 + /**
3.196 + * Store properties to a stream.
3.197 + * @param stream an output stream
3.198 + * @throws IOException if the stream could not be written to
3.199 + */
3.200 + public void store(OutputStream stream) throws IOException {
3.201 + boolean previousLineWasEmpty = true;
3.202 + BufferedWriter output = new BufferedWriter(new OutputStreamWriter(stream, "ISO-8859-1"));
3.203 + for (Item item : items) {
3.204 + if (item.isSeparate() && !previousLineWasEmpty) {
3.205 + output.newLine();
3.206 + }
3.207 + String line = null;
3.208 + Iterator<String> it = item.getRawData().iterator();
3.209 + while (it.hasNext()) {
3.210 + line = it.next();
3.211 + output.write(line);
3.212 + output.newLine();
3.213 + }
3.214 + if (line != null) {
3.215 + previousLineWasEmpty = isEmpty(line);
3.216 + }
3.217 + }
3.218 + output.flush();
3.219 + }
3.220 +
3.221 + @Override
3.222 + public String put(String key, String value) {
3.223 + Parameters.notNull("key", key);
3.224 + Parameters.notNull(key, value);
3.225 + Item item = itemIndex.get(key);
3.226 + String result = null;
3.227 + if (item != null) {
3.228 + result = item.getValue();
3.229 + item.setValue(value);
3.230 + } else {
3.231 + item = new Item(key, value);
3.232 + addItem(item, alphabetize);
3.233 + }
3.234 + return result;
3.235 + }
3.236 +
3.237 + /**
3.238 + * Convenience method to get a property as a string.
3.239 + * Same as {@link #get}; only here because of pre-generic code.
3.240 + * @param key a property name; cannot be null nor empty
3.241 + * @return the property value, or null if it was not defined
3.242 + */
3.243 + public String getProperty(String key) {
3.244 + return get(key);
3.245 + }
3.246 +
3.247 + /**
3.248 + * Convenience method to set a property.
3.249 + * Same as {@link #put}; only here because of pre-generic code.
3.250 + * @param key a property name; cannot be null nor empty
3.251 + * @param value the desired value; cannot be null
3.252 + * @return previous value of the property or null if there was not any
3.253 + */
3.254 + public String setProperty(String key, String value) {
3.255 + return put(key, value);
3.256 + }
3.257 +
3.258 + /**
3.259 + * Sets a property to a value broken into segments for readability.
3.260 + * Same behavior as {@link #setProperty(String,String)} with the difference that each item
3.261 + * will be stored on its own line of text. {@link #getProperty} will simply concatenate
3.262 + * all the items into one string, so generally separators
3.263 + * (such as <samp>:</samp> for path-like properties) must be included in
3.264 + * the items (for example, at the end of all but the last item).
3.265 + * @param key a property name; cannot be null nor empty
3.266 + * @param value the desired value; cannot be null; can be empty array
3.267 + * @return previous value of the property or null if there was not any
3.268 + */
3.269 + public String setProperty(String key, String[] value) {
3.270 + String result = get(key);
3.271 + if (key == null || value == null) {
3.272 + throw new NullPointerException();
3.273 + }
3.274 + List<String> valueList = Arrays.asList(value);
3.275 + Item item = itemIndex.get(key);
3.276 + if (item != null) {
3.277 + item.setValue(valueList);
3.278 + } else {
3.279 + addItem(new Item(key, valueList), alphabetize);
3.280 + }
3.281 + return result;
3.282 + }
3.283 +
3.284 + /**
3.285 + * Returns comment associated with the property. The comment lines are
3.286 + * returned as defined in properties file, that is comment delimiter is
3.287 + * included. Comment for property is defined as: continuous block of lines
3.288 + * starting with comment delimiter which are followed by property
3.289 + * declaration (no empty line separator allowed).
3.290 + * @param key a property name; cannot be null nor empty
3.291 + * @return array of String lines as specified in properties file; comment
3.292 + * delimiter character is included
3.293 + */
3.294 + public String[] getComment(String key) {
3.295 + Item item = itemIndex.get(key);
3.296 + if (item == null) {
3.297 + return new String[0];
3.298 + }
3.299 + return item.getComment();
3.300 + }
3.301 +
3.302 + /**
3.303 + * Create comment for the property.
3.304 + * <p>Note: if a comment includes non-ISO-8859-1 characters, they will be written
3.305 + * to disk using Unicode escapes (and {@link #getComment} will interpret
3.306 + * such escapes), but of course they will be unreadable for humans.
3.307 + * @param key a property name; cannot be null nor empty
3.308 + * @param comment lines of comment which will be written just above
3.309 + * the property; no reformatting; comment lines must start with
3.310 + * comment delimiter; cannot be null; cannot be emty array
3.311 + * @param separate whether the comment should be separated from previous
3.312 + * item by empty line
3.313 + */
3.314 + public void setComment(String key, String[] comment, boolean separate) {
3.315 + // XXX: check validity of comment parameter
3.316 + Item item = itemIndex.get(key);
3.317 + if (item == null) {
3.318 + throw new IllegalArgumentException("Cannot set comment for non-existing property "+key);
3.319 + }
3.320 + item.setComment(comment, separate);
3.321 + }
3.322 +
3.323 + @Override
3.324 + public Object clone() {
3.325 + return cloneProperties();
3.326 + }
3.327 +
3.328 + /**
3.329 + * Create an exact copy of this properties object.
3.330 + * @return a clone of this object
3.331 + */
3.332 + public EditableProperties cloneProperties() {
3.333 + return new EditableProperties(this);
3.334 + }
3.335 +
3.336 + // non-key item is block of empty lines/comment not associated with any property
3.337 + private void createNonKeyItem(List<String> lines) {
3.338 + // First check that previous item is not non-key item.
3.339 + if (!items.isEmpty()) {
3.340 + Item item = items.getLast();
3.341 + if (item.getKey() == null) {
3.342 + // it is non-key item: merge them
3.343 + item.addCommentLines(lines);
3.344 + lines.clear();
3.345 + return;
3.346 + }
3.347 + }
3.348 + // create new non-key item
3.349 + Item item = new Item(lines);
3.350 + addItem(item, false);
3.351 + lines.clear();
3.352 + }
3.353 +
3.354 + // opposite to non-key item: item with valid property declaration and
3.355 + // perhaps some comment lines
3.356 + private void createKeyItem(List<String> lines, int commentLinesCount) {
3.357 + Item item = new Item(lines.subList(0, commentLinesCount), lines.subList(commentLinesCount, lines.size()));
3.358 + addItem(item, false);
3.359 + lines.clear();
3.360 + }
3.361 +
3.362 + private void addItem(Item item, boolean sort) {
3.363 + String key = item.getKey();
3.364 + if (sort) {
3.365 + assert key != null;
3.366 + ListIterator<Item> it = items.listIterator();
3.367 + while (it.hasNext()) {
3.368 + String k = it.next().getKey();
3.369 + if (k != null && k.compareToIgnoreCase(key) > 0) {
3.370 + it.previous();
3.371 + it.add(item);
3.372 + itemIndex.put(key, item);
3.373 + return;
3.374 + }
3.375 + }
3.376 + }
3.377 + items.add(item);
3.378 + if (key != null) {
3.379 + itemIndex.put(key, item);
3.380 + }
3.381 + }
3.382 +
3.383 + // does property declaration continue on next line?
3.384 + private boolean isContinue(String line) {
3.385 + int index = line.length() - 1;
3.386 + int slashCount = 0;
3.387 + while (index >= 0 && line.charAt(index) == '\\') {
3.388 + slashCount++;
3.389 + index--;
3.390 + }
3.391 + // if line ends with odd number of backslash then property definition
3.392 + // continues on next line
3.393 + return (slashCount % 2 != 0);
3.394 + }
3.395 +
3.396 + // does line start with comment delimiter? (whitespaces are ignored)
3.397 + private static boolean isComment(String line) {
3.398 + line = trimLeft(line);
3.399 + if (line.length() != 0 && commentChars.indexOf(line.charAt(0)) != -1) {
3.400 + return true;
3.401 + } else {
3.402 + return false;
3.403 + }
3.404 + }
3.405 +
3.406 + // is line empty? (whitespaces are ignored)
3.407 + private static boolean isEmpty(String line) {
3.408 + return trimLeft(line).length() == 0;
3.409 + }
3.410 +
3.411 + // remove all whitespaces from left
3.412 + private static String trimLeft(String line) {
3.413 + int start = 0;
3.414 + while (start < line.length()) {
3.415 + if (whiteSpaceChars.indexOf(line.charAt(start)) == -1) {
3.416 + break;
3.417 + }
3.418 + start++;
3.419 + }
3.420 + return line.substring(start);
3.421 + }
3.422 +
3.423 + /**
3.424 + * Representation of one item read from properties file. It can be either
3.425 + * valid property declaration with associated comment or chunk of empty
3.426 + * lines or lines with comment which are not associated with any property.
3.427 + */
3.428 + private static class Item implements Cloneable {
3.429 +
3.430 + /** Lines of comment as read from properties file and as they will be
3.431 + * written back to properties file. */
3.432 + private List<String> commentLines;
3.433 +
3.434 + /** Lines with property name and value declaration as read from
3.435 + * properties file and as they will be written back to properties file. */
3.436 + private List<String> keyValueLines;
3.437 +
3.438 + /** Property key */
3.439 + private String key;
3.440 +
3.441 + /** Property value */
3.442 + private String value;
3.443 +
3.444 + /** Should this property be separated from previous one by at least
3.445 + * one empty line. */
3.446 + private boolean separate;
3.447 +
3.448 + // constructor only for cloning
3.449 + private Item() {
3.450 + }
3.451 +
3.452 + /**
3.453 + * Create instance which does not have any key and value - just
3.454 + * some empty or comment lines. This item is READ-ONLY.
3.455 + */
3.456 + public Item(List<String> commentLines) {
3.457 + this.commentLines = new ArrayList<String>(commentLines);
3.458 + }
3.459 +
3.460 + /**
3.461 + * Create instance from the lines of comment and property declaration.
3.462 + * Property name and value will be split.
3.463 + */
3.464 + public Item(List<String> commentLines, List<String> keyValueLines) {
3.465 + this.commentLines = new ArrayList<String>(commentLines);
3.466 + this.keyValueLines = new ArrayList<String>(keyValueLines);
3.467 + parse(keyValueLines);
3.468 + }
3.469 +
3.470 + /**
3.471 + * Create new instance with key and value.
3.472 + */
3.473 + public Item(String key, String value) {
3.474 + this.key = key;
3.475 + this.value = value;
3.476 + }
3.477 +
3.478 + /**
3.479 + * Create new instance with key and value.
3.480 + */
3.481 + public Item(String key, List<String> value) {
3.482 + this.key = key;
3.483 + setValue(value);
3.484 + }
3.485 +
3.486 + // backdoor for merging non-key items
3.487 + void addCommentLines(List<String> lines) {
3.488 + assert key == null;
3.489 + commentLines.addAll(lines);
3.490 + }
3.491 +
3.492 + public String[] getComment() {
3.493 + String[] res = new String[commentLines.size()];
3.494 + for (int i = 0; i < res.length; i++) {
3.495 + // #60249: the comment might have Unicode chars in escapes.
3.496 + res[i] = decodeUnicode(commentLines.get(i));
3.497 + }
3.498 + return res;
3.499 + }
3.500 +
3.501 + public void setComment(String[] commentLines, boolean separate) {
3.502 + this.separate = separate;
3.503 + this.commentLines = new ArrayList<String>(commentLines.length);
3.504 + for (int i = 0; i < commentLines.length; i++) {
3.505 + // #60249 again - write only ISO-8859-1.
3.506 + this.commentLines.add(encodeUnicode(commentLines[i]));
3.507 + }
3.508 + }
3.509 +
3.510 + public String getKey() {
3.511 + return key;
3.512 + }
3.513 +
3.514 + public String getValue() {
3.515 + return value;
3.516 + }
3.517 +
3.518 + public void setValue(String value) {
3.519 + this.value = value;
3.520 + keyValueLines = null;
3.521 + }
3.522 +
3.523 + public void setValue(List<String> value) {
3.524 + StringBuffer val = new StringBuffer();
3.525 + List<String> l = new ArrayList<String>();
3.526 + if (!value.isEmpty()) {
3.527 + l.add(encode(key, true) + "=\\"); // NOI18N
3.528 + Iterator<String> it = value.iterator();
3.529 + while (it.hasNext()) {
3.530 + String s = it.next();
3.531 + val.append(s);
3.532 + s = encode(s, false);
3.533 + l.add(it.hasNext() ? INDENT + s + '\\' : INDENT + s); // NOI18N
3.534 + }
3.535 + } else {
3.536 + // #45061: for no vals, use just "prop="
3.537 + l.add(encode(key, true) + '='); // NOI18N
3.538 + }
3.539 + this.value = val.toString();
3.540 + keyValueLines = l;
3.541 + }
3.542 +
3.543 + public boolean isSeparate() {
3.544 + return separate;
3.545 + }
3.546 +
3.547 + /**
3.548 + * Returns persistent image of this property.
3.549 + */
3.550 + public List<String> getRawData() {
3.551 + List<String> l = new ArrayList<String>();
3.552 + if (commentLines != null) {
3.553 + l.addAll(commentLines);
3.554 + }
3.555 + if (keyValueLines == null) {
3.556 + keyValueLines = new ArrayList<String>();
3.557 + if (key != null && value != null) {
3.558 + keyValueLines.add(encode(key, true)+"="+encode(value, false));
3.559 + }
3.560 + }
3.561 + l.addAll(keyValueLines);
3.562 + return l;
3.563 + }
3.564 +
3.565 + private void parse(List<String> keyValueLines) {
3.566 + // merge lines into one:
3.567 + String line = mergeLines(keyValueLines);
3.568 + // split key and value
3.569 + splitKeyValue(line);
3.570 + }
3.571 +
3.572 + private String mergeLines(List<String> lines) {
3.573 + StringBuilder line = new StringBuilder();
3.574 + Iterator<String> it = lines.iterator();
3.575 + while (it.hasNext()) {
3.576 + String l = trimLeft(it.next());
3.577 + // if this is not the last line then remove last backslash
3.578 + if (it.hasNext()) {
3.579 + assert l.endsWith("\\") : lines;
3.580 + l = l.substring(0, l.length()-1);
3.581 + }
3.582 + line.append(l);
3.583 + }
3.584 + return line.toString();
3.585 + }
3.586 +
3.587 + private void splitKeyValue(String line) {
3.588 + int separatorIndex = 0;
3.589 + while (separatorIndex < line.length()) {
3.590 + char ch = line.charAt(separatorIndex);
3.591 + if (ch == '\\') {
3.592 + // ignore next one character
3.593 + separatorIndex++;
3.594 + } else {
3.595 + if (keyValueSeparators.indexOf(ch) != -1) {
3.596 + break;
3.597 + }
3.598 + }
3.599 + separatorIndex++;
3.600 + }
3.601 + key = decode(line.substring(0, separatorIndex));
3.602 + line = trimLeft(line.substring(separatorIndex));
3.603 + if (line.length() == 0) {
3.604 + value = "";
3.605 + return;
3.606 + }
3.607 + if (strictKeyValueSeparators.indexOf(line.charAt(0)) != -1) {
3.608 + line = trimLeft(line.substring(1));
3.609 + }
3.610 + value = decode(line);
3.611 + }
3.612 +
3.613 + private static String decode(String input) {
3.614 + char ch;
3.615 + int len = input.length();
3.616 + StringBuffer output = new StringBuffer(len);
3.617 + for (int x=0; x<len; x++) {
3.618 + ch = input.charAt(x);
3.619 + if (ch != '\\') {
3.620 + output.append(ch);
3.621 + continue;
3.622 + }
3.623 + x++;
3.624 + if (x==len) {
3.625 + // backslash at the end? syntax error: ignore it
3.626 + continue;
3.627 + }
3.628 + ch = input.charAt(x);
3.629 + if (ch == 'u') {
3.630 + if (x+5>len) {
3.631 + // unicode character not finished? syntax error: ignore
3.632 + output.append(input.substring(x-1));
3.633 + x += 4;
3.634 + continue;
3.635 + }
3.636 + String val = input.substring(x+1, x+5);
3.637 + try {
3.638 + output.append((char)Integer.parseInt(val, 16));
3.639 + } catch (NumberFormatException e) {
3.640 + // #46234: handle gracefully
3.641 + output.append(input.substring(x - 1, x + 5));
3.642 + }
3.643 + x += 4;
3.644 + } else {
3.645 + if (ch == 't') ch = '\t';
3.646 + else if (ch == 'r') ch = '\r';
3.647 + else if (ch == 'n') ch = '\n';
3.648 + else if (ch == 'f') ch = '\f';
3.649 + output.append(ch);
3.650 + }
3.651 + }
3.652 + return output.toString();
3.653 + }
3.654 +
3.655 + private static String encode(String input, boolean escapeSpace) {
3.656 + int len = input.length();
3.657 + StringBuffer output = new StringBuffer(len*2);
3.658 +
3.659 + for(int x=0; x<len; x++) {
3.660 + char ch = input.charAt(x);
3.661 + switch(ch) {
3.662 + case ' ':
3.663 + if (x == 0 || escapeSpace) {
3.664 + output.append('\\');
3.665 + }
3.666 + output.append(' ');
3.667 + break;
3.668 + case '\\':
3.669 + output.append("\\\\");
3.670 + break;
3.671 + case '\t':
3.672 + output.append("\\t");
3.673 + break;
3.674 + case '\n':
3.675 + output.append("\\n");
3.676 + break;
3.677 + case '\r':
3.678 + output.append("\\r");
3.679 + break;
3.680 + case '\f':
3.681 + output.append("\\f");
3.682 + break;
3.683 + default:
3.684 + if ((ch < 0x0020) || (ch > 0x007e)) {
3.685 + output.append("\\u");
3.686 + String hex = Integer.toHexString(ch);
3.687 + for (int i = 0; i < 4 - hex.length(); i++) {
3.688 + output.append('0');
3.689 + }
3.690 + output.append(hex);
3.691 + } else {
3.692 + output.append(ch);
3.693 + }
3.694 + }
3.695 + }
3.696 + return output.toString();
3.697 + }
3.698 +
3.699 + private static String decodeUnicode(String input) {
3.700 + char ch;
3.701 + int len = input.length();
3.702 + StringBuffer output = new StringBuffer(len);
3.703 + for (int x = 0; x < len; x++) {
3.704 + ch = input.charAt(x);
3.705 + if (ch != '\\') {
3.706 + output.append(ch);
3.707 + continue;
3.708 + }
3.709 + x++;
3.710 + if (x==len) {
3.711 + // backslash at the end? syntax error: ignore it
3.712 + continue;
3.713 + }
3.714 + ch = input.charAt(x);
3.715 + if (ch == 'u') {
3.716 + if (x+5>len) {
3.717 + // unicode character not finished? syntax error: ignore
3.718 + output.append(input.substring(x-1));
3.719 + x += 4;
3.720 + continue;
3.721 + }
3.722 + String val = input.substring(x+1, x+5);
3.723 + try {
3.724 + output.append((char)Integer.parseInt(val, 16));
3.725 + } catch (NumberFormatException e) {
3.726 + // #46234: handle gracefully
3.727 + output.append(input.substring(x - 1, x + 5));
3.728 + }
3.729 + x += 4;
3.730 + } else {
3.731 + output.append(ch);
3.732 + }
3.733 + }
3.734 + return output.toString();
3.735 + }
3.736 +
3.737 + private static String encodeUnicode(String input) {
3.738 + int len = input.length();
3.739 + StringBuffer output = new StringBuffer(len * 2);
3.740 + for (int x = 0; x < len; x++) {
3.741 + char ch = input.charAt(x);
3.742 + if ((ch < 0x0020) || (ch > 0x007e)) {
3.743 + output.append("\\u"); // NOI18N
3.744 + String hex = Integer.toHexString(ch);
3.745 + for (int i = 0; i < 4 - hex.length(); i++) {
3.746 + output.append('0');
3.747 + }
3.748 + output.append(hex);
3.749 + } else {
3.750 + output.append(ch);
3.751 + }
3.752 + }
3.753 + return output.toString();
3.754 + }
3.755 +
3.756 + @Override
3.757 + public Object clone() {
3.758 + Item item = new Item();
3.759 + if (keyValueLines != null) {
3.760 + item.keyValueLines = new ArrayList<String>(keyValueLines);
3.761 + }
3.762 + if (commentLines != null) {
3.763 + item.commentLines = new ArrayList<String>(commentLines);
3.764 + }
3.765 + item.key = key;
3.766 + item.value = value;
3.767 + item.separate = separate;
3.768 + return item;
3.769 + }
3.770 +
3.771 + }
3.772 +
3.773 + private static class SetImpl extends AbstractSet<Map.Entry<String,String>> {
3.774 +
3.775 + private EditableProperties props;
3.776 +
3.777 + public SetImpl(EditableProperties props) {
3.778 + this.props = props;
3.779 + }
3.780 +
3.781 + public Iterator<Map.Entry<String,String>> iterator() {
3.782 + return new IteratorImpl(props);
3.783 + }
3.784 +
3.785 + public int size() {
3.786 + return props.items.size();
3.787 + }
3.788 +
3.789 + }
3.790 +
3.791 + private static class IteratorImpl implements Iterator<Map.Entry<String,String>> {
3.792 +
3.793 + private final EditableProperties props;
3.794 + private ListIterator<Item> delegate;
3.795 +
3.796 + public IteratorImpl(EditableProperties props) {
3.797 + this.props = props;
3.798 + delegate = props.items.listIterator();
3.799 + }
3.800 +
3.801 + public boolean hasNext() {
3.802 + return findNext() != null;
3.803 + }
3.804 +
3.805 + public Map.Entry<String,String> next() {
3.806 + Item item = findNext();
3.807 + if (item == null) {
3.808 + throw new NoSuchElementException();
3.809 + }
3.810 + delegate.next();
3.811 + return new MapEntryImpl(item);
3.812 + }
3.813 +
3.814 + public void remove() {
3.815 + delegate.previous();
3.816 + Item item = findNext();
3.817 + if (item == null) {
3.818 + throw new IllegalStateException();
3.819 + }
3.820 + int index = delegate.nextIndex();
3.821 + props.items.remove(item);
3.822 + props.itemIndex.remove(item.getKey());
3.823 + delegate = props.items.listIterator(index);
3.824 + }
3.825 +
3.826 + private Item findNext() {
3.827 + while (delegate.hasNext()) {
3.828 + Item item = delegate.next();
3.829 + if (item.getKey() != null && item.getValue() != null) {
3.830 + // Found one. Back up!
3.831 + delegate.previous();
3.832 + return item;
3.833 + }
3.834 + }
3.835 + return null;
3.836 + }
3.837 +
3.838 + }
3.839 +
3.840 + private static class MapEntryImpl implements Map.Entry<String,String> {
3.841 +
3.842 + private Item item;
3.843 +
3.844 + public MapEntryImpl(Item item) {
3.845 + this.item = item;
3.846 + }
3.847 +
3.848 + public String getKey() {
3.849 + return item.getKey();
3.850 + }
3.851 +
3.852 + public String getValue() {
3.853 + return item.getValue();
3.854 + }
3.855 +
3.856 + public String setValue(String value) {
3.857 + String result = item.getValue();
3.858 + item.setValue(value);
3.859 + return result;
3.860 + }
3.861 +
3.862 + }
3.863 +
3.864 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/openide.util/test/unit/src/org/openide/util/EditablePropertiesTest.java Wed Jul 29 11:49:18 2009 -0400
4.3 @@ -0,0 +1,366 @@
4.4 +/*
4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4.6 + *
4.7 + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
4.8 + *
4.9 + * The contents of this file are subject to the terms of either the GNU
4.10 + * General Public License Version 2 only ("GPL") or the Common
4.11 + * Development and Distribution License("CDDL") (collectively, the
4.12 + * "License"). You may not use this file except in compliance with the
4.13 + * License. You can obtain a copy of the License at
4.14 + * http://www.netbeans.org/cddl-gplv2.html
4.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
4.16 + * specific language governing permissions and limitations under the
4.17 + * License. When distributing the software, include this License Header
4.18 + * Notice in each file and include the License file at
4.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
4.20 + * particular file as subject to the "Classpath" exception as provided
4.21 + * by Sun in the GPL Version 2 section of the License file that
4.22 + * accompanied this code. If applicable, add the following below the
4.23 + * License Header, with the fields enclosed by brackets [] replaced by
4.24 + * your own identifying information:
4.25 + * "Portions Copyrighted [year] [name of copyright owner]"
4.26 + *
4.27 + * Contributor(s):
4.28 + *
4.29 + * The Original Software is NetBeans. The Initial Developer of the Original
4.30 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
4.31 + * Microsystems, Inc. All Rights Reserved.
4.32 + *
4.33 + * If you wish your version of this file to be governed by only the CDDL
4.34 + * or only the GPL Version 2, indicate your decision by adding
4.35 + * "[Contributor] elects to include this software in this distribution
4.36 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
4.37 + * single choice of license, a recipient has the option to distribute
4.38 + * your version of this file under either the CDDL, the GPL Version 2 or
4.39 + * to extend the choice of license to its licensees as provided above.
4.40 + * However, if you add GPL Version 2 code and therefore, elected the GPL
4.41 + * Version 2 license, then the option applies only if the new code is
4.42 + * made subject to such option by the copyright holder.
4.43 + */
4.44 +
4.45 +package org.openide.util;
4.46 +
4.47 +import java.io.ByteArrayInputStream;
4.48 +import java.io.ByteArrayOutputStream;
4.49 +import java.io.File;
4.50 +import java.io.FileOutputStream;
4.51 +import java.io.IOException;
4.52 +import java.io.InputStream;
4.53 +import java.io.OutputStream;
4.54 +import java.net.URI;
4.55 +import java.net.URL;
4.56 +import java.util.Arrays;
4.57 +import java.util.Collections;
4.58 +import java.util.HashMap;
4.59 +import java.util.Iterator;
4.60 +import java.util.Map;
4.61 +import org.netbeans.junit.NbTestCase;
4.62 +
4.63 +public class EditablePropertiesTest extends NbTestCase {
4.64 +
4.65 + public EditablePropertiesTest(String name) {
4.66 + super(name);
4.67 + }
4.68 +
4.69 + public void testLoad() throws Exception {
4.70 + Map<String,String> content = new HashMap<String,String>();
4.71 + for (int i=1; i<=26; i++) {
4.72 + content.put("key"+i, "value"+i);
4.73 + }
4.74 + content.put("@!#$%^keyA", "valueA!@#$%^&*(){}");
4.75 + content.put(" =:keyB", "valueB =:");
4.76 + content.put(""+(char)0x1234+"keyC", "valueC"+(char)0x9876);
4.77 + content.put("keyD", "");
4.78 + content.put("keyE", "");
4.79 + content.put("keyF", "");
4.80 + content.put("keyG", "");
4.81 + content.put("keyH", "value#this is not comment");
4.82 + content.put("keyI", "incorrect end: \\u123");
4.83 + // #46234: does not handle bad Unicode escapes well
4.84 + content.put("keyJ", "malformed Unicode escape: \\uabyz");
4.85 +
4.86 + EditableProperties ep = loadTestProperties();
4.87 +
4.88 + for (Map.Entry<String,String> entry : content.entrySet()) {
4.89 + String key = entry.getKey();
4.90 + String value = entry.getValue();
4.91 + String epValue = ep.getProperty(key);
4.92 + assertEquals("Expected value for key "+key+" is different", value, epValue);
4.93 + }
4.94 + int count = 0;
4.95 + for (Map.Entry<String,String> entry : ep.entrySet()) {
4.96 + if (entry.getKey() != null) {
4.97 + count++;
4.98 + }
4.99 + }
4.100 + assertEquals("Number of items in property file", content.keySet().size(), count);
4.101 + }
4.102 +
4.103 + /* Doesn't work; java.util.Properties throws IAE for malformed Unicode escapes:
4.104 + public void testJavaUtilPropertiesEquivalence() throws Exception {
4.105 + Properties p = loadTestJavaUtilProperties();
4.106 + EditableProperties ep = loadTestProperties();
4.107 + Iterator it = p.entrySet().iterator();
4.108 + while (it.hasNext()) {
4.109 + Map.Entry entry = (Map.Entry) it.next();
4.110 + String key = (String) entry.getKey();
4.111 + String val = (String) entry.getValue();
4.112 + assertEquals("right value for " + key, val, ep.getProperty(key));
4.113 + }
4.114 + assertEquals("right number of items", p.size(), ep.size());
4.115 + }
4.116 + */
4.117 +
4.118 + public void testSave() throws Exception {
4.119 + clearWorkDir();
4.120 + EditableProperties ep = loadTestProperties();
4.121 + String dest = getWorkDirPath()+File.separatorChar+"new.properties";
4.122 + saveProperties(ep, dest);
4.123 + assertFile("Saved properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
4.124 + }
4.125 +
4.126 + public void testClonability() throws Exception {
4.127 + clearWorkDir();
4.128 + EditableProperties ep = loadTestProperties();
4.129 +
4.130 + EditableProperties ep2 = ep.cloneProperties();
4.131 + String dest = getWorkDirPath()+File.separatorChar+"new2.properties";
4.132 + saveProperties(ep2, dest);
4.133 + assertFile("Saved cloned properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
4.134 +
4.135 + EditableProperties ep3 = (EditableProperties)ep.clone();
4.136 + dest = getWorkDirPath()+File.separatorChar+"new3.properties";
4.137 + saveProperties(ep3, dest);
4.138 + assertFile("Saved cloned properties must be the same as original one", filenameOfTestProperties(), dest, (String)null);
4.139 + }
4.140 +
4.141 + // test that array values are stored correctly
4.142 + public void testArrayValues() throws Exception {
4.143 + EditableProperties ep = new EditableProperties(false);
4.144 + ep.setProperty("key1", new String[]{"1. line;", "2. line;", "3. line"});
4.145 + ep.setProperty("key2", "1. line;2. line;3. line");
4.146 + String output = getAsString(ep);
4.147 + String expected =
4.148 + "key1=\\"+System.getProperty("line.separator")+
4.149 + " 1. line;\\"+System.getProperty("line.separator")+
4.150 + " 2. line;\\"+System.getProperty("line.separator")+
4.151 + " 3. line"+System.getProperty("line.separator")+
4.152 + "key2=1. line;2. line;3. line"+System.getProperty("line.separator");
4.153 + assertEquals(expected, output);
4.154 + assertEquals(ep.getProperty("key1"), "1. line;2. line;3. line");
4.155 + assertEquals(ep.getProperty("key2"), "1. line;2. line;3. line");
4.156 + ep.setProperty("key1", "one; two; three");
4.157 + output = getAsString(ep);
4.158 + expected =
4.159 + "key1=one; two; three"+System.getProperty("line.separator")+
4.160 + "key2=1. line;2. line;3. line"+System.getProperty("line.separator");
4.161 + assertEquals(expected, output);
4.162 + assertEquals(ep.getProperty("key1"), "one; two; three");
4.163 + assertEquals(ep.getProperty("key2"), "1. line;2. line;3. line");
4.164 + ep.setProperty("key2", new String[]{"1. line;", "2. line;", "3. line", "one;", "more;", "line;"});
4.165 + ep.setProperty("key", new String[0]);
4.166 + output = getAsString(ep);
4.167 + expected =
4.168 + "key1=one; two; three"+System.getProperty("line.separator")+
4.169 + "key2=\\"+System.getProperty("line.separator")+
4.170 + " 1. line;\\"+System.getProperty("line.separator")+
4.171 + " 2. line;\\"+System.getProperty("line.separator")+
4.172 + " 3. line\\"+System.getProperty("line.separator")+
4.173 + " one;\\"+System.getProperty("line.separator")+
4.174 + " more;\\"+System.getProperty("line.separator")+
4.175 + " line;"+System.getProperty("line.separator")+
4.176 + "key="+System.getProperty("line.separator"); // #45061
4.177 + assertEquals(expected, output);
4.178 + assertEquals(ep.getProperty("key1"), "one; two; three");
4.179 + assertEquals(ep.getProperty("key2"), "1. line;2. line;3. lineone;more;line;");
4.180 + assertEquals(ep.getProperty("key"), "");
4.181 + }
4.182 +
4.183 + public void testSorting() throws Exception {
4.184 + EditableProperties ep = new EditableProperties(false);
4.185 + ep.setProperty("a", "val-a");
4.186 + ep.setProperty("c", "val-c");
4.187 + ep.put("b", "val-b");
4.188 + String output = getAsString(ep);
4.189 + String expected = "a=val-a"+System.getProperty("line.separator")+"c=val-c"+
4.190 + System.getProperty("line.separator")+"b=val-b"+
4.191 + System.getProperty("line.separator");
4.192 + assertEquals(expected, output);
4.193 +
4.194 + ep = new EditableProperties(false);
4.195 + ep.setProperty("a", "val-a");
4.196 + ep.setProperty("c", "val-c");
4.197 + ep.put("b", "val-b");
4.198 + output = getAsString(ep);
4.199 + expected = "a=val-a"+System.getProperty("line.separator")+"c=val-c"+
4.200 + System.getProperty("line.separator")+"b=val-b"+
4.201 + System.getProperty("line.separator");
4.202 + assertEquals(expected, output);
4.203 +
4.204 + ep = new EditableProperties(true);
4.205 + ep.setProperty("a", "val-a");
4.206 + ep.setProperty("c", "val-c");
4.207 + ep.put("b", "val-b");
4.208 + output = getAsString(ep);
4.209 + expected = "a=val-a"+System.getProperty("line.separator")+"b=val-b"+
4.210 + System.getProperty("line.separator")+"c=val-c"+
4.211 + System.getProperty("line.separator");
4.212 + assertEquals(expected, output);
4.213 + }
4.214 +
4.215 + // test that changing comments work and modify only comments
4.216 + // test that misc chars are correctly escaped, unicode encoded, etc.
4.217 + public void testEscaping() throws Exception {
4.218 + String umlaut = "" + (char)252;
4.219 + EditableProperties ep = new EditableProperties(false);
4.220 + ep.setProperty("a a", "a space a");
4.221 + ep.setProperty("b"+(char)0x4567, "val"+(char)0x1234);
4.222 + ep.setProperty("@!#$%^\\", "!@#$%^&*(){}\\");
4.223 + ep.setProperty("d\nd", "d\nnewline\nd");
4.224 + ep.setProperty("umlaut", umlaut);
4.225 + ep.setProperty("_a a", new String[]{"a space a"});
4.226 + ep.setProperty("_b"+(char)0x4567, new String[]{"val"+(char)0x1234});
4.227 + ep.setProperty("_@!#$%^\\", new String[]{"!@#$%^&*\\", "(){}\\"});
4.228 + ep.setProperty("_d\nd", new String[]{"d\nnew","line\nd", "\n", "end"});
4.229 + ep.setProperty("_umlaut", new String[]{umlaut, umlaut});
4.230 + String output = getAsString(ep);
4.231 + String expected = "a\\ a=a space a"+System.getProperty("line.separator")+
4.232 + "b\\u4567=val\\u1234"+System.getProperty("line.separator")+
4.233 + "@!#$%^\\\\=!@#$%^&*(){}\\\\"+System.getProperty("line.separator")+
4.234 + "d\\nd=d\\nnewline\\nd"+System.getProperty("line.separator")+
4.235 + "umlaut=\\u00fc"+System.getProperty("line.separator")+
4.236 + "_a\\ a=\\"+System.getProperty("line.separator")+" a space a"+System.getProperty("line.separator")+
4.237 + "_b\\u4567=\\"+System.getProperty("line.separator")+" val\\u1234"+System.getProperty("line.separator")+
4.238 + "_@!#$%^\\\\=\\"+System.getProperty("line.separator")+" !@#$%^&*\\\\\\"+System.getProperty("line.separator")+
4.239 + " (){}\\\\"+System.getProperty("line.separator")+
4.240 + "_d\\nd=\\"+System.getProperty("line.separator")+" d\\nnew\\"+System.getProperty("line.separator")+
4.241 + " line\\nd\\"+System.getProperty("line.separator")+
4.242 + " \\n\\"+System.getProperty("line.separator")+
4.243 + " end"+System.getProperty("line.separator")+
4.244 + "_umlaut=\\" +System.getProperty("line.separator")+" \\u00fc\\"+System.getProperty("line.separator")+
4.245 + " \\u00fc"+System.getProperty("line.separator");
4.246 + assertEquals(expected, output);
4.247 + assertEquals("a space a", ep.getProperty("a a"));
4.248 + assertEquals("val"+(char)0x1234, ep.getProperty("b"+(char)0x4567));
4.249 + assertEquals("!@#$%^&*(){}\\", ep.getProperty("@!#$%^\\"));
4.250 + assertEquals("d\nnewline\nd", ep.getProperty("d\nd"));
4.251 + assertEquals(umlaut, ep.getProperty("umlaut"));
4.252 + assertEquals("a space a", ep.getProperty("_a a"));
4.253 + assertEquals("val"+(char)0x1234, ep.getProperty("_b"+(char)0x4567));
4.254 + assertEquals("!@#$%^&*\\(){}\\", ep.getProperty("_@!#$%^\\"));
4.255 + assertEquals("d\nnewline\nd\nend", ep.getProperty("_d\nd"));
4.256 + assertEquals(umlaut+umlaut, ep.getProperty("_umlaut"));
4.257 + }
4.258 +
4.259 + // test that iterator implementation is OK
4.260 + public void testIterator() throws Exception {
4.261 + EditableProperties ep = loadTestProperties();
4.262 + Iterator<Map.Entry<String,String>> it1 = ep.entrySet().iterator();
4.263 + while (it1.hasNext()) {
4.264 + it1.next();
4.265 + }
4.266 + Iterator<String> it2 = ep.keySet().iterator();
4.267 + while (it2.hasNext()) {
4.268 + it2.next();
4.269 + }
4.270 + it2 = ep.keySet().iterator();
4.271 + while (it2.hasNext()) {
4.272 + it2.next();
4.273 + it2.remove();
4.274 + }
4.275 + ep.put("a", "aval");
4.276 + ep.remove("a");
4.277 + ep = loadTestProperties();
4.278 + it1 = ep.entrySet().iterator();
4.279 + while (it1.hasNext()) {
4.280 + Map.Entry<String,String> entry = it1.next();
4.281 + assertNotNull("Property key cannot be null", entry.getKey());
4.282 + assertNotNull("Property value cannot be null", entry.getValue());
4.283 + entry.setValue(entry.getValue()+"-something-new");
4.284 + }
4.285 + it1 = ep.entrySet().iterator();
4.286 + while (it1.hasNext()) {
4.287 + it1.next();
4.288 + it1.remove();
4.289 + }
4.290 + }
4.291 +
4.292 + // test that syntax errors are survived
4.293 + public void testInvalidPropertiesFile() throws Exception {
4.294 + String invalidProperty = "key=value without correct end\\";
4.295 + ByteArrayInputStream is = new ByteArrayInputStream(invalidProperty.getBytes());
4.296 + EditableProperties ep = new EditableProperties(false);
4.297 + ep.load(is);
4.298 + assertEquals("Syntax error should be resolved", 1, ep.keySet().size());
4.299 + assertEquals("value without correct end", ep.getProperty("key"));
4.300 + }
4.301 +
4.302 + public void testNonLatinComments() throws Exception {
4.303 + // #60249.
4.304 + String lsep = System.getProperty("line.separator");
4.305 + EditableProperties p = new EditableProperties(false);
4.306 + p.setProperty("k", "v");
4.307 + p.setComment("k", new String[] {"# \u0158ekni koment teda!"}, false);
4.308 + String expected = "# \\u0158ekni koment teda!" + lsep + "k=v" + lsep;
4.309 + assertEquals("Storing non-Latin chars in comments works", expected, getAsString(p));
4.310 + p = new EditableProperties(false);
4.311 + p.load(new ByteArrayInputStream(expected.getBytes("ISO-8859-1")));
4.312 + assertEquals("Reading non-Latin chars in comments works", Collections.singletonList("# \u0158ekni koment teda!"), Arrays.asList(p.getComment("k")));
4.313 + p.setProperty("k", "v2");
4.314 + expected = "# \\u0158ekni koment teda!" + lsep + "k=v2" + lsep;
4.315 + assertEquals("Reading and re-writing non-Latin chars in comments works", expected, getAsString(p));
4.316 + }
4.317 +
4.318 +
4.319 + // helper methods:
4.320 +
4.321 +
4.322 + private String filenameOfTestProperties() {
4.323 + // #50987: never use URL.path for this purpose...
4.324 + return new File(URI.create(EditablePropertiesTest.class.getResource("data/test.properties").toExternalForm())).getAbsolutePath();
4.325 + }
4.326 +
4.327 + private EditableProperties loadTestProperties() throws IOException {
4.328 + URL u = EditablePropertiesTest.class.getResource("data/test.properties");
4.329 + EditableProperties ep = new EditableProperties(false);
4.330 + InputStream is = u.openStream();
4.331 + try {
4.332 + ep.load(is);
4.333 + } finally {
4.334 + is.close();
4.335 + }
4.336 + return ep;
4.337 + }
4.338 +
4.339 + /*
4.340 + private Properties loadTestJavaUtilProperties() throws IOException {
4.341 + URL u = EditablePropertiesTest.class.getResource("data/test.properties");
4.342 + Properties p = new Properties();
4.343 + InputStream is = u.openStream();
4.344 + try {
4.345 + p.load(is);
4.346 + } finally {
4.347 + is.close();
4.348 + }
4.349 + return p;
4.350 + }
4.351 + */
4.352 +
4.353 + private void saveProperties(EditableProperties ep, String path) throws Exception {
4.354 + OutputStream os = new FileOutputStream(path);
4.355 + try {
4.356 + ep.store(os);
4.357 + } finally {
4.358 + os.close();
4.359 + }
4.360 + }
4.361 +
4.362 + private String getAsString(EditableProperties ep) throws Exception {
4.363 + ByteArrayOutputStream os = new ByteArrayOutputStream();
4.364 + ep.store(os);
4.365 + os.close();
4.366 + return os.toString("ISO-8859-1");
4.367 + }
4.368 +
4.369 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/openide.util/test/unit/src/org/openide/util/data/test.properties Wed Jul 29 11:49:18 2009 -0400
5.3 @@ -0,0 +1,134 @@
5.4 +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5.5 +#
5.6 +# Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5.7 +#
5.8 +# The contents of this file are subject to the terms of either the GNU
5.9 +# General Public License Version 2 only ("GPL") or the Common
5.10 +# Development and Distribution License("CDDL") (collectively, the
5.11 +# "License"). You may not use this file except in compliance with the
5.12 +# License. You can obtain a copy of the License at
5.13 +# http://www.netbeans.org/cddl-gplv2.html
5.14 +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
5.15 +# specific language governing permissions and limitations under the
5.16 +# License. When distributing the software, include this License Header
5.17 +# Notice in each file and include the License file at
5.18 +# nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
5.19 +# particular file as subject to the "Classpath" exception as provided
5.20 +# by Sun in the GPL Version 2 section of the License file that
5.21 +# accompanied this code. If applicable, add the following below the
5.22 +# License Header, with the fields enclosed by brackets [] replaced by
5.23 +# your own identifying information:
5.24 +# "Portions Copyrighted [year] [name of copyright owner]"
5.25 +#
5.26 +# Contributor(s):
5.27 +#
5.28 +# The Original Software is NetBeans. The Initial Developer of the Original
5.29 +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
5.30 +# Microsystems, Inc. All Rights Reserved.
5.31 +#
5.32 +# If you wish your version of this file to be governed by only the CDDL
5.33 +# or only the GPL Version 2, indicate your decision by adding
5.34 +# "[Contributor] elects to include this software in this distribution
5.35 +# under the [CDDL or GPL Version 2] license." If you do not indicate a
5.36 +# single choice of license, a recipient has the option to distribute
5.37 +# your version of this file under either the CDDL, the GPL Version 2 or
5.38 +# to extend the choice of license to its licensees as provided above.
5.39 +# However, if you add GPL Version 2 code and therefore, elected the GPL
5.40 +# Version 2 license, then the option applies only if the new code is
5.41 +# made subject to such option by the copyright holder.
5.42 +
5.43 +key1=value1
5.44 +key2 = value2
5.45 +key3 =value3
5.46 +key4= value4
5.47 +
5.48 +key5:value5
5.49 +key6 : value6
5.50 +key7 :value7
5.51 +key8: value8
5.52 +
5.53 +key9 value9
5.54 +key10 value10
5.55 +key11 value11
5.56 +key12 value12
5.57 +
5.58 +#comment can end with slash but it does not mean anything\
5.59 +
5.60 +!different type of comment
5.61 +
5.62 + #
5.63 + #indented comment
5.64 + #
5.65 +
5.66 + !
5.67 + ! indented comment
5.68 + !
5.69 +
5.70 +# on the lines below there are some whitespaces which must be ignored
5.71 +
5.72 +
5.73 +
5.74 +
5.75 +
5.76 +
5.77 +
5.78 + key13 = value13
5.79 +
5.80 +
5.81 +key\
5.82 +14 value14
5.83 +key\
5.84 + 15 value15
5.85 +k\
5.86 +e\
5.87 +y\
5.88 +1\
5.89 +6 value16
5.90 +key\
5.91 +\
5.92 + \
5.93 + 17 value17
5.94 +key18\
5.95 +:value18
5.96 +key19 \
5.97 +value19
5.98 +key20 value\
5.99 +20
5.100 +key21 value\
5.101 + 21
5.102 +key22 v\
5.103 +a\
5.104 + lue22
5.105 +key23 value\
5.106 +\
5.107 + \
5.108 + 23
5.109 +
5.110 +#comment for property 24
5.111 +key24=value24
5.112 +#comment for property 25
5.113 +#second line of comment for property 25
5.114 +key25=\
5.115 +value25
5.116 +#comment for property 26
5.117 +key26=value26
5.118 +
5.119 +
5.120 +@!#$%^keyA valueA!@#$%^&*(){}
5.121 +\ \=\:keyB=valueB\ \=\:
5.122 +\u1234\keyC value\C\u9876
5.123 +
5.124 + keyD =
5.125 + keyE
5.126 +keyF
5.127 +
5.128 +keyG\
5.129 +
5.130 +
5.131 + keyH = value\
5.132 + #this is not comment
5.133 +
5.134 +keyI=incorrect end: \u123
5.135 +
5.136 +keyJ=malformed Unicode escape: \uabyz
5.137 +