├── .gitignore
├── .travis.yml
├── src
├── main
│ └── java
│ │ └── org
│ │ └── freedesktop
│ │ ├── DesktopEntryReader.java
│ │ ├── DesktopEntryWriter.java
│ │ ├── IniStyleFileWriter.java
│ │ ├── IniStyleFileReader.java
│ │ ├── DesktopEntry.java
│ │ ├── BaseDirectory.java
│ │ └── IniStyleFile.java
└── test
│ └── java
│ └── org
│ └── freedesktop
│ ├── IniStyleFileWriterTest.java
│ ├── IniStyleFileReaderTest.java
│ ├── DesktopEntryTest.java
│ ├── BaseDirectoryTest.java
│ └── IniStyleFileTest.java
├── pom.xml
├── CONTRIBUTING.md
├── README.md
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - openjdk7
4 | - openjdk8
5 |
--------------------------------------------------------------------------------
/src/main/java/org/freedesktop/DesktopEntryReader.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 |
6 | public class DesktopEntryReader {
7 |
8 | private IniStyleFileReader delegate;
9 |
10 | public DesktopEntryReader(BufferedReader reader) {
11 | delegate = new IniStyleFileReader(reader);
12 | }
13 |
14 | public DesktopEntry read() throws IOException {
15 | return new DesktopEntry(delegate.read());
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/org/freedesktop/DesktopEntryWriter.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import java.io.IOException;
4 | import java.io.Writer;
5 |
6 | public class DesktopEntryWriter {
7 |
8 | private IniStyleFileWriter delegate;
9 |
10 | /**
11 | * @param writer the writer must be using a utf-8 encoding
12 | */
13 | public DesktopEntryWriter(Writer writer) {
14 | this.delegate = new IniStyleFileWriter(writer);
15 | }
16 |
17 | public void write(DesktopEntry file) throws IOException {
18 | delegate.write(file);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | 4.0.0
8 |
9 | org.freedesktop
10 | xdg-java
11 | 0.0.1-SNAPSHOT
12 |
13 |
14 | UTF-8
15 |
16 |
17 |
18 |
19 | junit
20 | junit
21 | 4.10
22 |
23 |
24 |
25 |
26 |
27 |
28 | org.apache.maven.plugins
29 | maven-compiler-plugin
30 | 3.1
31 |
32 | 1.7
33 | 1.7
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/java/org/freedesktop/IniStyleFileWriter.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import java.io.IOException;
4 | import java.io.Writer;
5 | import java.util.Map;
6 |
7 | public class IniStyleFileWriter {
8 | private final Writer writer;
9 |
10 | /**
11 | * @param writer the writer must be using a utf-8 encoding
12 | */
13 | public IniStyleFileWriter(Writer writer) {
14 | this.writer = writer;
15 | }
16 |
17 | public void write(IniStyleFile file) throws IOException {
18 | writeGroup(file, DesktopEntry.GROUP_DEFAULT);
19 | for (String name : file.data.keySet()) {
20 | if (!name.equals(DesktopEntry.GROUP_DEFAULT)) {
21 | writeGroup(file, name);
22 | }
23 | }
24 | }
25 |
26 | private void writeGroup(IniStyleFile file, String groupName) throws IOException {
27 | if (!file.data.containsKey(groupName)) {
28 | return;
29 | }
30 |
31 | writer.write("[" + groupName + "]\n");
32 | Map group = file.data.get(groupName);
33 | for (String key : group.keySet()) {
34 | writer.write(key + "=" + group.get(key) + "\n");
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Contributing
2 | ============
3 |
4 | This is a rough guide for contributing.
5 |
6 | To contribute a bugfix:
7 | - Create a pull request on github
8 | - Create one pull request per bug
9 | - Include as much information about your bug as possible, preferably a
10 | unit test or a stand alone minimal reproducer
11 |
12 | If you are looking to contribute features please create a pull
13 | request. If you are looking for contribution ideas, the following
14 | specifications are excellent candidates:
15 |
16 | - The [Desktop Menu Specification](https://www.freedesktop.org/wiki/Specifications/menu-spec/)
17 | - The [Desktop Trash Can Specification](https://www.freedesktop.org/wiki/Specifications/trash-spec/)
18 | - The [Icon Theme Specification](http://www.freedesktop.org/wiki/Specifications/icon-theme-spec/)
19 | - The [Shared MIME Info Database](https://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec/)
20 | - The [Recent File Storage Specification](https://www.freedesktop.org/wiki/Specifications/recent-file-spec/)
21 |
22 | Code Guidelines
23 | ---------------
24 |
25 | This is a Java 7 project (for now). Please avoid using features only
26 | available in newer Java versions. I am thinking about switching to
27 | Java 8, any feedback about this is appreciated.
28 |
--------------------------------------------------------------------------------
/src/main/java/org/freedesktop/IniStyleFileReader.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 |
6 | /**
7 | * Allows reading Desktop Entry files.
8 | */
9 | public class IniStyleFileReader {
10 | /* TODO: preserve comments */
11 |
12 | private BufferedReader reader;
13 |
14 | public IniStyleFileReader(BufferedReader reader) {
15 | this.reader = reader;
16 | }
17 |
18 | public IniStyleFile read() throws IOException {
19 |
20 | IniStyleFile iniStyleFile = new IniStyleFile();
21 | String line = null;
22 | String groupName = null;
23 | while ((line = reader.readLine()) != null) {
24 | line = line.trim();
25 | if (line.startsWith("[") && line.endsWith("]")) {
26 | groupName = line.substring(1, line.length() - 1);
27 | iniStyleFile.addGroup(groupName);
28 | } else if (groupName != null) {
29 | String[] parts = line.split("=");
30 | String key = parts[0].trim();
31 | String value = "";
32 | if (parts.length > 1) {
33 | value = parts[1].trim();
34 | }
35 | iniStyleFile.add(groupName, key, value);
36 | }
37 | }
38 |
39 | return iniStyleFile;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/org/freedesktop/IniStyleFileWriterTest.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNotNull;
5 |
6 | import java.io.IOException;
7 | import java.io.StringWriter;
8 |
9 | import org.junit.Test;
10 |
11 | public class IniStyleFileWriterTest {
12 |
13 | @Test
14 | public void testSimpleWriting() throws IOException {
15 | StringWriter w = new StringWriter();
16 | IniStyleFileWriter writer = new IniStyleFileWriter(w);
17 | writer.write(new IniStyleFile("temp"));
18 | String result = w.getBuffer().toString();
19 | assertNotNull(result);
20 | assertEquals("", result);
21 | }
22 |
23 | @Test
24 | public void testWritingWithOneGroup() throws IOException {
25 | StringWriter w = new StringWriter();
26 | IniStyleFileWriter writer = new IniStyleFileWriter(w);
27 | IniStyleFile file = new IniStyleFile("group");
28 | file.add("key", "value");
29 | writer.write(file);
30 | String result = w.getBuffer().toString();
31 | assertNotNull(result);
32 | assertEquals("[group]\nkey=value\n", result);
33 | }
34 |
35 | @Test
36 | public void testWritingWithOneGroupReplacingExistingKeys() throws IOException {
37 | StringWriter w = new StringWriter();
38 | IniStyleFileWriter writer = new IniStyleFileWriter(w);
39 | IniStyleFile file = new IniStyleFile("group");
40 | file.add("key", "value");
41 | file.add("key", "new-value");
42 | writer.write(file);
43 | String result = w.getBuffer().toString();
44 | assertNotNull(result);
45 | assertEquals("[group]\nkey=new-value\n", result);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | `xdg-java`
2 | ==========
3 |
4 | This is a Java API that provides an easy to use implementations of
5 | various
6 | [Free Desktop specifications](https://www.freedesktop.org/wiki/Specifications/).
7 | Use the correct application specific configuration, data and cache
8 | directories. Safely read and write desktop entries without worrying
9 | about parsing and escaping.
10 |
11 | Specifications
12 | --------------
13 |
14 | The following specifications are currently implemented:
15 |
16 | ### [Base Directory Specification](https://www.freedesktop.org/wiki/Specifications/basedir-spec/)
17 |
18 | Use this to access standard directories for data, cache and
19 | configuration.
20 |
21 | Here's how to find the path to a cache file with the name `myCache`:
22 |
23 | File cacheFile = new File(BaseDirectory.get(BaseDirectory.XDG_CACHE_HOME), "myCache");
24 |
25 |
26 | ### The [Desktop Entry Specification](http://www.freedesktop.org/wiki/Specifications/desktop-entry-spec/)
27 |
28 | Use this to read and write desktop entries, with full validation.
29 |
30 | Here's how to read a desktop entry named `some.desktop`:
31 |
32 | BufferedReader reader = new BufferedReader(new FileReader("some.desktop"));
33 | DesktopEntry entry = new DesktopEntryReader(reader).read();
34 | System.out.println("Name: " + entry.get(DesktopEntry.KEY_NAME));
35 |
36 |
37 | Install
38 | -------
39 |
40 | Build and install using maven:
41 |
42 | $ mvn clean install
43 |
44 |
45 | If you are using maven, add a dependency to your `pom.xml` file:
46 |
47 |
48 | org.freedesktop
49 | xdg-java
50 | 0.0.1-SNAPSHOT
51 |
52 |
53 | If you are using gradle, add a runtime dependency to your
54 | `build.gradle` file:
55 |
56 | runtime "org.freedesktop:xdg-java:0.0.1-SNAPSHOT@jar"
57 |
58 | For Ivy, add the following dependency to your `ivy.xml` file:
59 |
60 |
61 |
62 |
63 | Contributing
64 | ------------
65 | See `CONTRIBUTING.md` for details.
66 |
67 |
68 | License
69 | -------
70 |
71 | LGPL v2. Please see the `LICENSE` file for details.
72 |
73 | © Copyright Omair Majid
74 |
--------------------------------------------------------------------------------
/src/test/java/org/freedesktop/IniStyleFileReaderTest.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertFalse;
5 | import static org.junit.Assert.assertNotNull;
6 | import static org.junit.Assert.assertTrue;
7 |
8 | import java.io.BufferedReader;
9 | import java.io.IOException;
10 | import java.io.StringReader;
11 |
12 | import org.junit.Test;
13 |
14 | public class IniStyleFileReaderTest {
15 |
16 | @Test
17 | public void testMinimalRead() throws IOException {
18 | String input = "[Desktop Entry]\n" +
19 | "Name=Foo\n";
20 | StringReader reader = new StringReader(input);
21 | IniStyleFileReader fileReader = new IniStyleFileReader(new BufferedReader(reader));
22 | IniStyleFile file = fileReader.read();
23 | assertNotNull(file);
24 |
25 | // check groups
26 | assertFalse(file.getGroupNames().isEmpty());
27 | assertEquals(1, file.getGroupNames().size());
28 | assertEquals("Desktop Entry", file.getGroupNames().toArray(new String[0])[0]);
29 |
30 | // check default group
31 | assertFalse(file.containsKey("Name"));
32 | assertEquals(null, file.get("Name"));
33 |
34 | // check group-values
35 | assertTrue(file.containsGroup("Desktop Entry"));
36 | assertTrue(file.containsKey("Desktop Entry", "Name"));
37 | assertEquals("Foo", file.get("Desktop Entry", "Name"));
38 | }
39 |
40 | @Test
41 | public void testBasicValidInput() throws IOException {
42 | String input = "" +
43 | "[Desktop Entry]\n" +
44 | "Version=1.0\n" +
45 | "Type=Application\n" +
46 | "Name=Foo Viewer\n" +
47 | "Comment=The best viewer for Foo objects available!\n" +
48 | "TryExec=fooview\n" +
49 | "Exec=fooview %F\n" +
50 | "Icon=fooview\n" +
51 | "MimeType=image/x-foo;\n" +
52 | "X-KDE-Library=libfooview\n" +
53 | "X-KDE-FactoryName=fooviewfactory\n" +
54 | "X-KDE-ServiceType=FooService\n";
55 | StringReader reader = new StringReader(input);
56 | IniStyleFileReader fileReader = new IniStyleFileReader(new BufferedReader(reader));
57 | IniStyleFile file = fileReader.read();
58 | assertNotNull(file);
59 |
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/test/java/org/freedesktop/DesktopEntryTest.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNotNull;
5 | import static org.junit.Assert.assertTrue;
6 |
7 | import java.io.BufferedReader;
8 | import java.io.IOException;
9 | import java.io.StringReader;
10 | import java.util.Locale;
11 |
12 | import org.junit.Test;
13 |
14 | public class DesktopEntryTest {
15 |
16 | @Test
17 | public void testConstructor() {
18 | DesktopEntry entry = new DesktopEntry();
19 | assertNotNull(entry);
20 | }
21 |
22 | @Test
23 | public void testConstructor2() {
24 | IniStyleFile source = new IniStyleFile();
25 | assertNotNull(source);
26 | DesktopEntry entry = new DesktopEntry(source);
27 | assertNotNull(entry);
28 | }
29 |
30 | public DesktopEntry createBasicDesktopEntry() throws IOException {
31 | String input = "" +
32 | "[Desktop Entry]\n" +
33 | "Version=1.0\n" +
34 | "Type=Application\n" +
35 | "Name=Foo Viewer\n" +
36 | "Name[en_CA]=en_CA Foo Viewer\n" +
37 | "NAME=Not the actual name - ignored\n" +
38 | "Comment=The best viewer for Foo objects available!\n" +
39 | "TryExec=fooview\n" +
40 | "Exec=fooview %F\n" +
41 | "Icon=fooview\n" +
42 | "MimeType=image/x-foo;\n" +
43 | "X-KDE-Library=libfooview\n" +
44 | "X-KDE-FactoryName=fooviewfactory\n" +
45 | "X-KDE-ServiceType=FooService\n" +
46 | "[Another Group]\n" +
47 | "Version=1.1\n" +
48 | "Icon=icon\n";
49 | DesktopEntry entry = new DesktopEntry(new IniStyleFileReader(new BufferedReader(new StringReader(input))).read());
50 | return entry;
51 | }
52 |
53 | @Test
54 | public void testBasicParsing() throws IOException {
55 | DesktopEntry entry = createBasicDesktopEntry();
56 | assertTrue(entry.containsGroup(DesktopEntry.GROUP_REQUIRED));
57 |
58 | assertTrue(entry.containsKey(DesktopEntry.KEY_VERSION));
59 | assertEquals("1.0", entry.get(DesktopEntry.KEY_VERSION));
60 | assertTrue(entry.containsKey(DesktopEntry.KEY_TYPE));
61 | assertEquals("Application", entry.get(DesktopEntry.KEY_TYPE));
62 | assertTrue(entry.containsKey(DesktopEntry.KEY_NAME));
63 | assertEquals("Foo Viewer", entry.get(DesktopEntry.KEY_NAME));
64 |
65 | assertTrue(entry.containsKey(DesktopEntry.KEY_COMMENT));
66 |
67 | assertTrue(entry.containsKey(DesktopEntry.KEY_EXEC));
68 | }
69 |
70 | @Test
71 | public void testExtensionsPresent() throws IOException {
72 | DesktopEntry entry = createBasicDesktopEntry();
73 | assertTrue(entry.containsKey(DesktopEntry.GROUP_REQUIRED, "X-KDE-Library"));
74 | assertTrue(entry.containsKey(DesktopEntry.GROUP_REQUIRED, "X-KDE-FactoryName"));
75 | assertTrue(entry.containsKey(DesktopEntry.GROUP_REQUIRED, "X-KDE-ServiceType"));
76 | }
77 |
78 | @Test
79 | public void testLocalizedNames() throws IOException {
80 | DesktopEntry entry = createBasicDesktopEntry();
81 | String value;
82 | value = entry.getLocalizedValue(DesktopEntry.KEY_NAME, new Locale("en", "CA"));
83 | assertEquals("en_CA Foo Viewer", value);
84 |
85 | value = entry.getLocalizedValue(DesktopEntry.KEY_NAME, new Locale("en"));
86 | assertEquals("Foo Viewer", value);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/org/freedesktop/DesktopEntry.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import java.util.Locale;
4 |
5 | /**
6 | * An interface to the Desktop Entry Specification.
7 | *
8 | * @version 1.1
9 | */
10 | public class DesktopEntry extends IniStyleFile {
11 |
12 | public static final String EXTENSION_FILE = ".desktop";
13 | public static final String EXTENSION_DIRECTORY = ".directory";
14 | public static final String FILE_CHARSET = "UTF-8";
15 |
16 | public static final String GROUP_REQUIRED = "Desktop Entry";
17 | public static final String GROUP_DEFAULT = GROUP_REQUIRED;
18 |
19 | public static final String KEY_TYPE = "Type";
20 | public static final String KEY_VERSION = "Version";
21 | public static final String KEY_NAME = "Name";
22 | public static final String KEY_GENERIC_NAME = "GenericName";
23 | public static final String KEY_NO_DISPLAY = "NoDisplay";
24 | public static final String KEY_COMMENT = "Comment";
25 | public static final String KEY_ICON = "Icon";
26 | public static final String KEY_HIDDEN = "Hidden";
27 | public static final String KEY_ONLY_SHOW_IN = "OnlyShowIn";
28 | public static final String KEY_NOT_SHOW_IN = "NotShowIn";
29 | public static final String KEY_DBUS_ACTIVATABLE = "DBusActivatable";
30 | public static final String KEY_TRY_EXEC = "TryExec";
31 | public static final String KEY_EXEC = "Exec";
32 | public static final String KEY_PATH = "Path";
33 | public static final String KEY_TERMINAL = "Terminal";
34 | public static final String KEY_ACTIONS = "Actions";
35 | public static final String KEY_MIME_TYPE = "MimeType";
36 | public static final String KEY_CATEGORIES = "Categories";
37 | public static final String KEY_KEYWORDS = "Keywords";
38 | public static final String KEY_STARTUP_NOTIFY = "StartupNotify";
39 | public static final String KEY_STARTUP_WM_CLASS = "StartupWMClass";
40 | public static final String KEY_URL = "URL";
41 |
42 | public DesktopEntry() {
43 | super(GROUP_DEFAULT);
44 |
45 | initKnownTypes();
46 | }
47 |
48 | public DesktopEntry(IniStyleFile source) {
49 | super(source, GROUP_DEFAULT);
50 |
51 | initKnownTypes();
52 | }
53 |
54 | private void initKnownTypes() {
55 | addKnownType(GROUP_DEFAULT, KEY_TYPE, ValueType.STRING);
56 | addKnownType(GROUP_DEFAULT, KEY_VERSION, ValueType.STRING);
57 | addKnownType(GROUP_DEFAULT, KEY_NAME, ValueType.LOCALE_STRING);
58 | addKnownType(GROUP_DEFAULT, KEY_GENERIC_NAME, ValueType.LOCALE_STRING);
59 | addKnownType(GROUP_DEFAULT, KEY_NO_DISPLAY, ValueType.BOOLEAN);
60 | addKnownType(GROUP_DEFAULT, KEY_COMMENT, ValueType.LOCALE_STRING);
61 | addKnownType(GROUP_DEFAULT, KEY_ICON, ValueType.LOCALE_STRING);
62 | addKnownType(GROUP_DEFAULT, KEY_HIDDEN, ValueType.BOOLEAN);
63 | addKnownType(GROUP_DEFAULT, KEY_ONLY_SHOW_IN, ValueType.STRINGS);
64 | addKnownType(GROUP_DEFAULT, KEY_NOT_SHOW_IN, ValueType.STRINGS);
65 | addKnownType(GROUP_DEFAULT, KEY_DBUS_ACTIVATABLE, ValueType.BOOLEAN);
66 | addKnownType(GROUP_DEFAULT, KEY_TRY_EXEC, ValueType.STRING);
67 | addKnownType(GROUP_DEFAULT, KEY_EXEC, ValueType.STRING);
68 | addKnownType(GROUP_DEFAULT, KEY_PATH, ValueType.STRING);
69 | addKnownType(GROUP_DEFAULT, KEY_TERMINAL, ValueType.BOOLEAN);
70 | addKnownType(GROUP_DEFAULT, KEY_ACTIONS, ValueType.STRINGS);
71 | addKnownType(GROUP_DEFAULT, KEY_MIME_TYPE, ValueType.STRINGS);
72 | addKnownType(GROUP_DEFAULT, KEY_CATEGORIES, ValueType.STRINGS);
73 | addKnownType(GROUP_DEFAULT, KEY_KEYWORDS, ValueType.LOCALE_STRING);
74 | addKnownType(GROUP_DEFAULT, KEY_STARTUP_NOTIFY, ValueType.BOOLEAN);
75 | addKnownType(GROUP_DEFAULT, KEY_STARTUP_WM_CLASS, ValueType.STRING);
76 | addKnownType(GROUP_DEFAULT, KEY_URL, ValueType.STRING);
77 | }
78 |
79 | public String getLocalizedValue(String key, Locale locale) {
80 | return getLocalizedValue(defaultGroup, key, locale);
81 | }
82 |
83 | public String getLocalizedValue(String group, String key, Locale locale) {
84 | String suffix = locale.getLanguage();
85 | if (!locale.getCountry().equals("")) {
86 | suffix = suffix + "_" + locale.getCountry();
87 | }
88 | String result;
89 |
90 | result = get(group, key + "[" + suffix + "]");
91 | if (result != null) {
92 | return result;
93 | }
94 |
95 | suffix = locale.getLanguage();
96 | result = get(group, key + "[" + suffix + "]");
97 | if (result != null) {
98 | return result;
99 | }
100 |
101 | result = get(group, key);
102 | return result;
103 | }
104 |
105 | @Override
106 | protected void checkAllValid() {
107 | super.checkAllValid();
108 | // TODO add more
109 | }
110 |
111 | @Override
112 | protected void checkValidKeyValue(String group, String key, String value) {
113 | super.checkValidKeyValue(group, key, value);
114 |
115 | if (group.equals(GROUP_DEFAULT) && key.equals(KEY_EXEC)) {
116 | checkValidExecValue(value);
117 | }
118 | }
119 |
120 | private void checkValidExecValue(String value) {
121 | // TODO implement
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/org/freedesktop/BaseDirectory.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import java.io.File;
4 | import java.util.Map;
5 |
6 | /**
7 | * A simple way to utilize the Base Directory Specification
8 | *
9 | * @version 0.7
10 | */
11 | public class BaseDirectory {
12 |
13 | /**
14 | * base directory relative to which user-specific non-essential (cached)
15 | * data should be written
16 | */
17 | public static final String XDG_CACHE_HOME = "XDG_CACHE_HOME";
18 |
19 | /**
20 | * base directory relative to which user-specific configuration files should
21 | * be written
22 | */
23 | public static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
24 |
25 | /**
26 | * set of preference ordered base directories relative to which
27 | * configuration files should be searched. The directories are separated by
28 | * by {@link File#pathSeparator}
29 | */
30 | public static final String XDG_CONFIG_DIRS = "XDG_CONFIG_DIRS";
31 |
32 | /**
33 | * base directory relative to which user-specific data files should be
34 | * written
35 | */
36 | public static final String XDG_DATA_HOME = "XDG_DATA_HOME";
37 |
38 | /**
39 | * set of preference ordered base directories relative to which data files
40 | * should be searched. Directories are separated by {@link File#pathSeparator}
41 | */
42 | public static final String XDG_DATA_DIRS = "XDG_DATA_DIRS";
43 |
44 | /**
45 | * base directory relative to which user-specific runtime files and other
46 | * file objects should be placed. May be {@code null}.
47 | */
48 | public static final String XDG_RUNTIME_DIR = "XDG_RUNTIME_DIR";
49 |
50 | private static Map environment = System.getenv();
51 |
52 | /**
53 | * Get the base directory or set of base directories defined by the
54 | * specification.
55 | *
56 | * @param name one of the XDG_* constants defined in {@link BaseDirectory}
57 | * @return the directory (or the set of directories) as a string. For the
58 | * exact return type, see the docs for the constant. May be null if the
59 | * standard does not specify a default and the associated environment
60 | * variable(s) are undefined.
61 | */
62 | public static String get(String name) {
63 | switch (name) {
64 | case XDG_CACHE_HOME:
65 | return getCacheHome();
66 | case XDG_CONFIG_HOME:
67 | return getConfigHome();
68 | case XDG_CONFIG_DIRS:
69 | return getConfigDirs();
70 | case XDG_DATA_HOME:
71 | return getDataHome();
72 | case XDG_DATA_DIRS:
73 | return getDataDirs();
74 | case XDG_RUNTIME_DIR:
75 | return getRuntimeDir();
76 | default:
77 | return null;
78 | }
79 | }
80 |
81 | /** This is meant only for testing */
82 | static void setEnvironment(Map env) {
83 | environment = env;
84 | }
85 |
86 | private static String getCacheHome() {
87 | String value = environment.get(XDG_CACHE_HOME);
88 | if (value == null || value.trim().length() == 0) {
89 | String XDG_CACHE_HOME_DEFAULT = environment.get("HOME") + File.separator + ".cache";
90 | value = XDG_CACHE_HOME_DEFAULT;
91 | }
92 | return value;
93 | }
94 |
95 | private static String getConfigHome() {
96 | String value = environment.get(XDG_CONFIG_HOME);
97 | if (value == null || value.trim().length() == 0) {
98 | String XDG_CONFIG_HOME_DEFAULT = environment.get("HOME") + File.separator + ".config";
99 | value = XDG_CONFIG_HOME_DEFAULT;
100 | }
101 | return value;
102 | }
103 |
104 | private static String getConfigDirs() {
105 | String value = environment.get(XDG_CONFIG_DIRS);
106 | if (value == null || value.trim().length() == 0) {
107 | String XDG_CONFIG_DIRS_DEFAULT = File.separator + "etc" + File.separator + "xdg";
108 | value = XDG_CONFIG_DIRS_DEFAULT;
109 | }
110 | return value;
111 | }
112 |
113 | private static String getDataHome() {
114 | String value = environment.get(XDG_DATA_HOME);
115 | if (value == null || value.trim().length() == 0) {
116 | String XDG_DATA_HOME_DEFAULT = environment.get("HOME") +
117 | File.separator + ".local" + File.separator + "share";
118 | value = XDG_DATA_HOME_DEFAULT;
119 | }
120 | return value;
121 | }
122 |
123 | private static String getDataDirs() {
124 | String value = environment.get(XDG_DATA_DIRS);
125 | if (value == null || value.trim().length() == 0) {
126 | String XDG_DATA_DIRS_DEFAULT = File.separator + "usr" + File.separator + "local" + File.separator + "share" + File.separator;
127 | XDG_DATA_DIRS_DEFAULT = XDG_DATA_DIRS_DEFAULT + File.pathSeparator;
128 | XDG_DATA_DIRS_DEFAULT = XDG_DATA_DIRS_DEFAULT + File.separator + "usr" + File.separator + "share" + File.separator;
129 | value = XDG_DATA_DIRS_DEFAULT;
130 | }
131 | return value;
132 | }
133 |
134 | private static String getRuntimeDir() {
135 | String value = environment.get(XDG_RUNTIME_DIR);
136 | return value;
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/src/test/java/org/freedesktop/BaseDirectoryTest.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNotNull;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | import org.junit.After;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 |
13 | public class BaseDirectoryTest {
14 |
15 | private Map buildCustomEnvironment() {
16 | Map environment = new HashMap<>();
17 | environment.put("HOME", "${HOME}");
18 | return environment;
19 | }
20 |
21 | @Before
22 | public void setUp() {
23 | BaseDirectory.setEnvironment(System.getenv());
24 | }
25 |
26 | @After
27 | public void tearDown() {
28 | BaseDirectory.setEnvironment(System.getenv());
29 | }
30 |
31 | @Test
32 | public void testUnknown() {
33 | String unknownVar = BaseDirectory.get("UNKNOWN_AND_IMPOSSIBLE_XDG_VARIABLE");
34 | assertEquals(null, unknownVar);
35 | }
36 |
37 | @Test
38 | public void testDataHomeWithEnvSet() {
39 | Map env = buildCustomEnvironment();
40 | env.put("XDG_DATA_HOME", "${XDG_DATA_HOME}");
41 | BaseDirectory.setEnvironment(env);
42 | String dataHome = BaseDirectory.get(BaseDirectory.XDG_DATA_HOME);
43 | assertNotNull(dataHome);
44 | assertEquals("${XDG_DATA_HOME}", dataHome);
45 | }
46 |
47 | @Test
48 | public void testDataHomeDefault() {
49 | BaseDirectory.setEnvironment(buildCustomEnvironment());
50 | String dataHome = BaseDirectory.get(BaseDirectory.XDG_DATA_HOME);
51 | assertNotNull(dataHome);
52 | assertEquals("${HOME}/.local/share", dataHome);
53 | }
54 |
55 | @Test
56 | public void testConfigHomeWithEnvSet() {
57 | Map env = buildCustomEnvironment();
58 | env.put("XDG_CONFIG_HOME", "${XDG_CONFIG_HOME}");
59 | BaseDirectory.setEnvironment(env);
60 | String dir = BaseDirectory.get(BaseDirectory.XDG_CONFIG_HOME);
61 | assertNotNull(dir);
62 | assertEquals("${XDG_CONFIG_HOME}", dir);
63 | }
64 |
65 | @Test
66 | public void testConfigHomeDefault() {
67 | BaseDirectory.setEnvironment(buildCustomEnvironment());
68 | String configHome = BaseDirectory.get(BaseDirectory.XDG_CONFIG_HOME);
69 | assertNotNull(configHome);
70 | assertEquals("${HOME}/.config", configHome);
71 | }
72 |
73 | @Test
74 | public void testDataDirsWithEnvSet() {
75 | Map env = buildCustomEnvironment();
76 | env.put("XDG_DATA_DIRS", "${XDG_DATA_DIRS}");
77 | BaseDirectory.setEnvironment(env);
78 | String dataDirs = BaseDirectory.get(BaseDirectory.XDG_DATA_DIRS);
79 | assertNotNull(dataDirs);
80 | assertEquals("${XDG_DATA_DIRS}", dataDirs);
81 | }
82 |
83 | @Test
84 | public void testDataDirsDefault() {
85 | BaseDirectory.setEnvironment(buildCustomEnvironment());
86 | String dataDirs = BaseDirectory.get(BaseDirectory.XDG_DATA_DIRS);
87 | assertNotNull(dataDirs);
88 | assertEquals("/usr/local/share/:/usr/share/", dataDirs);
89 | }
90 |
91 | @Test
92 | public void testConfigDirsWithEnvSet() {
93 | Map env = buildCustomEnvironment();
94 | env.put("XDG_CONFIG_DIRS", "${XDG_CONFIG_DIRS}");
95 | BaseDirectory.setEnvironment(env);
96 | String configDirs = BaseDirectory.get(BaseDirectory.XDG_CONFIG_DIRS);
97 | assertNotNull(configDirs);
98 | assertEquals("${XDG_CONFIG_DIRS}", configDirs);
99 | }
100 |
101 | @Test
102 | public void testConfigDirsDefault() {
103 | BaseDirectory.setEnvironment(buildCustomEnvironment());
104 | String configDirs = BaseDirectory.get(BaseDirectory.XDG_CONFIG_DIRS);
105 | assertNotNull(configDirs);
106 | assertEquals("/etc/xdg", configDirs);
107 |
108 | }
109 |
110 | @Test
111 | public void testCacheHomeWithEnvSet() {
112 | Map env = buildCustomEnvironment();
113 | env.put("XDG_CACHE_HOME", "${XDG_CACHE_HOME}");
114 | BaseDirectory.setEnvironment(env);
115 | String dir = BaseDirectory.get(BaseDirectory.XDG_CACHE_HOME);
116 | assertNotNull(dir);
117 | assertEquals("${XDG_CACHE_HOME}", dir);
118 | }
119 |
120 | @Test
121 | public void testCacheHomeDefault() {
122 | BaseDirectory.setEnvironment(buildCustomEnvironment());
123 | String dir = BaseDirectory.get(BaseDirectory.XDG_CACHE_HOME);
124 | assertNotNull(dir);
125 | assertEquals("${HOME}/.cache", dir);
126 | }
127 |
128 | @Test
129 | public void testRuntimeDirWithEnvSet() {
130 | Map env = buildCustomEnvironment();
131 | env.put("XDG_RUNTIME_DIR", "${XDG_RUNTIME_DIR}");
132 | BaseDirectory.setEnvironment(env);
133 | String runtimeDir = BaseDirectory.get(BaseDirectory.XDG_RUNTIME_DIR);
134 | assertNotNull(runtimeDir);
135 | assertEquals("${XDG_RUNTIME_DIR}", runtimeDir);
136 | }
137 |
138 | @Test
139 | public void testRuntimeDirWithoutEnvSet() {
140 | BaseDirectory.setEnvironment(buildCustomEnvironment());
141 | String runtimeDir = BaseDirectory.get(BaseDirectory.XDG_RUNTIME_DIR);
142 | assertEquals(null, runtimeDir);
143 | }
144 |
145 | @Test
146 | public void testRuntimeDirMatchesSystemEnv() {
147 | BaseDirectory.setEnvironment(System.getenv());
148 | String runtimeDir = BaseDirectory.get(BaseDirectory.XDG_RUNTIME_DIR);
149 | assertEquals(System.getenv("XDG_RUNTIME_DIR"), runtimeDir);
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/org/freedesktop/IniStyleFile.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.Map.Entry;
6 | import java.util.Set;
7 | import java.util.TreeSet;
8 |
9 | public class IniStyleFile {
10 |
11 | public enum ValueType {
12 | LOCALE_STRING,
13 | STRINGS,
14 | STRING,
15 | INTEGERS,
16 | INTEGER,
17 | BOOLEAN,
18 | POINTS,
19 | }
20 |
21 | protected final Map> data;
22 | protected final Map> knownTypes;
23 |
24 | protected final String defaultGroup;
25 |
26 | public IniStyleFile() {
27 | this("default");
28 | }
29 |
30 | public IniStyleFile(String defaultGroup) {
31 | this.defaultGroup = defaultGroup;
32 | this.data = new HashMap<>();
33 | this.knownTypes = new HashMap<>();
34 | }
35 |
36 | public IniStyleFile(IniStyleFile other) {
37 | this(other, other.defaultGroup);
38 | }
39 |
40 | public IniStyleFile(IniStyleFile other, String defaultGroup) {
41 | // FIXME deep copy the data
42 | this.defaultGroup = defaultGroup;
43 | this.data = other.data;
44 | this.knownTypes = other.knownTypes;
45 | checkAllValid();
46 | }
47 |
48 | public void addGroup(String groupName) {
49 | checkValidGroupName(groupName);
50 |
51 | if (data.containsKey(groupName)) {
52 | return;
53 | }
54 |
55 | data.put(groupName, new HashMap());
56 |
57 | }
58 |
59 | public void add(String group, String key, String value) {
60 | checkValidGroupName(group);
61 | checkValidKeyValue(group, key, value);
62 |
63 | if (!(data.containsKey(group))) {
64 | addGroup(group);
65 | }
66 |
67 | Map groupData = data.get(group);
68 |
69 | groupData.put(key, value);
70 | }
71 |
72 | /**
73 | * Add a key/value pair to the default group
74 | */
75 | public void add(String key, String value) {
76 | add(defaultGroup, key, value);
77 | }
78 |
79 | public boolean containsGroup(String group) {
80 | return data.containsKey(group);
81 | }
82 |
83 | public boolean containsKey(String key) {
84 | return containsKey(defaultGroup, key);
85 | }
86 |
87 | public boolean containsKey(String group, String key) {
88 | if (!data.containsKey(group)) {
89 | return false;
90 | }
91 | return data.get(group).containsKey(key);
92 | }
93 |
94 | public String get(String group, String key) {
95 | if (data.containsKey(group)) {
96 | Map groupData = data.get(group);
97 | if (groupData.containsKey(key)) {
98 | return groupData.get(key);
99 | }
100 | }
101 | return null;
102 | }
103 |
104 | public String get(String key) {
105 | return get(defaultGroup, key);
106 | }
107 |
108 | protected String[] getAsList(String key) {
109 | String value = get(key);
110 | if (value == null) {
111 | return null;
112 | }
113 | String[] directories = value.split(",");
114 | // TODO check for empty elements and remove them
115 | return directories;
116 | }
117 |
118 | public void remove(String key) {
119 | remove(defaultGroup, key);
120 | }
121 |
122 | public void remove(String group, String key) {
123 | if (!data.containsKey(group)) {
124 | throw new IllegalArgumentException();
125 | }
126 |
127 | if (!data.get(group).containsKey(key)) {
128 | throw new IllegalArgumentException();
129 | }
130 |
131 | data.get(group).remove(key);
132 | }
133 |
134 | public String getDefaultGroupName() {
135 | return defaultGroup;
136 | }
137 |
138 | public Set getGroupNames() {
139 | return new TreeSet<>(data.keySet());
140 | }
141 |
142 | public Map getGroup(String group) {
143 | if (!data.containsKey(group)) {
144 | return null;
145 | }
146 |
147 | return new HashMap<>(data.get(group));
148 | }
149 |
150 | public void removeGroup(String groupName) {
151 | if (!data.containsKey(groupName)) {
152 | throw new IllegalArgumentException();
153 | }
154 |
155 | data.remove(groupName);
156 | }
157 |
158 | protected void checkAllValid() {
159 | for (Entry> group : data.entrySet()) {
160 | String groupName = group.getKey();
161 | checkValidGroupName(groupName);
162 | for (Entry entry : group.getValue().entrySet()) {
163 | checkValidKeyValue(groupName, entry.getKey(), entry.getValue());
164 | }
165 | }
166 | }
167 |
168 | protected void checkValidGroupName(String group) {
169 | if (group == null) {
170 | throw new IllegalArgumentException("Group name is null");
171 | }
172 | if (group.trim().length() == 0) {
173 | throw new IllegalArgumentException("Group name is empty");
174 | }
175 | if (group.contains("[") || group.contains("]")) {
176 | throw new IllegalArgumentException("Group name contains invalid character");
177 | }
178 | if (group.contains("\n")) {
179 | throw new IllegalArgumentException("Group name contains a newline");
180 | }
181 | }
182 |
183 | protected void checkValidKeyValue(String group, String key, String value) {
184 | if (key == null || key.contains("\n") || key.contains("=") || key.trim().length() == 0) {
185 | throw new IllegalArgumentException();
186 | }
187 | if (value == null || value.contains("\n")) {
188 | throw new IllegalArgumentException();
189 | }
190 | if (knownTypes.containsKey(group)) {
191 | Map knownGroup = knownTypes.get(group);
192 | ValueType valueType = knownGroup == null ? null : knownGroup.get(key);
193 | if (valueType == ValueType.LOCALE_STRING) {
194 | // pass
195 | // FIXME newlines?
196 | } else {
197 | if (key.contains("[") || key.contains("]")) {
198 | throw new IllegalArgumentException("non-locale key contains '[' or ']'");
199 | }
200 | }
201 |
202 | if (valueType == ValueType.BOOLEAN) {
203 | if (!isValidBoolean(value)) {
204 | throw new IllegalArgumentException("boolean is not true or false");
205 | }
206 | }
207 |
208 | if (valueType == ValueType.INTEGER) {
209 | if (!isValidNumber(value)) {
210 | throw new IllegalArgumentException("type numeric is not a valid number");
211 | }
212 | }
213 |
214 | }
215 |
216 | }
217 |
218 | protected boolean isValidBoolean(String value) {
219 | return value.toLowerCase().equals("true") || value.toLowerCase().equals("false");
220 | }
221 |
222 | protected boolean isValidNumber(String value) {
223 | try {
224 | Double.parseDouble(value);
225 | return true;
226 | } catch (NumberFormatException notDouble) {
227 | return false;
228 | }
229 |
230 | }
231 |
232 | public void addKnownType(String group, String key, ValueType valueType) {
233 | Map groupType = knownTypes.get(group);
234 | if (groupType == null) {
235 | groupType = new HashMap<>();
236 | knownTypes.put(group, groupType);
237 | }
238 | groupType.put(key, valueType);
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/test/java/org/freedesktop/IniStyleFileTest.java:
--------------------------------------------------------------------------------
1 | package org.freedesktop;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertFalse;
5 | import static org.junit.Assert.assertNotNull;
6 | import static org.junit.Assert.assertTrue;
7 |
8 | import java.util.Map;
9 | import java.util.Set;
10 |
11 | import org.junit.Test;
12 |
13 | public class IniStyleFileTest {
14 |
15 | @Test
16 | public void testConstructor() {
17 | IniStyleFile entry = new IniStyleFile();
18 | assertNotNull(entry);
19 | }
20 |
21 | @Test
22 | public void testConstructor2() {
23 | IniStyleFile file1 = new IniStyleFile();
24 | file1.add("test", "test", "test");
25 |
26 | IniStyleFile file2 = new IniStyleFile(file1);
27 | assertTrue(file2.containsKey("test", "test"));
28 | assertEquals("test", file2.get("test", "test"));
29 | }
30 |
31 | @Test
32 | public void testWithDefaultGroup() {
33 | IniStyleFile file = new IniStyleFile("default-group");
34 | file.add("key", "value");
35 | }
36 |
37 | @Test(expected = IllegalArgumentException.class)
38 | public void testAddNullGroupWithKeyValue() {
39 | IniStyleFile file = new IniStyleFile();
40 | file.add(null, "key", "value");
41 | }
42 |
43 | /*
44 | * Add group
45 | */
46 |
47 | @Test
48 | public void testAddGroup() {
49 | IniStyleFile file = new IniStyleFile();
50 | file.addGroup("test");
51 | assertTrue(file.containsGroup("test"));
52 | }
53 |
54 | @Test(expected = IllegalArgumentException.class)
55 | public void testAddGroupWithNewLine() {
56 | IniStyleFile file = new IniStyleFile();
57 | file.addGroup("test\nandmore");
58 | }
59 |
60 | @Test(expected = IllegalArgumentException.class)
61 | public void testAddNullGroup() {
62 | IniStyleFile file = new IniStyleFile();
63 | file.addGroup(null);
64 | }
65 |
66 | @Test
67 | public void testAddGroupMultipleTimes() {
68 | IniStyleFile file = new IniStyleFile();
69 | file.addGroup("test");
70 | assertTrue(file.containsGroup("test"));
71 | file.addGroup("test");
72 | assertTrue(file.containsGroup("test"));
73 | }
74 |
75 | @Test(expected = IllegalArgumentException.class)
76 | public void testAddInvalidGroupName1() {
77 | IniStyleFile entry = new IniStyleFile();
78 | entry.add("[", "foo", "bar");
79 | }
80 |
81 | @Test(expected = IllegalArgumentException.class)
82 | public void testAddInvalidGroupName2() {
83 | IniStyleFile entry = new IniStyleFile();
84 | entry.add("]", "foo", "bar");
85 | }
86 |
87 | @Test(expected = IllegalArgumentException.class)
88 | public void testAddInvalidGroupName3() {
89 | IniStyleFile entry = new IniStyleFile();
90 | entry.add(" ", "foo", "bar");
91 | }
92 |
93 | @Test(expected = IllegalArgumentException.class)
94 | public void testAddInvalidGroupName4() {
95 | IniStyleFile file = new IniStyleFile();
96 | file.add("\n", "foo", "bar");
97 | }
98 |
99 | /*
100 | * Contains group
101 | */
102 |
103 | @Test
104 | public void testContainsGroup() {
105 | IniStyleFile file = new IniStyleFile("default-group");
106 | file.add("key", "value");
107 | assertTrue(file.containsGroup("default-group"));
108 | }
109 |
110 | @Test
111 | public void testContainsNonExistingGroup() {
112 | IniStyleFile file = new IniStyleFile("default-group");
113 | assertFalse(file.containsGroup("some-other-group"));
114 | }
115 |
116 | /*
117 | * Get group
118 | */
119 |
120 | @Test
121 | public void testGetGroup() {
122 | IniStyleFile file = new IniStyleFile("default-group");
123 | file.add("key", "value");
124 | Map group = file.getGroup("default-group");
125 | assertEquals(1, group.entrySet().size());
126 | }
127 |
128 | @Test
129 | public void testGetGroupMissing() {
130 | IniStyleFile file = new IniStyleFile("default-group");
131 | Map group = file.getGroup("some-random-group");
132 | assertEquals(null, group);
133 | }
134 |
135 | @Test
136 | public void testGetDefaultGroupName() {
137 | IniStyleFile file = new IniStyleFile("default-group");
138 | String groupName = file.getDefaultGroupName();
139 | assertEquals("default-group", groupName);
140 | }
141 |
142 | @Test
143 | public void testGetDefaultGroupMissing() {
144 | IniStyleFile file = new IniStyleFile("default-group");
145 | String defaultGroup = file.getDefaultGroupName();
146 | Map group = file.getGroup(defaultGroup);
147 | assertEquals(null, group);
148 | }
149 |
150 | @Test
151 | public void testGetGroupNames() {
152 | IniStyleFile file = new IniStyleFile("default-group");
153 | file.add("key", "value");
154 | Set groupNames = file.getGroupNames();
155 | assertFalse(groupNames.isEmpty());
156 | assertEquals(1, groupNames.size());
157 | assertEquals("default-group", groupNames.toArray(new String[0])[0]);
158 | }
159 |
160 | /*
161 | * Remove group
162 | */
163 |
164 | @Test
165 | public void testRemoveGroup() {
166 | IniStyleFile file = new IniStyleFile("default-group");
167 | file.add("new-group", "key", "value");
168 | assertTrue(file.containsKey("new-group", "key"));
169 | file.removeGroup("new-group");
170 | assertFalse(file.containsGroup("new-group"));
171 | }
172 |
173 | @Test(expected = IllegalArgumentException.class)
174 | public void testRemoveNullGroup() {
175 | IniStyleFile file = new IniStyleFile("default-group");
176 | assertFalse(file.containsGroup(null));
177 | file.removeGroup(null);
178 | }
179 |
180 | /*
181 | * Add key/value
182 | */
183 |
184 | @Test
185 | public void testAddKeyValue() {
186 | IniStyleFile file = new IniStyleFile("default-group");
187 | file.add("key", "value");
188 | assertEquals("value", file.get("default-group", "key"));
189 | }
190 |
191 | @Test(expected = IllegalArgumentException.class)
192 | public void testAddInvalidKeyName() {
193 | IniStyleFile file = new IniStyleFile();
194 | file.add("test", "=", "value");
195 | }
196 |
197 | @Test(expected = IllegalArgumentException.class)
198 | public void testAddEmptyKeyName() {
199 | IniStyleFile file = new IniStyleFile();
200 | file.add("test", "", "value");
201 | }
202 |
203 | @Test
204 | public void testAddEmptyValue() {
205 | IniStyleFile file = new IniStyleFile();
206 | file.add("test", "key", "");
207 | }
208 |
209 | @Test(expected = IllegalArgumentException.class)
210 | public void testAddNullKeyName() {
211 | IniStyleFile file = new IniStyleFile();
212 | file.add(null, "dummy");
213 | }
214 |
215 | @Test(expected = IllegalArgumentException.class)
216 | public void testAddNewLines1() {
217 | IniStyleFile entry = new IniStyleFile();
218 | entry.add("\n", "value");
219 | }
220 |
221 | @Test(expected = IllegalArgumentException.class)
222 | public void testAddNewLines2() {
223 | IniStyleFile entry = new IniStyleFile();
224 | entry.add("key", "\n");
225 | }
226 |
227 | @Test(expected = IllegalArgumentException.class)
228 | public void testAddNewLines3() {
229 | IniStyleFile entry = new IniStyleFile();
230 | entry.add("key\nkey", "value");
231 | }
232 |
233 | @Test(expected = IllegalArgumentException.class)
234 | public void testAddNewLines4() {
235 | IniStyleFile entry = new IniStyleFile();
236 | entry.add("key", "value\nvalue");
237 | }
238 |
239 | @Test
240 | public void testAddingKnownLocaleStringValueType() {
241 | IniStyleFile entry = new IniStyleFile();
242 | entry.addKnownType("FOO", "BAR", IniStyleFile.ValueType.LOCALE_STRING);
243 | entry.add("FOO", "BAR", "[en_US]");
244 | }
245 |
246 | @Test
247 | public void testAddingKnownStringsValueType() {
248 | IniStyleFile entry = new IniStyleFile();
249 | entry.addKnownType("FOO", "BAR", IniStyleFile.ValueType.STRINGS);
250 | entry.add("FOO", "BAR", "GNOME:UNITY");
251 | }
252 |
253 | @Test
254 | public void testAddingKnownStringValueType() {
255 | IniStyleFile entry = new IniStyleFile();
256 | entry.addKnownType("FOO", "BAR", IniStyleFile.ValueType.STRING);
257 | entry.add("FOO", "BAR", "foo");
258 | }
259 |
260 | // TODO
261 | // @Test
262 | // public void testAddingKnownIntegersValueType() {
263 | // }
264 |
265 | // TODO
266 | // @Test(expected = IllegalArgumentException.class)
267 | // public void testKnownValueTypeRejectsBadIntegersValueType() {
268 | // }
269 |
270 | @Test
271 | public void testAddingKnownIntegerValueType() {
272 | IniStyleFile entry = new IniStyleFile();
273 | entry.addKnownType("FOO", "BAR", IniStyleFile.ValueType.INTEGER);
274 | entry.add("FOO", "BAR", "10");
275 | }
276 |
277 | @Test(expected = IllegalArgumentException.class)
278 | public void testKnownValueTypeRejectsBadIntegerValueType() {
279 | IniStyleFile entry = new IniStyleFile();
280 | entry.addKnownType("FOO", "BAR", IniStyleFile.ValueType.INTEGER);
281 | entry.add("FOO", "BAR", "ten");
282 | }
283 |
284 | @Test
285 | public void testAddingKnownBooleanValueType() {
286 | IniStyleFile entry = new IniStyleFile();
287 | entry.addKnownType("FOO", "BAR", IniStyleFile.ValueType.BOOLEAN);
288 | entry.add("FOO", "BAR", "true");
289 | }
290 |
291 | @Test(expected = IllegalArgumentException.class)
292 | public void testKnownValueTypeRejectsBadBooleanValueType() {
293 | IniStyleFile entry = new IniStyleFile();
294 | entry.addKnownType("FOO", "BAR", IniStyleFile.ValueType.BOOLEAN);
295 | entry.add("FOO", "BAR", "falsy");
296 | }
297 |
298 | // TODO
299 | // @Test
300 | // public void testAddingKnownPointsValueType() {
301 | // }
302 |
303 | // TODO
304 | // @Test(expected = IllegalArgumentException.class)
305 | // public void testKnownValueTypeRejectsBadPointsValueType() {
306 | // }
307 |
308 | /*
309 | * Contains key/value
310 | */
311 |
312 | @Test
313 | public void testContainsKeyValue() {
314 | IniStyleFile file = new IniStyleFile("default-group");
315 | file.add("key", "value");
316 | assertTrue(file.containsKey("default-group", "key"));
317 | }
318 |
319 | @Test
320 | public void testContainsMissingKey() {
321 | IniStyleFile file = new IniStyleFile("default-group");
322 | assertFalse(file.containsGroup(null));
323 | assertFalse(file.containsKey("group", "key"));
324 | }
325 |
326 | /*
327 | * Get key/value
328 | */
329 |
330 | @Test
331 | public void testGetKeyValueFromDefaultGroup() {
332 | IniStyleFile file = new IniStyleFile("default-group");
333 | file.add("key", "value");
334 | assertEquals("value", file.get("key"));
335 | }
336 |
337 | @Test
338 | public void testGetNonExistingKeyValueFromDefaultGroup() {
339 | IniStyleFile file = new IniStyleFile("default-group");
340 | assertEquals(null, file.get("key"));
341 | }
342 |
343 | @Test
344 | public void testGetKeyValueFromGroup() {
345 | IniStyleFile file = new IniStyleFile();
346 | file.add("group", "key", "value");
347 | assertEquals("value", file.get("group", "key"));
348 | }
349 |
350 | @Test
351 | public void testGetNonExistingKeyValueFromGroup() {
352 | IniStyleFile file = new IniStyleFile();
353 | assertEquals(null, file.get("group", "key"));
354 | }
355 |
356 | /*
357 | * Remove key/value
358 | */
359 |
360 | @Test(expected = IllegalArgumentException.class)
361 | public void testRemoveNonExistingKey() {
362 | IniStyleFile file = new IniStyleFile();
363 | file.remove("some-non-existing-key");
364 | }
365 |
366 | @Test(expected = IllegalArgumentException.class)
367 | public void testRemoveNonExistingKey2() {
368 | IniStyleFile file = new IniStyleFile("default-group");
369 | file.remove("default-group", "some-non-existing-key");
370 | }
371 |
372 | @Test(expected = IllegalArgumentException.class)
373 | public void testRemoveNonExistingKey3() {
374 | IniStyleFile file = new IniStyleFile("default-group");
375 | file.add("default-group", "another-key", "another-value");
376 | file.remove("default-group", "some-non-existing-key");
377 | }
378 |
379 | @Test
380 | public void testRemoveExistingKey() {
381 | IniStyleFile file = new IniStyleFile();
382 | file.add("key", "value");
383 | assertTrue(file.containsKey("key"));
384 | file.remove("key");
385 | assertFalse(file.containsKey("key"));
386 | }
387 | }
388 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 2.1, February 1999
3 |
4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | [This is the first released version of the Lesser GPL. It also counts
10 | as the successor of the GNU Library Public License, version 2, hence
11 | the version number 2.1.]
12 |
13 | Preamble
14 |
15 | The licenses for most software are designed to take away your
16 | freedom to share and change it. By contrast, the GNU General Public
17 | Licenses are intended to guarantee your freedom to share and change
18 | free software--to make sure the software is free for all its users.
19 |
20 | This license, the Lesser General Public License, applies to some
21 | specially designated software packages--typically libraries--of the
22 | Free Software Foundation and other authors who decide to use it. You
23 | can use it too, but we suggest you first think carefully about whether
24 | this license or the ordinary General Public License is the better
25 | strategy to use in any particular case, based on the explanations below.
26 |
27 | When we speak of free software, we are referring to freedom of use,
28 | not price. Our General Public Licenses are designed to make sure that
29 | you have the freedom to distribute copies of free software (and charge
30 | for this service if you wish); that you receive source code or can get
31 | it if you want it; that you can change the software and use pieces of
32 | it in new free programs; and that you are informed that you can do
33 | these things.
34 |
35 | To protect your rights, we need to make restrictions that forbid
36 | distributors to deny you these rights or to ask you to surrender these
37 | rights. These restrictions translate to certain responsibilities for
38 | you if you distribute copies of the library or if you modify it.
39 |
40 | For example, if you distribute copies of the library, whether gratis
41 | or for a fee, you must give the recipients all the rights that we gave
42 | you. You must make sure that they, too, receive or can get the source
43 | code. If you link other code with the library, you must provide
44 | complete object files to the recipients, so that they can relink them
45 | with the library after making changes to the library and recompiling
46 | it. And you must show them these terms so they know their rights.
47 |
48 | We protect your rights with a two-step method: (1) we copyright the
49 | library, and (2) we offer you this license, which gives you legal
50 | permission to copy, distribute and/or modify the library.
51 |
52 | To protect each distributor, we want to make it very clear that
53 | there is no warranty for the free library. Also, if the library is
54 | modified by someone else and passed on, the recipients should know
55 | that what they have is not the original version, so that the original
56 | author's reputation will not be affected by problems that might be
57 | introduced by others.
58 |
59 | Finally, software patents pose a constant threat to the existence of
60 | any free program. We wish to make sure that a company cannot
61 | effectively restrict the users of a free program by obtaining a
62 | restrictive license from a patent holder. Therefore, we insist that
63 | any patent license obtained for a version of the library must be
64 | consistent with the full freedom of use specified in this license.
65 |
66 | Most GNU software, including some libraries, is covered by the
67 | ordinary GNU General Public License. This license, the GNU Lesser
68 | General Public License, applies to certain designated libraries, and
69 | is quite different from the ordinary General Public License. We use
70 | this license for certain libraries in order to permit linking those
71 | libraries into non-free programs.
72 |
73 | When a program is linked with a library, whether statically or using
74 | a shared library, the combination of the two is legally speaking a
75 | combined work, a derivative of the original library. The ordinary
76 | General Public License therefore permits such linking only if the
77 | entire combination fits its criteria of freedom. The Lesser General
78 | Public License permits more lax criteria for linking other code with
79 | the library.
80 |
81 | We call this license the "Lesser" General Public License because it
82 | does Less to protect the user's freedom than the ordinary General
83 | Public License. It also provides other free software developers Less
84 | of an advantage over competing non-free programs. These disadvantages
85 | are the reason we use the ordinary General Public License for many
86 | libraries. However, the Lesser license provides advantages in certain
87 | special circumstances.
88 |
89 | For example, on rare occasions, there may be a special need to
90 | encourage the widest possible use of a certain library, so that it becomes
91 | a de-facto standard. To achieve this, non-free programs must be
92 | allowed to use the library. A more frequent case is that a free
93 | library does the same job as widely used non-free libraries. In this
94 | case, there is little to gain by limiting the free library to free
95 | software only, so we use the Lesser General Public License.
96 |
97 | In other cases, permission to use a particular library in non-free
98 | programs enables a greater number of people to use a large body of
99 | free software. For example, permission to use the GNU C Library in
100 | non-free programs enables many more people to use the whole GNU
101 | operating system, as well as its variant, the GNU/Linux operating
102 | system.
103 |
104 | Although the Lesser General Public License is Less protective of the
105 | users' freedom, it does ensure that the user of a program that is
106 | linked with the Library has the freedom and the wherewithal to run
107 | that program using a modified version of the Library.
108 |
109 | The precise terms and conditions for copying, distribution and
110 | modification follow. Pay close attention to the difference between a
111 | "work based on the library" and a "work that uses the library". The
112 | former contains code derived from the library, whereas the latter must
113 | be combined with the library in order to run.
114 |
115 | GNU LESSER GENERAL PUBLIC LICENSE
116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
117 |
118 | 0. This License Agreement applies to any software library or other
119 | program which contains a notice placed by the copyright holder or
120 | other authorized party saying it may be distributed under the terms of
121 | this Lesser General Public License (also called "this License").
122 | Each licensee is addressed as "you".
123 |
124 | A "library" means a collection of software functions and/or data
125 | prepared so as to be conveniently linked with application programs
126 | (which use some of those functions and data) to form executables.
127 |
128 | The "Library", below, refers to any such software library or work
129 | which has been distributed under these terms. A "work based on the
130 | Library" means either the Library or any derivative work under
131 | copyright law: that is to say, a work containing the Library or a
132 | portion of it, either verbatim or with modifications and/or translated
133 | straightforwardly into another language. (Hereinafter, translation is
134 | included without limitation in the term "modification".)
135 |
136 | "Source code" for a work means the preferred form of the work for
137 | making modifications to it. For a library, complete source code means
138 | all the source code for all modules it contains, plus any associated
139 | interface definition files, plus the scripts used to control compilation
140 | and installation of the library.
141 |
142 | Activities other than copying, distribution and modification are not
143 | covered by this License; they are outside its scope. The act of
144 | running a program using the Library is not restricted, and output from
145 | such a program is covered only if its contents constitute a work based
146 | on the Library (independent of the use of the Library in a tool for
147 | writing it). Whether that is true depends on what the Library does
148 | and what the program that uses the Library does.
149 |
150 | 1. You may copy and distribute verbatim copies of the Library's
151 | complete source code as you receive it, in any medium, provided that
152 | you conspicuously and appropriately publish on each copy an
153 | appropriate copyright notice and disclaimer of warranty; keep intact
154 | all the notices that refer to this License and to the absence of any
155 | warranty; and distribute a copy of this License along with the
156 | Library.
157 |
158 | You may charge a fee for the physical act of transferring a copy,
159 | and you may at your option offer warranty protection in exchange for a
160 | fee.
161 |
162 | 2. You may modify your copy or copies of the Library or any portion
163 | of it, thus forming a work based on the Library, and copy and
164 | distribute such modifications or work under the terms of Section 1
165 | above, provided that you also meet all of these conditions:
166 |
167 | a) The modified work must itself be a software library.
168 |
169 | b) You must cause the files modified to carry prominent notices
170 | stating that you changed the files and the date of any change.
171 |
172 | c) You must cause the whole of the work to be licensed at no
173 | charge to all third parties under the terms of this License.
174 |
175 | d) If a facility in the modified Library refers to a function or a
176 | table of data to be supplied by an application program that uses
177 | the facility, other than as an argument passed when the facility
178 | is invoked, then you must make a good faith effort to ensure that,
179 | in the event an application does not supply such function or
180 | table, the facility still operates, and performs whatever part of
181 | its purpose remains meaningful.
182 |
183 | (For example, a function in a library to compute square roots has
184 | a purpose that is entirely well-defined independent of the
185 | application. Therefore, Subsection 2d requires that any
186 | application-supplied function or table used by this function must
187 | be optional: if the application does not supply it, the square
188 | root function must still compute square roots.)
189 |
190 | These requirements apply to the modified work as a whole. If
191 | identifiable sections of that work are not derived from the Library,
192 | and can be reasonably considered independent and separate works in
193 | themselves, then this License, and its terms, do not apply to those
194 | sections when you distribute them as separate works. But when you
195 | distribute the same sections as part of a whole which is a work based
196 | on the Library, the distribution of the whole must be on the terms of
197 | this License, whose permissions for other licensees extend to the
198 | entire whole, and thus to each and every part regardless of who wrote
199 | it.
200 |
201 | Thus, it is not the intent of this section to claim rights or contest
202 | your rights to work written entirely by you; rather, the intent is to
203 | exercise the right to control the distribution of derivative or
204 | collective works based on the Library.
205 |
206 | In addition, mere aggregation of another work not based on the Library
207 | with the Library (or with a work based on the Library) on a volume of
208 | a storage or distribution medium does not bring the other work under
209 | the scope of this License.
210 |
211 | 3. You may opt to apply the terms of the ordinary GNU General Public
212 | License instead of this License to a given copy of the Library. To do
213 | this, you must alter all the notices that refer to this License, so
214 | that they refer to the ordinary GNU General Public License, version 2,
215 | instead of to this License. (If a newer version than version 2 of the
216 | ordinary GNU General Public License has appeared, then you can specify
217 | that version instead if you wish.) Do not make any other change in
218 | these notices.
219 |
220 | Once this change is made in a given copy, it is irreversible for
221 | that copy, so the ordinary GNU General Public License applies to all
222 | subsequent copies and derivative works made from that copy.
223 |
224 | This option is useful when you wish to copy part of the code of
225 | the Library into a program that is not a library.
226 |
227 | 4. You may copy and distribute the Library (or a portion or
228 | derivative of it, under Section 2) in object code or executable form
229 | under the terms of Sections 1 and 2 above provided that you accompany
230 | it with the complete corresponding machine-readable source code, which
231 | must be distributed under the terms of Sections 1 and 2 above on a
232 | medium customarily used for software interchange.
233 |
234 | If distribution of object code is made by offering access to copy
235 | from a designated place, then offering equivalent access to copy the
236 | source code from the same place satisfies the requirement to
237 | distribute the source code, even though third parties are not
238 | compelled to copy the source along with the object code.
239 |
240 | 5. A program that contains no derivative of any portion of the
241 | Library, but is designed to work with the Library by being compiled or
242 | linked with it, is called a "work that uses the Library". Such a
243 | work, in isolation, is not a derivative work of the Library, and
244 | therefore falls outside the scope of this License.
245 |
246 | However, linking a "work that uses the Library" with the Library
247 | creates an executable that is a derivative of the Library (because it
248 | contains portions of the Library), rather than a "work that uses the
249 | library". The executable is therefore covered by this License.
250 | Section 6 states terms for distribution of such executables.
251 |
252 | When a "work that uses the Library" uses material from a header file
253 | that is part of the Library, the object code for the work may be a
254 | derivative work of the Library even though the source code is not.
255 | Whether this is true is especially significant if the work can be
256 | linked without the Library, or if the work is itself a library. The
257 | threshold for this to be true is not precisely defined by law.
258 |
259 | If such an object file uses only numerical parameters, data
260 | structure layouts and accessors, and small macros and small inline
261 | functions (ten lines or less in length), then the use of the object
262 | file is unrestricted, regardless of whether it is legally a derivative
263 | work. (Executables containing this object code plus portions of the
264 | Library will still fall under Section 6.)
265 |
266 | Otherwise, if the work is a derivative of the Library, you may
267 | distribute the object code for the work under the terms of Section 6.
268 | Any executables containing that work also fall under Section 6,
269 | whether or not they are linked directly with the Library itself.
270 |
271 | 6. As an exception to the Sections above, you may also combine or
272 | link a "work that uses the Library" with the Library to produce a
273 | work containing portions of the Library, and distribute that work
274 | under terms of your choice, provided that the terms permit
275 | modification of the work for the customer's own use and reverse
276 | engineering for debugging such modifications.
277 |
278 | You must give prominent notice with each copy of the work that the
279 | Library is used in it and that the Library and its use are covered by
280 | this License. You must supply a copy of this License. If the work
281 | during execution displays copyright notices, you must include the
282 | copyright notice for the Library among them, as well as a reference
283 | directing the user to the copy of this License. Also, you must do one
284 | of these things:
285 |
286 | a) Accompany the work with the complete corresponding
287 | machine-readable source code for the Library including whatever
288 | changes were used in the work (which must be distributed under
289 | Sections 1 and 2 above); and, if the work is an executable linked
290 | with the Library, with the complete machine-readable "work that
291 | uses the Library", as object code and/or source code, so that the
292 | user can modify the Library and then relink to produce a modified
293 | executable containing the modified Library. (It is understood
294 | that the user who changes the contents of definitions files in the
295 | Library will not necessarily be able to recompile the application
296 | to use the modified definitions.)
297 |
298 | b) Use a suitable shared library mechanism for linking with the
299 | Library. A suitable mechanism is one that (1) uses at run time a
300 | copy of the library already present on the user's computer system,
301 | rather than copying library functions into the executable, and (2)
302 | will operate properly with a modified version of the library, if
303 | the user installs one, as long as the modified version is
304 | interface-compatible with the version that the work was made with.
305 |
306 | c) Accompany the work with a written offer, valid for at
307 | least three years, to give the same user the materials
308 | specified in Subsection 6a, above, for a charge no more
309 | than the cost of performing this distribution.
310 |
311 | d) If distribution of the work is made by offering access to copy
312 | from a designated place, offer equivalent access to copy the above
313 | specified materials from the same place.
314 |
315 | e) Verify that the user has already received a copy of these
316 | materials or that you have already sent this user a copy.
317 |
318 | For an executable, the required form of the "work that uses the
319 | Library" must include any data and utility programs needed for
320 | reproducing the executable from it. However, as a special exception,
321 | the materials to be distributed need not include anything that is
322 | normally distributed (in either source or binary form) with the major
323 | components (compiler, kernel, and so on) of the operating system on
324 | which the executable runs, unless that component itself accompanies
325 | the executable.
326 |
327 | It may happen that this requirement contradicts the license
328 | restrictions of other proprietary libraries that do not normally
329 | accompany the operating system. Such a contradiction means you cannot
330 | use both them and the Library together in an executable that you
331 | distribute.
332 |
333 | 7. You may place library facilities that are a work based on the
334 | Library side-by-side in a single library together with other library
335 | facilities not covered by this License, and distribute such a combined
336 | library, provided that the separate distribution of the work based on
337 | the Library and of the other library facilities is otherwise
338 | permitted, and provided that you do these two things:
339 |
340 | a) Accompany the combined library with a copy of the same work
341 | based on the Library, uncombined with any other library
342 | facilities. This must be distributed under the terms of the
343 | Sections above.
344 |
345 | b) Give prominent notice with the combined library of the fact
346 | that part of it is a work based on the Library, and explaining
347 | where to find the accompanying uncombined form of the same work.
348 |
349 | 8. You may not copy, modify, sublicense, link with, or distribute
350 | the Library except as expressly provided under this License. Any
351 | attempt otherwise to copy, modify, sublicense, link with, or
352 | distribute the Library is void, and will automatically terminate your
353 | rights under this License. However, parties who have received copies,
354 | or rights, from you under this License will not have their licenses
355 | terminated so long as such parties remain in full compliance.
356 |
357 | 9. You are not required to accept this License, since you have not
358 | signed it. However, nothing else grants you permission to modify or
359 | distribute the Library or its derivative works. These actions are
360 | prohibited by law if you do not accept this License. Therefore, by
361 | modifying or distributing the Library (or any work based on the
362 | Library), you indicate your acceptance of this License to do so, and
363 | all its terms and conditions for copying, distributing or modifying
364 | the Library or works based on it.
365 |
366 | 10. Each time you redistribute the Library (or any work based on the
367 | Library), the recipient automatically receives a license from the
368 | original licensor to copy, distribute, link with or modify the Library
369 | subject to these terms and conditions. You may not impose any further
370 | restrictions on the recipients' exercise of the rights granted herein.
371 | You are not responsible for enforcing compliance by third parties with
372 | this License.
373 |
374 | 11. If, as a consequence of a court judgment or allegation of patent
375 | infringement or for any other reason (not limited to patent issues),
376 | conditions are imposed on you (whether by court order, agreement or
377 | otherwise) that contradict the conditions of this License, they do not
378 | excuse you from the conditions of this License. If you cannot
379 | distribute so as to satisfy simultaneously your obligations under this
380 | License and any other pertinent obligations, then as a consequence you
381 | may not distribute the Library at all. For example, if a patent
382 | license would not permit royalty-free redistribution of the Library by
383 | all those who receive copies directly or indirectly through you, then
384 | the only way you could satisfy both it and this License would be to
385 | refrain entirely from distribution of the Library.
386 |
387 | If any portion of this section is held invalid or unenforceable under any
388 | particular circumstance, the balance of the section is intended to apply,
389 | and the section as a whole is intended to apply in other circumstances.
390 |
391 | It is not the purpose of this section to induce you to infringe any
392 | patents or other property right claims or to contest validity of any
393 | such claims; this section has the sole purpose of protecting the
394 | integrity of the free software distribution system which is
395 | implemented by public license practices. Many people have made
396 | generous contributions to the wide range of software distributed
397 | through that system in reliance on consistent application of that
398 | system; it is up to the author/donor to decide if he or she is willing
399 | to distribute software through any other system and a licensee cannot
400 | impose that choice.
401 |
402 | This section is intended to make thoroughly clear what is believed to
403 | be a consequence of the rest of this License.
404 |
405 | 12. If the distribution and/or use of the Library is restricted in
406 | certain countries either by patents or by copyrighted interfaces, the
407 | original copyright holder who places the Library under this License may add
408 | an explicit geographical distribution limitation excluding those countries,
409 | so that distribution is permitted only in or among countries not thus
410 | excluded. In such case, this License incorporates the limitation as if
411 | written in the body of this License.
412 |
413 | 13. The Free Software Foundation may publish revised and/or new
414 | versions of the Lesser General Public License from time to time.
415 | Such new versions will be similar in spirit to the present version,
416 | but may differ in detail to address new problems or concerns.
417 |
418 | Each version is given a distinguishing version number. If the Library
419 | specifies a version number of this License which applies to it and
420 | "any later version", you have the option of following the terms and
421 | conditions either of that version or of any later version published by
422 | the Free Software Foundation. If the Library does not specify a
423 | license version number, you may choose any version ever published by
424 | the Free Software Foundation.
425 |
426 | 14. If you wish to incorporate parts of the Library into other free
427 | programs whose distribution conditions are incompatible with these,
428 | write to the author to ask for permission. For software which is
429 | copyrighted by the Free Software Foundation, write to the Free
430 | Software Foundation; we sometimes make exceptions for this. Our
431 | decision will be guided by the two goals of preserving the free status
432 | of all derivatives of our free software and of promoting the sharing
433 | and reuse of software generally.
434 |
435 | NO WARRANTY
436 |
437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
446 |
447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
456 | DAMAGES.
457 |
458 | END OF TERMS AND CONDITIONS
459 |
460 | How to Apply These Terms to Your New Libraries
461 |
462 | If you develop a new library, and you want it to be of the greatest
463 | possible use to the public, we recommend making it free software that
464 | everyone can redistribute and change. You can do so by permitting
465 | redistribution under these terms (or, alternatively, under the terms of the
466 | ordinary General Public License).
467 |
468 | To apply these terms, attach the following notices to the library. It is
469 | safest to attach them to the start of each source file to most effectively
470 | convey the exclusion of warranty; and each file should have at least the
471 | "copyright" line and a pointer to where the full notice is found.
472 |
473 |
474 | Copyright (C)
475 |
476 | This library is free software; you can redistribute it and/or
477 | modify it under the terms of the GNU Lesser General Public
478 | License as published by the Free Software Foundation; either
479 | version 2.1 of the License, or (at your option) any later version.
480 |
481 | This library is distributed in the hope that it will be useful,
482 | but WITHOUT ANY WARRANTY; without even the implied warranty of
483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
484 | Lesser General Public License for more details.
485 |
486 | You should have received a copy of the GNU Lesser General Public
487 | License along with this library; if not, write to the Free Software
488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
489 |
490 | Also add information on how to contact you by electronic and paper mail.
491 |
492 | You should also get your employer (if you work as a programmer) or your
493 | school, if any, to sign a "copyright disclaimer" for the library, if
494 | necessary. Here is a sample; alter the names:
495 |
496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the
497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker.
498 |
499 | , 1 April 1990
500 | Ty Coon, President of Vice
501 |
502 | That's all there is to it!
503 |
--------------------------------------------------------------------------------