2011-12-28 20:00:07 +01:00
|
|
|
/*
|
|
|
|
PreferencesMap - A Map<String, String> with some useful features
|
|
|
|
to handle preferences.
|
|
|
|
Part of the Arduino project - http://www.arduino.cc/
|
|
|
|
|
2014-01-31 22:54:45 +01:00
|
|
|
Copyright (c) 2014 Cristian Maglie
|
2011-12-28 20:00:07 +01:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
|
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
2011-12-30 15:46:04 +01:00
|
|
|
package processing.app.helpers;
|
2011-12-28 20:00:07 +01:00
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
2013-03-02 13:24:59 +01:00
|
|
|
import java.util.LinkedHashMap;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.SortedSet;
|
|
|
|
import java.util.TreeSet;
|
2011-12-28 20:00:07 +01:00
|
|
|
|
2014-08-19 11:53:44 +02:00
|
|
|
import processing.app.legacy.PApplet;
|
2011-12-28 20:00:07 +01:00
|
|
|
|
2013-03-02 13:24:59 +01:00
|
|
|
@SuppressWarnings("serial")
|
2012-10-29 17:56:31 +01:00
|
|
|
public class PreferencesMap extends LinkedHashMap<String, String> {
|
2011-12-28 20:00:07 +01:00
|
|
|
|
2012-10-29 17:56:31 +01:00
|
|
|
public PreferencesMap(Map<String, String> table) {
|
2012-02-05 23:17:15 +01:00
|
|
|
super(table);
|
|
|
|
}
|
|
|
|
|
2013-03-02 13:24:59 +01:00
|
|
|
/**
|
|
|
|
* Create a PreferencesMap and load the content of the file passed as
|
|
|
|
* argument.
|
|
|
|
*
|
|
|
|
* Is equivalent to:
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* PreferencesMap map = new PreferencesMap();
|
|
|
|
* map.load(file);
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* @param file
|
|
|
|
* @throws IOException
|
|
|
|
*/
|
|
|
|
public PreferencesMap(File file) throws IOException {
|
|
|
|
super();
|
|
|
|
load(file);
|
|
|
|
}
|
|
|
|
|
2012-02-05 23:17:15 +01:00
|
|
|
public PreferencesMap() {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse a property list file and put kev/value pairs into the Map
|
|
|
|
*
|
|
|
|
* @param file
|
|
|
|
* @throws FileNotFoundException
|
|
|
|
* @throws IOException
|
|
|
|
*/
|
2013-02-06 11:53:54 +01:00
|
|
|
public void load(File file) throws IOException {
|
2012-02-05 23:17:15 +01:00
|
|
|
load(new FileInputStream(file));
|
|
|
|
}
|
|
|
|
|
Process platform-specific suffixes immediately
In preferences files, platform-specific versions can be indicated by a
.linux, .windows or .macos suffix on the key name. Previously, these
keys were loaded as normal and then afterwards, all keys were scanned
after loading them and any platform-specific versions replaced the
regular ones.
However, this means that these platform-specific versions get an
unexpected form of priority. Normally, when a single key is set twice,
the latter overrides the first. However, the platform-specific values
could override the regular versions, even when the regular version
occurs later in the file.
This problem was particularly confusing when using the new
platform.local.txt: a regular preference in platform.local.txt did not
override a platform-specific preference in platform.txt.
This commit changes behaviour to process these suffixes directly as they
are read from the preference files. If a suffix for the current platform
is found, the line is processed as if the suffix was not present. If a
suffix for another platform is found, the line is ignored altogether.
This can slightly change the way preferences files are parsed, but as
long as platform-specific preferences are defined after the
corresponding regular preferences, the behaviour should be the same.
2014-04-14 15:12:32 +02:00
|
|
|
protected String processPlatformSuffix(String key, String suffix, boolean isCurrentPlatform) {
|
|
|
|
if (key == null)
|
|
|
|
return null;
|
|
|
|
// Key does not end with the given suffix? Process as normal
|
|
|
|
if (!key.endsWith(suffix))
|
|
|
|
return key;
|
|
|
|
// Not the current platform? Ignore this key
|
|
|
|
if (!isCurrentPlatform)
|
|
|
|
return null;
|
|
|
|
// Strip the suffix from the key
|
|
|
|
return key.substring(0, key.length() - suffix.length());
|
|
|
|
}
|
|
|
|
|
2012-02-05 23:17:15 +01:00
|
|
|
/**
|
|
|
|
* Parse a property list stream and put key/value pairs into the Map
|
|
|
|
*
|
|
|
|
* @param input
|
|
|
|
* @throws IOException
|
|
|
|
*/
|
|
|
|
public void load(InputStream input) throws IOException {
|
|
|
|
String[] lines = PApplet.loadStrings(input);
|
|
|
|
for (String line : lines) {
|
|
|
|
if (line.length() == 0 || line.charAt(0) == '#')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int equals = line.indexOf('=');
|
|
|
|
if (equals != -1) {
|
Process platform-specific suffixes immediately
In preferences files, platform-specific versions can be indicated by a
.linux, .windows or .macos suffix on the key name. Previously, these
keys were loaded as normal and then afterwards, all keys were scanned
after loading them and any platform-specific versions replaced the
regular ones.
However, this means that these platform-specific versions get an
unexpected form of priority. Normally, when a single key is set twice,
the latter overrides the first. However, the platform-specific values
could override the regular versions, even when the regular version
occurs later in the file.
This problem was particularly confusing when using the new
platform.local.txt: a regular preference in platform.local.txt did not
override a platform-specific preference in platform.txt.
This commit changes behaviour to process these suffixes directly as they
are read from the preference files. If a suffix for the current platform
is found, the line is processed as if the suffix was not present. If a
suffix for another platform is found, the line is ignored altogether.
This can slightly change the way preferences files are parsed, but as
long as platform-specific preferences are defined after the
corresponding regular preferences, the behaviour should be the same.
2014-04-14 15:12:32 +02:00
|
|
|
String key = line.substring(0, equals).trim();
|
|
|
|
String value = line.substring(equals + 1).trim();
|
|
|
|
|
2014-08-19 16:48:34 +02:00
|
|
|
key = processPlatformSuffix(key, ".linux", OSUtils.isLinux());
|
|
|
|
key = processPlatformSuffix(key, ".windows", OSUtils.isWindows());
|
|
|
|
key = processPlatformSuffix(key, ".macosx", OSUtils.isMacOS());
|
2012-05-25 19:03:00 +02:00
|
|
|
|
Process platform-specific suffixes immediately
In preferences files, platform-specific versions can be indicated by a
.linux, .windows or .macos suffix on the key name. Previously, these
keys were loaded as normal and then afterwards, all keys were scanned
after loading them and any platform-specific versions replaced the
regular ones.
However, this means that these platform-specific versions get an
unexpected form of priority. Normally, when a single key is set twice,
the latter overrides the first. However, the platform-specific values
could override the regular versions, even when the regular version
occurs later in the file.
This problem was particularly confusing when using the new
platform.local.txt: a regular preference in platform.local.txt did not
override a platform-specific preference in platform.txt.
This commit changes behaviour to process these suffixes directly as they
are read from the preference files. If a suffix for the current platform
is found, the line is processed as if the suffix was not present. If a
suffix for another platform is found, the line is ignored altogether.
This can slightly change the way preferences files are parsed, but as
long as platform-specific preferences are defined after the
corresponding regular preferences, the behaviour should be the same.
2014-04-14 15:12:32 +02:00
|
|
|
if (key != null)
|
|
|
|
put(key, value);
|
2012-05-25 19:03:00 +02:00
|
|
|
}
|
|
|
|
}
|
2012-02-05 23:17:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-03-02 13:24:59 +01:00
|
|
|
* Create a new PreferenceMap that contains all the top level pairs of the
|
|
|
|
* current mapping. E.g. the folowing mapping:<br />
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* Map (
|
|
|
|
* alpha = Alpha
|
|
|
|
* alpha.some.keys = v1
|
|
|
|
* alpha.other.keys = v2
|
|
|
|
* beta = Beta
|
|
|
|
* beta.some.keys = v3
|
|
|
|
* )
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* will generate the following result:
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* Map (
|
|
|
|
* alpha = Alpha
|
|
|
|
* beta = Beta
|
|
|
|
* )
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public PreferencesMap topLevelMap() {
|
|
|
|
PreferencesMap res = new PreferencesMap();
|
|
|
|
for (String key : keySet()) {
|
|
|
|
if (key.contains("."))
|
|
|
|
continue;
|
|
|
|
res.put(key, get(key));
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new Map<String, PreferenceMap> where keys are the first level of
|
|
|
|
* the current mapping. Top level pairs are discarded. E.g. the folowing
|
|
|
|
* mapping:<br />
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* Map (
|
|
|
|
* alpha = Alpha
|
|
|
|
* alpha.some.keys = v1
|
|
|
|
* alpha.other.keys = v2
|
|
|
|
* beta = Beta
|
2012-02-05 23:17:15 +01:00
|
|
|
* beta.some.keys = v3
|
|
|
|
* )
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* will generate the following result:
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* alpha = Map(
|
|
|
|
* some.keys = v1
|
|
|
|
* other.keys = v2
|
|
|
|
* )
|
|
|
|
* beta = Map(
|
|
|
|
* some.keys = v3
|
|
|
|
* )
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
*/
|
2013-03-02 13:24:59 +01:00
|
|
|
public Map<String, PreferencesMap> firstLevelMap() {
|
2012-10-29 17:56:31 +01:00
|
|
|
Map<String, PreferencesMap> res = new LinkedHashMap<String, PreferencesMap>();
|
2012-02-05 23:17:15 +01:00
|
|
|
for (String key : keySet()) {
|
|
|
|
int dot = key.indexOf('.');
|
|
|
|
if (dot == -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
String parent = key.substring(0, dot);
|
|
|
|
String child = key.substring(dot + 1);
|
|
|
|
|
|
|
|
if (!res.containsKey(parent))
|
|
|
|
res.put(parent, new PreferencesMap());
|
|
|
|
res.get(parent).put(child, get(key));
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2012-02-01 14:27:41 +01:00
|
|
|
/**
|
2013-03-02 13:24:59 +01:00
|
|
|
* Create a new PreferenceMap using a subtree of the current mapping. Top
|
|
|
|
* level pairs are ignored. E.g. with the following mapping:<br />
|
2012-02-05 23:17:15 +01:00
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* Map (
|
2013-03-02 13:24:59 +01:00
|
|
|
* alpha = Alpha
|
2012-02-05 23:17:15 +01:00
|
|
|
* alpha.some.keys = v1
|
|
|
|
* alpha.other.keys = v2
|
2013-03-02 13:24:59 +01:00
|
|
|
* beta = Beta
|
2012-02-05 23:17:15 +01:00
|
|
|
* beta.some.keys = v3
|
|
|
|
* )
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* a call to createSubTree("alpha") will generate the following result:
|
|
|
|
*
|
|
|
|
* <pre>
|
|
|
|
* Map(
|
|
|
|
* some.keys = v1
|
|
|
|
* other.keys = v2
|
|
|
|
* )
|
|
|
|
* </pre>
|
|
|
|
*
|
|
|
|
* @param parent
|
|
|
|
* @return
|
|
|
|
*/
|
2013-03-02 13:24:59 +01:00
|
|
|
public PreferencesMap subTree(String parent) {
|
2012-02-05 23:17:15 +01:00
|
|
|
PreferencesMap res = new PreferencesMap();
|
|
|
|
parent += ".";
|
|
|
|
int parentLen = parent.length();
|
|
|
|
for (String key : keySet()) {
|
|
|
|
if (key.startsWith(parent))
|
|
|
|
res.put(key.substring(parentLen), get(key));
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-03-02 13:24:59 +01:00
|
|
|
public String toString(String indent) {
|
|
|
|
String res = indent + "{\n";
|
|
|
|
SortedSet<String> treeSet = new TreeSet<String>(keySet());
|
|
|
|
for (String k : treeSet)
|
|
|
|
res += indent + k + " = " + get(k) + "\n";
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2013-07-12 19:50:57 +02:00
|
|
|
/**
|
|
|
|
* Returns the value to which the specified key is mapped, or throws a
|
|
|
|
* PreferencesMapException if not found
|
|
|
|
*
|
|
|
|
* @param k
|
|
|
|
* the key whose associated value is to be returned
|
|
|
|
* @return the value to which the specified key is mapped
|
|
|
|
* @throws PreferencesMapException
|
|
|
|
*/
|
|
|
|
public String getOrExcept(String k) throws PreferencesMapException {
|
|
|
|
String r = get(k);
|
|
|
|
if (r == null)
|
|
|
|
throw new PreferencesMapException(k);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-03-02 13:24:59 +01:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return toString("");
|
|
|
|
}
|
2013-12-24 20:08:22 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new File instance by converting the value of the key into an
|
|
|
|
* abstract pathname. If the the given key doesn't exists or his value is the
|
|
|
|
* empty string, the result is <b>null</b>.
|
|
|
|
*
|
|
|
|
* @param key
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public File getFile(String key) {
|
|
|
|
if (!containsKey(key))
|
|
|
|
return null;
|
|
|
|
String path = get(key).trim();
|
|
|
|
if (path.length() == 0)
|
|
|
|
return null;
|
|
|
|
return new File(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new File instance by converting the value of the key into an
|
|
|
|
* abstract pathname with the specified sub folder. If the the given key
|
|
|
|
* doesn't exists or his value is the empty string, the result is <b>null</b>.
|
|
|
|
*
|
|
|
|
* @param key
|
|
|
|
* @param subFolder
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public File getFile(String key, String subFolder) {
|
|
|
|
File file = getFile(key);
|
|
|
|
if (file == null)
|
|
|
|
return null;
|
|
|
|
return new File(file, subFolder);
|
|
|
|
}
|
2014-01-31 22:54:45 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the value of the specified key as boolean.
|
|
|
|
*
|
|
|
|
* @param key
|
|
|
|
* @return <b>true</b> if the value of the key is the string "true" (case
|
|
|
|
* insensitive compared), <b>false</b> in any other case
|
|
|
|
*/
|
|
|
|
public boolean getBoolean(String key) {
|
|
|
|
return new Boolean(get(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the value of the specified key to the string <b>"true"</b> or
|
|
|
|
* <b>"false"</b> based on value of the boolean parameter
|
|
|
|
*
|
|
|
|
* @param key
|
|
|
|
* @param value
|
|
|
|
* @return <b>true</b> if the previous value of the key was the string "true"
|
|
|
|
* (case insensitive compared), <b>false</b> in any other case
|
|
|
|
*/
|
|
|
|
public boolean putBoolean(String key, boolean value) {
|
|
|
|
String prev = put(key, value ? "true" : "false");
|
|
|
|
return new Boolean(prev);
|
|
|
|
}
|
|
|
|
|
2011-12-28 20:00:07 +01:00
|
|
|
}
|