use submodules
authorNiki Roo <niki@nikiroo.be>
Sun, 30 Jun 2024 17:06:21 +0000 (19:06 +0200)
committerNiki Roo <niki@nikiroo.be>
Sun, 30 Jun 2024 17:06:21 +0000 (19:06 +0200)
120 files changed:
.gitmodules [new file with mode: 0644]
src/be/nikiroo/tests/utils/BufferedInputStreamTest.java [moved from src/be/nikiroo/utils/test_code/BufferedInputStreamTest.java with 99% similarity]
src/be/nikiroo/tests/utils/BufferedOutputStreamTest.java [moved from src/be/nikiroo/utils/test_code/BufferedOutputStreamTest.java with 98% similarity]
src/be/nikiroo/tests/utils/BundleTest.java [moved from src/be/nikiroo/utils/test_code/BundleTest.java with 99% similarity]
src/be/nikiroo/tests/utils/CryptUtilsTest.java [moved from src/be/nikiroo/utils/test_code/CryptUtilsTest.java with 99% similarity]
src/be/nikiroo/tests/utils/IOUtilsTest.java [moved from src/be/nikiroo/utils/test_code/IOUtilsTest.java with 93% similarity]
src/be/nikiroo/tests/utils/NextableInputStreamTest.java [moved from src/be/nikiroo/utils/test_code/NextableInputStreamTest.java with 99% similarity]
src/be/nikiroo/tests/utils/ProgressTest.java [moved from src/be/nikiroo/utils/test_code/ProgressTest.java with 99% similarity]
src/be/nikiroo/tests/utils/ReplaceInputStreamTest.java [moved from src/be/nikiroo/utils/test_code/ReplaceInputStreamTest.java with 99% similarity]
src/be/nikiroo/tests/utils/ReplaceOutputStreamTest.java [moved from src/be/nikiroo/utils/test_code/ReplaceOutputStreamTest.java with 99% similarity]
src/be/nikiroo/tests/utils/SerialServerTest.java [moved from src/be/nikiroo/utils/test_code/SerialServerTest.java with 99% similarity]
src/be/nikiroo/tests/utils/SerialTest.java [moved from src/be/nikiroo/utils/test_code/SerialTest.java with 99% similarity]
src/be/nikiroo/tests/utils/StringUtilsTest.java [moved from src/be/nikiroo/utils/test_code/StringUtilsTest.java with 99% similarity]
src/be/nikiroo/tests/utils/TempFilesTest.java [moved from src/be/nikiroo/utils/test_code/TempFilesTest.java with 98% similarity]
src/be/nikiroo/tests/utils/Test.java [moved from src/be/nikiroo/utils/test_code/Test.java with 97% similarity]
src/be/nikiroo/tests/utils/VersionTest.java [moved from src/be/nikiroo/utils/test_code/VersionTest.java with 99% similarity]
src/be/nikiroo/tests/utils/bundle_test.properties [moved from src/be/nikiroo/utils/test_code/bundle_test.properties with 100% similarity]
src/be/nikiroo/utils [new submodule]
src/be/nikiroo/utils/Cache.java [deleted file]
src/be/nikiroo/utils/CacheMemory.java [deleted file]
src/be/nikiroo/utils/CookieUtils.java [deleted file]
src/be/nikiroo/utils/CryptUtils.java [deleted file]
src/be/nikiroo/utils/Downloader.java [deleted file]
src/be/nikiroo/utils/HashUtils.java [deleted file]
src/be/nikiroo/utils/IOUtils.java [deleted file]
src/be/nikiroo/utils/Image.java [deleted file]
src/be/nikiroo/utils/ImageUtils.java [deleted file]
src/be/nikiroo/utils/LoginResult.java [deleted file]
src/be/nikiroo/utils/MarkableFileInputStream.java [deleted file]
src/be/nikiroo/utils/NanoHTTPD.java [deleted file]
src/be/nikiroo/utils/Progress.java [deleted file]
src/be/nikiroo/utils/Proxy.java [deleted file]
src/be/nikiroo/utils/StringJustifier.java [deleted file]
src/be/nikiroo/utils/StringUtils.java [deleted file]
src/be/nikiroo/utils/TempFiles.java [deleted file]
src/be/nikiroo/utils/TraceHandler.java [deleted file]
src/be/nikiroo/utils/Version.java [deleted file]
src/be/nikiroo/utils/VersionCheck.java [deleted file]
src/be/nikiroo/utils/android/ImageUtilsAndroid.java [deleted file]
src/be/nikiroo/utils/android/test/TestAndroid.java [deleted file]
src/be/nikiroo/utils/main/bridge.java [deleted file]
src/be/nikiroo/utils/main/img2aa.java [deleted file]
src/be/nikiroo/utils/main/justify.java [deleted file]
src/be/nikiroo/utils/resources/Bundle.java [deleted file]
src/be/nikiroo/utils/resources/BundleHelper.java [deleted file]
src/be/nikiroo/utils/resources/Bundles.java [deleted file]
src/be/nikiroo/utils/resources/FixedResourceBundleControl.java [deleted file]
src/be/nikiroo/utils/resources/Meta.java [deleted file]
src/be/nikiroo/utils/resources/MetaInfo.java [deleted file]
src/be/nikiroo/utils/resources/TransBundle.java [deleted file]
src/be/nikiroo/utils/resources/TransBundle_ResourceList.java [deleted file]
src/be/nikiroo/utils/resources/package-info.java [deleted file]
src/be/nikiroo/utils/serial/CustomSerializer.java [deleted file]
src/be/nikiroo/utils/serial/Exporter.java [deleted file]
src/be/nikiroo/utils/serial/Importer.java [deleted file]
src/be/nikiroo/utils/serial/SerialUtils.java [deleted file]
src/be/nikiroo/utils/serial/server/ConnectAction.java [deleted file]
src/be/nikiroo/utils/serial/server/ConnectActionClient.java [deleted file]
src/be/nikiroo/utils/serial/server/ConnectActionClientObject.java [deleted file]
src/be/nikiroo/utils/serial/server/ConnectActionClientString.java [deleted file]
src/be/nikiroo/utils/serial/server/ConnectActionServer.java [deleted file]
src/be/nikiroo/utils/serial/server/ConnectActionServerObject.java [deleted file]
src/be/nikiroo/utils/serial/server/ConnectActionServerString.java [deleted file]
src/be/nikiroo/utils/serial/server/Server.java [deleted file]
src/be/nikiroo/utils/serial/server/ServerBridge.java [deleted file]
src/be/nikiroo/utils/serial/server/ServerObject.java [deleted file]
src/be/nikiroo/utils/serial/server/ServerString.java [deleted file]
src/be/nikiroo/utils/streams/Base64.java [deleted file]
src/be/nikiroo/utils/streams/Base64InputStream.java [deleted file]
src/be/nikiroo/utils/streams/Base64OutputStream.java [deleted file]
src/be/nikiroo/utils/streams/BufferedInputStream.java [deleted file]
src/be/nikiroo/utils/streams/BufferedOutputStream.java [deleted file]
src/be/nikiroo/utils/streams/MarkableFileInputStream.java [deleted file]
src/be/nikiroo/utils/streams/NextableInputStream.java [deleted file]
src/be/nikiroo/utils/streams/NextableInputStreamStep.java [deleted file]
src/be/nikiroo/utils/streams/ReplaceInputStream.java [deleted file]
src/be/nikiroo/utils/streams/ReplaceOutputStream.java [deleted file]
src/be/nikiroo/utils/streams/StreamUtils.java [deleted file]
src/be/nikiroo/utils/test/TestCase.java [deleted file]
src/be/nikiroo/utils/test/TestLauncher.java [deleted file]
src/be/nikiroo/utils/ui/BreadCrumbsBar.java [deleted file]
src/be/nikiroo/utils/ui/ConfigEditor.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItem.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemBase.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemBoolean.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemBrowse.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemColor.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemCombobox.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemInteger.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemLocale.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemPassword.java [deleted file]
src/be/nikiroo/utils/ui/ConfigItemString.java [deleted file]
src/be/nikiroo/utils/ui/DataNode.java [deleted file]
src/be/nikiroo/utils/ui/DataTree.java [deleted file]
src/be/nikiroo/utils/ui/DelayWorker.java [deleted file]
src/be/nikiroo/utils/ui/ImageTextAwt.java [deleted file]
src/be/nikiroo/utils/ui/ImageUtilsAwt.java [deleted file]
src/be/nikiroo/utils/ui/Item.java [deleted file]
src/be/nikiroo/utils/ui/ListModel.java [deleted file]
src/be/nikiroo/utils/ui/ListSnapshot.java [deleted file]
src/be/nikiroo/utils/ui/ListenerItem.java [deleted file]
src/be/nikiroo/utils/ui/ListenerPanel.java [deleted file]
src/be/nikiroo/utils/ui/NavBar.java [deleted file]
src/be/nikiroo/utils/ui/ProgressBar.java [deleted file]
src/be/nikiroo/utils/ui/SearchBar.java [deleted file]
src/be/nikiroo/utils/ui/TreeCellSpanner.java [deleted file]
src/be/nikiroo/utils/ui/TreeModelTransformer.java [deleted file]
src/be/nikiroo/utils/ui/TreeSnapshot.java [deleted file]
src/be/nikiroo/utils/ui/UIUtils.java [deleted file]
src/be/nikiroo/utils/ui/WaitingDialog.java [deleted file]
src/be/nikiroo/utils/ui/WrapLayout.java [deleted file]
src/be/nikiroo/utils/ui/ZoomBox.java [deleted file]
src/be/nikiroo/utils/ui/clear-16x16.png [deleted file]
src/be/nikiroo/utils/ui/compat/DefaultListModel6.java [deleted file]
src/be/nikiroo/utils/ui/compat/JList6.java [deleted file]
src/be/nikiroo/utils/ui/compat/ListCellRenderer6.java [deleted file]
src/be/nikiroo/utils/ui/compat/ListModel6.java [deleted file]
src/be/nikiroo/utils/ui/search-16x16.png [deleted file]
src/be/nikiroo/utils/ui/test/ProgressBarManualTest.java [deleted file]
src/be/nikiroo/utils/ui/test/TestUI.java [deleted file]

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..0a81b17
--- /dev/null
@@ -0,0 +1,4 @@
+[submodule "src/be/nikiroo/utils"]
+       path = src/be/nikiroo/utils
+       url = .
+       branch = subtree
similarity index 99%
rename from src/be/nikiroo/utils/test_code/BufferedInputStreamTest.java
rename to src/be/nikiroo/tests/utils/BufferedInputStreamTest.java
index c715585f7da7afb93633c937672342f5b6ec3fc7..ed753bfe72756a7afa87282c22a054f6775731e0 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
similarity index 98%
rename from src/be/nikiroo/utils/test_code/BufferedOutputStreamTest.java
rename to src/be/nikiroo/tests/utils/BufferedOutputStreamTest.java
index 5646e61866fe96d6b16a5c44c705a72ccfc99cb0..928faadf52cc796946f9c283881d26aa61286822 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
similarity index 99%
rename from src/be/nikiroo/utils/test_code/BundleTest.java
rename to src/be/nikiroo/tests/utils/BundleTest.java
index 2e25eb0f4ddd9a0b77d5c053dbfbd8ebc4fcdd59..f8e833f341552725af79003816b2fbbe53b7d94d 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.File;
 import java.util.ArrayList;
similarity index 99%
rename from src/be/nikiroo/utils/test_code/CryptUtilsTest.java
rename to src/be/nikiroo/tests/utils/CryptUtilsTest.java
index 0c53461ea56aed8c5959e4b834ac90306994c8f4..826035ee71a1f04234912722400319649d7eced9 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
similarity index 93%
rename from src/be/nikiroo/utils/test_code/IOUtilsTest.java
rename to src/be/nikiroo/tests/utils/IOUtilsTest.java
index 9f22896f0d6fcd5e09bd4dd8e9302981d7615636..71acf1ce40dc95db9dfc9986d5d6f0855f0146e2 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.InputStream;
 
similarity index 99%
rename from src/be/nikiroo/utils/test_code/NextableInputStreamTest.java
rename to src/be/nikiroo/tests/utils/NextableInputStreamTest.java
index 4e5982363af1925d4b7422ba0eeb4a60ec69204a..3dc6fd1671cbedcccbe3d24bd139a6ceb785c7b7 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.ByteArrayInputStream;
 
similarity index 99%
rename from src/be/nikiroo/utils/test_code/ProgressTest.java
rename to src/be/nikiroo/tests/utils/ProgressTest.java
index 22e36cb0cf19d69d70294c0058d60ff988701e5a..c829e7028ba5f112737a088d8404ed19001e0e75 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import be.nikiroo.utils.Progress;
 import be.nikiroo.utils.test.TestCase;
similarity index 99%
rename from src/be/nikiroo/utils/test_code/ReplaceInputStreamTest.java
rename to src/be/nikiroo/tests/utils/ReplaceInputStreamTest.java
index efab8c73d62e5f79779a41b6f8c4c889ad8a8f74..08213e1f39916f45d0dc1c135ba7fa215575272c 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
similarity index 99%
rename from src/be/nikiroo/utils/test_code/ReplaceOutputStreamTest.java
rename to src/be/nikiroo/tests/utils/ReplaceOutputStreamTest.java
index 1db3397af9a09b4d32eb91eec99f83e70989c29c..4453320f5aed7b6af52fa263f2ed5dd7aa1158c3 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.ByteArrayOutputStream;
 
similarity index 99%
rename from src/be/nikiroo/utils/test_code/SerialServerTest.java
rename to src/be/nikiroo/tests/utils/SerialServerTest.java
index c10a158c347c1f9e0ed64f096a33352d8a3b4556..61d01a4f664d0b51a4f8505cbea1c92f27ae6a0a 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.net.URL;
 
similarity index 99%
rename from src/be/nikiroo/utils/test_code/SerialTest.java
rename to src/be/nikiroo/tests/utils/SerialTest.java
index bf08f5c1b4dbd19f146bdd5af4b480c3fa4fd531..46cda26179717f356debae9e15e2a60683530cce 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
similarity index 99%
rename from src/be/nikiroo/utils/test_code/StringUtilsTest.java
rename to src/be/nikiroo/tests/utils/StringUtilsTest.java
index a441195b24d6dd6dafd2da14da02d5e61fd01d23..a17731f4856aa650682e1cee445959b2702ffea8 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.util.Arrays;
 import java.util.Date;
similarity index 98%
rename from src/be/nikiroo/utils/test_code/TempFilesTest.java
rename to src/be/nikiroo/tests/utils/TempFilesTest.java
index dad4cacb6e51044c131892e441b48ea248871153..19ed2079ced15ae6a362d054e1b33b277cfef69c 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import java.io.File;
 import java.io.IOException;
similarity index 97%
rename from src/be/nikiroo/utils/test_code/Test.java
rename to src/be/nikiroo/tests/utils/Test.java
index 8d99cba8d898b38245986687c3f476f4412e4fbf..23f49468598198cc3f00e3c268fb090bbfe22603 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import be.nikiroo.utils.Cache;
 import be.nikiroo.utils.CacheMemory;
similarity index 99%
rename from src/be/nikiroo/utils/test_code/VersionTest.java
rename to src/be/nikiroo/tests/utils/VersionTest.java
index 2d8447635fbb13427331ddcd89a8851b6904e074..52d80e7aab89dac29209767bc17e21c855cddc37 100644 (file)
@@ -1,4 +1,4 @@
-package be.nikiroo.utils.test_code;
+package be.nikiroo.tests.utils;
 
 import be.nikiroo.utils.Version;
 import be.nikiroo.utils.test.TestCase;
diff --git a/src/be/nikiroo/utils b/src/be/nikiroo/utils
new file mode 160000 (submodule)
index 0000000..34835dd
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 34835dd204994ab0be1899848c4de2f9dde9f766
diff --git a/src/be/nikiroo/utils/Cache.java b/src/be/nikiroo/utils/Cache.java
deleted file mode 100644 (file)
index 6233082..0000000
+++ /dev/null
@@ -1,457 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Date;
-
-import be.nikiroo.utils.streams.MarkableFileInputStream;
-
-/**
- * A generic cache system, with special support for {@link URL}s.
- * <p>
- * This cache also manages timeout information.
- * 
- * @author niki
- */
-public class Cache {
-       private File dir;
-       private long tooOldChanging;
-       private long tooOldStable;
-       private TraceHandler tracer = new TraceHandler();
-
-       /**
-        * Only for inheritance.
-        */
-       protected Cache() {
-       }
-
-       /**
-        * Create a new {@link Cache} object.
-        * 
-        * @param dir
-        *            the directory to use as cache
-        * @param hoursChanging
-        *            the number of hours after which a cached file that is thought
-        *            to change ~often is considered too old (or -1 for
-        *            "never too old")
-        * @param hoursStable
-        *            the number of hours after which a cached file that is thought
-        *            to change rarely is considered too old (or -1 for
-        *            "never too old")
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public Cache(File dir, int hoursChanging, int hoursStable)
-                       throws IOException {
-               this.dir = dir;
-               this.tooOldChanging = 1000L * 60 * 60 * hoursChanging;
-               this.tooOldStable = 1000L * 60 * 60 * hoursStable;
-
-               if (dir != null && !dir.exists()) {
-                       dir.mkdirs();
-               }
-
-               if (dir == null || !dir.exists()) {
-                       throw new IOException("Cannot create the cache directory: "
-                                       + (dir == null ? "null" : dir.getAbsolutePath()));
-               }
-       }
-
-       /**
-        * The traces handler for this {@link Cache}.
-        * 
-        * @return the traces handler
-        */
-       public TraceHandler getTraceHandler() {
-               return tracer;
-       }
-
-       /**
-        * The traces handler for this {@link Cache}.
-        * 
-        * @param tracer
-        *            the new traces handler
-        */
-       public void setTraceHandler(TraceHandler tracer) {
-               if (tracer == null) {
-                       tracer = new TraceHandler(false, false, false);
-               }
-
-               this.tracer = tracer;
-       }
-
-       /**
-        * Check the resource to see if it is in the cache.
-        * 
-        * @param uniqueID
-        *            the resource to check
-        * @param allowTooOld
-        *            allow files even if they are considered too old
-        * @param stable
-        *            a stable file (that dones't change too often) -- parameter
-        *            used to check if the file is too old to keep or not
-        * 
-        * @return TRUE if it is
-        * 
-        */
-       public boolean check(String uniqueID, boolean allowTooOld, boolean stable) {
-               return check(getCached(uniqueID), allowTooOld, stable);
-       }
-
-       /**
-        * Check the resource to see if it is in the cache.
-        * 
-        * @param url
-        *            the resource to check
-        * @param allowTooOld
-        *            allow files even if they are considered too old
-        * @param stable
-        *            a stable file (that dones't change too often) -- parameter
-        *            used to check if the file is too old to keep or not
-        * 
-        * @return TRUE if it is
-        * 
-        */
-       public boolean check(URL url, boolean allowTooOld, boolean stable) {
-               return check(getCached(url), allowTooOld, stable);
-       }
-
-       /**
-        * Check the resource to see if it is in the cache.
-        * 
-        * @param cached
-        *            the resource to check
-        * @param allowTooOld
-        *            allow files even if they are considered too old
-        * @param stable
-        *            a stable file (that dones't change too often) -- parameter
-        *            used to check if the file is too old to keep or not
-        * 
-        * @return TRUE if it is
-        * 
-        */
-       private boolean check(File cached, boolean allowTooOld, boolean stable) {
-               if (cached.exists() && cached.isFile()) {
-                       if (!allowTooOld && isOld(cached, stable)) {
-                               if (!cached.delete()) {
-                                       tracer.error("Cannot delete temporary file: "
-                                                       + cached.getAbsolutePath());
-                               }
-                       } else {
-                               return true;
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Clean the cache (delete the cached items).
-        * 
-        * @param onlyOld
-        *            only clean the files that are considered too old for a stable
-        *            resource
-        * 
-        * @return the number of cleaned items
-        */
-       public int clean(boolean onlyOld) {
-               long ms = System.currentTimeMillis();
-
-               tracer.trace("Cleaning cache from old files...");
-
-               int num = clean(onlyOld, dir, -1);
-
-               tracer.trace(num + "cache items cleaned in "
-                               + (System.currentTimeMillis() - ms) + " ms");
-
-               return num;
-       }
-
-       /**
-        * Clean the cache (delete the cached items) in the given cache directory.
-        * 
-        * @param onlyOld
-        *            only clean the files that are considered too old for stable
-        *            resources
-        * @param cacheDir
-        *            the cache directory to clean
-        * @param limit
-        *            stop after limit files deleted, or -1 for unlimited
-        * 
-        * @return the number of cleaned items
-        */
-       private int clean(boolean onlyOld, File cacheDir, int limit) {
-               int num = 0;
-               File[] files = cacheDir.listFiles();
-               if (files != null) {
-                       for (File file : files) {
-                               if (limit >= 0 && num >= limit) {
-                                       return num;
-                               }
-
-                               if (file.isDirectory()) {
-                                       num += clean(onlyOld, file, limit);
-                                       file.delete(); // only if empty
-                               } else {
-                                       if (!onlyOld || isOld(file, true)) {
-                                               if (file.delete()) {
-                                                       num++;
-                                               } else {
-                                                       tracer.error("Cannot delete temporary file: "
-                                                                       + file.getAbsolutePath());
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               return num;
-       }
-
-       /**
-        * Open a resource from the cache if it exists.
-        * 
-        * @param uniqueID
-        *            the unique ID
-        * @param allowTooOld
-        *            allow files even if they are considered too old
-        * @param stable
-        *            a stable file (that dones't change too often) -- parameter
-        *            used to check if the file is too old to keep or not
-        * 
-        * @return the opened resource if found, NULL if not
-        */
-       public InputStream load(String uniqueID, boolean allowTooOld, boolean stable) {
-               return load(getCached(uniqueID), allowTooOld, stable);
-       }
-
-       /**
-        * Open a resource from the cache if it exists.
-        * 
-        * @param url
-        *            the resource to open
-        * @param allowTooOld
-        *            allow files even if they are considered too old
-        * @param stable
-        *            a stable file (that doesn't change too often) -- parameter
-        *            used to check if the file is too old to keep or not in the
-        *            cache
-        * 
-        * @return the opened resource if found, NULL if not
-        */
-       public InputStream load(URL url, boolean allowTooOld, boolean stable) {
-               return load(getCached(url), allowTooOld, stable);
-       }
-
-       /**
-        * Open a resource from the cache if it exists.
-        * 
-        * @param cached
-        *            the resource to open
-        * @param allowTooOld
-        *            allow files even if they are considered too old
-        * @param stable
-        *            a stable file (that dones't change too often) -- parameter
-        *            used to check if the file is too old to keep or not
-        * 
-        * @return the opened resource if found, NULL if not
-        */
-       private InputStream load(File cached, boolean allowTooOld, boolean stable) {
-               if (cached.exists() && cached.isFile()
-                               && (allowTooOld || !isOld(cached, stable))) {
-                       try {
-                               return new MarkableFileInputStream(cached);
-                       } catch (FileNotFoundException e) {
-                               return null;
-                       }
-               }
-
-               return null;
-       }
-
-       /**
-        * Save the given resource to the cache.
-        * 
-        * @param in
-        *            the input data
-        * @param uniqueID
-        *            a unique ID used to locate the cached resource
-        * 
-        * @return the number of bytes written
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public long save(InputStream in, String uniqueID) throws IOException {
-               File cached = getCached(uniqueID);
-               cached.getParentFile().mkdirs();
-               return save(in, cached);
-       }
-
-       /**
-        * Save the given resource to the cache.
-        * 
-        * @param in
-        *            the input data
-        * @param url
-        *            the {@link URL} used to locate the cached resource
-        * 
-        * @return the number of bytes written
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public long save(InputStream in, URL url) throws IOException {
-               File cached = getCached(url);
-               return save(in, cached);
-       }
-
-       /**
-        * Save the given resource to the cache.
-        * <p>
-        * Will also clean the {@link Cache} from old files.
-        * 
-        * @param in
-        *            the input data
-        * @param cached
-        *            the cached {@link File} to save to
-        * 
-        * @return the number of bytes written
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       private long save(InputStream in, File cached) throws IOException {
-               // We want to force at least an immediate SAVE/LOAD to work for some
-               // workflows, even if we don't accept cached files (times set to "0"
-               // -- and not "-1" or a positive value)
-               clean(true, dir, 10);
-               cached.getParentFile().mkdirs(); // in case we deleted our own parent
-               long bytes = IOUtils.write(in, cached);
-               return bytes;
-       }
-
-       /**
-        * Remove the given resource from the cache.
-        * 
-        * @param uniqueID
-        *            a unique ID used to locate the cached resource
-        * 
-        * @return TRUE if it was removed
-        */
-       public boolean remove(String uniqueID) {
-               File cached = getCached(uniqueID);
-               return cached.delete();
-       }
-
-       /**
-        * Remove the given resource from the cache.
-        * 
-        * @param url
-        *            the {@link URL} used to locate the cached resource
-        * 
-        * @return TRUE if it was removed
-        */
-       public boolean remove(URL url) {
-               File cached = getCached(url);
-               return cached.delete();
-       }
-
-       /**
-        * Check if the {@link File} is too old according to
-        * {@link Cache#tooOldChanging}.
-        * 
-        * @param file
-        *            the file to check
-        * @param stable
-        *            TRUE to denote stable files, that are not supposed to change
-        *            too often
-        * 
-        * @return TRUE if it is
-        */
-       private boolean isOld(File file, boolean stable) {
-               long max = tooOldChanging;
-               if (stable) {
-                       max = tooOldStable;
-               }
-
-               if (max < 0) {
-                       return false;
-               }
-
-               long time = new Date().getTime() - file.lastModified();
-               if (time < 0) {
-                       tracer.error("Timestamp in the future for file: "
-                                       + file.getAbsolutePath());
-               }
-
-               return time < 0 || time > max;
-       }
-
-       /**
-        * Return the associated cache {@link File} from this {@link URL}.
-        * 
-        * @param url
-        *            the {@link URL}
-        * 
-        * @return the cached {@link File} version of this {@link URL}
-        */
-       private File getCached(URL url) {
-               File subdir;
-
-               String name = url.getHost();
-               if (name == null || name.isEmpty()) {
-                       // File
-                       File file = new File(url.getFile());
-                       if (file.getParent() == null) {
-                               subdir = new File("+");
-                       } else {
-                               subdir = new File(file.getParent().replace("..", "__"));
-                       }
-                       subdir = new File(dir, allowedChars(subdir.getPath()));
-                       name = allowedChars(url.getFile());
-               } else {
-                       // URL
-                       File subsubDir = new File(dir, allowedChars(url.getHost()));
-                       subdir = new File(subsubDir, "_" + allowedChars(url.getPath()));
-                       name = allowedChars("_" + url.getQuery());
-               }
-
-               File cacheFile = new File(subdir, name);
-               subdir.mkdirs();
-
-               return cacheFile;
-       }
-
-       /**
-        * Get the basic cache resource file corresponding to this unique ID.
-        * <p>
-        * Note that you may need to add a sub-directory in some cases.
-        * 
-        * @param uniqueID
-        *            the id
-        * 
-        * @return the cached version if present, NULL if not
-        */
-       private File getCached(String uniqueID) {
-               File file = new File(dir, allowedChars(uniqueID));
-               File subdir = new File(file.getParentFile(), "_");
-               return new File(subdir, file.getName());
-       }
-
-       /**
-        * Replace not allowed chars (in a {@link File}) by "_".
-        * 
-        * @param raw
-        *            the raw {@link String}
-        * 
-        * @return the sanitised {@link String}
-        */
-       private String allowedChars(String raw) {
-               return raw.replace('/', '_').replace(':', '_').replace("\\", "_");
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/CacheMemory.java b/src/be/nikiroo/utils/CacheMemory.java
deleted file mode 100644 (file)
index de4fae3..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A memory only version of {@link Cache}.
- * 
- * @author niki
- */
-public class CacheMemory extends Cache {
-       private Map<String, byte[]> data;
-
-       /**
-        * Create a new {@link CacheMemory}.
-        */
-       public CacheMemory() {
-               data = new HashMap<String, byte[]>();
-       }
-
-       @Override
-       public boolean check(String uniqueID, boolean allowTooOld, boolean stable) {
-               return data.containsKey(getKey(uniqueID));
-       }
-
-       @Override
-       public boolean check(URL url, boolean allowTooOld, boolean stable) {
-               return data.containsKey(getKey(url));
-       }
-
-       @Override
-       public int clean(boolean onlyOld) {
-               int cleaned = 0;
-               if (!onlyOld) {
-                       cleaned = data.size();
-                       data.clear();
-               }
-
-               return cleaned;
-       }
-
-       @Override
-       public InputStream load(String uniqueID, boolean allowTooOld, boolean stable) {
-               if (check(uniqueID, allowTooOld, stable)) {
-                       return load(getKey(uniqueID));
-               }
-
-               return null;
-       }
-
-       @Override
-       public InputStream load(URL url, boolean allowTooOld, boolean stable) {
-               if (check(url, allowTooOld, stable)) {
-                       return load(getKey(url));
-               }
-
-               return null;
-       }
-
-       @Override
-       public boolean remove(String uniqueID) {
-               return data.remove(getKey(uniqueID)) != null;
-       }
-
-       @Override
-       public boolean remove(URL url) {
-               return data.remove(getKey(url)) != null;
-       }
-
-       @Override
-       public long save(InputStream in, String uniqueID) throws IOException {
-               byte[] bytes = IOUtils.toByteArray(in);
-               data.put(getKey(uniqueID), bytes);
-               return bytes.length;
-       }
-
-       @Override
-       public long save(InputStream in, URL url) throws IOException {
-               byte[] bytes = IOUtils.toByteArray(in);
-               data.put(getKey(url), bytes);
-               return bytes.length;
-       }
-
-       /**
-        * Return a key mapping to the given unique ID.
-        * 
-        * @param uniqueID the unique ID
-        * 
-        * @return the key
-        */
-       private String getKey(String uniqueID) {
-               return "UID:" + uniqueID;
-       }
-
-       /**
-        * Return a key mapping to the given urm.
-        * 
-        * @param url the url
-        * 
-        * @return the key
-        */
-       private String getKey(URL url) {
-               return "URL:" + url.toString();
-       }
-
-       /**
-        * Load the given key.
-        * 
-        * @param key the key to load
-        * @return the loaded data
-        */
-       private InputStream load(String key) {
-               byte[] data = this.data.get(key);
-               if (data != null) {
-                       return new ByteArrayInputStream(data);
-               }
-
-               return null;
-       }
-}
diff --git a/src/be/nikiroo/utils/CookieUtils.java b/src/be/nikiroo/utils/CookieUtils.java
deleted file mode 100644 (file)
index 8d307a2..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-package be.nikiroo.utils;
-
-import java.util.Date;
-
-/**
- * Some utilities for cookie management.
- * 
- * @author niki
- */
-public class CookieUtils {
-       /**
-        * The number of seconds for the period (we accept the current or the
-        * previous period as valid for a cookie, via "offset").
-        */
-       static public int GRACE_PERIOD = 3600 * 1000; // between 1 and 2h
-
-       /**
-        * Generate a new cookie value from the user (email) and an offset.
-        * <p>
-        * You should use an offset of "0" when creating the cookie, and an offset
-        * of "0" or "-1" if required when checking for the value (the idea is to
-        * allow a cookie to persist across two timespans; if not, the cookie will
-        * be expired the very second we switch to a new timespan).
-        * 
-        * @param value
-        *            the value to generate a cookie for -- you must be able to
-        *            regenerate it in order to check it later
-        * @param offset
-        *            the offset (should be 0 for creating, 0 then -1 if needed for
-        *            checking)
-        * 
-        * @return the new cookie
-        */
-       static public String generateCookie(String value, int offset) {
-               long unixTime = (long) Math.floor(new Date().getTime() / GRACE_PERIOD)
-                               + offset;
-               return HashUtils.sha512(value + Long.toString(unixTime));
-       }
-
-       /**
-        * Check the given cookie.
-        * 
-        * @param value
-        *            the value to generate a cookie for -- you must be able to
-        *            regenerate it in order to check it later
-        * @param cookie
-        *            the cookie to validate
-        * 
-        * @return TRUE if it is correct
-        */
-       static public boolean validateCookie(String value, String cookie) {
-               if (cookie != null)
-                       cookie = cookie.trim();
-
-               String newCookie = generateCookie(value, 0);
-               if (!newCookie.equals(cookie)) {
-                       newCookie = generateCookie(value, -1);
-               }
-
-               return newCookie.equals(cookie);
-       }
-}
diff --git a/src/be/nikiroo/utils/CryptUtils.java b/src/be/nikiroo/utils/CryptUtils.java
deleted file mode 100644 (file)
index 638f82f..0000000
+++ /dev/null
@@ -1,441 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.security.InvalidKeyException;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.net.ssl.SSLException;
-
-import be.nikiroo.utils.streams.Base64InputStream;
-import be.nikiroo.utils.streams.Base64OutputStream;
-
-/**
- * Small utility class to do AES encryption/decryption.
- * <p>
- * It is multi-thread compatible, but beware:
- * <ul>
- * <li>The encrypt/decrypt calls are serialized</li>
- * <li>The streams are independent (and thus parallel)</li>
- * </ul>
- * <p>
- * Do not assume it is secure; it just here to offer a more-or-less protected
- * exchange of data because anonymous and self-signed certificates backed SSL is
- * against Google wishes, and I need Android support.
- * 
- * @author niki
- */
-public class CryptUtils {
-       static private final String AES_NAME = "AES/CFB128/NoPadding";
-
-       private Cipher ecipher;
-       private Cipher dcipher;
-       private byte[] bytes32;
-
-       /**
-        * Small and lazy-easy way to initialize a 128 bits key with
-        * {@link CryptUtils}.
-        * <p>
-        * <b>Some</b> part of the key will be used to generate a 128 bits key and
-        * initialize the {@link CryptUtils}; even NULL will generate something.
-        * <p>
-        * <b>This is most probably not secure. Do not use if you actually care
-        * about security.</b>
-        * 
-        * @param key
-        *            the {@link String} to use as a base for the key, can be NULL
-        */
-       public CryptUtils(String key) {
-               try {
-                       init(key2key(key));
-               } catch (InvalidKeyException e) {
-                       // We made sure that the key is correct, so nothing here
-                       e.printStackTrace();
-               }
-       }
-
-       /**
-        * Create a new instance of {@link CryptUtils} with the given 128 bits key.
-        * <p>
-        * The key <b>must</b> be exactly 128 bits long.
-        * 
-        * @param bytes32
-        *            the 128 bits (32 bytes) of the key
-        * 
-        * @throws InvalidKeyException
-        *             if the key is not an array of 128 bits
-        */
-       public CryptUtils(byte[] bytes32) throws InvalidKeyException {
-               init(bytes32);
-       }
-
-       /**
-        * Wrap the given {@link InputStream} so it is transparently encrypted by
-        * the current {@link CryptUtils}.
-        * 
-        * @param in
-        *            the {@link InputStream} to wrap
-        * @return the auto-encode {@link InputStream}
-        */
-       public InputStream encrypt(InputStream in) {
-               Cipher ecipher = newCipher(Cipher.ENCRYPT_MODE);
-               return new CipherInputStream(in, ecipher);
-       }
-
-       /**
-        * Wrap the given {@link InputStream} so it is transparently encrypted by
-        * the current {@link CryptUtils} and encoded in base64.
-        * 
-        * @param in
-        *            the {@link InputStream} to wrap
-        * 
-        * @return the auto-encode {@link InputStream}
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public InputStream encrypt64(InputStream in) throws IOException {
-               return new Base64InputStream(encrypt(in), true);
-       }
-
-       /**
-        * Wrap the given {@link OutputStream} so it is transparently encrypted by
-        * the current {@link CryptUtils}.
-        * 
-        * @param out
-        *            the {@link OutputStream} to wrap
-        * 
-        * @return the auto-encode {@link OutputStream}
-        */
-       public OutputStream encrypt(OutputStream out) {
-               Cipher ecipher = newCipher(Cipher.ENCRYPT_MODE);
-               return new CipherOutputStream(out, ecipher);
-       }
-
-       /**
-        * Wrap the given {@link OutputStream} so it is transparently encrypted by
-        * the current {@link CryptUtils} and encoded in base64.
-        * 
-        * @param out
-        *            the {@link OutputStream} to wrap
-        * 
-        * @return the auto-encode {@link OutputStream}
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public OutputStream encrypt64(OutputStream out) throws IOException {
-               return encrypt(new Base64OutputStream(out, true));
-       }
-
-       /**
-        * Wrap the given {@link OutputStream} so it is transparently decoded by the
-        * current {@link CryptUtils}.
-        * 
-        * @param in
-        *            the {@link InputStream} to wrap
-        * 
-        * @return the auto-decode {@link InputStream}
-        */
-       public InputStream decrypt(InputStream in) {
-               Cipher dcipher = newCipher(Cipher.DECRYPT_MODE);
-               return new CipherInputStream(in, dcipher);
-       }
-
-       /**
-        * Wrap the given {@link OutputStream} so it is transparently decoded by the
-        * current {@link CryptUtils} and decoded from base64.
-        * 
-        * @param in
-        *            the {@link InputStream} to wrap
-        * 
-        * @return the auto-decode {@link InputStream}
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public InputStream decrypt64(InputStream in) throws IOException {
-               return decrypt(new Base64InputStream(in, false));
-       }
-
-       /**
-        * Wrap the given {@link OutputStream} so it is transparently decoded by the
-        * current {@link CryptUtils}.
-        * 
-        * @param out
-        *            the {@link OutputStream} to wrap
-        * @return the auto-decode {@link OutputStream}
-        */
-       public OutputStream decrypt(OutputStream out) {
-               Cipher dcipher = newCipher(Cipher.DECRYPT_MODE);
-               return new CipherOutputStream(out, dcipher);
-       }
-
-       /**
-        * Wrap the given {@link OutputStream} so it is transparently decoded by the
-        * current {@link CryptUtils} and decoded from base64.
-        * 
-        * @param out
-        *            the {@link OutputStream} to wrap
-        * 
-        * @return the auto-decode {@link OutputStream}
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public OutputStream decrypt64(OutputStream out) throws IOException {
-               return new Base64OutputStream(decrypt(out), false);
-       }
-
-       /**
-        * This method required an array of 128 bits.
-        * 
-        * @param bytes32
-        *            the array, which <b>must</b> be of 128 bits (32 bytes)
-        * 
-        * @throws InvalidKeyException
-        *             if the key is not an array of 128 bits (32 bytes)
-        */
-       private void init(byte[] bytes32) throws InvalidKeyException {
-               if (bytes32 == null || bytes32.length != 32) {
-                       throw new InvalidKeyException(
-                                       "The size of the key must be of 128 bits (32 bytes), it is: "
-                                                       + (bytes32 == null ? "null" : "" + bytes32.length)
-                                                       + " bytes");
-               }
-
-               this.bytes32 = bytes32;
-               this.ecipher = newCipher(Cipher.ENCRYPT_MODE);
-               this.dcipher = newCipher(Cipher.DECRYPT_MODE);
-       }
-
-       /**
-        * Create a new {@link Cipher}of the given mode (see
-        * {@link Cipher#ENCRYPT_MODE} and {@link Cipher#ENCRYPT_MODE}).
-        * 
-        * @param mode
-        *            the mode ({@link Cipher#ENCRYPT_MODE} or
-        *            {@link Cipher#ENCRYPT_MODE})
-        * 
-        * @return the new {@link Cipher}
-        */
-       private Cipher newCipher(int mode) {
-               try {
-                       // bytes32 = 32 bytes, 32 > 16
-                       byte[] iv = new byte[16];
-                       for (int i = 0; i < iv.length; i++) {
-                               iv[i] = bytes32[i];
-                       }
-                       IvParameterSpec ivspec = new IvParameterSpec(iv);
-                       Cipher cipher = Cipher.getInstance(AES_NAME);
-                       cipher.init(mode, new SecretKeySpec(bytes32, "AES"), ivspec);
-                       return cipher;
-               } catch (Exception e) {
-                       e.printStackTrace();
-                       throw new RuntimeException(
-                                       "Cannot initialize encryption sub-system", e);
-               }
-       }
-
-       /**
-        * Encrypt the data.
-        * 
-        * @param data
-        *            the data to encrypt
-        * 
-        * @return the encrypted data
-        * 
-        * @throws SSLException
-        *             in case of I/O error (i.e., the data is not what you assumed
-        *             it was)
-        */
-       public byte[] encrypt(byte[] data) throws SSLException {
-               synchronized (ecipher) {
-                       try {
-                               return ecipher.doFinal(data);
-                       } catch (IllegalBlockSizeException e) {
-                               throw new SSLException(e);
-                       } catch (BadPaddingException e) {
-                               throw new SSLException(e);
-                       }
-               }
-       }
-
-       /**
-        * Encrypt the data.
-        * 
-        * @param data
-        *            the data to encrypt
-        * 
-        * @return the encrypted data
-        * 
-        * @throws SSLException
-        *             in case of I/O error (i.e., the data is not what you assumed
-        *             it was)
-        */
-       public byte[] encrypt(String data) throws SSLException {
-               return encrypt(StringUtils.getBytes(data));
-       }
-
-       /**
-        * Encrypt the data, then encode it into Base64.
-        * 
-        * @param data
-        *            the data to encrypt
-        * @param zip
-        *            TRUE to also compress the data in GZIP format; remember that
-        *            compressed and not-compressed content are different; you need
-        *            to know which is which when decoding
-        * 
-        * @return the encrypted data, encoded in Base64
-        * 
-        * @throws SSLException
-        *             in case of I/O error (i.e., the data is not what you assumed
-        *             it was)
-        */
-       public String encrypt64(String data) throws SSLException {
-               return encrypt64(StringUtils.getBytes(data));
-       }
-
-       /**
-        * Encrypt the data, then encode it into Base64.
-        * 
-        * @param data
-        *            the data to encrypt
-        * 
-        * @return the encrypted data, encoded in Base64
-        * 
-        * @throws SSLException
-        *             in case of I/O error (i.e., the data is not what you assumed
-        *             it was)
-        */
-       public String encrypt64(byte[] data) throws SSLException {
-               try {
-                       return StringUtils.base64(encrypt(data));
-               } catch (IOException e) {
-                       // not exactly true, but we consider here that this error is a crypt
-                       // error, not a normal I/O error
-                       throw new SSLException(e);
-               }
-       }
-
-       /**
-        * Decode the data which is assumed to be encrypted with the same utilities.
-        * 
-        * @param data
-        *            the encrypted data to decode
-        * 
-        * @return the original, decoded data
-        * 
-        * @throws SSLException
-        *             in case of I/O error
-        */
-       public byte[] decrypt(byte[] data) throws SSLException {
-               synchronized (dcipher) {
-                       try {
-                               return dcipher.doFinal(data);
-                       } catch (IllegalBlockSizeException e) {
-                               throw new SSLException(e);
-                       } catch (BadPaddingException e) {
-                               throw new SSLException(e);
-                       }
-               }
-       }
-
-       /**
-        * Decode the data which is assumed to be encrypted with the same utilities
-        * and to be a {@link String}.
-        * 
-        * @param data
-        *            the encrypted data to decode
-        * 
-        * @return the original, decoded data,as a {@link String}
-        * 
-        * @throws SSLException
-        *             in case of I/O error
-        */
-       public String decrypts(byte[] data) throws SSLException {
-               try {
-                       return new String(decrypt(data), "UTF-8");
-               } catch (UnsupportedEncodingException e) {
-                       // UTF-8 is required in all conform JVMs
-                       e.printStackTrace();
-                       return null;
-               }
-       }
-
-       /**
-        * Decode the data which is assumed to be encrypted with the same utilities
-        * and is a Base64 encoded value.
-        * 
-        * @param data
-        *            the encrypted data to decode in Base64 format
-        * @param zip
-        *            TRUE to also uncompress the data from a GZIP format
-        *            automatically; if set to FALSE, zipped data can be returned
-        * 
-        * @return the original, decoded data
-        * 
-        * @throws SSLException
-        *             in case of I/O error
-        */
-       public byte[] decrypt64(String data) throws SSLException {
-               try {
-                       return decrypt(StringUtils.unbase64(data));
-               } catch (IOException e) {
-                       // not exactly true, but we consider here that this error is a crypt
-                       // error, not a normal I/O error
-                       throw new SSLException(e);
-               }
-       }
-
-       /**
-        * Decode the data which is assumed to be encrypted with the same utilities
-        * and is a Base64 encoded value, then convert it into a String (this method
-        * assumes the data <b>was</b> indeed a UTF-8 encoded {@link String}).
-        * 
-        * @param data
-        *            the encrypted data to decode in Base64 format
-        * @param zip
-        *            TRUE to also uncompress the data from a GZIP format
-        *            automatically; if set to FALSE, zipped data can be returned
-        * 
-        * @return the original, decoded data
-        * 
-        * @throws SSLException
-        *             in case of I/O error
-        */
-       public String decrypt64s(String data) throws SSLException {
-               try {
-                       return new String(decrypt(StringUtils.unbase64(data)), "UTF-8");
-               } catch (UnsupportedEncodingException e) {
-                       // UTF-8 is required in all conform JVMs
-                       e.printStackTrace();
-                       return null;
-               } catch (IOException e) {
-                       // not exactly true, but we consider here that this error is a crypt
-                       // error, not a normal I/O error
-                       throw new SSLException(e);
-               }
-       }
-
-       /**
-        * This is probably <b>NOT</b> secure!
-        * 
-        * @param input
-        *            some {@link String} input
-        * 
-        * @return a 128 bits key computed from the given input
-        */
-       static private byte[] key2key(String input) {
-               return StringUtils.getMd5Hash("" + input).getBytes();
-       }
-}
diff --git a/src/be/nikiroo/utils/Downloader.java b/src/be/nikiroo/utils/Downloader.java
deleted file mode 100644 (file)
index 4191d0a..0000000
+++ /dev/null
@@ -1,480 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.CookiePolicy;
-import java.net.CookieStore;
-import java.net.HttpCookie;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLEncoder;
-import java.util.Map;
-import java.util.zip.GZIPInputStream;
-
-/**
- * This class will help you download content from Internet Sites ({@link URL}
- * based).
- * <p>
- * It allows you to control some options often required on web sites that do not
- * want to simply serve HTML, but actively makes your life difficult with stupid
- * checks.
- * 
- * @author niki
- */
-public class Downloader {
-       private String UA;
-       private CookieManager cookies;
-       private TraceHandler tracer = new TraceHandler();
-       private Cache cache;
-       private boolean offline;
-
-       /**
-        * Create a new {@link Downloader}.
-        * 
-        * @param UA
-        *            the User-Agent to use to download the resources -- note that
-        *            some websites require one, some actively blacklist real UAs
-        *            like the one from wget, some whitelist a couple of browsers
-        *            only (!) -- can be NULL
-        */
-       public Downloader(String UA) {
-               this(UA, null);
-       }
-
-       /**
-        * Create a new {@link Downloader}.
-        * 
-        * @param UA
-        *            the User-Agent to use to download the resources -- note that
-        *            some websites require one, some actively blacklist real UAs
-        *            like the one from wget, some whitelist a couple of browsers
-        *            only (!) -- can be NULL
-        * @param cache
-        *            the {@link Cache} to use for all access (can be NULL)
-        */
-       public Downloader(String UA, Cache cache) {
-               this.UA = UA;
-
-               cookies = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
-               CookieHandler.setDefault(cookies);
-
-               setCache(cache);
-       }
-       
-       /**
-        * This {@link Downloader} is forbidden to try and connect to the network.
-        * <p>
-        * If TRUE, it will only check the cache if any.
-        * <p>
-        * Default is FALSE.
-        * 
-        * @return TRUE if offline
-        */
-       public boolean isOffline() {
-               return offline;
-       }
-       
-       /**
-        * This {@link Downloader} is forbidden to try and connect to the network.
-        * <p>
-        * If TRUE, it will only check the cache if any.
-        * <p>
-        * Default is FALSE.
-        * 
-        * @param offline TRUE for offline, FALSE for online
-        */
-       public void setOffline(boolean offline) {
-               this.offline = offline;
-       }
-
-       /**
-        * The traces handler for this {@link Cache}.
-        * 
-        * @return the traces handler
-        */
-       public TraceHandler getTraceHandler() {
-               return tracer;
-       }
-
-       /**
-        * The traces handler for this {@link Cache}.
-        * 
-        * @param tracer
-        *            the new traces handler
-        */
-       public void setTraceHandler(TraceHandler tracer) {
-               if (tracer == null) {
-                       tracer = new TraceHandler(false, false, false);
-               }
-
-               this.tracer = tracer;
-       }
-
-       /**
-        * The {@link Cache} to use for all access (can be NULL).
-        * 
-        * @return the cache
-        */
-       public Cache getCache() {
-               return cache;
-       }
-
-       /**
-        * The {@link Cache} to use for all access (can be NULL).
-        * 
-        * @param cache
-        *            the new cache
-        */
-       public void setCache(Cache cache) {
-               this.cache = cache;
-       }
-
-       /**
-        * Clear all the cookies currently in the jar.
-        * <p>
-        * As long as you don't, the cookies are kept.
-        */
-       public void clearCookies() {
-               cookies.getCookieStore().removeAll();
-       }
-
-       /**
-        * Open the given {@link URL} and update the cookies.
-        * 
-        * @param url
-        *            the {@link URL} to open
-        * @return the {@link InputStream} of the opened page
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        **/
-       public InputStream open(URL url) throws IOException {
-               return open(url, false);
-       }
-
-       /**
-        * Open the given {@link URL} and update the cookies.
-        * 
-        * @param url
-        *            the {@link URL} to open
-        * @param stable
-        *            stable a stable file (that doesn't change too often) --
-        *            parameter used to check if the file is too old to keep or not
-        *            in the cache (default is false)
-        * 
-        * @return the {@link InputStream} of the opened page
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        **/
-       public InputStream open(URL url, boolean stable) throws IOException {
-               return open(url, url, url, null, null, null, null, stable);
-       }
-
-       /**
-        * Open the given {@link URL} and update the cookies.
-        * 
-        * @param url
-        *            the {@link URL} to open
-        * @param currentReferer
-        *            the current referer, for websites that needs this info
-        * @param cookiesValues
-        *            the cookies
-        * @param postParams
-        *            the POST parameters
-        * @param getParams
-        *            the GET parameters (priority over POST)
-        * @param oauth
-        *            OAuth authorization (aka, "bearer XXXXXXX")
-        * 
-        * @return the {@link InputStream} of the opened page
-        * 
-        * @throws IOException
-        *             in case of I/O error (including offline mode + not in cache)
-        */
-       public InputStream open(URL url, URL currentReferer,
-                       Map<String, String> cookiesValues, Map<String, String> postParams,
-                       Map<String, String> getParams, String oauth) throws IOException {
-               return open(url, currentReferer, cookiesValues, postParams, getParams,
-                               oauth, false);
-       }
-
-       /**
-        * Open the given {@link URL} and update the cookies.
-        * 
-        * @param url
-        *            the {@link URL} to open
-        * @param currentReferer
-        *            the current referer, for websites that needs this info
-        * @param cookiesValues
-        *            the cookies
-        * @param postParams
-        *            the POST parameters
-        * @param getParams
-        *            the GET parameters (priority over POST)
-        * @param oauth
-        *            OAuth authorization (aka, "bearer XXXXXXX")
-        * @param stable
-        *            stable a stable file (that doesn't change too often) --
-        *            parameter used to check if the file is too old to keep or not
-        *            in the cache (default is false)
-        * 
-        * @return the {@link InputStream} of the opened page
-        * 
-        * @throws IOException
-        *             in case of I/O error (including offline mode + not in cache)
-        */
-       public InputStream open(URL url, URL currentReferer,
-                       Map<String, String> cookiesValues, Map<String, String> postParams,
-                       Map<String, String> getParams, String oauth, boolean stable)
-                       throws IOException {
-               return open(url, url, currentReferer, cookiesValues, postParams,
-                               getParams, oauth, stable);
-       }
-
-       /**
-        * Open the given {@link URL} and update the cookies.
-        * 
-        * @param url
-        *            the {@link URL} to open
-        * @param originalUrl
-        *            the original {@link URL} before any redirection occurs, which
-        *            is also used for the cache ID if needed (so we can retrieve
-        *            the content with this URL if needed)
-        * @param currentReferer
-        *            the current referer, for websites that needs this info
-        * @param cookiesValues
-        *            the cookies
-        * @param postParams
-        *            the POST parameters
-        * @param getParams
-        *            the GET parameters (priority over POST)
-        * @param oauth
-        *            OAuth authorisation (aka, "bearer XXXXXXX")
-        * @param stable
-        *            a stable file (that doesn't change too often) -- parameter
-        *            used to check if the file is too old to keep or not in the
-        *            cache
-        * 
-        * @return the {@link InputStream} of the opened page
-        * 
-        * @throws IOException
-        *             in case of I/O error (including offline mode + not in cache)
-        */
-       public InputStream open(URL url, final URL originalUrl, URL currentReferer,
-                       Map<String, String> cookiesValues, Map<String, String> postParams,
-                       Map<String, String> getParams, String oauth, boolean stable)
-                       throws IOException {
-
-               tracer.trace("Request: " + url);
-
-               if (cache != null) {
-                       InputStream in = cache.load(originalUrl, false, stable);
-                       if (in != null) {
-                               tracer.trace("Use the cache: " + url);
-                               tracer.trace("Original URL : " + originalUrl);
-                               return in;
-                       }
-               }
-
-               String protocol = originalUrl == null ? null : originalUrl
-                               .getProtocol();
-               if (isOffline() && !"file".equalsIgnoreCase(protocol)) {
-                       tracer.error("Downloader OFFLINE, cannot proceed to URL: " + url);
-                       throw new IOException("Downloader is currently OFFLINE, cannot download: " + url);
-               }
-               
-               tracer.trace("Download: " + url);
-
-               URLConnection conn = openConnectionWithCookies(url, currentReferer,
-                               cookiesValues);
-
-               // Priority: GET over POST
-               Map<String, String> params = getParams;
-               if (getParams == null) {
-                       params = postParams;
-               }
-
-               StringBuilder requestData = null;
-               if ((params != null || oauth != null)
-                               && conn instanceof HttpURLConnection) {
-                       if (params != null) {
-                               requestData = new StringBuilder();
-                               for (Map.Entry<String, String> param : params.entrySet()) {
-                                       if (requestData.length() != 0)
-                                               requestData.append('&');
-                                       requestData.append(URLEncoder.encode(param.getKey(),
-                                                       "UTF-8"));
-                                       requestData.append('=');
-                                       requestData.append(URLEncoder.encode(
-                                                       String.valueOf(param.getValue()), "UTF-8"));
-                               }
-
-                               if (getParams == null && postParams != null) {
-                                       ((HttpURLConnection) conn).setRequestMethod("POST");
-                               }
-
-                               conn.setRequestProperty("Content-Type",
-                                               "application/x-www-form-urlencoded");
-                               conn.setRequestProperty("Content-Length",
-                                               Integer.toString(requestData.length()));
-                       }
-
-                       if (oauth != null) {
-                               conn.setRequestProperty("Authorization", oauth);
-                       }
-
-                       if (requestData != null) {
-                               conn.setDoOutput(true);
-                               OutputStreamWriter writer = new OutputStreamWriter(
-                                               conn.getOutputStream());
-                               try {
-                                       writer.write(requestData.toString());
-                                       writer.flush();
-                               } finally {
-                                       writer.close();
-                               }
-                       }
-               }
-
-               // Manual redirection, much better for POST data
-               if (conn instanceof HttpURLConnection) {
-                       ((HttpURLConnection) conn).setInstanceFollowRedirects(false);
-               }
-
-               conn.connect();
-
-               // Check if redirect
-               // BEWARE! POST data cannot be redirected (some webservers complain) for
-               // HTTP codes 302 and 303
-               if (conn instanceof HttpURLConnection) {
-                       int repCode = 0;
-                       try {
-                               // Can fail in some circumstances
-                               repCode = ((HttpURLConnection) conn).getResponseCode();
-                       } catch (IOException e) {
-                       }
-
-                       if (repCode / 100 == 3) {
-                               String newUrl = conn.getHeaderField("Location");
-                               return open(new URL(newUrl), originalUrl, currentReferer,
-                                               cookiesValues, //
-                                               (repCode == 302 || repCode == 303) ? null : postParams, //
-                                               getParams, oauth, stable);
-                       }
-               }
-
-               try {
-                       InputStream in = conn.getInputStream();
-                       if ("gzip".equals(conn.getContentEncoding())) {
-                               in = new GZIPInputStream(in);
-                       }
-
-                       if (in == null) {
-                               throw new IOException("No InputStream!");
-                       }
-
-                       if (cache != null) {
-                               String size = conn.getContentLength() < 0 ? "unknown size"
-                                               : StringUtils.formatNumber(conn.getContentLength())
-                                                               + "bytes";
-                               tracer.trace("Save to cache (" + size + "): " + originalUrl);
-                               try {
-                                       try {
-                                               long bytes = cache.save(in, originalUrl);
-                                               tracer.trace("Saved to cache: "
-                                                               + StringUtils.formatNumber(bytes) + "bytes");
-                                       } finally {
-                                               in.close();
-                                       }
-                                       in = cache.load(originalUrl, true, true);
-                               } catch (IOException e) {
-                                       tracer.error(new IOException(
-                                                       "Cannot save URL to cache, will ignore cache: "
-                                                                       + url, e));
-                               }
-                       }
-
-                       if (in == null) {
-                               throw new IOException(
-                                               "Cannot retrieve the file after storing it in the cache (??)");
-                       }
-                       
-                       return in;
-               } catch (IOException e) {
-                       throw new IOException(String.format(
-                                       "Cannot find %s (current URL: %s)", originalUrl, url), e);
-               }
-       }
-
-       /**
-        * Open a connection on the given {@link URL}, and manage the cookies that
-        * come with it.
-        * 
-        * @param url
-        *            the {@link URL} to open
-        * 
-        * @return the connection
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       private URLConnection openConnectionWithCookies(URL url,
-                       URL currentReferer, Map<String, String> cookiesValues)
-                       throws IOException {
-               URLConnection conn = url.openConnection();
-
-               String cookies = generateCookies(cookiesValues);
-               if (cookies != null && !cookies.isEmpty()) {
-                       conn.setRequestProperty("Cookie", cookies);
-               }
-
-               if (UA != null) {
-                       conn.setRequestProperty("User-Agent", UA);
-               }
-               conn.setRequestProperty("Accept-Encoding", "gzip");
-               conn.setRequestProperty("Accept", "*/*");
-               conn.setRequestProperty("Charset", "utf-8");
-
-               if (currentReferer != null) {
-                       conn.setRequestProperty("Referer", currentReferer.toString());
-                       conn.setRequestProperty("Host", currentReferer.getHost());
-               }
-
-               return conn;
-       }
-
-       /**
-        * Generate the cookie {@link String} from the local {@link CookieStore} so
-        * it is ready to be passed.
-        * 
-        * @return the cookie
-        */
-       private String generateCookies(Map<String, String> cookiesValues) {
-               StringBuilder builder = new StringBuilder();
-               for (HttpCookie cookie : cookies.getCookieStore().getCookies()) {
-                       if (builder.length() > 0) {
-                               builder.append(';');
-                       }
-
-                       builder.append(cookie.toString());
-               }
-
-               if (cookiesValues != null) {
-                       for (Map.Entry<String, String> set : cookiesValues.entrySet()) {
-                               if (builder.length() > 0) {
-                                       builder.append(';');
-                               }
-                               builder.append(set.getKey());
-                               builder.append('=');
-                               builder.append(set.getValue());
-                       }
-               }
-
-               return builder.toString();
-       }
-}
diff --git a/src/be/nikiroo/utils/HashUtils.java b/src/be/nikiroo/utils/HashUtils.java
deleted file mode 100644 (file)
index df8d7c6..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Small class to easily hash some values in a few different ways.
- * <p>
- * Does <b>not</b> handle the salt itself, you have to add it yourself.
- * 
- * @author niki
- */
-public class HashUtils {
-       /**
-        * Hash the given value.
-        * 
-        * @param value
-        *            the value to hash
-        * 
-        * @return the hash that can be used to confirm a value
-        * 
-        * @throws RuntimeException
-        *             if UTF-8 support is not available (!) or SHA-512 support is
-        *             not available
-        * @throws NullPointerException
-        *             if email or pass is NULL
-        */
-       static public String sha512(String value) {
-               return hash("SHA-512", value);
-       }
-
-       /**
-        * Hash the given value.
-        * 
-        * @param value
-        *            the value to hash
-        * 
-        * @return the hash that can be used to confirm the a value
-        * 
-        * @throws RuntimeException
-        *             if UTF-8 support is not available (!) or MD5 support is not
-        *             available
-        * @throws NullPointerException
-        *             if email or pass is NULL
-        */
-       static public String md5(String value) {
-               return hash("MD5", value);
-       }
-
-       /**
-        * Hash the given value.
-        * 
-        * @param algo
-        *            the hash algorithm to use ("MD5" and "SHA-512" are supported)
-        * @param value
-        *            the value to hash
-        * 
-        * @return the hash that can be used to confirm a value
-        * 
-        * @throws RuntimeException
-        *             if UTF-8 support is not available (!) or the algorithm
-        *             support is not available
-        * @throws NullPointerException
-        *             if email or pass is NULL
-        */
-       static private String hash(String algo, String value) {
-               try {
-                       MessageDigest md = MessageDigest.getInstance(algo);
-                       md.update(value.getBytes("UTF-8"));
-                       byte byteData[] = md.digest();
-
-                       StringBuffer hexString = new StringBuffer();
-                       for (int i = 0; i < byteData.length; i++) {
-                               String hex = Integer.toHexString(0xff & byteData[i]);
-                               if (hex.length() % 2 == 1)
-                                       hexString.append('0');
-                               hexString.append(hex);
-                       }
-
-                       return hexString.toString();
-               } catch (NoSuchAlgorithmException e) {
-                       throw new RuntimeException(algo + " hashing not available", e);
-               } catch (UnsupportedEncodingException e) {
-                       throw new RuntimeException(
-                                       "UTF-8 encoding is required in a compatible JVM", e);
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/IOUtils.java b/src/be/nikiroo/utils/IOUtils.java
deleted file mode 100644 (file)
index 439964d..0000000
+++ /dev/null
@@ -1,521 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-
-import be.nikiroo.utils.streams.MarkableFileInputStream;
-
-/**
- * This class offer some utilities based around Streams and Files.
- * 
- * @author niki
- */
-public class IOUtils {
-       /**
-        * Write the data to the given {@link File}.
-        * 
-        * @param in
-        *            the data source
-        * @param target
-        *            the target {@link File}
-        * 
-        * @return the number of bytes written
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static long write(InputStream in, File target) throws IOException {
-               OutputStream out = new FileOutputStream(target);
-               try {
-                       return write(in, out);
-               } finally {
-                       out.close();
-               }
-       }
-
-       /**
-        * Write the data to the given {@link OutputStream}.
-        * 
-        * @param in
-        *            the data source
-        * @param out
-        *            the target {@link OutputStream}
-        * 
-        * @return the number of bytes written
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static long write(InputStream in, OutputStream out)
-                       throws IOException {
-               long written = 0;
-               byte buffer[] = new byte[4096];
-               int len = in.read(buffer);
-               while (len > -1) {
-                       out.write(buffer, 0, len);
-                       written += len;
-                       len = in.read(buffer);
-               }
-
-               return written;
-       }
-
-       /**
-        * Recursively Add a {@link File} (which can thus be a directory, too) to a
-        * {@link ZipOutputStream}.
-        * 
-        * @param zip
-        *            the stream
-        * @param base
-        *            the path to prepend to the ZIP info before the actual
-        *            {@link File} path
-        * @param target
-        *            the source {@link File} (which can be a directory)
-        * @param targetIsRoot
-        *            FALSE if we need to add a {@link ZipEntry} for base/target,
-        *            TRUE to add it at the root of the ZIP
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static void zip(ZipOutputStream zip, String base, File target,
-                       boolean targetIsRoot) throws IOException {
-               if (target.isDirectory()) {
-                       if (!targetIsRoot) {
-                               if (base == null || base.isEmpty()) {
-                                       base = target.getName();
-                               } else {
-                                       base += "/" + target.getName();
-                               }
-                               zip.putNextEntry(new ZipEntry(base + "/"));
-                       }
-
-                       File[] files = target.listFiles();
-                       if (files != null) {
-                               for (File file : files) {
-                                       zip(zip, base, file, false);
-                               }
-                       }
-               } else {
-                       if (base == null || base.isEmpty()) {
-                               base = target.getName();
-                       } else {
-                               base += "/" + target.getName();
-                       }
-                       zip.putNextEntry(new ZipEntry(base));
-                       FileInputStream in = new FileInputStream(target);
-                       try {
-                               IOUtils.write(in, zip);
-                       } finally {
-                               in.close();
-                       }
-               }
-       }
-
-       /**
-        * Zip the given source into dest.
-        * 
-        * @param src
-        *            the source {@link File} (which can be a directory)
-        * @param dest
-        *            the destination <tt>.zip</tt> file
-        * @param srcIsRoot
-        *            FALSE if we need to add a {@link ZipEntry} for src, TRUE to
-        *            add it at the root of the ZIP
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static void zip(File src, File dest, boolean srcIsRoot)
-                       throws IOException {
-               OutputStream out = new FileOutputStream(dest);
-               try {
-                       ZipOutputStream zip = new ZipOutputStream(out);
-                       try {
-                               IOUtils.zip(zip, "", src, srcIsRoot);
-                       } finally {
-                               zip.close();
-                       }
-               } finally {
-                       out.close();
-               }
-       }
-
-       /**
-        * Unzip the given ZIP file into the target directory.
-        * 
-        * @param zipFile
-        *            the ZIP file
-        * @param targetDirectory
-        *            the target directory
-        * 
-        * @return the number of extracted files (not directories)
-        * 
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       public static long unzip(File zipFile, File targetDirectory)
-                       throws IOException {
-               long count = 0;
-
-               if (targetDirectory.exists() && targetDirectory.isFile()) {
-                       throw new IOException("Cannot unzip " + zipFile + " into "
-                                       + targetDirectory + ": it is not a directory");
-               }
-
-               targetDirectory.mkdir();
-               if (!targetDirectory.exists()) {
-                       throw new IOException("Cannot create target directory "
-                                       + targetDirectory);
-               }
-
-               FileInputStream in = new FileInputStream(zipFile);
-               try {
-                       ZipInputStream zipStream = new ZipInputStream(in);
-                       try {
-                               for (ZipEntry entry = zipStream.getNextEntry(); entry != null; entry = zipStream
-                                               .getNextEntry()) {
-                                       File file = new File(targetDirectory, entry.getName());
-                                       if (entry.isDirectory()) {
-                                               file.mkdirs();
-                                       } else {
-                                               IOUtils.write(zipStream, file);
-                                               count++;
-                                       }
-                               }
-                       } finally {
-                               zipStream.close();
-                       }
-               } finally {
-                       in.close();
-               }
-
-               return count;
-       }
-
-       /**
-        * Write the {@link String} content to {@link File}.
-        * 
-        * @param dir
-        *            the directory where to write the {@link File}
-        * @param filename
-        *            the {@link File} name
-        * @param content
-        *            the content
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static void writeSmallFile(File dir, String filename, String content)
-                       throws IOException {
-               if (!dir.exists()) {
-                       dir.mkdirs();
-               }
-
-               writeSmallFile(new File(dir, filename), content);
-       }
-
-       /**
-        * Write the {@link String} content to {@link File}.
-        * 
-        * @param file
-        *            the {@link File} to write
-        * @param content
-        *            the content
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static void writeSmallFile(File file, String content)
-                       throws IOException {
-               FileOutputStream out = new FileOutputStream(file);
-               try {
-                       out.write(StringUtils.getBytes(content));
-               } finally {
-                       out.close();
-               }
-       }
-
-       /**
-        * Read the whole {@link File} content into a {@link String}.
-        * 
-        * @param file
-        *            the {@link File}
-        * 
-        * @return the content
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static String readSmallFile(File file) throws IOException {
-               InputStream stream = new FileInputStream(file);
-               try {
-                       return readSmallStream(stream);
-               } finally {
-                       stream.close();
-               }
-       }
-
-       /**
-        * Read the whole {@link InputStream} content into a {@link String}.
-        * 
-        * @param stream
-        *            the {@link InputStream}
-        * 
-        * @return the content
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static String readSmallStream(InputStream stream) throws IOException {
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               try {
-                       write(stream, out);
-                       return out.toString("UTF-8");
-               } finally {
-                       out.close();
-               }
-       }
-
-       /**
-        * Recursively delete the given {@link File}, which may of course also be a
-        * directory.
-        * <p>
-        * Will either silently continue or throw an exception in case of error,
-        * depending upon the parameters.
-        * 
-        * @param target
-        *            the target to delete
-        * @param exception
-        *            TRUE to throw an {@link IOException} in case of error, FALSE
-        *            to silently continue
-        * 
-        * @return TRUE if all files were deleted, FALSE if an error occurred
-        * 
-        * @throws IOException
-        *             if an error occurred and the parameters allow an exception to
-        *             be thrown
-        */
-       public static boolean deltree(File target, boolean exception)
-                       throws IOException {
-               List<File> list = deltree(target, null);
-               if (exception && !list.isEmpty()) {
-                       StringBuilder slist = new StringBuilder();
-                       for (File file : list) {
-                               slist.append("\n").append(file.getPath());
-                       }
-
-                       throw new IOException("Cannot delete all the files from: <" //
-                                       + target + ">:" + slist.toString());
-               }
-
-               return list.isEmpty();
-       }
-
-       /**
-        * Recursively delete the given {@link File}, which may of course also be a
-        * directory.
-        * <p>
-        * Will silently continue in case of error.
-        * 
-        * @param target
-        *            the target to delete
-        * 
-        * @return TRUE if all files were deleted, FALSE if an error occurred
-        */
-       public static boolean deltree(File target) {
-               return deltree(target, null).isEmpty();
-       }
-
-       /**
-        * Recursively delete the given {@link File}, which may of course also be a
-        * directory.
-        * <p>
-        * Will collect all {@link File} that cannot be deleted in the given
-        * accumulator.
-        * 
-        * @param target
-        *            the target to delete
-        * @param errorAcc
-        *            the accumulator to use for errors, or NULL to create a new one
-        * 
-        * @return the errors accumulator
-        */
-       public static List<File> deltree(File target, List<File> errorAcc) {
-               if (errorAcc == null) {
-                       errorAcc = new ArrayList<File>();
-               }
-
-               File[] files = target.listFiles();
-               if (files != null) {
-                       for (File file : files) {
-                               errorAcc = deltree(file, errorAcc);
-                       }
-               }
-
-               if (!target.delete()) {
-                       errorAcc.add(target);
-               }
-
-               return errorAcc;
-       }
-
-       /**
-        * Open the resource next to the given {@link Class}.
-        * 
-        * @param location
-        *            the location where to look for the resource
-        * @param name
-        *            the resource name (only the filename, no path)
-        * 
-        * @return the opened resource if found, NULL if not
-        */
-       public static InputStream openResource(
-                       @SuppressWarnings("rawtypes") Class location, String name) {
-               String loc = location.getName().replace(".", "/")
-                               .replaceAll("/[^/]*$", "/");
-               return openResource(loc + name);
-       }
-
-       /**
-        * Open the given /-separated resource (from the binary root).
-        * 
-        * @param name
-        *            the resource name (the full path, with "/" as separator)
-        * 
-        * @return the opened resource if found, NULL if not
-        */
-       public static InputStream openResource(String name) {
-               ClassLoader loader = IOUtils.class.getClassLoader();
-               if (loader == null) {
-                       loader = ClassLoader.getSystemClassLoader();
-               }
-
-               return loader.getResourceAsStream(name);
-       }
-       
-       /**
-        * Return the running directory/file, that is, the root binary directory for
-        * running java classes or the running JAR file for JAR files.
-        * 
-        * @param clazz
-        *            a Class from the running program (will only have an impact
-        *            when not running from a JAR file)
-        * @param base
-        *            return the base directory (the one where the binary root or
-        *            the JAR file resides)
-        * 
-        * @return the directory or file
-        */
-       public static File getRunningDirectory(
-                       @SuppressWarnings("rawtypes") Class clazz, boolean base) {
-               String uri = clazz.getProtectionDomain().getCodeSource().getLocation()
-                               .toString();
-               if (uri.startsWith("file:"))
-                       uri = uri.substring("file:".length());
-               File root = new File(uri);
-
-               if (base) {
-                       root = root.getParentFile();
-               }
-
-               return root;
-       }
-
-       /**
-        * Return a resetable {@link InputStream} from this stream, and reset it.
-        * 
-        * @param in
-        *            the input stream
-        * @return the resetable stream, which <b>may</b> be the same
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static InputStream forceResetableStream(InputStream in)
-                       throws IOException {
-               boolean resetable = in.markSupported();
-               if (resetable) {
-                       try {
-                               in.reset();
-                       } catch (IOException e) {
-                               resetable = false;
-                       }
-               }
-
-               if (resetable) {
-                       return in;
-               }
-
-               final File tmp = File.createTempFile(".tmp-stream.", ".tmp");
-               try {
-                       write(in, tmp);
-                       in.close();
-
-                       return new MarkableFileInputStream(tmp) {
-                               @Override
-                               public void close() throws IOException {
-                                       try {
-                                               super.close();
-                                       } finally {
-                                               tmp.delete();
-                                       }
-                               }
-                       };
-               } catch (IOException e) {
-                       tmp.delete();
-                       throw e;
-               }
-       }
-
-       /**
-        * Convert the {@link InputStream} into a byte array.
-        * 
-        * @param in
-        *            the input stream
-        * 
-        * @return the array
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static byte[] toByteArray(InputStream in) throws IOException {
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               try {
-                       write(in, out);
-                       return out.toByteArray();
-               } finally {
-                       out.close();
-               }
-       }
-
-       /**
-        * Convert the {@link File} into a byte array.
-        * 
-        * @param file
-        *            the input {@link File}
-        * 
-        * @return the array
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static byte[] toByteArray(File file) throws IOException {
-               FileInputStream fis = new FileInputStream(file);
-               try {
-                       return toByteArray(fis);
-               } finally {
-                       fis.close();
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/Image.java b/src/be/nikiroo/utils/Image.java
deleted file mode 100644 (file)
index 76d2b20..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.ByteArrayInputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.io.Serializable;
-
-import be.nikiroo.utils.streams.Base64InputStream;
-import be.nikiroo.utils.streams.MarkableFileInputStream;
-
-/**
- * This class represents an image data.
- * 
- * @author niki
- */
-public class Image implements Closeable, Serializable {
-       static private final long serialVersionUID = 1L;
-
-       static private File tempRoot;
-       static private TempFiles tmpRepository;
-       static private long count = 0;
-       static private Object lock = new Object();
-
-       private Object instanceLock = new Object();
-       private File data;
-       private long size;
-
-       /**
-        * Do not use -- for serialisation purposes only.
-        */
-       @SuppressWarnings("unused")
-       private Image() {
-       }
-
-       /**
-        * Create a new {@link Image} with the given data.
-        * 
-        * @param data
-        *            the data
-        */
-       public Image(byte[] data) {
-               ByteArrayInputStream in = new ByteArrayInputStream(data);
-               try {
-                       this.data = getTemporaryFile();
-                       size = IOUtils.write(in, this.data);
-               } catch (IOException e) {
-                       throw new RuntimeException(e);
-               } finally {
-                       try {
-                               in.close();
-                       } catch (IOException e) {
-                               throw new RuntimeException(e);
-                       }
-               }
-       }
-
-       /**
-        * Create an image from Base64 encoded data.
-        * 
-        * <p>
-        * Please use {@link Image#Image(InputStream)} when possible instead, with a
-        * {@link Base64InputStream}; it can be much more efficient.
-        * 
-        * @param base64EncodedData
-        *            the Base64 encoded data as a String
-        * 
-        * @throws IOException
-        *             in case of I/O error or badly formated Base64
-        */
-       public Image(String base64EncodedData) throws IOException {
-               this(new Base64InputStream(new ByteArrayInputStream(
-                               StringUtils.getBytes(base64EncodedData)), false));
-       }
-
-       /**
-        * Create a new {@link Image} from a stream.
-        * 
-        * @param in
-        *            the stream
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public Image(InputStream in) throws IOException {
-               data = getTemporaryFile();
-               size = IOUtils.write(in, data);
-       }
-
-       /**
-        * The size of the enclosed image in bytes.
-        * 
-        * @return the size
-        */
-       public long getSize() {
-               return size;
-       }
-
-       /**
-        * Generate an {@link InputStream} that you can {@link InputStream#reset()}
-        * for this {@link Image}.
-        * <p>
-        * This {@link InputStream} will (always) be a new one, and <b>you</b> are
-        * responsible for it.
-        * <p>
-        * Note: take care that the {@link InputStream} <b>must not</b> live past
-        * the {@link Image} life time!
-        * 
-        * @return the stream
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public InputStream newInputStream() throws IOException {
-               synchronized (instanceLock) {
-                       if (data == null) {
-                               throw new IOException("Image was close()d");
-                       }
-                       
-                       return new MarkableFileInputStream(data);       
-               }
-       }
-
-       /**
-        * <b>Read</b> the actual image data, as a byte array.
-        * 
-        * @deprecated if possible, prefer the {@link Image#newInputStream()}
-        *             method, as it can be more efficient
-        * 
-        * @return the image data
-        */
-       @Deprecated
-       public byte[] getData() {
-               try {
-                       InputStream in = newInputStream();
-                       try {
-                               return IOUtils.toByteArray(in);
-                       } finally {
-                               in.close();
-                       }
-               } catch (IOException e) {
-                       throw new RuntimeException(e);
-               }
-       }
-
-       /**
-        * Convert the given {@link Image} object into a Base64 representation of
-        * the same {@link Image} object.
-        * 
-        * @deprecated Please use {@link Image#newInputStream()} instead, it is more
-        *             efficient
-        * 
-        * @return the Base64 representation
-        */
-       @Deprecated
-       public String toBase64() {
-               try {
-                       Base64InputStream stream = new Base64InputStream(newInputStream(),
-                                       true);
-                       try {
-                               return IOUtils.readSmallStream(stream);
-                       } finally {
-                               stream.close();
-                       }
-               } catch (IOException e) {
-                       return null;
-               }
-       }
-
-       /**
-        * Closing the {@link Image} will delete the associated temporary file on
-        * disk.
-        * <p>
-        * Note that even if you don't, the program will still <b>try</b> to delete
-        * all the temporary files at JVM termination.
-        */
-       @Override
-       public void close() throws IOException {
-               synchronized (instanceLock) {
-                       if (size >= 0) {
-                               size = -1;
-                               data.delete();
-                               data = null;
-
-                               synchronized (lock) {
-                                       count--;
-                                       if (count <= 0) {
-                                               count = 0;
-                                               tmpRepository.close();
-                                               tmpRepository = null;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       @Override
-       protected void finalize() throws Throwable {
-               try {
-                       close();
-               } finally {
-                       super.finalize();
-               }
-       }
-
-       /**
-        * Return a newly created temporary file to work on.
-        * 
-        * @return the file
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       private File getTemporaryFile() throws IOException {
-               synchronized (lock) {
-                       if (tmpRepository == null) {
-                               tmpRepository = new TempFiles(tempRoot, "images");
-                               count = 0;
-                       }
-
-                       count++;
-
-                       return tmpRepository.createTempFile("image");
-               }
-       }
-
-       /**
-        * Write this {@link Image} for serialization purposes; that is, write the
-        * content of the backing temporary file.
-        * 
-        * @param out
-        *            the {@link OutputStream} to write to
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       private void writeObject(ObjectOutputStream out) throws IOException {
-               InputStream in = newInputStream();
-               try {
-                       IOUtils.write(in, out);
-               } finally {
-                       in.close();
-               }
-       }
-
-       /**
-        * Read an {@link Image} written by
-        * {@link Image#writeObject(java.io.ObjectOutputStream)}; that is, create a
-        * new temporary file with the saved content.
-        * 
-        * @param in
-        *            the {@link InputStream} to read from
-        * @throws IOException
-        *             in case of I/O error
-        * @throws ClassNotFoundException
-        *             will not be thrown by this method
-        */
-       @SuppressWarnings("unused")
-       private void readObject(ObjectInputStream in) throws IOException,
-                       ClassNotFoundException {
-               data = getTemporaryFile();
-               IOUtils.write(in, data);
-       }
-
-       /**
-        * Change the temporary root directory used by the program.
-        * <p>
-        * Caution: the directory will be <b>owned</b> by the system, all its files
-        * now belong to us (and will most probably be deleted).
-        * <p>
-        * Note: it may take some time until the new temporary root is used, we
-        * first need to make sure the previous one is not used anymore (i.e., we
-        * must reach a point where no unclosed {@link Image} remains in memory) to
-        * switch the temporary root.
-        * 
-        * @param root
-        *            the new temporary root, which will be <b>owned</b> by the
-        *            system
-        */
-       public static void setTemporaryFilesRoot(File root) {
-               tempRoot = root;
-       }
-}
diff --git a/src/be/nikiroo/utils/ImageUtils.java b/src/be/nikiroo/utils/ImageUtils.java
deleted file mode 100644 (file)
index 877c8fa..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-import be.nikiroo.utils.serial.SerialUtils;
-
-/**
- * This class offer some utilities based around images.
- * 
- * @author niki
- */
-public abstract class ImageUtils {
-       private static ImageUtils instance = newObject();
-
-       /**
-        * Get a (unique) instance of an {@link ImageUtils} compatible with your
-        * system.
-        * 
-        * @return an {@link ImageUtils}
-        */
-       public static ImageUtils getInstance() {
-               return instance;
-       }
-
-       /**
-        * Save the given resource as an image on disk using the given image format
-        * for content, or with "png" format if it fails.
-        * 
-        * @param img
-        *            the resource
-        * @param target
-        *            the target file
-        * @param format
-        *            the file format ("png", "jpeg", "bmp"...)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public abstract void saveAsImage(Image img, File target, String format)
-                       throws IOException;
-
-       /**
-        * Scale a dimension.
-        * 
-        * 
-        * @param imageWidth
-        *            the actual image width
-        * @param imageHeight
-        *            the actual image height
-        * @param areaWidth
-        *            the base width of the target dimension for snap sizes
-        * @param areaHeight
-        *            the base height of the target dimension for snap sizes
-        * @param zoom
-        *            the zoom factor (ignored on snap mode)
-        * @param snapMode
-        *            NULL for no snap mode, TRUE to snap to width and FALSE for
-        *            snap to height)
-        * 
-        * @return the scaled size, width is [0] and height is [1] (minimum is 1x1)
-        */
-       protected static Integer[] scaleSize(int imageWidth, int imageHeight,
-                       int areaWidth, int areaHeight, double zoom, Boolean snapMode) {
-               int width;
-               int height;
-               if (snapMode == null) {
-                       width = (int) Math.round(imageWidth * zoom);
-                       height = (int) Math.round(imageHeight * zoom);
-               } else if (snapMode) {
-                       width = areaWidth;
-                       height = (int) Math
-                                       .round((((double) areaWidth) / imageWidth) * imageHeight);
-               } else {
-                       height = areaHeight;
-                       width = (int) Math
-                                       .round((((double) areaHeight) / imageHeight) * imageWidth);
-
-               }
-
-               if (width < 1)
-                       width = 1;
-               if (height < 1)
-                       height = 1;
-
-               return new Integer[] { width, height };
-       }
-       
-       /**
-        * Return the EXIF transformation flag of this image if any.
-        * 
-        * <p>
-        * Note: this code has been found on internet; thank you anonymous coder.
-        * </p>
-        * 
-        * @param in
-        *            the data {@link InputStream}
-        * 
-        * @return the transformation flag if any
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       protected static int getExifTransorm(InputStream in) throws IOException {
-               int[] exif_data = new int[100];
-               int set_flag = 0;
-               int is_motorola = 0;
-
-               /* Read File head, check for JPEG SOI + Exif APP1 */
-               for (int i = 0; i < 4; i++)
-                       exif_data[i] = in.read();
-
-               if (exif_data[0] != 0xFF || exif_data[1] != 0xD8
-                               || exif_data[2] != 0xFF || exif_data[3] != 0xE1)
-                       return -2;
-
-               /* Get the marker parameter length count */
-               int length = (in.read() << 8 | in.read());
-
-               /* Length includes itself, so must be at least 2 */
-               /* Following Exif data length must be at least 6 */
-               if (length < 8)
-                       return -1;
-               length -= 8;
-               /* Read Exif head, check for "Exif" */
-               for (int i = 0; i < 6; i++)
-                       exif_data[i] = in.read();
-
-               if (exif_data[0] != 0x45 || exif_data[1] != 0x78
-                               || exif_data[2] != 0x69 || exif_data[3] != 0x66
-                               || exif_data[4] != 0 || exif_data[5] != 0)
-                       return -1;
-
-               /* Read Exif body */
-               length = length > exif_data.length ? exif_data.length : length;
-               for (int i = 0; i < length; i++)
-                       exif_data[i] = in.read();
-
-               if (length < 12)
-                       return -1; /* Length of an IFD entry */
-
-               /* Discover byte order */
-               if (exif_data[0] == 0x49 && exif_data[1] == 0x49)
-                       is_motorola = 0;
-               else if (exif_data[0] == 0x4D && exif_data[1] == 0x4D)
-                       is_motorola = 1;
-               else
-                       return -1;
-
-               /* Check Tag Mark */
-               if (is_motorola == 1) {
-                       if (exif_data[2] != 0)
-                               return -1;
-                       if (exif_data[3] != 0x2A)
-                               return -1;
-               } else {
-                       if (exif_data[3] != 0)
-                               return -1;
-                       if (exif_data[2] != 0x2A)
-                               return -1;
-               }
-
-               /* Get first IFD offset (offset to IFD0) */
-               int offset;
-               if (is_motorola == 1) {
-                       if (exif_data[4] != 0)
-                               return -1;
-                       if (exif_data[5] != 0)
-                               return -1;
-                       offset = exif_data[6];
-                       offset <<= 8;
-                       offset += exif_data[7];
-               } else {
-                       if (exif_data[7] != 0)
-                               return -1;
-                       if (exif_data[6] != 0)
-                               return -1;
-                       offset = exif_data[5];
-                       offset <<= 8;
-                       offset += exif_data[4];
-               }
-               if (offset > length - 2)
-                       return -1; /* check end of data segment */
-
-               /* Get the number of directory entries contained in this IFD */
-               int number_of_tags;
-               if (is_motorola == 1) {
-                       number_of_tags = exif_data[offset];
-                       number_of_tags <<= 8;
-                       number_of_tags += exif_data[offset + 1];
-               } else {
-                       number_of_tags = exif_data[offset + 1];
-                       number_of_tags <<= 8;
-                       number_of_tags += exif_data[offset];
-               }
-               if (number_of_tags == 0)
-                       return -1;
-               offset += 2;
-
-               /* Search for Orientation Tag in IFD0 */
-               for (;;) {
-                       if (offset > length - 12)
-                               return -1; /* check end of data segment */
-                       /* Get Tag number */
-                       int tagnum;
-                       if (is_motorola == 1) {
-                               tagnum = exif_data[offset];
-                               tagnum <<= 8;
-                               tagnum += exif_data[offset + 1];
-                       } else {
-                               tagnum = exif_data[offset + 1];
-                               tagnum <<= 8;
-                               tagnum += exif_data[offset];
-                       }
-                       if (tagnum == 0x0112)
-                               break; /* found Orientation Tag */
-                       if (--number_of_tags == 0)
-                               return -1;
-                       offset += 12;
-               }
-
-               /* Get the Orientation value */
-               if (is_motorola == 1) {
-                       if (exif_data[offset + 8] != 0)
-                               return -1;
-                       set_flag = exif_data[offset + 9];
-               } else {
-                       if (exif_data[offset + 9] != 0)
-                               return -1;
-                       set_flag = exif_data[offset + 8];
-               }
-               if (set_flag > 8)
-                       return -1;
-
-               return set_flag;
-       }
-
-       /**
-        * Check that the class can operate (for instance, that all the required
-        * libraries or frameworks are present).
-        * 
-        * @return TRUE if it works
-        */
-       abstract protected boolean check();
-
-       /**
-        * Create a new {@link ImageUtils}.
-        * 
-        * @return the {@link ImageUtils}
-        */
-       private static ImageUtils newObject() {
-               for (String clazz : new String[] { "be.nikiroo.utils.ui.ImageUtilsAwt",
-                               "be.nikiroo.utils.android.ImageUtilsAndroid" }) {
-                       try {
-                               ImageUtils obj = (ImageUtils) SerialUtils.createObject(clazz);
-                               if (obj.check()) {
-                                       return obj;
-                               }
-                       } catch (Throwable e) {
-                       }
-               }
-
-               return null;
-       }
-}
diff --git a/src/be/nikiroo/utils/LoginResult.java b/src/be/nikiroo/utils/LoginResult.java
deleted file mode 100644 (file)
index ddc148b..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-package be.nikiroo.utils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A simple login facility using cookies.
- * 
- * @author niki
- */
-public class LoginResult {
-       private boolean success;
-       private String cookie;
-       private boolean badLogin;
-       private boolean badCookie;
-       private String option;
-
-       /**
-        * Generate a failed login.
-        * 
-        * @param badLogin
-        *            TRUE if the login failed because of a who/key/subkey error
-        * @param badCookie
-        *            TRUE if the login failed because of a bad cookie
-        * 
-        */
-       public LoginResult(boolean badLogin, boolean badCookie) {
-               this.badLogin = badLogin;
-               this.badCookie = badCookie;
-       }
-
-       /**
-        * Generate a successful login for the given user.
-        * 
-        * @param who
-        *            the user (can be NULL)
-        * @param key
-        *            the password (can be NULL)
-        * @param subkey
-        *            a sub-password (can be NULL)
-        * @param option
-        *            an option assigned to this login (can be NULL)
-        */
-       public LoginResult(String who, String key, String subkey, String option) {
-               this.option = option;
-               this.cookie = generateCookie(who, key, subkey, option);
-               this.success = true;
-       }
-
-       /**
-        * Generate a login via this token and checks its validity.
-        * <p>
-        * Will fail with a NULL <tt>token</tt>, but
-        * {@link LoginResult#isBadCookie()} will still be false.
-        * 
-        * @param cookie
-        *            the token to check (if NULL, will simply fail but
-        *            {@link LoginResult#isBadCookie()} will still be false)
-        * @param who
-        *            the user (can be NULL)
-        * @param key
-        *            the password (can be NULL)
-        */
-       public LoginResult(String cookie, String who, String key) {
-               this(cookie, who, key, null, true);
-       }
-
-       /**
-        * Generate a login via this token and checks its validity.
-        * <p>
-        * Will fail with a NULL <tt>token</tt>, but
-        * {@link LoginResult#isBadCookie()} will still be false.
-        * 
-        * @param cookie
-        *            the token to check (if NULL, will simply fail but
-        *            {@link LoginResult#isBadCookie()} will still be false)
-        * @param who
-        *            the user (can be NULL)
-        * @param key
-        *            the password (can be NULL)
-        * @param subkeys
-        *            the list of candidate subkey (can be NULL)
-        * @param allowNoSubkey
-        *            allow the login if no subkey was present in the token
-        */
-       public LoginResult(String cookie, String who, String key,
-                       List<String> subkeys, boolean allowNoSubkey) {
-               if (cookie != null) {
-                       String hashes[] = cookie.split("~");
-                       if (hashes.length >= 2) {
-                               String wookie = hashes[0];
-                               String rehashed = hashes[1];
-                               String opts = hashes.length > 2 ? hashes[2] : "";
-
-                               if (CookieUtils.validateCookie(who + key, wookie)) {
-                                       if (subkeys == null) {
-                                               subkeys = new ArrayList<String>();
-                                       }
-
-                                       if (allowNoSubkey) {
-                                               subkeys = new ArrayList<String>(subkeys);
-                                               subkeys.add("");
-                                       }
-
-                                       for (String subkey : subkeys) {
-                                               if (CookieUtils.validateCookie(wookie + subkey + opts,
-                                                               rehashed)) {
-                                                       this.cookie = generateCookie(who, key, subkey,
-                                                                       opts);
-                                                       this.option = opts;
-                                                       this.success = true;
-                                               }
-                                       }
-                               }
-                       }
-
-                       this.badCookie = !success;
-               }
-
-               // No token -> no bad token
-       }
-
-       /**
-        * The login wa successful.
-        * 
-        * @return TRUE if it is
-        */
-       public boolean isSuccess() {
-               return success;
-       }
-
-       /**
-        * The refreshed token if the login is successful (NULL if not).
-        * 
-        * @return the token, or NULL
-        */
-       public String getCookie() {
-               return cookie;
-       }
-
-       /**
-        * An option that was used to generate this login (always NULL if the login
-        * was not successful).
-        * <p>
-        * It can come from a manually generated {@link LoginResult}, but also from
-        * a {@link LoginResult} generated with a token.
-        * 
-        * @return the option
-        */
-       public String getOption() {
-               return option;
-       }
-
-       /**
-        * The login failed because of a who/key/subkey error.
-        * 
-        * @return TRUE if it failed because of a who/key/subkey error
-        */
-       public boolean isBadLogin() {
-               return badLogin;
-       }
-
-       /**
-        * The login failed because the cookie was not accepted
-        * 
-        * @return TRUE if it failed because the cookie was not accepted
-        */
-       public boolean isBadCookie() {
-               return badCookie;
-       }
-
-       @Override
-       public String toString() {
-               if (success)
-                       return "Login succeeded";
-
-               if (badLogin && badCookie)
-                       return "Login failed because of bad login and bad cookie";
-
-               if (badLogin)
-                       return "Login failed because of bad login";
-
-               if (badCookie)
-                       return "Login failed because of bad cookie";
-
-               return "Login failed without giving a reason";
-       }
-
-       /**
-        * Generate a cookie.
-        * 
-        * @param who
-        *            the user name (can be NULL)
-        * @param key
-        *            the password (can be NULL)
-        * @param subkey
-        *            a subkey (can be NULL)
-        * @param option
-        *            an option linked to the login (can be NULL)
-        * 
-        * @return a fresh cookie
-        */
-       private String generateCookie(String who, String key, String subkey,
-                       String option) {
-               String wookie = CookieUtils.generateCookie(who + key, 0);
-               return wookie + "~"
-                               + CookieUtils.generateCookie(
-                                               wookie + (subkey == null ? "" : subkey) + option, 0)
-                               + "~" + option;
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/MarkableFileInputStream.java b/src/be/nikiroo/utils/MarkableFileInputStream.java
deleted file mode 100644 (file)
index 3f28064..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-
-/**
- * Class was moved to {@link be.nikiroo.utils.streams.MarkableFileInputStream}.
- * 
- * @author niki
- */
-@Deprecated
-public class MarkableFileInputStream extends
-               be.nikiroo.utils.streams.MarkableFileInputStream {
-       public MarkableFileInputStream(File file) throws FileNotFoundException {
-               super(file);
-       }
-
-       public MarkableFileInputStream(FileInputStream fis) {
-               super(fis);
-       }
-}
diff --git a/src/be/nikiroo/utils/NanoHTTPD.java b/src/be/nikiroo/utils/NanoHTTPD.java
deleted file mode 100644 (file)
index 8d183c1..0000000
+++ /dev/null
@@ -1,2358 +0,0 @@
-package be.nikiroo.utils;
-
-/*
- * #%L
- * NanoHttpd-Core
- * %%
- * Copyright (C) 2012 - 2015 nanohttpd
- * %%
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- * 
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 
- * 3. Neither the name of the nanohttpd nor the names of its contributors
- *    may be used to endorse or promote products derived from this software without
- *    specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * #L%
- */
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.DataOutput;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.RandomAccessFile;
-import java.io.UnsupportedEncodingException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.security.KeyStore;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.TimeZone;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.GZIPOutputStream;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.TrustManagerFactory;
-
-import be.nikiroo.utils.NanoHTTPD.Response.IStatus;
-import be.nikiroo.utils.NanoHTTPD.Response.Status;
-
-/**
- * A simple, tiny, nicely embeddable HTTP server in Java
- * <p/>
- * <p/>
- * NanoHTTPD
- * <p>
- * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen,
- * 2010 by Konstantinos Togias
- * </p>
- * <p/>
- * <p/>
- * <b>Features + limitations: </b>
- * <ul>
- * <p/>
- * <li>Only one Java file</li>
- * <li>Java 5 compatible</li>
- * <li>Released as open source, Modified BSD licence</li>
- * <li>No fixed config files, logging, authorization etc. (Implement yourself if
- * you need them.)</li>
- * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT
- * support in 1.25)</li>
- * <li>Supports both dynamic content and file serving</li>
- * <li>Supports file upload (since version 1.2, 2010)</li>
- * <li>Supports partial content (streaming)</li>
- * <li>Supports ETags</li>
- * <li>Never caches anything</li>
- * <li>Doesn't limit bandwidth, request time or simultaneous connections</li>
- * <li>Default code serves files and shows all HTTP parameters and headers</li>
- * <li>File server supports directory listing, index.html and index.htm</li>
- * <li>File server supports partial content (streaming)</li>
- * <li>File server supports ETags</li>
- * <li>File server does the 301 redirection trick for directories without '/'</li>
- * <li>File server supports simple skipping for files (continue download)</li>
- * <li>File server serves also very long files without memory overhead</li>
- * <li>Contains a built-in list of most common MIME types</li>
- * <li>All header names are converted to lower case so they don't vary between
- * browsers/clients</li>
- * <p/>
- * </ul>
- * <p/>
- * <p/>
- * <b>How to use: </b>
- * <ul>
- * <p/>
- * <li>Subclass and implement serve() and embed to your own program</li>
- * <p/>
- * </ul>
- * <p/>
- * See the separate "LICENSE.md" file for the distribution license (Modified BSD
- * licence)
- */
-public abstract class NanoHTTPD {
-
-    /**
-     * Pluggable strategy for asynchronously executing requests.
-     */
-    public interface AsyncRunner {
-
-        void closeAll();
-
-        void closed(ClientHandler clientHandler);
-
-        void exec(ClientHandler code);
-    }
-
-    /**
-     * The runnable that will be used for every new client connection.
-     */
-    public class ClientHandler implements Runnable {
-
-        private final InputStream inputStream;
-
-        private final Socket acceptSocket;
-
-        public ClientHandler(InputStream inputStream, Socket acceptSocket) {
-            this.inputStream = inputStream;
-            this.acceptSocket = acceptSocket;
-        }
-
-        public void close() {
-            safeClose(this.inputStream);
-            safeClose(this.acceptSocket);
-        }
-
-        @Override
-        public void run() {
-            OutputStream outputStream = null;
-            try {
-                outputStream = this.acceptSocket.getOutputStream();
-                TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create();
-                HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress());
-                while (!this.acceptSocket.isClosed()) {
-                    session.execute();
-                }
-            } catch (Exception e) {
-                // When the socket is closed by the client,
-                // we throw our own SocketException
-                // to break the "keep alive" loop above. If
-                // the exception was anything other
-                // than the expected SocketException OR a
-                // SocketTimeoutException, print the
-                // stacktrace
-                if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) {
-                    NanoHTTPD.LOG.log(Level.SEVERE, "Communication with the client broken, or an bug in the handler code", e);
-                }
-            } finally {
-                safeClose(outputStream);
-                safeClose(this.inputStream);
-                safeClose(this.acceptSocket);
-                NanoHTTPD.this.asyncRunner.closed(this);
-            }
-        }
-    }
-
-    public static class Cookie {
-
-        public static String getHTTPTime(int days) {
-            Calendar calendar = Calendar.getInstance();
-            SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
-            dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
-            calendar.add(Calendar.DAY_OF_MONTH, days);
-            return dateFormat.format(calendar.getTime());
-        }
-
-        private final String n, v, e;
-
-        public Cookie(String name, String value) {
-            this(name, value, 30);
-        }
-
-        public Cookie(String name, String value, int numDays) {
-            this.n = name;
-            this.v = value;
-            this.e = getHTTPTime(numDays);
-        }
-
-        public Cookie(String name, String value, String expires) {
-            this.n = name;
-            this.v = value;
-            this.e = expires;
-        }
-
-        public String getHTTPHeader() {
-            String fmt = "%s=%s; expires=%s";
-            return String.format(fmt, this.n, this.v, this.e);
-        }
-    }
-
-    /**
-     * Provides rudimentary support for cookies. Doesn't support 'path',
-     * 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported
-     * features.
-     * 
-     * @author LordFokas
-     */
-    public class CookieHandler implements Iterable<String> {
-
-        private final HashMap<String, String> cookies = new HashMap<String, String>();
-
-        private final ArrayList<Cookie> queue = new ArrayList<Cookie>();
-
-        public CookieHandler(Map<String, String> httpHeaders) {
-            String raw = httpHeaders.get("cookie");
-            if (raw != null) {
-                String[] tokens = raw.split(";");
-                for (String token : tokens) {
-                    String[] data = token.trim().split("=");
-                    if (data.length == 2) {
-                        this.cookies.put(data[0], data[1]);
-                    }
-                }
-            }
-        }
-
-        /**
-         * Set a cookie with an expiration date from a month ago, effectively
-         * deleting it on the client side.
-         * 
-         * @param name
-         *            The cookie name.
-         */
-        public void delete(String name) {
-            set(name, "-delete-", -30);
-        }
-
-        @Override
-        public Iterator<String> iterator() {
-            return this.cookies.keySet().iterator();
-        }
-
-        /**
-         * Read a cookie from the HTTP Headers.
-         * 
-         * @param name
-         *            The cookie's name.
-         * @return The cookie's value if it exists, null otherwise.
-         */
-        public String read(String name) {
-            return this.cookies.get(name);
-        }
-
-        public void set(Cookie cookie) {
-            this.queue.add(cookie);
-        }
-
-        /**
-         * Sets a cookie.
-         * 
-         * @param name
-         *            The cookie's name.
-         * @param value
-         *            The cookie's value.
-         * @param expires
-         *            How many days until the cookie expires.
-         */
-        public void set(String name, String value, int expires) {
-            this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires)));
-        }
-
-        /**
-         * Internally used by the webserver to add all queued cookies into the
-         * Response's HTTP Headers.
-         * 
-         * @param response
-         *            The Response object to which headers the queued cookies
-         *            will be added.
-         */
-        public void unloadQueue(Response response) {
-            for (Cookie cookie : this.queue) {
-                response.addHeader("Set-Cookie", cookie.getHTTPHeader());
-            }
-        }
-    }
-
-    /**
-     * Default threading strategy for NanoHTTPD.
-     * <p/>
-     * <p>
-     * By default, the server spawns a new Thread for every incoming request.
-     * These are set to <i>daemon</i> status, and named according to the request
-     * number. The name is useful when profiling the application.
-     * </p>
-     */
-    public static class DefaultAsyncRunner implements AsyncRunner {
-
-        private long requestCount;
-
-        private final List<ClientHandler> running = Collections.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>());
-
-        /**
-         * @return a list with currently running clients.
-         */
-        public List<ClientHandler> getRunning() {
-            return running;
-        }
-
-        @Override
-        public void closeAll() {
-            // copy of the list for concurrency
-            for (ClientHandler clientHandler : new ArrayList<ClientHandler>(this.running)) {
-                clientHandler.close();
-            }
-        }
-
-        @Override
-        public void closed(ClientHandler clientHandler) {
-            this.running.remove(clientHandler);
-        }
-
-        @Override
-        public void exec(ClientHandler clientHandler) {
-            ++this.requestCount;
-            Thread t = new Thread(clientHandler);
-            t.setDaemon(true);
-            t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")");
-            this.running.add(clientHandler);
-            t.start();
-        }
-    }
-
-    /**
-     * Default strategy for creating and cleaning up temporary files.
-     * <p/>
-     * <p>
-     * By default, files are created by <code>File.createTempFile()</code> in
-     * the directory specified.
-     * </p>
-     */
-    public static class DefaultTempFile implements TempFile {
-
-        private final File file;
-
-        private final OutputStream fstream;
-
-        public DefaultTempFile(File tempdir) throws IOException {
-            this.file = File.createTempFile("NanoHTTPD-", "", tempdir);
-            this.fstream = new FileOutputStream(this.file);
-        }
-
-        @Override
-        public void delete() throws Exception {
-            safeClose(this.fstream);
-            if (!this.file.delete()) {
-                throw new Exception("could not delete temporary file: " + this.file.getAbsolutePath());
-            }
-        }
-
-        @Override
-        public String getName() {
-            return this.file.getAbsolutePath();
-        }
-
-        @Override
-        public OutputStream open() throws Exception {
-            return this.fstream;
-        }
-    }
-
-    /**
-     * Default strategy for creating and cleaning up temporary files.
-     * <p/>
-     * <p>
-     * This class stores its files in the standard location (that is, wherever
-     * <code>java.io.tmpdir</code> points to). Files are added to an internal
-     * list, and deleted when no longer needed (that is, when
-     * <code>clear()</code> is invoked at the end of processing a request).
-     * </p>
-     */
-    public static class DefaultTempFileManager implements TempFileManager {
-
-        private final File tmpdir;
-
-        private final List<TempFile> tempFiles;
-
-        public DefaultTempFileManager() {
-            this.tmpdir = new File(System.getProperty("java.io.tmpdir"));
-            if (!tmpdir.exists()) {
-                tmpdir.mkdirs();
-            }
-            this.tempFiles = new ArrayList<TempFile>();
-        }
-
-        @Override
-        public void clear() {
-            for (TempFile file : this.tempFiles) {
-                try {
-                    file.delete();
-                } catch (Exception ignored) {
-                    NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored);
-                }
-            }
-            this.tempFiles.clear();
-        }
-
-        @Override
-        public TempFile createTempFile(String filename_hint) throws Exception {
-            DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir);
-            this.tempFiles.add(tempFile);
-            return tempFile;
-        }
-    }
-
-    /**
-     * Default strategy for creating and cleaning up temporary files.
-     */
-    private class DefaultTempFileManagerFactory implements TempFileManagerFactory {
-
-        @Override
-        public TempFileManager create() {
-            return new DefaultTempFileManager();
-        }
-    }
-
-    /**
-     * Creates a normal ServerSocket for TCP connections
-     */
-    public static class DefaultServerSocketFactory implements ServerSocketFactory {
-
-        @Override
-        public ServerSocket create() throws IOException {
-            return new ServerSocket();
-        }
-
-    }
-
-    /**
-     * Creates a new SSLServerSocket
-     */
-    public static class SecureServerSocketFactory implements ServerSocketFactory {
-
-        private SSLServerSocketFactory sslServerSocketFactory;
-
-        private String[] sslProtocols;
-
-        public SecureServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) {
-            this.sslServerSocketFactory = sslServerSocketFactory;
-            this.sslProtocols = sslProtocols;
-        }
-
-        @Override
-        public ServerSocket create() throws IOException {
-            SSLServerSocket ss = null;
-            ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket();
-            if (this.sslProtocols != null) {
-                ss.setEnabledProtocols(this.sslProtocols);
-            } else {
-                ss.setEnabledProtocols(ss.getSupportedProtocols());
-            }
-            ss.setUseClientMode(false);
-            ss.setWantClientAuth(false);
-            ss.setNeedClientAuth(false);
-            return ss;
-        }
-
-    }
-
-    private static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)";
-
-    private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, Pattern.CASE_INSENSITIVE);
-
-    private static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)";
-
-    private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE);
-
-    private static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]";
-
-    private static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern.compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX);
-
-    protected static class ContentType {
-
-        private static final String ASCII_ENCODING = "US-ASCII";
-
-        private static final String MULTIPART_FORM_DATA_HEADER = "multipart/form-data";
-
-        private static final String CONTENT_REGEX = "[ |\t]*([^/^ ^;^,]+/[^ ^;^,]+)";
-
-        private static final Pattern MIME_PATTERN = Pattern.compile(CONTENT_REGEX, Pattern.CASE_INSENSITIVE);
-
-        private static final String CHARSET_REGEX = "[ |\t]*(charset)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?";
-
-        private static final Pattern CHARSET_PATTERN = Pattern.compile(CHARSET_REGEX, Pattern.CASE_INSENSITIVE);
-
-        private static final String BOUNDARY_REGEX = "[ |\t]*(boundary)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;^,]*)['|\"]?";
-
-        private static final Pattern BOUNDARY_PATTERN = Pattern.compile(BOUNDARY_REGEX, Pattern.CASE_INSENSITIVE);
-
-        private final String contentTypeHeader;
-
-        private final String contentType;
-
-        private final String encoding;
-
-        private final String boundary;
-
-        public ContentType(String contentTypeHeader) {
-            this.contentTypeHeader = contentTypeHeader;
-            if (contentTypeHeader != null) {
-                contentType = getDetailFromContentHeader(contentTypeHeader, MIME_PATTERN, "", 1);
-                encoding = getDetailFromContentHeader(contentTypeHeader, CHARSET_PATTERN, null, 2);
-            } else {
-                contentType = "";
-                encoding = "UTF-8";
-            }
-            if (MULTIPART_FORM_DATA_HEADER.equalsIgnoreCase(contentType)) {
-                boundary = getDetailFromContentHeader(contentTypeHeader, BOUNDARY_PATTERN, null, 2);
-            } else {
-                boundary = null;
-            }
-        }
-
-        private String getDetailFromContentHeader(String contentTypeHeader, Pattern pattern, String defaultValue, int group) {
-            Matcher matcher = pattern.matcher(contentTypeHeader);
-            return matcher.find() ? matcher.group(group) : defaultValue;
-        }
-
-        public String getContentTypeHeader() {
-            return contentTypeHeader;
-        }
-
-        public String getContentType() {
-            return contentType;
-        }
-
-        public String getEncoding() {
-            return encoding == null ? ASCII_ENCODING : encoding;
-        }
-
-        public String getBoundary() {
-            return boundary;
-        }
-
-        public boolean isMultipart() {
-            return MULTIPART_FORM_DATA_HEADER.equalsIgnoreCase(contentType);
-        }
-
-        public ContentType tryUTF8() {
-            if (encoding == null) {
-                return new ContentType(this.contentTypeHeader + "; charset=UTF-8");
-            }
-            return this;
-        }
-    }
-
-    protected class HTTPSession implements IHTTPSession {
-
-        private static final int REQUEST_BUFFER_LEN = 512;
-
-        private static final int MEMORY_STORE_LIMIT = 1024;
-
-        public static final int BUFSIZE = 8192;
-
-        public static final int MAX_HEADER_SIZE = 1024;
-
-        private final TempFileManager tempFileManager;
-
-        private final OutputStream outputStream;
-
-        private final BufferedInputStream inputStream;
-
-        private int splitbyte;
-
-        private int rlen;
-
-        private String uri;
-
-        private Method method;
-
-        private Map<String, List<String>> parms;
-
-        private Map<String, String> headers;
-
-        private CookieHandler cookies;
-
-        private String queryParameterString;
-
-        private String remoteIp;
-
-        private String remoteHostname;
-
-        private String protocolVersion;
-
-        public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
-            this.tempFileManager = tempFileManager;
-            this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE);
-            this.outputStream = outputStream;
-        }
-
-        public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) {
-            this.tempFileManager = tempFileManager;
-            this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE);
-            this.outputStream = outputStream;
-            this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString();
-            this.remoteHostname = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "localhost" : inetAddress.getHostName().toString();
-            this.headers = new HashMap<String, String>();
-        }
-
-        /**
-         * Decodes the sent headers and loads the data into Key/value pairs
-         */
-        private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, List<String>> parms, Map<String, String> headers) throws ResponseException {
-            try {
-                // Read the request line
-                String inLine = in.readLine();
-                if (inLine == null) {
-                    return;
-                }
-
-                StringTokenizer st = new StringTokenizer(inLine);
-                if (!st.hasMoreTokens()) {
-                    throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html");
-                }
-
-                pre.put("method", st.nextToken());
-
-                if (!st.hasMoreTokens()) {
-                    throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html");
-                }
-
-                String uri = st.nextToken();
-
-                // Decode parameters from the URI
-                int qmi = uri.indexOf('?');
-                if (qmi >= 0) {
-                    decodeParms(uri.substring(qmi + 1), parms);
-                    uri = decodePercent(uri.substring(0, qmi));
-                } else {
-                    uri = decodePercent(uri);
-                }
-
-                // If there's another token, its protocol version,
-                // followed by HTTP headers.
-                // NOTE: this now forces header names lower case since they are
-                // case insensitive and vary by client.
-                if (st.hasMoreTokens()) {
-                    protocolVersion = st.nextToken();
-                } else {
-                    protocolVersion = "HTTP/1.1";
-                    NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1.");
-                }
-                String line = in.readLine();
-                while (line != null && !line.trim().isEmpty()) {
-                    int p = line.indexOf(':');
-                    if (p >= 0) {
-                        headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim());
-                    }
-                    line = in.readLine();
-                }
-
-                pre.put("uri", uri);
-            } catch (IOException ioe) {
-                throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
-            }
-        }
-
-        /**
-         * Decodes the Multipart Body data and put it into Key/Value pairs.
-         */
-        private void decodeMultipartFormData(ContentType contentType, ByteBuffer fbuf, Map<String, List<String>> parms, Map<String, String> files) throws ResponseException {
-            int pcount = 0;
-            try {
-                int[] boundaryIdxs = getBoundaryPositions(fbuf, contentType.getBoundary().getBytes());
-                if (boundaryIdxs.length < 2) {
-                    throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but contains less than two boundary strings.");
-                }
-
-                byte[] partHeaderBuff = new byte[MAX_HEADER_SIZE];
-                for (int boundaryIdx = 0; boundaryIdx < boundaryIdxs.length - 1; boundaryIdx++) {
-                    fbuf.position(boundaryIdxs[boundaryIdx]);
-                    int len = (fbuf.remaining() < MAX_HEADER_SIZE) ? fbuf.remaining() : MAX_HEADER_SIZE;
-                    fbuf.get(partHeaderBuff, 0, len);
-                    BufferedReader in =
-                            new BufferedReader(new InputStreamReader(new ByteArrayInputStream(partHeaderBuff, 0, len), Charset.forName(contentType.getEncoding())), len);
-
-                    int headerLines = 0;
-                    // First line is boundary string
-                    String mpline = in.readLine();
-                    headerLines++;
-                    if (mpline == null || !mpline.contains(contentType.getBoundary())) {
-                        throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but chunk does not start with boundary.");
-                    }
-
-                    String partName = null, fileName = null, partContentType = null;
-                    // Parse the reset of the header lines
-                    mpline = in.readLine();
-                    headerLines++;
-                    while (mpline != null && mpline.trim().length() > 0) {
-                        Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(mpline);
-                        if (matcher.matches()) {
-                            String attributeString = matcher.group(2);
-                            matcher = CONTENT_DISPOSITION_ATTRIBUTE_PATTERN.matcher(attributeString);
-                            while (matcher.find()) {
-                                String key = matcher.group(1);
-                                if ("name".equalsIgnoreCase(key)) {
-                                    partName = matcher.group(2);
-                                } else if ("filename".equalsIgnoreCase(key)) {
-                                    fileName = matcher.group(2);
-                                    // add these two line to support multiple
-                                    // files uploaded using the same field Id
-                                    if (!fileName.isEmpty()) {
-                                        if (pcount > 0)
-                                            partName = partName + String.valueOf(pcount++);
-                                        else
-                                            pcount++;
-                                    }
-                                }
-                            }
-                        }
-                        matcher = CONTENT_TYPE_PATTERN.matcher(mpline);
-                        if (matcher.matches()) {
-                            partContentType = matcher.group(2).trim();
-                        }
-                        mpline = in.readLine();
-                        headerLines++;
-                    }
-                    int partHeaderLength = 0;
-                    while (headerLines-- > 0) {
-                        partHeaderLength = scipOverNewLine(partHeaderBuff, partHeaderLength);
-                    }
-                    // Read the part data
-                    if (partHeaderLength >= len - 4) {
-                        throw new ResponseException(Response.Status.INTERNAL_ERROR, "Multipart header size exceeds MAX_HEADER_SIZE.");
-                    }
-                    int partDataStart = boundaryIdxs[boundaryIdx] + partHeaderLength;
-                    int partDataEnd = boundaryIdxs[boundaryIdx + 1] - 4;
-
-                    fbuf.position(partDataStart);
-
-                    List<String> values = parms.get(partName);
-                    if (values == null) {
-                        values = new ArrayList<String>();
-                        parms.put(partName, values);
-                    }
-
-                    if (partContentType == null) {
-                        // Read the part into a string
-                        byte[] data_bytes = new byte[partDataEnd - partDataStart];
-                        fbuf.get(data_bytes);
-
-                        values.add(new String(data_bytes, contentType.getEncoding()));
-                    } else {
-                        // Read it into a file
-                        String path = saveTmpFile(fbuf, partDataStart, partDataEnd - partDataStart, fileName);
-                        if (!files.containsKey(partName)) {
-                            files.put(partName, path);
-                        } else {
-                            int count = 2;
-                            while (files.containsKey(partName + count)) {
-                                count++;
-                            }
-                            files.put(partName + count, path);
-                        }
-                        values.add(fileName);
-                    }
-                }
-            } catch (ResponseException re) {
-                throw re;
-            } catch (Exception e) {
-                throw new ResponseException(Response.Status.INTERNAL_ERROR, e.toString());
-            }
-        }
-
-        private int scipOverNewLine(byte[] partHeaderBuff, int index) {
-            while (partHeaderBuff[index] != '\n') {
-                index++;
-            }
-            return ++index;
-        }
-
-        /**
-         * Decodes parameters in percent-encoded URI-format ( e.g.
-         * "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given
-         * Map.
-         */
-        private void decodeParms(String parms, Map<String, List<String>> p) {
-            if (parms == null) {
-                this.queryParameterString = "";
-                return;
-            }
-
-            this.queryParameterString = parms;
-            StringTokenizer st = new StringTokenizer(parms, "&");
-            while (st.hasMoreTokens()) {
-                String e = st.nextToken();
-                int sep = e.indexOf('=');
-                String key = null;
-                String value = null;
-
-                if (sep >= 0) {
-                    key = decodePercent(e.substring(0, sep)).trim();
-                    value = decodePercent(e.substring(sep + 1));
-                } else {
-                    key = decodePercent(e).trim();
-                    value = "";
-                }
-
-                List<String> values = p.get(key);
-                if (values == null) {
-                    values = new ArrayList<String>();
-                    p.put(key, values);
-                }
-
-                values.add(value);
-            }
-        }
-
-        @Override
-        public void execute() throws IOException {
-            Response r = null;
-            try {
-                // Read the first 8192 bytes.
-                // The full header should fit in here.
-                // Apache's default header limit is 8KB.
-                // Do NOT assume that a single read will get the entire header
-                // at once!
-                byte[] buf = new byte[HTTPSession.BUFSIZE];
-                this.splitbyte = 0;
-                this.rlen = 0;
-
-                int read = -1;
-                this.inputStream.mark(HTTPSession.BUFSIZE);
-                try {
-                    read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE);
-                } catch (SSLException e) {
-                    throw e;
-                } catch (IOException e) {
-                    safeClose(this.inputStream);
-                    safeClose(this.outputStream);
-                    throw new SocketException("NanoHttpd Shutdown");
-                }
-                if (read == -1) {
-                    // socket was been closed
-                    safeClose(this.inputStream);
-                    safeClose(this.outputStream);
-                    throw new SocketException("NanoHttpd Shutdown");
-                }
-                while (read > 0) {
-                    this.rlen += read;
-                    this.splitbyte = findHeaderEnd(buf, this.rlen);
-                    if (this.splitbyte > 0) {
-                        break;
-                    }
-                    read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen);
-                }
-
-                if (this.splitbyte < this.rlen) {
-                    this.inputStream.reset();
-                    this.inputStream.skip(this.splitbyte);
-                }
-
-                this.parms = new HashMap<String, List<String>>();
-                if (null == this.headers) {
-                    this.headers = new HashMap<String, String>();
-                } else {
-                    this.headers.clear();
-                }
-
-                // Create a BufferedReader for parsing the header.
-                BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen)));
-
-                // Decode the header into parms and header java properties
-                Map<String, String> pre = new HashMap<String, String>();
-                decodeHeader(hin, pre, this.parms, this.headers);
-
-                if (null != this.remoteIp) {
-                    this.headers.put("remote-addr", this.remoteIp);
-                    this.headers.put("http-client-ip", this.remoteIp);
-                }
-
-                this.method = Method.lookup(pre.get("method"));
-                if (this.method == null) {
-                    throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. HTTP verb " + pre.get("method") + " unhandled.");
-                }
-
-                this.uri = pre.get("uri");
-
-                this.cookies = new CookieHandler(this.headers);
-
-                String connection = this.headers.get("connection");
-                boolean keepAlive = "HTTP/1.1".equals(protocolVersion) && (connection == null || !connection.matches("(?i).*close.*"));
-
-                // Ok, now do the serve()
-
-                // TODO: long body_size = getBodySize();
-                // TODO: long pos_before_serve = this.inputStream.totalRead()
-                // (requires implementation for totalRead())
-                r = serve(this);
-                // TODO: this.inputStream.skip(body_size -
-                // (this.inputStream.totalRead() - pos_before_serve))
-
-                if (r == null) {
-                    throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response.");
-                } else {
-                    String acceptEncoding = this.headers.get("accept-encoding");
-                    this.cookies.unloadQueue(r);
-                    r.setRequestMethod(this.method);
-                    r.setGzipEncoding(useGzipWhenAccepted(r) && acceptEncoding != null && acceptEncoding.contains("gzip"));
-                    r.setKeepAlive(keepAlive);
-                    r.send(this.outputStream);
-                }
-                if (!keepAlive || r.isCloseConnection()) {
-                    throw new SocketException("NanoHttpd Shutdown");
-                }
-            } catch (SocketException e) {
-                // throw it out to close socket object (finalAccept)
-                throw e;
-            } catch (SocketTimeoutException ste) {
-                // treat socket timeouts the same way we treat socket exceptions
-                // i.e. close the stream & finalAccept object by throwing the
-                // exception up the call stack.
-                throw ste;
-            } catch (SSLException ssle) {
-                Response resp = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SSL PROTOCOL FAILURE: " + ssle.getMessage());
-                resp.send(this.outputStream);
-                safeClose(this.outputStream);
-            } catch (IOException ioe) {
-                Response resp = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
-                resp.send(this.outputStream);
-                safeClose(this.outputStream);
-            } catch (ResponseException re) {
-                Response resp = newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage());
-                resp.send(this.outputStream);
-                safeClose(this.outputStream);
-            } finally {
-                safeClose(r);
-                this.tempFileManager.clear();
-            }
-        }
-
-        /**
-         * Find byte index separating header from body. It must be the last byte
-         * of the first two sequential new lines.
-         */
-        private int findHeaderEnd(final byte[] buf, int rlen) {
-            int splitbyte = 0;
-            while (splitbyte + 1 < rlen) {
-
-                // RFC2616
-                if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && splitbyte + 3 < rlen && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') {
-                    return splitbyte + 4;
-                }
-
-                // tolerance
-                if (buf[splitbyte] == '\n' && buf[splitbyte + 1] == '\n') {
-                    return splitbyte + 2;
-                }
-                splitbyte++;
-            }
-            return 0;
-        }
-
-        /**
-         * Find the byte positions where multipart boundaries start. This reads
-         * a large block at a time and uses a temporary buffer to optimize
-         * (memory mapped) file access.
-         */
-        private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) {
-            int[] res = new int[0];
-            if (b.remaining() < boundary.length) {
-                return res;
-            }
-
-            int search_window_pos = 0;
-            byte[] search_window = new byte[4 * 1024 + boundary.length];
-
-            int first_fill = (b.remaining() < search_window.length) ? b.remaining() : search_window.length;
-            b.get(search_window, 0, first_fill);
-            int new_bytes = first_fill - boundary.length;
-
-            do {
-                // Search the search_window
-                for (int j = 0; j < new_bytes; j++) {
-                    for (int i = 0; i < boundary.length; i++) {
-                        if (search_window[j + i] != boundary[i])
-                            break;
-                        if (i == boundary.length - 1) {
-                            // Match found, add it to results
-                            int[] new_res = new int[res.length + 1];
-                            System.arraycopy(res, 0, new_res, 0, res.length);
-                            new_res[res.length] = search_window_pos + j;
-                            res = new_res;
-                        }
-                    }
-                }
-                search_window_pos += new_bytes;
-
-                // Copy the end of the buffer to the start
-                System.arraycopy(search_window, search_window.length - boundary.length, search_window, 0, boundary.length);
-
-                // Refill search_window
-                new_bytes = search_window.length - boundary.length;
-                new_bytes = (b.remaining() < new_bytes) ? b.remaining() : new_bytes;
-                b.get(search_window, boundary.length, new_bytes);
-            } while (new_bytes > 0);
-            return res;
-        }
-
-        @Override
-        public CookieHandler getCookies() {
-            return this.cookies;
-        }
-
-        @Override
-        public final Map<String, String> getHeaders() {
-            return this.headers;
-        }
-
-        @Override
-        public final InputStream getInputStream() {
-            return this.inputStream;
-        }
-
-        @Override
-        public final Method getMethod() {
-            return this.method;
-        }
-
-        /**
-         * @deprecated use {@link #getParameters()} instead.
-         */
-        @Override
-        @Deprecated
-        public final Map<String, String> getParms() {
-            Map<String, String> result = new HashMap<String, String>();
-            for (String key : this.parms.keySet()) {
-                result.put(key, this.parms.get(key).get(0));
-            }
-
-            return result;
-        }
-
-        @Override
-        public final Map<String, List<String>> getParameters() {
-            return this.parms;
-        }
-
-        @Override
-        public String getQueryParameterString() {
-            return this.queryParameterString;
-        }
-
-        private RandomAccessFile getTmpBucket() {
-            try {
-                TempFile tempFile = this.tempFileManager.createTempFile(null);
-                return new RandomAccessFile(tempFile.getName(), "rw");
-            } catch (Exception e) {
-                throw new Error(e); // we won't recover, so throw an error
-            }
-        }
-
-        @Override
-        public final String getUri() {
-            return this.uri;
-        }
-
-        /**
-         * Deduce body length in bytes. Either from "content-length" header or
-         * read bytes.
-         */
-        public long getBodySize() {
-            if (this.headers.containsKey("content-length")) {
-                return Long.parseLong(this.headers.get("content-length"));
-            } else if (this.splitbyte < this.rlen) {
-                return this.rlen - this.splitbyte;
-            }
-            return 0;
-        }
-
-        @Override
-        public void parseBody(Map<String, String> files) throws IOException, ResponseException {
-            RandomAccessFile randomAccessFile = null;
-            try {
-                long size = getBodySize();
-                ByteArrayOutputStream baos = null;
-                DataOutput requestDataOutput = null;
-
-                // Store the request in memory or a file, depending on size
-                if (size < MEMORY_STORE_LIMIT) {
-                    baos = new ByteArrayOutputStream();
-                    requestDataOutput = new DataOutputStream(baos);
-                } else {
-                    randomAccessFile = getTmpBucket();
-                    requestDataOutput = randomAccessFile;
-                }
-
-                // Read all the body and write it to request_data_output
-                byte[] buf = new byte[REQUEST_BUFFER_LEN];
-                while (this.rlen >= 0 && size > 0) {
-                    this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, REQUEST_BUFFER_LEN));
-                    size -= this.rlen;
-                    if (this.rlen > 0) {
-                        requestDataOutput.write(buf, 0, this.rlen);
-                    }
-                }
-
-                ByteBuffer fbuf = null;
-                if (baos != null) {
-                    fbuf = ByteBuffer.wrap(baos.toByteArray(), 0, baos.size());
-                } else {
-                    fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length());
-                    randomAccessFile.seek(0);
-                }
-
-                // If the method is POST, there may be parameters
-                // in data section, too, read it:
-                if (Method.POST.equals(this.method)) {
-                    ContentType contentType = new ContentType(this.headers.get("content-type"));
-                    if (contentType.isMultipart()) {
-                        String boundary = contentType.getBoundary();
-                        if (boundary == null) {
-                            throw new ResponseException(Response.Status.BAD_REQUEST,
-                                    "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html");
-                        }
-                        decodeMultipartFormData(contentType, fbuf, this.parms, files);
-                    } else {
-                        byte[] postBytes = new byte[fbuf.remaining()];
-                        fbuf.get(postBytes);
-                        String postLine = new String(postBytes, contentType.getEncoding()).trim();
-                        // Handle application/x-www-form-urlencoded
-                        if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType.getContentType())) {
-                            decodeParms(postLine, this.parms);
-                        } else if (postLine.length() != 0) {
-                            // Special case for raw POST data => create a
-                            // special files entry "postData" with raw content
-                            // data
-                            files.put("postData", postLine);
-                        }
-                    }
-                } else if (Method.PUT.equals(this.method)) {
-                    files.put("content", saveTmpFile(fbuf, 0, fbuf.limit(), null));
-                }
-            } finally {
-                safeClose(randomAccessFile);
-            }
-        }
-
-        /**
-         * Retrieves the content of a sent file and saves it to a temporary
-         * file. The full path to the saved file is returned.
-         */
-        private String saveTmpFile(ByteBuffer b, int offset, int len, String filename_hint) {
-            String path = "";
-            if (len > 0) {
-                FileOutputStream fileOutputStream = null;
-                try {
-                    TempFile tempFile = this.tempFileManager.createTempFile(filename_hint);
-                    ByteBuffer src = b.duplicate();
-                    fileOutputStream = new FileOutputStream(tempFile.getName());
-                    FileChannel dest = fileOutputStream.getChannel();
-                    src.position(offset).limit(offset + len);
-                    dest.write(src.slice());
-                    path = tempFile.getName();
-                } catch (Exception e) { // Catch exception if any
-                    throw new Error(e); // we won't recover, so throw an error
-                } finally {
-                    safeClose(fileOutputStream);
-                }
-            }
-            return path;
-        }
-
-        @Override
-        public String getRemoteIpAddress() {
-            return this.remoteIp;
-        }
-
-        @Override
-        public String getRemoteHostName() {
-            return this.remoteHostname;
-        }
-    }
-
-    /**
-     * Handles one session, i.e. parses the HTTP request and returns the
-     * response.
-     */
-    public interface IHTTPSession {
-
-        void execute() throws IOException;
-
-        CookieHandler getCookies();
-
-        Map<String, String> getHeaders();
-
-        InputStream getInputStream();
-
-        Method getMethod();
-
-        /**
-         * This method will only return the first value for a given parameter.
-         * You will want to use getParameters if you expect multiple values for
-         * a given key.
-         * 
-         * @deprecated use {@link #getParameters()} instead.
-         */
-        @Deprecated
-        Map<String, String> getParms();
-
-        Map<String, List<String>> getParameters();
-
-        String getQueryParameterString();
-
-        /**
-         * @return the path part of the URL.
-         */
-        String getUri();
-
-        /**
-         * Adds the files in the request body to the files map.
-         * 
-         * @param files
-         *            map to modify
-         */
-        void parseBody(Map<String, String> files) throws IOException, ResponseException;
-
-        /**
-         * Get the remote ip address of the requester.
-         * 
-         * @return the IP address.
-         */
-        String getRemoteIpAddress();
-
-        /**
-         * Get the remote hostname of the requester.
-         * 
-         * @return the hostname.
-         */
-        String getRemoteHostName();
-    }
-
-    /**
-     * HTTP Request methods, with the ability to decode a <code>String</code>
-     * back to its enum value.
-     */
-    public enum Method {
-        GET,
-        PUT,
-        POST,
-        DELETE,
-        HEAD,
-        OPTIONS,
-        TRACE,
-        CONNECT,
-        PATCH,
-        PROPFIND,
-        PROPPATCH,
-        MKCOL,
-        MOVE,
-        COPY,
-        LOCK,
-        UNLOCK;
-
-        static Method lookup(String method) {
-            if (method == null)
-                return null;
-
-            try {
-                return valueOf(method);
-            } catch (IllegalArgumentException e) {
-                // TODO: Log it?
-                return null;
-            }
-        }
-    }
-
-    /**
-     * HTTP response. Return one of these from serve().
-     */
-    public static class Response implements Closeable {
-
-        public interface IStatus {
-
-            String getDescription();
-
-            int getRequestStatus();
-        }
-
-        /**
-         * Some HTTP response status codes
-         */
-        public enum Status implements IStatus {
-            SWITCH_PROTOCOL(101, "Switching Protocols"),
-
-            OK(200, "OK"),
-            CREATED(201, "Created"),
-            ACCEPTED(202, "Accepted"),
-            NO_CONTENT(204, "No Content"),
-            PARTIAL_CONTENT(206, "Partial Content"),
-            MULTI_STATUS(207, "Multi-Status"),
-
-            REDIRECT(301, "Moved Permanently"),
-            /**
-             * Many user agents mishandle 302 in ways that violate the RFC1945
-             * spec (i.e., redirect a POST to a GET). 303 and 307 were added in
-             * RFC2616 to address this. You should prefer 303 and 307 unless the
-             * calling user agent does not support 303 and 307 functionality
-             */
-            @Deprecated
-            FOUND(302, "Found"),
-            REDIRECT_SEE_OTHER(303, "See Other"),
-            NOT_MODIFIED(304, "Not Modified"),
-            TEMPORARY_REDIRECT(307, "Temporary Redirect"),
-
-            BAD_REQUEST(400, "Bad Request"),
-            UNAUTHORIZED(401, "Unauthorized"),
-            FORBIDDEN(403, "Forbidden"),
-            NOT_FOUND(404, "Not Found"),
-            METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
-            NOT_ACCEPTABLE(406, "Not Acceptable"),
-            REQUEST_TIMEOUT(408, "Request Timeout"),
-            CONFLICT(409, "Conflict"),
-            GONE(410, "Gone"),
-            LENGTH_REQUIRED(411, "Length Required"),
-            PRECONDITION_FAILED(412, "Precondition Failed"),
-            PAYLOAD_TOO_LARGE(413, "Payload Too Large"),
-            UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
-            RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"),
-            EXPECTATION_FAILED(417, "Expectation Failed"),
-            TOO_MANY_REQUESTS(429, "Too Many Requests"),
-
-            INTERNAL_ERROR(500, "Internal Server Error"),
-            NOT_IMPLEMENTED(501, "Not Implemented"),
-            SERVICE_UNAVAILABLE(503, "Service Unavailable"),
-            UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported");
-
-            private final int requestStatus;
-
-            private final String description;
-
-            Status(int requestStatus, String description) {
-                this.requestStatus = requestStatus;
-                this.description = description;
-            }
-
-            public static Status lookup(int requestStatus) {
-                for (Status status : Status.values()) {
-                    if (status.getRequestStatus() == requestStatus) {
-                        return status;
-                    }
-                }
-                return null;
-            }
-
-            @Override
-            public String getDescription() {
-                return "" + this.requestStatus + " " + this.description;
-            }
-
-            @Override
-            public int getRequestStatus() {
-                return this.requestStatus;
-            }
-
-        }
-
-        /**
-         * Output stream that will automatically send every write to the wrapped
-         * OutputStream according to chunked transfer:
-         * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
-         */
-        private static class ChunkedOutputStream extends FilterOutputStream {
-
-            public ChunkedOutputStream(OutputStream out) {
-                super(out);
-            }
-
-            @Override
-            public void write(int b) throws IOException {
-                byte[] data = {
-                    (byte) b
-                };
-                write(data, 0, 1);
-            }
-
-            @Override
-            public void write(byte[] b) throws IOException {
-                write(b, 0, b.length);
-            }
-
-            @Override
-            public void write(byte[] b, int off, int len) throws IOException {
-                if (len == 0)
-                    return;
-                out.write(String.format("%x\r\n", len).getBytes());
-                out.write(b, off, len);
-                out.write("\r\n".getBytes());
-            }
-
-            public void finish() throws IOException {
-                out.write("0\r\n\r\n".getBytes());
-            }
-
-        }
-
-        /**
-         * HTTP status code after processing, e.g. "200 OK", Status.OK
-         */
-        private IStatus status;
-
-        /**
-         * MIME type of content, e.g. "text/html"
-         */
-        private String mimeType;
-
-        /**
-         * Data of the response, may be null.
-         */
-        private InputStream data;
-
-        private long contentLength;
-
-        /**
-         * Headers for the HTTP response. Use addHeader() to add lines. the
-         * lowercase map is automatically kept up to date.
-         */
-        @SuppressWarnings("serial")
-        private final Map<String, String> header = new HashMap<String, String>() {
-
-            public String put(String key, String value) {
-                lowerCaseHeader.put(key == null ? key : key.toLowerCase(), value);
-                return super.put(key, value);
-            };
-        };
-
-        /**
-         * copy of the header map with all the keys lowercase for faster
-         * searching.
-         */
-        private final Map<String, String> lowerCaseHeader = new HashMap<String, String>();
-
-        /**
-         * The request method that spawned this response.
-         */
-        private Method requestMethod;
-
-        /**
-         * Use chunkedTransfer
-         */
-        private boolean chunkedTransfer;
-
-        private boolean encodeAsGzip;
-
-        private boolean keepAlive;
-
-        /**
-         * Creates a fixed length response if totalBytes>=0, otherwise chunked.
-         */
-        protected Response(IStatus status, String mimeType, InputStream data, long totalBytes) {
-            this.status = status;
-            this.mimeType = mimeType;
-            if (data == null) {
-                this.data = new ByteArrayInputStream(new byte[0]);
-                this.contentLength = 0L;
-            } else {
-                this.data = data;
-                this.contentLength = totalBytes;
-            }
-            this.chunkedTransfer = this.contentLength < 0;
-            keepAlive = true;
-        }
-
-        @Override
-        public void close() throws IOException {
-            if (this.data != null) {
-                this.data.close();
-            }
-        }
-
-        /**
-         * Adds given line to the header.
-         */
-        public void addHeader(String name, String value) {
-            this.header.put(name, value);
-        }
-
-        /**
-         * Indicate to close the connection after the Response has been sent.
-         * 
-         * @param close
-         *            {@code true} to hint connection closing, {@code false} to
-         *            let connection be closed by client.
-         */
-        public void closeConnection(boolean close) {
-            if (close)
-                this.header.put("connection", "close");
-            else
-                this.header.remove("connection");
-        }
-
-        /**
-         * @return {@code true} if connection is to be closed after this
-         *         Response has been sent.
-         */
-        public boolean isCloseConnection() {
-            return "close".equals(getHeader("connection"));
-        }
-
-        public InputStream getData() {
-            return this.data;
-        }
-
-        public String getHeader(String name) {
-            return this.lowerCaseHeader.get(name.toLowerCase());
-        }
-
-        public String getMimeType() {
-            return this.mimeType;
-        }
-
-        public Method getRequestMethod() {
-            return this.requestMethod;
-        }
-
-        public IStatus getStatus() {
-            return this.status;
-        }
-
-        public void setGzipEncoding(boolean encodeAsGzip) {
-            this.encodeAsGzip = encodeAsGzip;
-        }
-
-        public void setKeepAlive(boolean useKeepAlive) {
-            this.keepAlive = useKeepAlive;
-        }
-
-        /**
-         * Sends given response to the socket.
-         */
-        protected void send(OutputStream outputStream) {
-            SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
-            gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
-
-            try {
-                if (this.status == null) {
-                    throw new Error("sendResponse(): Status can't be null.");
-                }
-                PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, new ContentType(this.mimeType).getEncoding())), false);
-                pw.append("HTTP/1.1 ").append(this.status.getDescription()).append(" \r\n");
-                if (this.mimeType != null) {
-                    printHeader(pw, "Content-Type", this.mimeType);
-                }
-                if (getHeader("date") == null) {
-                    printHeader(pw, "Date", gmtFrmt.format(new Date()));
-                }
-                for (Entry<String, String> entry : this.header.entrySet()) {
-                    printHeader(pw, entry.getKey(), entry.getValue());
-                }
-                if (getHeader("connection") == null) {
-                    printHeader(pw, "Connection", (this.keepAlive ? "keep-alive" : "close"));
-                }
-                if (getHeader("content-length") != null) {
-                    encodeAsGzip = false;
-                }
-                if (encodeAsGzip) {
-                    printHeader(pw, "Content-Encoding", "gzip");
-                    setChunkedTransfer(true);
-                }
-                long pending = this.data != null ? this.contentLength : 0;
-                if (this.requestMethod != Method.HEAD && this.chunkedTransfer) {
-                    printHeader(pw, "Transfer-Encoding", "chunked");
-                } else if (!encodeAsGzip) {
-                    pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, pending);
-                }
-                pw.append("\r\n");
-                pw.flush();
-                sendBodyWithCorrectTransferAndEncoding(outputStream, pending);
-                outputStream.flush();
-                safeClose(this.data);
-            } catch (IOException ioe) {
-                NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe);
-            }
-        }
-
-        @SuppressWarnings("static-method")
-        protected void printHeader(PrintWriter pw, String key, String value) {
-            pw.append(key).append(": ").append(value).append("\r\n");
-        }
-
-        protected long sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, long defaultSize) {
-            String contentLengthString = getHeader("content-length");
-            long size = defaultSize;
-            if (contentLengthString != null) {
-                try {
-                    size = Long.parseLong(contentLengthString);
-                } catch (NumberFormatException ex) {
-                    LOG.severe("content-length was no number " + contentLengthString);
-                }
-            }
-            pw.print("Content-Length: " + size + "\r\n");
-            return size;
-        }
-
-        private void sendBodyWithCorrectTransferAndEncoding(OutputStream outputStream, long pending) throws IOException {
-            if (this.requestMethod != Method.HEAD && this.chunkedTransfer) {
-                ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(outputStream);
-                sendBodyWithCorrectEncoding(chunkedOutputStream, -1);
-                chunkedOutputStream.finish();
-            } else {
-                sendBodyWithCorrectEncoding(outputStream, pending);
-            }
-        }
-
-        private void sendBodyWithCorrectEncoding(OutputStream outputStream, long pending) throws IOException {
-            if (encodeAsGzip) {
-                GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
-                sendBody(gzipOutputStream, -1);
-                gzipOutputStream.finish();
-            } else {
-                sendBody(outputStream, pending);
-            }
-        }
-
-        /**
-         * Sends the body to the specified OutputStream. The pending parameter
-         * limits the maximum amounts of bytes sent unless it is -1, in which
-         * case everything is sent.
-         * 
-         * @param outputStream
-         *            the OutputStream to send data to
-         * @param pending
-         *            -1 to send everything, otherwise sets a max limit to the
-         *            number of bytes sent
-         * @throws IOException
-         *             if something goes wrong while sending the data.
-         */
-        private void sendBody(OutputStream outputStream, long pending) throws IOException {
-            long BUFFER_SIZE = 16 * 1024;
-            byte[] buff = new byte[(int) BUFFER_SIZE];
-            boolean sendEverything = pending == -1;
-            while (pending > 0 || sendEverything) {
-                long bytesToRead = sendEverything ? BUFFER_SIZE : Math.min(pending, BUFFER_SIZE);
-                int read = this.data.read(buff, 0, (int) bytesToRead);
-                if (read <= 0) {
-                    break;
-                }
-                outputStream.write(buff, 0, read);
-                if (!sendEverything) {
-                    pending -= read;
-                }
-            }
-        }
-
-        public void setChunkedTransfer(boolean chunkedTransfer) {
-            this.chunkedTransfer = chunkedTransfer;
-        }
-
-        public void setData(InputStream data) {
-            this.data = data;
-        }
-
-        public void setMimeType(String mimeType) {
-            this.mimeType = mimeType;
-        }
-
-        public void setRequestMethod(Method requestMethod) {
-            this.requestMethod = requestMethod;
-        }
-
-        public void setStatus(IStatus status) {
-            this.status = status;
-        }
-    }
-
-    public static final class ResponseException extends Exception {
-
-        private static final long serialVersionUID = 6569838532917408380L;
-
-        private final Response.Status status;
-
-        public ResponseException(Response.Status status, String message) {
-            super(message);
-            this.status = status;
-        }
-
-        public ResponseException(Response.Status status, String message, Exception e) {
-            super(message, e);
-            this.status = status;
-        }
-
-        public Response.Status getStatus() {
-            return this.status;
-        }
-    }
-
-    /**
-     * The runnable that will be used for the main listening thread.
-     */
-    public class ServerRunnable implements Runnable {
-
-        private final int timeout;
-
-        private IOException bindException;
-
-        private boolean hasBinded = false;
-
-        public ServerRunnable(int timeout) {
-            this.timeout = timeout;
-        }
-
-        @Override
-        public void run() {
-            try {
-                myServerSocket.bind(hostname != null ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
-                hasBinded = true;
-            } catch (IOException e) {
-                this.bindException = e;
-                return;
-            }
-            do {
-                try {
-                    final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept();
-                    if (this.timeout > 0) {
-                        finalAccept.setSoTimeout(this.timeout);
-                    }
-                    final InputStream inputStream = finalAccept.getInputStream();
-                    NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream));
-                } catch (IOException e) {
-                    NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
-                }
-            } while (!NanoHTTPD.this.myServerSocket.isClosed());
-        }
-    }
-
-    /**
-     * A temp file.
-     * <p/>
-     * <p>
-     * Temp files are responsible for managing the actual temporary storage and
-     * cleaning themselves up when no longer needed.
-     * </p>
-     */
-    public interface TempFile {
-
-        public void delete() throws Exception;
-
-        public String getName();
-
-        public OutputStream open() throws Exception;
-    }
-
-    /**
-     * Temp file manager.
-     * <p/>
-     * <p>
-     * Temp file managers are created 1-to-1 with incoming requests, to create
-     * and cleanup temporary files created as a result of handling the request.
-     * </p>
-     */
-    public interface TempFileManager {
-
-        void clear();
-
-        public TempFile createTempFile(String filename_hint) throws Exception;
-    }
-
-    /**
-     * Factory to create temp file managers.
-     */
-    public interface TempFileManagerFactory {
-
-        public TempFileManager create();
-    }
-
-    /**
-     * Factory to create ServerSocketFactories.
-     */
-    public interface ServerSocketFactory {
-
-        public ServerSocket create() throws IOException;
-
-    }
-
-    /**
-     * Maximum time to wait on Socket.getInputStream().read() (in milliseconds)
-     * This is required as the Keep-Alive HTTP connections would otherwise block
-     * the socket reading thread forever (or as long the browser is open).
-     */
-    public static final int SOCKET_READ_TIMEOUT = 5000;
-
-    /**
-     * Common MIME type for dynamic content: plain text
-     */
-    public static final String MIME_PLAINTEXT = "text/plain";
-
-    /**
-     * Common MIME type for dynamic content: html
-     */
-    public static final String MIME_HTML = "text/html";
-
-    /**
-     * Pseudo-Parameter to use to store the actual query string in the
-     * parameters map for later re-processing.
-     */
-    private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING";
-
-    /**
-     * logger to log to.
-     */
-    private static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName());
-
-    /**
-     * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE
-     */
-    protected static Map<String, String> MIME_TYPES;
-
-    public static Map<String, String> mimeTypes() {
-        if (MIME_TYPES == null) {
-            MIME_TYPES = new HashMap<String, String>();
-            loadMimeTypes(MIME_TYPES, "META-INF/nanohttpd/default-mimetypes.properties");
-            loadMimeTypes(MIME_TYPES, "META-INF/nanohttpd/mimetypes.properties");
-            if (MIME_TYPES.isEmpty()) {
-                LOG.log(Level.WARNING, "no mime types found in the classpath! please provide mimetypes.properties");
-            }
-        }
-        return MIME_TYPES;
-    }
-
-    @SuppressWarnings({
-        "unchecked",
-        "rawtypes"
-    })
-    private static void loadMimeTypes(Map<String, String> result, String resourceName) {
-        try {
-            Enumeration<URL> resources = NanoHTTPD.class.getClassLoader().getResources(resourceName);
-            while (resources.hasMoreElements()) {
-                URL url = (URL) resources.nextElement();
-                Properties properties = new Properties();
-                InputStream stream = null;
-                try {
-                    stream = url.openStream();
-                    properties.load(stream);
-                } catch (IOException e) {
-                    LOG.log(Level.SEVERE, "could not load mimetypes from " + url, e);
-                } finally {
-                    safeClose(stream);
-                }
-                result.putAll((Map) properties);
-            }
-        } catch (IOException e) {
-            LOG.log(Level.INFO, "no mime types available at " + resourceName);
-        }
-    };
-
-    /**
-     * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an
-     * array of loaded KeyManagers. These objects must properly
-     * loaded/initialized by the caller.
-     */
-    public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException {
-        SSLServerSocketFactory res = null;
-        try {
-            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
-            trustManagerFactory.init(loadedKeyStore);
-            SSLContext ctx = SSLContext.getInstance("TLS");
-            ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null);
-            res = ctx.getServerSocketFactory();
-        } catch (Exception e) {
-            throw new IOException(e.getMessage());
-        }
-        return res;
-    }
-
-    /**
-     * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a
-     * loaded KeyManagerFactory. These objects must properly loaded/initialized
-     * by the caller.
-     */
-    public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException {
-        try {
-            return makeSSLSocketFactory(loadedKeyStore, loadedKeyFactory.getKeyManagers());
-        } catch (Exception e) {
-            throw new IOException(e.getMessage());
-        }
-    }
-
-    /**
-     * Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your
-     * certificate and passphrase
-     */
-    public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException {
-        try {
-            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
-            InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath);
-
-            if (keystoreStream == null) {
-                throw new IOException("Unable to load keystore from classpath: " + keyAndTrustStoreClasspathPath);
-            }
-
-            keystore.load(keystoreStream, passphrase);
-            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
-            keyManagerFactory.init(keystore, passphrase);
-            return makeSSLSocketFactory(keystore, keyManagerFactory);
-        } catch (Exception e) {
-            throw new IOException(e.getMessage());
-        }
-    }
-
-    /**
-     * Get MIME type from file name extension, if possible
-     * 
-     * @param uri
-     *            the string representing a file
-     * @return the connected mime/type
-     */
-    public static String getMimeTypeForFile(String uri) {
-        int dot = uri.lastIndexOf('.');
-        String mime = null;
-        if (dot >= 0) {
-            mime = mimeTypes().get(uri.substring(dot + 1).toLowerCase());
-        }
-        return mime == null ? "application/octet-stream" : mime;
-    }
-
-    private static final void safeClose(Object closeable) {
-        try {
-            if (closeable != null) {
-                if (closeable instanceof Closeable) {
-                    ((Closeable) closeable).close();
-                } else if (closeable instanceof Socket) {
-                    ((Socket) closeable).close();
-                } else if (closeable instanceof ServerSocket) {
-                    ((ServerSocket) closeable).close();
-                } else {
-                    throw new IllegalArgumentException("Unknown object to close");
-                }
-            }
-        } catch (IOException e) {
-            NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e);
-        }
-    }
-
-    private final String hostname;
-
-    private final int myPort;
-
-    private volatile ServerSocket myServerSocket;
-
-    private ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory();
-
-    private Thread myThread;
-
-    /**
-     * Pluggable strategy for asynchronously executing requests.
-     */
-    protected AsyncRunner asyncRunner;
-
-    /**
-     * Pluggable strategy for creating and cleaning up temporary files.
-     */
-    private TempFileManagerFactory tempFileManagerFactory;
-
-    /**
-     * Constructs an HTTP server on given port.
-     */
-    public NanoHTTPD(int port) {
-        this(null, port);
-    }
-
-    // -------------------------------------------------------------------------------
-    // //
-    //
-    // Threading Strategy.
-    //
-    // -------------------------------------------------------------------------------
-    // //
-
-    /**
-     * Constructs an HTTP server on given hostname and port.
-     */
-    public NanoHTTPD(String hostname, int port) {
-        this.hostname = hostname;
-        this.myPort = port;
-        setTempFileManagerFactory(new DefaultTempFileManagerFactory());
-        setAsyncRunner(new DefaultAsyncRunner());
-    }
-
-    /**
-     * Forcibly closes all connections that are open.
-     */
-    public synchronized void closeAllConnections() {
-        stop();
-    }
-
-    /**
-     * create a instance of the client handler, subclasses can return a subclass
-     * of the ClientHandler.
-     * 
-     * @param finalAccept
-     *            the socket the cleint is connected to
-     * @param inputStream
-     *            the input stream
-     * @return the client handler
-     */
-    protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) {
-        return new ClientHandler(inputStream, finalAccept);
-    }
-
-    /**
-     * Instantiate the server runnable, can be overwritten by subclasses to
-     * provide a subclass of the ServerRunnable.
-     * 
-     * @param timeout
-     *            the socet timeout to use.
-     * @return the server runnable.
-     */
-    protected ServerRunnable createServerRunnable(final int timeout) {
-        return new ServerRunnable(timeout);
-    }
-
-    /**
-     * Decode parameters from a URL, handing the case where a single parameter
-     * name might have been supplied several times, by return lists of values.
-     * In general these lists will contain a single element.
-     * 
-     * @param parms
-     *            original <b>NanoHTTPD</b> parameters values, as passed to the
-     *            <code>serve()</code> method.
-     * @return a map of <code>String</code> (parameter name) to
-     *         <code>List&lt;String&gt;</code> (a list of the values supplied).
-     */
-    protected static Map<String, List<String>> decodeParameters(Map<String, String> parms) {
-        return decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER));
-    }
-
-    // -------------------------------------------------------------------------------
-    // //
-
-    /**
-     * Decode parameters from a URL, handing the case where a single parameter
-     * name might have been supplied several times, by return lists of values.
-     * In general these lists will contain a single element.
-     * 
-     * @param queryString
-     *            a query string pulled from the URL.
-     * @return a map of <code>String</code> (parameter name) to
-     *         <code>List&lt;String&gt;</code> (a list of the values supplied).
-     */
-    protected static Map<String, List<String>> decodeParameters(String queryString) {
-        Map<String, List<String>> parms = new HashMap<String, List<String>>();
-        if (queryString != null) {
-            StringTokenizer st = new StringTokenizer(queryString, "&");
-            while (st.hasMoreTokens()) {
-                String e = st.nextToken();
-                int sep = e.indexOf('=');
-                String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();
-                if (!parms.containsKey(propertyName)) {
-                    parms.put(propertyName, new ArrayList<String>());
-                }
-                String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null;
-                if (propertyValue != null) {
-                    parms.get(propertyName).add(propertyValue);
-                }
-            }
-        }
-        return parms;
-    }
-
-    /**
-     * Decode percent encoded <code>String</code> values.
-     * 
-     * @param str
-     *            the percent encoded <code>String</code>
-     * @return expanded form of the input, for example "foo%20bar" becomes
-     *         "foo bar"
-     */
-    protected static String decodePercent(String str) {
-        String decoded = null;
-        try {
-            decoded = URLDecoder.decode(str, "UTF8");
-        } catch (UnsupportedEncodingException ignored) {
-            NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored);
-        }
-        return decoded;
-    }
-
-    /**
-     * @return true if the gzip compression should be used if the client
-     *         accespts it. Default this option is on for text content and off
-     *         for everything. Override this for custom semantics.
-     */
-    @SuppressWarnings("static-method")
-    protected boolean useGzipWhenAccepted(Response r) {
-        return r.getMimeType() != null && (r.getMimeType().toLowerCase().contains("text/") || r.getMimeType().toLowerCase().contains("/json"));
-    }
-
-    public final int getListeningPort() {
-        return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort();
-    }
-
-    public final boolean isAlive() {
-        return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive();
-    }
-
-    public ServerSocketFactory getServerSocketFactory() {
-        return serverSocketFactory;
-    }
-
-    public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) {
-        this.serverSocketFactory = serverSocketFactory;
-    }
-
-    public String getHostname() {
-        return hostname;
-    }
-
-    public TempFileManagerFactory getTempFileManagerFactory() {
-        return tempFileManagerFactory;
-    }
-
-    /**
-     * Call before start() to serve over HTTPS instead of HTTP
-     */
-    public void makeSecure(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) {
-        this.serverSocketFactory = new SecureServerSocketFactory(sslServerSocketFactory, sslProtocols);
-    }
-
-    /**
-     * Create a response with unknown length (using HTTP 1.1 chunking).
-     */
-    public static Response newChunkedResponse(IStatus status, String mimeType, InputStream data) {
-        return new Response(status, mimeType, data, -1);
-    }
-
-    /**
-     * Create a response with known length.
-     */
-    public static Response newFixedLengthResponse(IStatus status, String mimeType, InputStream data, long totalBytes) {
-        return new Response(status, mimeType, data, totalBytes);
-    }
-
-    /**
-     * Create a text response with known length.
-     */
-    public static Response newFixedLengthResponse(IStatus status, String mimeType, String txt) {
-        ContentType contentType = new ContentType(mimeType);
-        if (txt == null) {
-            return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0);
-        } else {
-            byte[] bytes;
-            try {
-                CharsetEncoder newEncoder = Charset.forName(contentType.getEncoding()).newEncoder();
-                if (!newEncoder.canEncode(txt)) {
-                    contentType = contentType.tryUTF8();
-                }
-                bytes = txt.getBytes(contentType.getEncoding());
-            } catch (UnsupportedEncodingException e) {
-                NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e);
-                bytes = new byte[0];
-            }
-            return newFixedLengthResponse(status, contentType.getContentTypeHeader(), new ByteArrayInputStream(bytes), bytes.length);
-        }
-    }
-
-    /**
-     * Create a text response with known length.
-     */
-    public static Response newFixedLengthResponse(String msg) {
-        return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, msg);
-    }
-
-    /**
-     * Override this to customize the server.
-     * <p/>
-     * <p/>
-     * (By default, this returns a 404 "Not Found" plain text error response.)
-     * 
-     * @param session
-     *            The HTTP session
-     * @return HTTP response, see class Response for details
-     */
-    public Response serve(IHTTPSession session) {
-        Map<String, String> files = new HashMap<String, String>();
-        Method method = session.getMethod();
-        if (Method.PUT.equals(method) || Method.POST.equals(method)) {
-            try {
-                session.parseBody(files);
-            } catch (IOException ioe) {
-                return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
-            } catch (ResponseException re) {
-                return newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage());
-            }
-        }
-
-        Map<String, String> parms = session.getParms();
-        parms.put(NanoHTTPD.QUERY_STRING_PARAMETER, session.getQueryParameterString());
-        return serve(session.getUri(), method, session.getHeaders(), parms, files);
-    }
-
-    /**
-     * Override this to customize the server.
-     * <p/>
-     * <p/>
-     * (By default, this returns a 404 "Not Found" plain text error response.)
-     * 
-     * @param uri
-     *            Percent-decoded URI without parameters, for example
-     *            "/index.cgi"
-     * @param method
-     *            "GET", "POST" etc.
-     * @param parms
-     *            Parsed, percent decoded parameters from URI and, in case of
-     *            POST, data.
-     * @param headers
-     *            Header entries, percent decoded
-     * @return HTTP response, see class Response for details
-     */
-    @Deprecated
-    public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) {
-        return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found");
-    }
-
-    /**
-     * Pluggable strategy for asynchronously executing requests.
-     * 
-     * @param asyncRunner
-     *            new strategy for handling threads.
-     */
-    public void setAsyncRunner(AsyncRunner asyncRunner) {
-        this.asyncRunner = asyncRunner;
-    }
-
-    /**
-     * Pluggable strategy for creating and cleaning up temporary files.
-     * 
-     * @param tempFileManagerFactory
-     *            new strategy for handling temp files.
-     */
-    public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
-        this.tempFileManagerFactory = tempFileManagerFactory;
-    }
-
-    /**
-     * Start the server.
-     * 
-     * @throws IOException
-     *             if the socket is in use.
-     */
-    public void start() throws IOException {
-        start(NanoHTTPD.SOCKET_READ_TIMEOUT);
-    }
-
-    /**
-     * Starts the server (in setDaemon(true) mode).
-     */
-    public void start(final int timeout) throws IOException {
-        start(timeout, true);
-    }
-
-    /**
-     * Start the server.
-     * 
-     * @param timeout
-     *            timeout to use for socket connections.
-     * @param daemon
-     *            start the thread daemon or not.
-     * @throws IOException
-     *             if the socket is in use.
-     */
-    public void start(final int timeout, boolean daemon) throws IOException {
-        this.myServerSocket = this.getServerSocketFactory().create();
-        this.myServerSocket.setReuseAddress(true);
-
-        ServerRunnable serverRunnable = createServerRunnable(timeout);
-        this.myThread = new Thread(serverRunnable);
-        this.myThread.setDaemon(daemon);
-        this.myThread.setName("NanoHttpd Main Listener");
-        this.myThread.start();
-        while (!serverRunnable.hasBinded && serverRunnable.bindException == null) {
-            try {
-                Thread.sleep(10L);
-            } catch (Throwable e) {
-                // on android this may not be allowed, that's why we
-                // catch throwable the wait should be very short because we are
-                // just waiting for the bind of the socket
-            }
-        }
-        if (serverRunnable.bindException != null) {
-            throw serverRunnable.bindException;
-        }
-    }
-
-    /**
-     * Stop the server.
-     */
-    public void stop() {
-        try {
-            safeClose(this.myServerSocket);
-            this.asyncRunner.closeAll();
-            if (this.myThread != null) {
-                this.myThread.join();
-            }
-        } catch (Exception e) {
-            NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e);
-        }
-    }
-
-    public final boolean wasStarted() {
-        return this.myServerSocket != null && this.myThread != null;
-    }
-}
diff --git a/src/be/nikiroo/utils/Progress.java b/src/be/nikiroo/utils/Progress.java
deleted file mode 100644 (file)
index bb143ef..0000000
+++ /dev/null
@@ -1,535 +0,0 @@
-package be.nikiroo.utils;
-
-import java.util.ArrayList;
-import java.util.EventListener;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- * Progress reporting system, possibly nested.
- * <p>
- * A {@link Progress} can have a name, and that name will be reported through
- * the event system (it will report the first non-null name in the stack from
- * the {@link Progress} from which the event originated to the parent the event
- * is listened on).
- * <p>
- * The {@link Progress} also has a table of keys/values shared amongst all the
- * hierarchy (note that when adding a {@link Progress} to others, its values
- * will be prioritized if some with the same keys were already present in the
- * hierarchy).
- * 
- * @author niki
- */
-public class Progress {
-       /**
-        * This event listener is designed to report progress events from
-        * {@link Progress}.
-        * 
-        * @author niki
-        */
-       public interface ProgressListener extends EventListener {
-               /**
-                * A progression event.
-                * 
-                * @param progress
-                *            the {@link Progress} object that generated it, not
-                *            necessarily the same as the one where the listener was
-                *            attached (it could be a child {@link Progress} of this
-                *            {@link Progress}).
-                * @param name
-                *            the first non-null name of the {@link Progress} step that
-                *            generated this event
-                */
-               public void progress(Progress progress, String name);
-       }
-
-       private Map<Object, Object> map = new HashMap<Object, Object>();
-       private Progress parent = null;
-       private Object lock = new Object();
-       private String name;
-       private Map<Progress, Double> children;
-       private List<ProgressListener> listeners;
-       private int min;
-       private int max;
-       private double relativeLocalProgress;
-       private double relativeProgress; // children included
-
-       /**
-        * Create a new default unnamed {@link Progress}, from 0 to 100.
-        */
-       public Progress() {
-               this(null);
-       }
-
-       /**
-        * Create a new default {@link Progress}, from 0 to 100.
-        * 
-        * @param name
-        *            the name of this {@link Progress} step
-        */
-       public Progress(String name) {
-               this(name, 0, 100);
-       }
-
-       /**
-        * Create a new unnamed {@link Progress}, from min to max.
-        * 
-        * @param min
-        *            the minimum progress value (and starting value) -- must be
-        *            non-negative
-        * @param max
-        *            the maximum progress value
-        */
-       public Progress(int min, int max) {
-               this(null, min, max);
-       }
-
-       /**
-        * Create a new {@link Progress}, from min to max.
-        * 
-        * @param name
-        *            the name of this {@link Progress} step
-        * @param min
-        *            the minimum progress value (and starting value) -- must be
-        *            non-negative
-        * @param max
-        *            the maximum progress value
-        */
-       public Progress(String name, int min, int max) {
-               this.name = name;
-               this.children = new HashMap<Progress, Double>();
-               this.listeners = new ArrayList<Progress.ProgressListener>();
-               setMinMax(min, max);
-               setProgress(min);
-       }
-
-       /**
-        * The name of this {@link Progress} step.
-        * 
-        * @return the name, can be NULL
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * The name of this {@link Progress} step.
-        * 
-        * @param name
-        *            the new name
-        */
-       public void setName(String name) {
-               this.name = name;
-               changed(this, name);
-       }
-
-       /**
-        * The minimum progress value.
-        * 
-        * @return the min
-        */
-       public int getMin() {
-               return min;
-       }
-
-       /**
-        * The minimum progress value.
-        * 
-        * @param min
-        *            the min to set
-        * 
-        * 
-        * @throws RuntimeException
-        *             if min &lt; 0 or if min &gt; max
-        */
-       public void setMin(int min) {
-               if (min < 0) {
-                       throw new RuntimeException("negative values not supported");
-               }
-
-               synchronized (lock) {
-                       if (min > max) {
-                               throw new RuntimeException(
-                                               "The minimum progress value must be <= the maximum progress value");
-                       }
-
-                       this.min = min;
-               }
-       }
-
-       /**
-        * The maximum progress value.
-        * 
-        * @return the max
-        */
-       public int getMax() {
-               return max;
-       }
-
-       /**
-        * The maximum progress value (must be >= the minimum progress value).
-        * 
-        * @param max
-        *            the max to set
-        * 
-        * 
-        * @throws RuntimeException
-        *             if max &lt; min
-        */
-       public void setMax(int max) {
-               synchronized (lock) {
-                       if (max < min) {
-                               throw new Error(
-                                               "The maximum progress value must be >= the minimum progress value");
-                       }
-
-                       this.max = max;
-               }
-       }
-
-       /**
-        * Set both the minimum and maximum progress values.
-        * 
-        * @param min
-        *            the min
-        * @param max
-        *            the max
-        * 
-        * @throws RuntimeException
-        *             if min &lt; 0 or if min &gt; max
-        */
-       public void setMinMax(int min, int max) {
-               if (min < 0) {
-                       throw new RuntimeException("negative values not supported");
-               }
-
-               if (min > max) {
-                       throw new RuntimeException(
-                                       "The minimum progress value must be <= the maximum progress value");
-               }
-
-               synchronized (lock) {
-                       this.min = min;
-                       this.max = max;
-               }
-       }
-
-       /**
-        * Get the total progress value (including the optional children
-        * {@link Progress}) on a {@link Progress#getMin()} to
-        * {@link Progress#getMax()} scale.
-        * 
-        * @return the progress the value
-        */
-       public int getProgress() {
-               return (int) Math.round(relativeProgress * (max - min));
-       }
-
-       /**
-        * Set the local progress value (not including the optional children
-        * {@link Progress}), on a {@link Progress#getMin()} to
-        * {@link Progress#getMax()} scale.
-        * 
-        * @param progress
-        *            the progress to set
-        */
-       public void setProgress(int progress) {
-               synchronized (lock) {
-                       double childrenProgress = relativeProgress - relativeLocalProgress;
-
-                       relativeLocalProgress = ((double) progress) / (max - min);
-
-                       setRelativeProgress(this, name,
-                                       relativeLocalProgress + childrenProgress);
-               }
-       }
-
-       /**
-        * Get the total progress value (including the optional children
-        * {@link Progress}) on a 0.0 to 1.0 scale.
-        * 
-        * @return the progress
-        */
-       public double getRelativeProgress() {
-               return relativeProgress;
-       }
-
-       /**
-        * Set the total progress value (including the optional children
-        * {@link Progress}), on a 0 to 1 scale.
-        * <p>
-        * Will generate a changed event from this very {@link Progress}.
-        * 
-        * @param relativeProgress
-        *            the progress to set
-        */
-       public void setRelativeProgress(double relativeProgress) {
-               setRelativeProgress(this, name, relativeProgress);
-       }
-
-       /**
-        * Set the total progress value (including the optional children
-        * {@link Progress}), on a 0 to 1 scale.
-        * 
-        * @param pg
-        *            the {@link Progress} to report as the progression emitter (can
-        *            be NULL, will then be considered the same as <tt>this</tt>)
-        * @param name
-        *            the current name (if it is NULL, the first non-null name in
-        *            the hierarchy will overwrite it) of the {@link Progress} who
-        *            emitted this change
-        * @param relativeProgress
-        *            the progress to set
-        */
-       private void setRelativeProgress(Progress pg, String name,
-                       double relativeProgress) {
-               synchronized (lock) {
-                       relativeProgress = Math.max(0, relativeProgress);
-                       relativeProgress = Math.min(1, relativeProgress);
-                       this.relativeProgress = relativeProgress;
-
-                       changed(pg, name);
-               }
-       }
-
-       /**
-        * Get the total progress value (including the optional children
-        * {@link Progress}) on a 0 to 1 scale.
-        * 
-        * @return the progress the value
-        */
-       private int getLocalProgress() {
-               return (int) Math.round(relativeLocalProgress * (max - min));
-       }
-
-       /**
-        * Add some value to the current progression of this {@link Progress}.
-        * 
-        * @param step
-        *            the amount to add
-        */
-       public void add(int step) {
-               synchronized (lock) {
-                       setProgress(getLocalProgress() + step);
-               }
-       }
-
-       /**
-        * Check if the action corresponding to this {@link Progress} is done (i.e.,
-        * if its progress value == its max value).
-        * 
-        * @return TRUE if it is
-        */
-       public boolean isDone() {
-               return getProgress() == max;
-       }
-
-       /**
-        * Mark the {@link Progress} as done by setting its value to max.
-        */
-       public void done() {
-               synchronized (lock) {
-                       double childrenProgress = relativeProgress - relativeLocalProgress;
-                       relativeLocalProgress = 1 - childrenProgress;
-                       setRelativeProgress(this, name, 1d);
-               }
-       }
-
-       /**
-        * Return the list of direct children of this {@link Progress}.
-        * <p>
-        * Can return an empty list, but never NULL.
-        * 
-        * @return the children (Who will think of the children??)
-        */
-       public List<Progress> getChildren() {
-               synchronized (lock) {
-                       return new ArrayList<Progress>(children.keySet());
-               }
-       }
-
-       /**
-        * The weight of this children if it is actually a child of this.
-        * 
-        * @param child
-        *            the child to get the weight of
-        * 
-        * @return NULL if this is not a child of this
-        */
-       public Double getWeight(Progress child) {
-               synchronized (lock) {
-                       return children.get(child);
-               }
-       }
-
-       /**
-        * Notify the listeners that this {@link Progress} changed value.
-        * 
-        * @param pg
-        *            the emmiter, that is, the (sub-){link Progress} that just
-        *            reported some change, not always the same as <tt>this</tt>
-        * @param name
-        *            the current name (if it is NULL, the first non-null name in
-        *            the hierarchy will overwrite it) of the {@link Progress} who
-        *            emitted this change
-        */
-       private void changed(Progress pg, String name) {
-               if (pg == null) {
-                       pg = this;
-               }
-
-               if (name == null) {
-                       name = this.name;
-               }
-
-               synchronized (lock) {
-                       for (ProgressListener l : listeners) {
-                               l.progress(pg, name);
-                       }
-               }
-       }
-
-       /**
-        * Add a {@link ProgressListener} that will trigger on progress changes.
-        * <p>
-        * Note: the {@link Progress} that will be reported will be the active
-        * progress, not necessarily the same as the current one (it could be a
-        * child {@link Progress} of this {@link Progress}).
-        * 
-        * @param l
-        *            the listener
-        */
-       public void addProgressListener(ProgressListener l) {
-               synchronized (lock) {
-                       this.listeners.add(l);
-               }
-       }
-
-       /**
-        * Remove a {@link ProgressListener} that would trigger on progress changes.
-        * 
-        * @param l
-        *            the listener
-        * 
-        * @return TRUE if it was found (and removed)
-        */
-       public boolean removeProgressListener(ProgressListener l) {
-               synchronized (lock) {
-                       return this.listeners.remove(l);
-               }
-       }
-
-       /**
-        * Add a child {@link Progress} of the given weight.
-        * 
-        * @param progress
-        *            the child {@link Progress} to add
-        * @param weight
-        *            the weight (on a {@link Progress#getMin()} to
-        *            {@link Progress#getMax()} scale) of this child
-        *            {@link Progress} in relation to its parent
-        * 
-        * @throws RuntimeException
-        *             if weight exceed {@link Progress#getMax()} or if progress
-        *             already has a parent
-        */
-       public void addProgress(Progress progress, double weight) {
-               if (weight < min || weight > max) {
-                       throw new RuntimeException(String.format(
-                                       "Progress object %s cannot have a weight of %f, "
-                                                       + "it is outside of its parent (%s) range (%d)",
-                                       progress.name, weight, name, max));
-               }
-
-               if (progress.parent != null) {
-                       throw new RuntimeException(String.format(
-                                       "Progress object %s cannot be added to %s, "
-                                                       + "as it already has a parent (%s)",
-                                       progress.name, name, progress.parent.name));
-               }
-
-               ProgressListener progressListener = new ProgressListener() {
-                       @Override
-                       public void progress(Progress pg, String name) {
-                               synchronized (lock) {
-                                       double total = relativeLocalProgress;
-                                       for (Entry<Progress, Double> entry : children.entrySet()) {
-                                               total += (entry.getValue() / (max - min))
-                                                               * entry.getKey().getRelativeProgress();
-                                       }
-
-                                       setRelativeProgress(pg, name, total);
-                               }
-                       }
-               };
-
-               synchronized (lock) {
-                       // Should not happen but just in case
-                       if (this.map != progress.map) {
-                               this.map.putAll(progress.map);
-                       }
-                       progress.map = this.map;
-                       progress.parent = this;
-                       this.children.put(progress, weight);
-                       progress.addProgressListener(progressListener);
-               }
-       }
-
-       /**
-        * Set the given value for the given key on this {@link Progress} and it's
-        * children.
-        * 
-        * @param key
-        *            the key
-        * @param value
-        *            the value
-        */
-       public void put(Object key, Object value) {
-               map.put(key, value);
-       }
-
-       /**
-        * Return the value associated with this key as a {@link String} if any,
-        * NULL if not.
-        * <p>
-        * If the value is not NULL but not a {@link String}, it will be converted
-        * via {@link Object#toString()}.
-        * 
-        * @param key
-        *            the key to check
-        * 
-        * @return the value or NULL
-        */
-       public String getString(Object key) {
-               Object value = map.get(key);
-               if (value == null) {
-                       return null;
-               }
-
-               return value.toString();
-       }
-
-       /**
-        * Return the value associated with this key if any, NULL if not.
-        * 
-        * @param key
-        *            the key to check
-        * 
-        * @return the value or NULL
-        */
-       public Object get(Object key) {
-               return map.get(key);
-       }
-
-       @Override
-       public String toString() {
-               return "[Progress]" //
-                               + (name == null || name.isEmpty() ? "" : " " + name) //
-                               + ": " + getProgress() + " / " + getMax() //
-                               + (children.isEmpty() ? ""
-                                               : " (with " + children.size() + " children)") //
-               ;
-       }
-}
diff --git a/src/be/nikiroo/utils/Proxy.java b/src/be/nikiroo/utils/Proxy.java
deleted file mode 100644 (file)
index 750b3ee..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-package be.nikiroo.utils;
-
-import java.net.Authenticator;
-import java.net.PasswordAuthentication;
-
-/**
- * Simple proxy helper to select a default internet proxy.
- * 
- * @author niki
- */
-public class Proxy {
-       /**
-        * Use the proxy described by this string:
-        * <ul>
-        * <li><tt>((user(:pass)@)proxy:port)</tt></li>
-        * <li>System proxy is noted <tt>:</tt></li>
-        * </ul>
-        * Some examples:
-        * <ul>
-        * <li><tt></tt> → do not use any proxy</li>
-        * <li><tt>:</tt> → use the system proxy</li>
-        * <li><tt>user@prox.com</tt> → use the proxy "prox.com" with default port
-        * and user "user"</li>
-        * <li><tt>prox.com:8080</tt> → use the proxy "prox.com" on port 8080</li>
-        * <li><tt>user:pass@prox.com:8080</tt> → use "prox.com" on port 8080
-        * authenticated as "user" with password "pass"</li>
-        * <li><tt>user:pass@:</tt> → use the system proxy authenticated as user
-        * "user" with password "pass"</li>
-        * </ul>
-        * 
-        * @param proxy
-        *            the proxy
-        */
-       static public void use(String proxy) {
-               if (proxy != null && !proxy.isEmpty()) {
-                       String user = null;
-                       String password = null;
-                       int port = 8080;
-
-                       if (proxy.contains("@")) {
-                               int pos = proxy.indexOf("@");
-                               user = proxy.substring(0, pos);
-                               proxy = proxy.substring(pos + 1);
-                               if (user.contains(":")) {
-                                       pos = user.indexOf(":");
-                                       password = user.substring(pos + 1);
-                                       user = user.substring(0, pos);
-                               }
-                       }
-
-                       if (proxy.equals(":")) {
-                               proxy = null;
-                       } else if (proxy.contains(":")) {
-                               int pos = proxy.indexOf(":");
-                               try {
-                                       port = Integer.parseInt(proxy.substring(0, pos));
-                                       proxy = proxy.substring(pos + 1);
-                               } catch (Exception e) {
-                               }
-                       }
-
-                       if (proxy == null) {
-                               Proxy.useSystemProxy(user, password);
-                       } else {
-                               Proxy.useProxy(proxy, port, user, password);
-                       }
-               }
-       }
-
-       /**
-        * Use the system proxy.
-        */
-       static public void useSystemProxy() {
-               useSystemProxy(null, null);
-       }
-
-       /**
-        * Use the system proxy with the given login/password, for authenticated
-        * proxies.
-        * 
-        * @param user
-        *            the user name or login
-        * @param password
-        *            the password
-        */
-       static public void useSystemProxy(String user, String password) {
-               System.setProperty("java.net.useSystemProxies", "true");
-               auth(user, password);
-       }
-
-       /**
-        * Use the give proxy.
-        * 
-        * @param host
-        *            the proxy host name or IP address
-        * @param port
-        *            the port to use
-        */
-       static public void useProxy(String host, int port) {
-               useProxy(host, port, null, null);
-       }
-
-       /**
-        * Use the given proxy with the given login/password, for authenticated
-        * proxies.
-        * 
-        * @param user
-        *            the user name or login
-        * @param password
-        *            the password
-        * @param host
-        *            the proxy host name or IP address
-        * @param port
-        *            the port to use
-        * @param user
-        *            the user name or login
-        * @param password
-        *            the password
-        */
-       static public void useProxy(String host, int port, String user,
-                       String password) {
-               System.setProperty("http.proxyHost", host);
-               System.setProperty("http.proxyPort", Integer.toString(port));
-               auth(user, password);
-       }
-
-       /**
-        * Select the default authenticator for proxy requests.
-        * 
-        * @param user
-        *            the user name or login
-        * @param password
-        *            the password
-        */
-       static private void auth(final String user, final String password) {
-               if (user != null && password != null) {
-                       Authenticator proxy = new Authenticator() {
-                               @Override
-                               protected PasswordAuthentication getPasswordAuthentication() {
-                                       if (getRequestorType() == RequestorType.PROXY) {
-                                               return new PasswordAuthentication(user,
-                                                               password.toCharArray());
-                                       }
-                                       return null;
-                               }
-                       };
-                       Authenticator.setDefault(proxy);
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/StringJustifier.java b/src/be/nikiroo/utils/StringJustifier.java
deleted file mode 100644 (file)
index ed20291..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * This file was taken from:
- * Jexer - Java Text User Interface
- *
- * The MIT License (MIT)
- *
- * Copyright (C) 2017 Kevin Lamonte
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * @author Kevin Lamonte [kevin.lamonte@gmail.com]
- * @version 1
- * 
- * I added some changes to integrate it here.
- * @author Niki
- */
-package be.nikiroo.utils;
-
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * StringJustifier contains methods to convert one or more long lines of strings
- * into justified text paragraphs.
- */
-class StringJustifier {
-       /**
-        * Process the given text into a list of left-justified lines of a given
-        * max-width.
-        * 
-        * @param data
-        *            the text to justify
-        * @param width
-        *            the maximum width of a line
-        * 
-        * @return the list of justified lines
-        */
-       static List<String> left(final String data, final int width) {
-               return left(data, width, false);
-       }
-
-       /**
-        * Right-justify a string into a list of lines.
-        * 
-        * @param str
-        *            the string
-        * @param n
-        *            the maximum number of characters in a line
-        * @return the list of lines
-        */
-       static List<String> right(final String str, final int n) {
-               List<String> result = new LinkedList<String>();
-
-               /*
-                * Same as left(), but preceed each line with spaces to make it n chars
-                * long.
-                */
-               List<String> lines = left(str, n);
-               for (String line : lines) {
-                       StringBuilder sb = new StringBuilder();
-                       for (int i = 0; i < n - line.length(); i++) {
-                               sb.append(' ');
-                       }
-                       sb.append(line);
-                       result.add(sb.toString());
-               }
-
-               return result;
-       }
-
-       /**
-        * Center a string into a list of lines.
-        * 
-        * @param str
-        *            the string
-        * @param n
-        *            the maximum number of characters in a line
-        * @return the list of lines
-        */
-       static List<String> center(final String str, final int n) {
-               List<String> result = new LinkedList<String>();
-
-               /*
-                * Same as left(), but preceed/succeed each line with spaces to make it
-                * n chars long.
-                */
-               List<String> lines = left(str, n);
-               for (String line : lines) {
-                       StringBuilder sb = new StringBuilder();
-                       int l = (n - line.length()) / 2;
-                       int r = n - line.length() - l;
-                       for (int i = 0; i < l; i++) {
-                               sb.append(' ');
-                       }
-                       sb.append(line);
-                       for (int i = 0; i < r; i++) {
-                               sb.append(' ');
-                       }
-                       result.add(sb.toString());
-               }
-
-               return result;
-       }
-
-       /**
-        * Fully-justify a string into a list of lines.
-        * 
-        * @param str
-        *            the string
-        * @param n
-        *            the maximum number of characters in a line
-        * @return the list of lines
-        */
-       static List<String> full(final String str, final int n) {
-               List<String> result = new LinkedList<String>();
-
-               /*
-                * Same as left(true), but insert spaces between words to make each line
-                * n chars long. The "algorithm" here is pretty dumb: it performs a
-                * split on space and then re-inserts multiples of n between words.
-                */
-               List<String> lines = left(str, n, true);
-               for (int lineI = 0; lineI < lines.size() - 1; lineI++) {
-                       String line = lines.get(lineI);
-                       String[] words = line.split(" ");
-                       if (words.length > 1) {
-                               int charCount = 0;
-                               for (int i = 0; i < words.length; i++) {
-                                       charCount += words[i].length();
-                               }
-                               int spaceCount = n - charCount;
-                               int q = spaceCount / (words.length - 1);
-                               int r = spaceCount % (words.length - 1);
-                               StringBuilder sb = new StringBuilder();
-                               for (int i = 0; i < words.length - 1; i++) {
-                                       sb.append(words[i]);
-                                       for (int j = 0; j < q; j++) {
-                                               sb.append(' ');
-                                       }
-                                       if (r > 0) {
-                                               sb.append(' ');
-                                               r--;
-                                       }
-                               }
-                               for (int j = 0; j < r; j++) {
-                                       sb.append(' ');
-                               }
-                               sb.append(words[words.length - 1]);
-                               result.add(sb.toString());
-                       } else {
-                               result.add(line);
-                       }
-               }
-               if (lines.size() > 0) {
-                       result.add(lines.get(lines.size() - 1));
-               }
-
-               return result;
-       }
-
-       /**
-        * Process the given text into a list of left-justified lines of a given
-        * max-width.
-        * 
-        * @param data
-        *            the text to justify
-        * @param width
-        *            the maximum width of a line
-        * @param minTwoWords
-        *            use 2 words per line minimum if the text allows it
-        * 
-        * @return the list of justified lines
-        */
-       static private List<String> left(final String data, final int width,
-                       boolean minTwoWords) {
-               List<String> lines = new LinkedList<String>();
-
-               for (String dataLine : data.split("\n")) {
-                       String line = rightTrim(dataLine.replace("\t", "    "));
-
-                       if (width > 0 && line.length() > width) {
-                               while (line.length() > 0) {
-                                       int i = Math.min(line.length(), width - 1); // -1 for "-"
-
-                                       boolean needDash = true;
-                                       // find the best space if any and if needed
-                                       int prevSpace = 0;
-                                       if (i < line.length()) {
-                                               prevSpace = -1;
-                                               int space = line.indexOf(' ');
-                                               int numOfSpaces = 0;
-
-                                               while (space > -1 && space <= i) {
-                                                       prevSpace = space;
-                                                       space = line.indexOf(' ', space + 1);
-                                                       numOfSpaces++;
-                                               }
-
-                                               if (prevSpace > 0 && (!minTwoWords || numOfSpaces >= 2)) {
-                                                       i = prevSpace;
-                                                       needDash = false;
-                                               }
-                                       }
-                                       //
-
-                                       // no dash before space/dash
-                                       if ((i + 1) < line.length()) {
-                                               char car = line.charAt(i);
-                                               char nextCar = line.charAt(i + 1);
-                                               if (car == ' ' || car == '-' || nextCar == ' ') {
-                                                       needDash = false;
-                                               } else if (i > 0) {
-                                                       char prevCar = line.charAt(i - 1);
-                                                       if (prevCar == ' ' || prevCar == '-') {
-                                                               needDash = false;
-                                                               i--;
-                                                       }
-                                               }
-                                       }
-
-                                       // if the space freed by the removed dash allows it, or if
-                                       // it is the last char, add the next char
-                                       if (!needDash || i >= line.length() - 1) {
-                                               int checkI = Math.min(i + 1, line.length());
-                                               if (checkI == i || checkI <= width) {
-                                                       needDash = false;
-                                                       i = checkI;
-                                               }
-                                       }
-
-                                       // no dash before parenthesis (but cannot add one more
-                                       // after)
-                                       if ((i + 1) < line.length()) {
-                                               char nextCar = line.charAt(i + 1);
-                                               if (nextCar == '(' || nextCar == ')') {
-                                                       needDash = false;
-                                               }
-                                       }
-
-                                       if (needDash) {
-                                               lines.add(rightTrim(line.substring(0, i)) + "-");
-                                       } else {
-                                               lines.add(rightTrim(line.substring(0, i)));
-                                       }
-
-                                       // full trim (remove spaces when cutting)
-                                       line = line.substring(i).trim();
-                               }
-                       } else {
-                               lines.add(line);
-                       }
-               }
-
-               return lines;
-       }
-
-       /**
-        * Trim the given {@link String} on the right only.
-        * 
-        * @param data
-        *            the source {@link String}
-        * @return the right-trimmed String or Empty if it was NULL
-        */
-       static private String rightTrim(String data) {
-               if (data == null)
-                       return "";
-
-               return ("|" + data).trim().substring(1);
-       }
-}
diff --git a/src/be/nikiroo/utils/StringUtils.java b/src/be/nikiroo/utils/StringUtils.java
deleted file mode 100644 (file)
index be1c654..0000000
+++ /dev/null
@@ -1,1165 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.text.Normalizer;
-import java.text.Normalizer.Form;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.regex.Pattern;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.GZIPOutputStream;
-
-import org.unbescape.html.HtmlEscape;
-import org.unbescape.html.HtmlEscapeLevel;
-import org.unbescape.html.HtmlEscapeType;
-
-import be.nikiroo.utils.streams.Base64InputStream;
-import be.nikiroo.utils.streams.Base64OutputStream;
-
-/**
- * This class offer some utilities based around {@link String}s.
- * 
- * @author niki
- */
-public class StringUtils {
-       /**
-        * This enum type will decide the alignment of a {@link String} when padding
-        * or justification is applied (if there is enough horizontal space for it
-        * to be aligned).
-        */
-       public enum Alignment {
-               /** Aligned at left. */
-               LEFT,
-               /** Centered. */
-               CENTER,
-               /** Aligned at right. */
-               RIGHT,
-               /** Full justified (to both left and right). */
-               JUSTIFY,
-
-               // Old Deprecated values:
-
-               /** DEPRECATED: please use LEFT. */
-               @Deprecated
-               Beginning,
-               /** DEPRECATED: please use CENTER. */
-               @Deprecated
-               Center,
-               /** DEPRECATED: please use RIGHT. */
-               @Deprecated
-               End;
-
-               /**
-                * Return the non-deprecated version of this enum if needed (or return
-                * self if not).
-                * 
-                * @return the non-deprecated value
-                */
-               Alignment undeprecate() {
-                       if (this == Beginning)
-                               return LEFT;
-                       if (this == Center)
-                               return CENTER;
-                       if (this == End)
-                               return RIGHT;
-                       return this;
-               }
-       }
-
-       static private Pattern marks = getMarks();
-
-       /**
-        * Fix the size of the given {@link String} either with space-padding or by
-        * shortening it.
-        * 
-        * @param text
-        *            the {@link String} to fix
-        * @param width
-        *            the size of the resulting {@link String} or -1 for a noop
-        * 
-        * @return the resulting {@link String} of size <i>size</i>
-        */
-       static public String padString(String text, int width) {
-               return padString(text, width, true, null);
-       }
-
-       /**
-        * Fix the size of the given {@link String} either with space-padding or by
-        * optionally shortening it.
-        * 
-        * @param text
-        *            the {@link String} to fix
-        * @param width
-        *            the size of the resulting {@link String} if the text fits or
-        *            if cut is TRUE or -1 for a noop
-        * @param cut
-        *            cut the {@link String} shorter if needed
-        * @param align
-        *            align the {@link String} in this position if we have enough
-        *            space (default is Alignment.Beginning)
-        * 
-        * @return the resulting {@link String} of size <i>size</i> minimum
-        */
-       static public String padString(String text, int width, boolean cut,
-                       Alignment align) {
-
-               if (align == null) {
-                       align = Alignment.LEFT;
-               }
-
-               align = align.undeprecate();
-
-               if (width >= 0) {
-                       if (text == null)
-                               text = "";
-
-                       int diff = width - text.length();
-
-                       if (diff < 0) {
-                               if (cut)
-                                       text = text.substring(0, width);
-                       } else if (diff > 0) {
-                               if (diff < 2 && align != Alignment.RIGHT)
-                                       align = Alignment.LEFT;
-
-                               switch (align) {
-                               case RIGHT:
-                                       text = new String(new char[diff]).replace('\0', ' ') + text;
-                                       break;
-                               case CENTER:
-                                       int pad1 = (diff) / 2;
-                                       int pad2 = (diff + 1) / 2;
-                                       text = new String(new char[pad1]).replace('\0', ' ') + text
-                                                       + new String(new char[pad2]).replace('\0', ' ');
-                                       break;
-                               case LEFT:
-                               default:
-                                       text = text + new String(new char[diff]).replace('\0', ' ');
-                                       break;
-                               }
-                       }
-               }
-
-               return text;
-       }
-
-       /**
-        * Justify a text into width-sized (at the maximum) lines and return all the
-        * lines concatenated into a single '\\n'-separated line of text.
-        * 
-        * @param text
-        *            the {@link String} to justify
-        * @param width
-        *            the maximum size of the resulting lines
-        * 
-        * @return a list of justified text lines concatenated into a single
-        *         '\\n'-separated line of text
-        */
-       static public String justifyTexts(String text, int width) {
-               StringBuilder builder = new StringBuilder();
-               for (String line : justifyText(text, width, null)) {
-                       if (builder.length() > 0) {
-                               builder.append('\n');
-                       }
-                       builder.append(line);
-               }
-
-               return builder.toString();
-       }
-
-       /**
-        * Justify a text into width-sized (at the maximum) lines.
-        * 
-        * @param text
-        *            the {@link String} to justify
-        * @param width
-        *            the maximum size of the resulting lines
-        * 
-        * @return a list of justified text lines
-        */
-       static public List<String> justifyText(String text, int width) {
-               return justifyText(text, width, null);
-       }
-
-       /**
-        * Justify a text into width-sized (at the maximum) lines.
-        * 
-        * @param text
-        *            the {@link String} to justify
-        * @param width
-        *            the maximum size of the resulting lines
-        * @param align
-        *            align the lines in this position (default is
-        *            Alignment.Beginning)
-        * 
-        * @return a list of justified text lines
-        */
-       static public List<String> justifyText(String text, int width,
-                       Alignment align) {
-               if (align == null) {
-                       align = Alignment.LEFT;
-               }
-
-               align = align.undeprecate();
-
-               switch (align) {
-               case CENTER:
-                       return StringJustifier.center(text, width);
-               case RIGHT:
-                       return StringJustifier.right(text, width);
-               case JUSTIFY:
-                       return StringJustifier.full(text, width);
-               case LEFT:
-               default:
-                       return StringJustifier.left(text, width);
-               }
-       }
-
-       /**
-        * Justify a text into width-sized (at the maximum) lines.
-        * 
-        * @param text
-        *            the {@link String} to justify
-        * @param width
-        *            the maximum size of the resulting lines
-        * 
-        * @return a list of justified text lines
-        */
-       static public List<String> justifyText(List<String> text, int width) {
-               return justifyText(text, width, null);
-       }
-
-       /**
-        * Justify a text into width-sized (at the maximum) lines.
-        * 
-        * @param text
-        *            the {@link String} to justify
-        * @param width
-        *            the maximum size of the resulting lines
-        * @param align
-        *            align the lines in this position (default is
-        *            Alignment.Beginning)
-        * 
-        * @return a list of justified text lines
-        */
-       static public List<String> justifyText(List<String> text, int width,
-                       Alignment align) {
-               List<String> result = new ArrayList<String>();
-
-               // Content <-> Bullet spacing (null = no spacing)
-               List<Entry<String, String>> lines = new ArrayList<Entry<String, String>>();
-               StringBuilder previous = null;
-               StringBuilder tmp = new StringBuilder();
-               String previousItemBulletSpacing = null;
-               String itemBulletSpacing = null;
-               for (String inputLine : text) {
-                       boolean previousLineComplete = true;
-
-                       String current = inputLine.replace("\t", "    ");
-                       itemBulletSpacing = getItemSpacing(current);
-                       boolean bullet = isItemLine(current);
-                       if ((previousItemBulletSpacing == null || itemBulletSpacing
-                                       .length() <= previousItemBulletSpacing.length()) && !bullet) {
-                               itemBulletSpacing = null;
-                       }
-
-                       if (itemBulletSpacing != null) {
-                               current = current.trim();
-                               if (!current.isEmpty() && bullet) {
-                                       current = current.substring(1);
-                               }
-                               current = current.trim();
-                               previousLineComplete = bullet;
-                       } else {
-                               tmp.setLength(0);
-                               for (String word : current.split(" ")) {
-                                       if (word.isEmpty()) {
-                                               continue;
-                                       }
-
-                                       if (tmp.length() > 0) {
-                                               tmp.append(' ');
-                                       }
-                                       tmp.append(word.trim());
-                               }
-                               current = tmp.toString();
-
-                               previousLineComplete = current.isEmpty()
-                                               || previousItemBulletSpacing != null
-                                               || (previous != null && isFullLine(previous))
-                                               || isHrLine(current) || isHrLine(previous);
-                       }
-
-                       if (previous == null) {
-                               previous = new StringBuilder();
-                       } else {
-                               if (previousLineComplete) {
-                                       lines.add(new AbstractMap.SimpleEntry<String, String>(
-                                                       previous.toString(), previousItemBulletSpacing));
-                                       previous.setLength(0);
-                                       previousItemBulletSpacing = itemBulletSpacing;
-                               } else {
-                                       previous.append(' ');
-                               }
-                       }
-
-                       previous.append(current);
-
-               }
-
-               if (previous != null) {
-                       lines.add(new AbstractMap.SimpleEntry<String, String>(previous
-                                       .toString(), previousItemBulletSpacing));
-               }
-
-               for (Entry<String, String> line : lines) {
-                       String content = line.getKey();
-                       String spacing = line.getValue();
-
-                       String bullet = "- ";
-                       if (spacing == null) {
-                               bullet = "";
-                               spacing = "";
-                       }
-
-                       if (spacing.length() > width + 3) {
-                               spacing = "";
-                       }
-
-                       for (String subline : StringUtils.justifyText(content, width
-                                       - (spacing.length() + bullet.length()), align)) {
-                               result.add(spacing + bullet + subline);
-                               if (!bullet.isEmpty()) {
-                                       bullet = "  ";
-                               }
-                       }
-               }
-
-               return result;
-       }
-
-       /**
-        * Sanitise the given input to make it more Terminal-friendly by removing
-        * combining characters.
-        * 
-        * @param input
-        *            the input to sanitise
-        * @param allowUnicode
-        *            allow Unicode or only allow ASCII Latin characters
-        * 
-        * @return the sanitised {@link String}
-        */
-       static public String sanitize(String input, boolean allowUnicode) {
-               return sanitize(input, allowUnicode, !allowUnicode);
-       }
-
-       /**
-        * Sanitise the given input to make it more Terminal-friendly by removing
-        * combining characters.
-        * 
-        * @param input
-        *            the input to sanitise
-        * @param allowUnicode
-        *            allow Unicode or only allow ASCII Latin characters
-        * @param removeAllAccents
-        *            TRUE to replace all accentuated characters by their non
-        *            accentuated counter-parts
-        * 
-        * @return the sanitised {@link String}
-        */
-       static public String sanitize(String input, boolean allowUnicode,
-                       boolean removeAllAccents) {
-
-               if (removeAllAccents) {
-                       input = Normalizer.normalize(input, Form.NFKD);
-                       if (marks != null) {
-                               input = marks.matcher(input).replaceAll("");
-                       }
-               }
-
-               input = Normalizer.normalize(input, Form.NFKC);
-
-               if (!allowUnicode) {
-                       StringBuilder builder = new StringBuilder();
-                       for (int index = 0; index < input.length(); index++) {
-                               char car = input.charAt(index);
-                               // displayable chars in ASCII are in the range 32<->255,
-                               // except DEL (127)
-                               if (car >= 32 && car <= 255 && car != 127) {
-                                       builder.append(car);
-                               }
-                       }
-                       input = builder.toString();
-               }
-
-               return input;
-       }
-
-       /**
-        * Convert between the time in milliseconds to a {@link String} in a "fixed"
-        * way (to exchange data over the wire, for instance).
-        * <p>
-        * Precise to the second.
-        * 
-        * @param time
-        *            the specified number of milliseconds since the standard base
-        *            time known as "the epoch", namely January 1, 1970, 00:00:00
-        *            GMT
-        * 
-        * @return the time as a {@link String}
-        */
-       static public String fromTime(long time) {
-               SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-               return sdf.format(new Date(time));
-       }
-
-       /**
-        * Convert between the time as a {@link String} to milliseconds in a "fixed"
-        * way (to exchange data over the wire, for instance).
-        * <p>
-        * Precise to the second.
-        * 
-        * @param displayTime
-        *            the time as a {@link String}
-        * 
-        * @return the number of milliseconds since the standard base time known as
-        *         "the epoch", namely January 1, 1970, 00:00:00 GMT, or -1 in case
-        *         of error
-        * 
-        * @throws ParseException
-        *             in case of parse error
-        */
-       static public long toTime(String displayTime) throws ParseException {
-               SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-               return sdf.parse(displayTime).getTime();
-       }
-
-       /**
-        * Return a hash of the given {@link String}.
-        * 
-        * @param input
-        *            the input data
-        * 
-        * @return the hash
-        * 
-        * @deprecated please use {@link HashUtils}
-        */
-       @Deprecated
-       static public String getMd5Hash(String input) {
-               try {
-                       MessageDigest md = MessageDigest.getInstance("MD5");
-                       md.update(getBytes(input));
-                       byte byteData[] = md.digest();
-
-                       StringBuffer hexString = new StringBuffer();
-                       for (int i = 0; i < byteData.length; i++) {
-                               String hex = Integer.toHexString(0xff & byteData[i]);
-                               if (hex.length() == 1)
-                                       hexString.append('0');
-                               hexString.append(hex);
-                       }
-
-                       return hexString.toString();
-               } catch (NoSuchAlgorithmException e) {
-                       return input;
-               }
-       }
-
-       /**
-        * Remove the HTML content from the given input, and un-html-ize the rest.
-        * 
-        * @param html
-        *            the HTML-encoded content
-        * 
-        * @return the HTML-free equivalent content
-        */
-       public static String unhtml(String html) {
-               StringBuilder builder = new StringBuilder();
-
-               int inTag = 0;
-               for (char car : html.toCharArray()) {
-                       if (car == '<') {
-                               inTag++;
-                       } else if (car == '>') {
-                               inTag--;
-                       } else if (inTag <= 0) {
-                               builder.append(car);
-                       }
-               }
-
-               char nbsp = ' '; // non-breakable space (a special char)
-               char space = ' ';
-               return HtmlEscape.unescapeHtml(builder.toString()).replace(nbsp, space);
-       }
-
-       /**
-        * Escape the given {@link String} so it can be used in XML, as content.
-        * 
-        * @param input
-        *            the input {@link String}
-        * 
-        * @return the escaped {@link String}
-        */
-       public static String xmlEscape(String input) {
-               if (input == null) {
-                       return "";
-               }
-
-               return HtmlEscape.escapeHtml(input,
-                               HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA,
-                               HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT);
-       }
-
-       /**
-        * Escape the given {@link String} so it can be used in XML, as text content
-        * inside double-quotes.
-        * 
-        * @param input
-        *            the input {@link String}
-        * 
-        * @return the escaped {@link String}
-        */
-       public static String xmlEscapeQuote(String input) {
-               if (input == null) {
-                       return "";
-               }
-
-               return HtmlEscape.escapeHtml(input,
-                               HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA,
-                               HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT);
-       }
-
-       /**
-        * Zip the data and then encode it into Base64.
-        * 
-        * @param data
-        *            the data
-        * 
-        * @return the Base64 zipped version
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static String zip64(String data) throws IOException {
-               try {
-                       return zip64(getBytes(data));
-               } catch (UnsupportedEncodingException e) {
-                       // All conforming JVM are required to support UTF-8
-                       e.printStackTrace();
-                       return null;
-               }
-       }
-
-       /**
-        * Zip the data and then encode it into Base64.
-        * 
-        * @param data
-        *            the data
-        * 
-        * @return the Base64 zipped version
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static String zip64(byte[] data) throws IOException {
-               // 1. compress
-               ByteArrayOutputStream bout = new ByteArrayOutputStream();
-               try {
-                       OutputStream out = new GZIPOutputStream(bout);
-                       try {
-                               out.write(data);
-                       } finally {
-                               out.close();
-                       }
-               } finally {
-                       data = bout.toByteArray();
-                       bout.close();
-               }
-
-               // 2. base64
-               InputStream in = new ByteArrayInputStream(data);
-               try {
-                       in = new Base64InputStream(in, true);
-                       return new String(IOUtils.toByteArray(in), "UTF-8");
-               } finally {
-                       in.close();
-               }
-       }
-
-       /**
-        * Unconvert from Base64 then unzip the content, which is assumed to be a
-        * String.
-        * 
-        * @param data
-        *            the data in Base64 format
-        * 
-        * @return the raw data
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static String unzip64s(String data) throws IOException {
-               return new String(unzip64(data), "UTF-8");
-       }
-
-       /**
-        * Unconvert from Base64 then unzip the content.
-        * 
-        * @param data
-        *            the data in Base64 format
-        * 
-        * @return the raw data
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static byte[] unzip64(String data) throws IOException {
-               InputStream in = new Base64InputStream(new ByteArrayInputStream(
-                               getBytes(data)), false);
-               try {
-                       in = new GZIPInputStream(in);
-                       return IOUtils.toByteArray(in);
-               } finally {
-                       in.close();
-               }
-       }
-
-       /**
-        * Convert the given data to Base64 format.
-        * 
-        * @param data
-        *            the data to convert
-        * 
-        * @return the Base64 {@link String} representation of the data
-        * 
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       public static String base64(String data) throws IOException {
-               return base64(getBytes(data));
-       }
-
-       /**
-        * Convert the given data to Base64 format.
-        * 
-        * @param data
-        *            the data to convert
-        * 
-        * @return the Base64 {@link String} representation of the data
-        * 
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       public static String base64(byte[] data) throws IOException {
-               Base64InputStream in = new Base64InputStream(new ByteArrayInputStream(
-                               data), true);
-               try {
-                       return new String(IOUtils.toByteArray(in), "UTF-8");
-               } finally {
-                       in.close();
-               }
-       }
-
-       /**
-        * Unconvert the given data from Base64 format back to a raw array of bytes.
-        * 
-        * @param data
-        *            the data to unconvert
-        * 
-        * @return the raw data represented by the given Base64 {@link String},
-        * 
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       public static byte[] unbase64(String data) throws IOException {
-               Base64InputStream in = new Base64InputStream(new ByteArrayInputStream(
-                               getBytes(data)), false);
-               try {
-                       return IOUtils.toByteArray(in);
-               } finally {
-                       in.close();
-               }
-       }
-
-       /**
-        * Unonvert the given data from Base64 format back to a {@link String}.
-        * 
-        * @param data
-        *            the data to unconvert
-        * 
-        * @return the {@link String} represented by the given Base64 {@link String}
-        * 
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       public static String unbase64s(String data) throws IOException {
-               return new String(unbase64(data), "UTF-8");
-       }
-
-       /**
-        * Return a display {@link String} for the given value, which can be
-        * suffixed with "k" or "M" depending upon the number, if it is big enough.
-        * <p>
-        * <p>
-        * Examples:
-        * <ul>
-        * <li><tt>8 765</tt> becomes "8 k"</li>
-        * <li><tt>998 765</tt> becomes "998 k"</li>
-        * <li><tt>12 987 364</tt> becomes "12 M"</li>
-        * <li><tt>5 534 333 221</tt> becomes "5 G"</li>
-        * </ul>
-        * 
-        * @param value
-        *            the value to convert
-        * 
-        * @return the display value
-        */
-       public static String formatNumber(long value) {
-               return formatNumber(value, 0);
-       }
-
-       /**
-        * Return a display {@link String} for the given value, which can be
-        * suffixed with "k" or "M" depending upon the number, if it is big enough.
-        * <p>
-        * Examples (assuming decimalPositions = 1):
-        * <ul>
-        * <li><tt>8 765</tt> becomes "8.7 k"</li>
-        * <li><tt>998 765</tt> becomes "998.7 k"</li>
-        * <li><tt>12 987 364</tt> becomes "12.9 M"</li>
-        * <li><tt>5 534 333 221</tt> becomes "5.5 G"</li>
-        * </ul>
-        * 
-        * @param value
-        *            the value to convert
-        * @param decimalPositions
-        *            the number of decimal positions to keep
-        * 
-        * @return the display value
-        */
-       public static String formatNumber(long value, int decimalPositions) {
-               long userValue = value;
-               String suffix = " ";
-               long mult = 1;
-
-               if (value >= 1000000000l) {
-                       mult = 1000000000l;
-                       userValue = value / 1000000000l;
-                       suffix = " G";
-               } else if (value >= 1000000l) {
-                       mult = 1000000l;
-                       userValue = value / 1000000l;
-                       suffix = " M";
-               } else if (value >= 1000l) {
-                       mult = 1000l;
-                       userValue = value / 1000l;
-                       suffix = " k";
-               }
-
-               String deci = "";
-               if (decimalPositions > 0) {
-                       deci = Long.toString(value % mult);
-                       int size = Long.toString(mult).length() - 1;
-                       while (deci.length() < size) {
-                               deci = "0" + deci;
-                       }
-
-                       deci = deci.substring(0, Math.min(decimalPositions, deci.length()));
-                       while (deci.length() < decimalPositions) {
-                               deci += "0";
-                       }
-
-                       deci = "." + deci;
-               }
-
-               return Long.toString(userValue) + deci + suffix;
-       }
-
-       /**
-        * The reverse operation to {@link StringUtils#formatNumber(long)}: it will
-        * read a "display" number that can contain a "M" or "k" suffix and return
-        * the full value.
-        * <p>
-        * Of course, the conversion to and from display form is lossy (example:
-        * <tt>6870</tt> to "6.5k" to <tt>6500</tt>).
-        * 
-        * @param value
-        *            the value in display form with possible "M" and "k" suffixes,
-        *            can be NULL
-        * 
-        * @return the value as a number, or 0 if not possible to convert
-        */
-       public static long toNumber(String value) {
-               return toNumber(value, 0l);
-       }
-
-       /**
-        * The reverse operation to {@link StringUtils#formatNumber(long)}: it will
-        * read a "display" number that can contain a "M" or "k" suffix and return
-        * the full value.
-        * <p>
-        * Of course, the conversion to and from display form is lossy (example:
-        * <tt>6870</tt> to "6.5k" to <tt>6500</tt>).
-        * 
-        * @param value
-        *            the value in display form with possible "M" and "k" suffixes,
-        *            can be NULL
-        * @param def
-        *            the default value if it is not possible to convert the given
-        *            value to a number
-        * 
-        * @return the value as a number, or 0 if not possible to convert
-        */
-       public static long toNumber(String value, long def) {
-               long count = def;
-               if (value != null) {
-                       value = value.trim().toLowerCase();
-                       try {
-                               long mult = 1;
-                               if (value.endsWith("g")) {
-                                       value = value.substring(0, value.length() - 1).trim();
-                                       mult = 1000000000;
-                               } else if (value.endsWith("m")) {
-                                       value = value.substring(0, value.length() - 1).trim();
-                                       mult = 1000000;
-                               } else if (value.endsWith("k")) {
-                                       value = value.substring(0, value.length() - 1).trim();
-                                       mult = 1000;
-                               }
-
-                               long deci = 0;
-                               if (value.contains(".")) {
-                                       String[] tab = value.split("\\.");
-                                       if (tab.length != 2) {
-                                               throw new NumberFormatException(value);
-                                       }
-                                       double decimal = Double.parseDouble("0."
-                                                       + tab[tab.length - 1]);
-                                       deci = ((long) (mult * decimal));
-                                       value = tab[0];
-                               }
-                               count = mult * Long.parseLong(value) + deci;
-                       } catch (Exception e) {
-                       }
-               }
-
-               return count;
-       }
-
-       /**
-        * Return the bytes array representation of the given {@link String} in
-        * UTF-8.
-        * 
-        * @param str
-        *            the {@link String} to transform into bytes
-        * @return the content in bytes
-        */
-       static public byte[] getBytes(String str) {
-               try {
-                       return str.getBytes("UTF-8");
-               } catch (UnsupportedEncodingException e) {
-                       // All conforming JVM must support UTF-8
-                       e.printStackTrace();
-                       return null;
-               }
-       }
-
-       /**
-        * The "remove accents" pattern.
-        * 
-        * @return the pattern, or NULL if a problem happens
-        */
-       private static Pattern getMarks() {
-               try {
-                       return Pattern
-                                       .compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+");
-               } catch (Exception e) {
-                       // Can fail on Android...
-                       return null;
-               }
-       }
-
-       //
-       // justify List<String> related:
-       //
-
-       /**
-        * Check if this line ends as a complete line (ends with a "." or similar).
-        * <p>
-        * Note that we consider an empty line as full, and a line ending with
-        * spaces as not complete.
-        * 
-        * @param line
-        *            the line to check
-        * 
-        * @return TRUE if it does
-        */
-       static private boolean isFullLine(StringBuilder line) {
-               if (line.length() == 0) {
-                       return true;
-               }
-
-               char lastCar = line.charAt(line.length() - 1);
-               switch (lastCar) {
-               case '.': // points
-               case '?':
-               case '!':
-
-               case '\'': // quotes
-               case '‘':
-               case '’':
-
-               case '"': // double quotes
-               case '”':
-               case '“':
-               case '»':
-               case '«':
-                       return true;
-               default:
-                       return false;
-               }
-       }
-
-       /**
-        * Check if this line represent an item in a list or description (i.e.,
-        * check that the first non-space char is "-").
-        * 
-        * @param line
-        *            the line to check
-        * 
-        * @return TRUE if it is
-        */
-       static private boolean isItemLine(String line) {
-               String spacing = getItemSpacing(line);
-               return spacing != null && !spacing.isEmpty()
-                               && line.charAt(spacing.length()) == '-';
-       }
-
-       /**
-        * Return all the spaces that start this line (or Empty if none).
-        * 
-        * @param line
-        *            the line to get the starting spaces from
-        * 
-        * @return the left spacing
-        */
-       static private String getItemSpacing(String line) {
-               int i;
-               for (i = 0; i < line.length(); i++) {
-                       if (line.charAt(i) != ' ') {
-                               return line.substring(0, i);
-                       }
-               }
-
-               return "";
-       }
-
-       /**
-        * This line is an horizontal spacer line.
-        * 
-        * @param line
-        *            the line to test
-        * 
-        * @return TRUE if it is
-        */
-       static private boolean isHrLine(CharSequence line) {
-               int count = 0;
-               if (line != null) {
-                       for (int i = 0; i < line.length(); i++) {
-                               char car = line.charAt(i);
-                               if (car == ' ' || car == '\t' || car == '*' || car == '-'
-                                               || car == '_' || car == '~' || car == '=' || car == '/'
-                                               || car == '\\') {
-                                       count++;
-                               } else {
-                                       return false;
-                               }
-                       }
-               }
-
-               return count > 2;
-       }
-
-       // Deprecated functions, please do not use //
-
-       /**
-        * @deprecated please use {@link StringUtils#zip64(byte[])} or
-        *             {@link StringUtils#base64(byte[])} instead.
-        * 
-        * @param data
-        *            the data to encode
-        * @param zip
-        *            TRUE to zip it before Base64 encoding it, FALSE for Base64
-        *            encoding only
-        * 
-        * @return the encoded data
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       @Deprecated
-       public static String base64(String data, boolean zip) throws IOException {
-               return base64(getBytes(data), zip);
-       }
-
-       /**
-        * @deprecated please use {@link StringUtils#zip64(String)} or
-        *             {@link StringUtils#base64(String)} instead.
-        * 
-        * @param data
-        *            the data to encode
-        * @param zip
-        *            TRUE to zip it before Base64 encoding it, FALSE for Base64
-        *            encoding only
-        * 
-        * @return the encoded data
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       @Deprecated
-       public static String base64(byte[] data, boolean zip) throws IOException {
-               if (zip) {
-                       return zip64(data);
-               }
-
-               Base64InputStream b64 = new Base64InputStream(new ByteArrayInputStream(
-                               data), true);
-               try {
-                       return IOUtils.readSmallStream(b64);
-               } finally {
-                       b64.close();
-               }
-       }
-
-       /**
-        * @deprecated please use {@link Base64OutputStream} and
-        *             {@link GZIPOutputStream} instead.
-        * 
-        * @param breakLines
-        *            NOT USED ANYMORE, it is always considered FALSE now
-        */
-       @Deprecated
-       public static OutputStream base64(OutputStream data, boolean zip,
-                       boolean breakLines) throws IOException {
-               OutputStream out = new Base64OutputStream(data);
-               if (zip) {
-                       out = new java.util.zip.GZIPOutputStream(out);
-               }
-
-               return out;
-       }
-
-       /**
-        * Unconvert the given data from Base64 format back to a raw array of bytes.
-        * <p>
-        * Will automatically detect zipped data and also uncompress it before
-        * returning, unless ZIP is false.
-        * 
-        * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
-        * 
-        * @param data
-        *            the data to unconvert
-        * @param zip
-        *            TRUE to also uncompress the data from a GZIP format
-        *            automatically; if set to FALSE, zipped data can be returned
-        * 
-        * @return the raw data represented by the given Base64 {@link String},
-        *         optionally compressed with GZIP
-        * 
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       @Deprecated
-       public static byte[] unbase64(String data, boolean zip) throws IOException {
-               byte[] buffer = unbase64(data);
-               if (!zip) {
-                       return buffer;
-               }
-
-               try {
-                       GZIPInputStream zipped = new GZIPInputStream(
-                                       new ByteArrayInputStream(buffer));
-                       try {
-                               ByteArrayOutputStream out = new ByteArrayOutputStream();
-                               try {
-                                       IOUtils.write(zipped, out);
-                                       return out.toByteArray();
-                               } finally {
-                                       out.close();
-                               }
-                       } finally {
-                               zipped.close();
-                       }
-               } catch (Exception e) {
-                       return buffer;
-               }
-       }
-
-       /**
-        * Unconvert the given data from Base64 format back to a raw array of bytes.
-        * <p>
-        * Will automatically detect zipped data and also uncompress it before
-        * returning, unless ZIP is false.
-        * 
-        * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
-        * 
-        * @param data
-        *            the data to unconvert
-        * @param zip
-        *            TRUE to also uncompress the data from a GZIP format
-        *            automatically; if set to FALSE, zipped data can be returned
-        * 
-        * @return the raw data represented by the given Base64 {@link String},
-        *         optionally compressed with GZIP
-        * 
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       @Deprecated
-       public static InputStream unbase64(InputStream data, boolean zip)
-                       throws IOException {
-               return new ByteArrayInputStream(unbase64(IOUtils.readSmallStream(data),
-                               zip));
-       }
-
-       /**
-        * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
-        */
-       @Deprecated
-       public static byte[] unbase64(byte[] data, int offset, int count,
-                       boolean zip) throws IOException {
-               byte[] dataPart = Arrays.copyOfRange(data, offset, offset + count);
-               return unbase64(new String(dataPart, "UTF-8"), zip);
-       }
-
-       /**
-        * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
-        */
-       @Deprecated
-       public static String unbase64s(String data, boolean zip) throws IOException {
-               return new String(unbase64(data, zip), "UTF-8");
-       }
-
-       /**
-        * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
-        */
-       @Deprecated
-       public static String unbase64s(byte[] data, int offset, int count,
-                       boolean zip) throws IOException {
-               return new String(unbase64(data, offset, count, zip), "UTF-8");
-       }
-}
diff --git a/src/be/nikiroo/utils/TempFiles.java b/src/be/nikiroo/utils/TempFiles.java
deleted file mode 100644 (file)
index b54f0bc..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * A small utility class to generate auto-delete temporary files in a
- * centralised location.
- * 
- * @author niki
- */
-public class TempFiles implements Closeable {
-       /**
-        * Root directory of this instance, owned by it, where all temporary files
-        * must reside.
-        */
-       protected File root;
-
-       /**
-        * Create a new {@link TempFiles} -- each instance is separate and have a
-        * dedicated sub-directory in a shared temporary root.
-        * <p>
-        * The whole repository will be deleted on close (if you fail to call it,
-        * the program will <b>try</b> to call it on JVM termination).
-        * 
-        * @param name
-        *            the instance name (will be <b>part</b> of the final directory
-        *            name)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public TempFiles(String name) throws IOException {
-               this(null, name);
-       }
-
-       /**
-        * Create a new {@link TempFiles} -- each instance is separate and have a
-        * dedicated sub-directory in a given temporary root.
-        * <p>
-        * The whole repository will be deleted on close (if you fail to call it,
-        * the program will <b>try</b> to call it on JVM termination).
-        * <p>
-        * Be careful, this instance will <b>own</b> the given root directory, and
-        * will most probably delete all its files.
-        * 
-        * @param base
-        *            the root base directory to use for all the temporary files of
-        *            this instance (if NULL, will be the default temporary
-        *            directory of the OS)
-        * @param name
-        *            the instance name (will be <b>part</b> of the final directory
-        *            name)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public TempFiles(File base, String name) throws IOException {
-               if (base == null) {
-                       base = File.createTempFile(".temp", "");
-               }
-
-               root = base;
-
-               if (root.exists()) {
-                       IOUtils.deltree(root, true);
-               }
-
-               root = new File(root.getParentFile(), ".temp");
-               root.mkdir();
-               if (!root.exists()) {
-                       throw new IOException("Cannot create root directory: " + root);
-               }
-
-               root.deleteOnExit();
-
-               root = createTempFile(name);
-               IOUtils.deltree(root, true);
-
-               root.mkdir();
-               if (!root.exists()) {
-                       throw new IOException("Cannot create root subdirectory: " + root);
-               }
-       }
-
-       /**
-        * Create an auto-delete temporary file.
-        * 
-        * @param name
-        *            a base for the final filename (only a <b>part</b> of said
-        *            filename)
-        * 
-        * @return the newly created file
-        * 
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       public synchronized File createTempFile(String name) throws IOException {
-               name += "_";
-               while (name.length() < 3) {
-                       name += "_";
-               }
-
-               while (true) {
-                       File tmp = File.createTempFile(name, "");
-                       IOUtils.deltree(tmp, true);
-
-                       File test = new File(root, tmp.getName());
-                       if (!test.exists()) {
-                               test.createNewFile();
-                               if (!test.exists()) {
-                                       throw new IOException(
-                                                       "Cannot create temporary file: " + test);
-                               }
-
-                               test.deleteOnExit();
-                               return test;
-                       }
-               }
-       }
-
-       /**
-        * Create an auto-delete temporary directory.
-        * <p>
-        * Note that creating 2 temporary directories with the same name will result
-        * in two <b>different</b> directories, even if the final name is the same
-        * (the absolute path will be different).
-        * 
-        * @param name
-        *            the actual directory name (not path)
-        * 
-        * @return the newly created file
-        * 
-        * @throws IOException
-        *             in case of I/O errors, or if the name was a path instead of a
-        *             name
-        */
-       public synchronized File createTempDir(String name) throws IOException {
-               File localRoot = createTempFile(name);
-               IOUtils.deltree(localRoot, true);
-
-               localRoot.mkdir();
-               if (!localRoot.exists()) {
-                       throw new IOException("Cannot create subdirectory: " + localRoot);
-               }
-
-               File dir = new File(localRoot, name);
-               if (!dir.getName().equals(name)) {
-                       throw new IOException(
-                                       "Cannot create temporary directory with a path, only names are allowed: "
-                                                       + dir);
-               }
-
-               dir.mkdir();
-               dir.deleteOnExit();
-
-               if (!dir.exists()) {
-                       throw new IOException("Cannot create subdirectory: " + dir);
-               }
-
-               return dir;
-       }
-
-       @Override
-       public synchronized void close() throws IOException {
-               File root = this.root;
-               this.root = null;
-
-               if (root != null) {
-                       IOUtils.deltree(root);
-
-                       // Since we allocate temp directories from a base point,
-                       // try and remove that base point
-                       root.getParentFile().delete(); // (only works if empty)
-               }
-       }
-
-       @Override
-       protected void finalize() throws Throwable {
-               try {
-                       close();
-               } finally {
-                       super.finalize();
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/TraceHandler.java b/src/be/nikiroo/utils/TraceHandler.java
deleted file mode 100644 (file)
index 0a09712..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-package be.nikiroo.utils;
-
-/**
- * A handler when a trace message is sent or when a recoverable exception was
- * caught by the program.
- * 
- * @author niki
- */
-public class TraceHandler {
-       private final boolean showErrors;
-       private final boolean showErrorDetails;
-       private final int traceLevel;
-       private final int maxPrintSize;
-
-       /**
-        * Create a default {@link TraceHandler} that will print errors on stderr
-        * (without details) and no traces.
-        */
-       public TraceHandler() {
-               this(true, false, false);
-       }
-
-       /**
-        * Create a default {@link TraceHandler}.
-        * 
-        * @param showErrors
-        *            show errors on stderr
-        * @param showErrorDetails
-        *            show more details when printing errors
-        * @param showTraces
-        *            show level 1 traces on stderr, or no traces at all
-        */
-       public TraceHandler(boolean showErrors, boolean showErrorDetails,
-                       boolean showTraces) {
-               this(showErrors, showErrorDetails, showTraces ? 1 : 0);
-       }
-
-       /**
-        * Create a default {@link TraceHandler}.
-        * 
-        * @param showErrors
-        *            show errors on stderr
-        * @param showErrorDetails
-        *            show more details when printing errors
-        * @param traceLevel
-        *            show traces of this level or lower (0 means "no traces",
-        *            higher means more traces)
-        */
-       public TraceHandler(boolean showErrors, boolean showErrorDetails,
-                       int traceLevel) {
-               this(showErrors, showErrorDetails, traceLevel, -1);
-       }
-
-       /**
-        * Create a default {@link TraceHandler}.
-        * 
-        * @param showErrors
-        *            show errors on stderr
-        * @param showErrorDetails
-        *            show more details when printing errors
-        * @param traceLevel
-        *            show traces of this level or lower (0 means "no traces",
-        *            higher means more traces)
-        * @param maxPrintSize
-        *            the maximum size at which to truncate traces data (or -1 for
-        *            "no limit")
-        */
-       public TraceHandler(boolean showErrors, boolean showErrorDetails,
-                       int traceLevel, int maxPrintSize) {
-               this.showErrors = showErrors;
-               this.showErrorDetails = showErrorDetails;
-               this.traceLevel = Math.max(traceLevel, 0);
-               this.maxPrintSize = maxPrintSize;
-       }
-
-       /**
-        * The trace level of this {@link TraceHandler}.
-        * 
-        * @return the level
-        */
-       public int getTraceLevel() {
-               return traceLevel;
-       }
-
-       /**
-        * An exception happened, log it.
-        * 
-        * @param e
-        *            the exception
-        */
-       public void error(Exception e) {
-               if (showErrors) {
-                       if (showErrorDetails) {
-                               long now = System.currentTimeMillis();
-                               System.err.print(StringUtils.fromTime(now) + ": ");
-                               e.printStackTrace();
-                       } else {
-                               error(e.toString());
-                       }
-               }
-       }
-
-       /**
-        * An error happened, log it.
-        * 
-        * @param message
-        *            the error message
-        */
-       public void error(String message) {
-               if (showErrors) {
-                       long now = System.currentTimeMillis();
-                       System.err.println(StringUtils.fromTime(now) + ": " + message);
-               }
-       }
-
-       /**
-        * A trace happened, show it.
-        * <p>
-        * By default, will only be effective if {@link TraceHandler#traceLevel} is
-        * not 0.
-        * <p>
-        * A call to this method is equivalent to a call to
-        * {@link TraceHandler#trace(String, int)} with a level of 1.
-        * 
-        * @param message
-        *            the trace message
-        */
-       public void trace(String message) {
-               trace(message, 1);
-       }
-
-       /**
-        * A trace happened, show it.
-        * <p>
-        * By default, will only be effective if {@link TraceHandler#traceLevel} is
-        * not 0 and the level is lower or equal to it.
-        * 
-        * @param message
-        *            the trace message
-        * @param level
-        *            the trace level
-        */
-       public void trace(String message, int level) {
-               if (traceLevel > 0 && level <= traceLevel) {
-                       long now = System.currentTimeMillis();
-                       System.err.print(StringUtils.fromTime(now) + ": ");
-                       if (maxPrintSize > 0 && message.length() > maxPrintSize) {
-
-                               System.err
-                                               .println(message.substring(0, maxPrintSize) + "[...]");
-                       } else {
-                               System.err.println(message);
-                       }
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/Version.java b/src/be/nikiroo/utils/Version.java
deleted file mode 100644 (file)
index 269edb6..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * This class describe a program {@link Version}.
- * 
- * @author niki
- */
-public class Version implements Comparable<Version> {
-       private String version;
-       private int major;
-       private int minor;
-       private int patch;
-       private String tag;
-       private int tagVersion;
-
-       /**
-        * Create a new, empty {@link Version}.
-        * 
-        */
-       public Version() {
-       }
-
-       /**
-        * Create a new {@link Version} with the given values.
-        * 
-        * @param major
-        *            the major version
-        * @param minor
-        *            the minor version
-        * @param patch
-        *            the patch version
-        */
-       public Version(int major, int minor, int patch) {
-               this(major, minor, patch, null, -1);
-       }
-
-       /**
-        * Create a new {@link Version} with the given values.
-        * 
-        * @param major
-        *            the major version
-        * @param minor
-        *            the minor version
-        * @param patch
-        *            the patch version
-        * @param tag
-        *            a tag name for this version
-        */
-       public Version(int major, int minor, int patch, String tag) {
-               this(major, minor, patch, tag, -1);
-       }
-
-       /**
-        * Create a new {@link Version} with the given values.
-        * 
-        * @param major
-        *            the major version
-        * @param minor
-        *            the minor version
-        * @param patch
-        *            the patch version the patch version
-        * @param tag
-        *            a tag name for this version
-        * @param tagVersion
-        *            the version of the tagged version
-        */
-       public Version(int major, int minor, int patch, String tag, int tagVersion) {
-               if (tagVersion >= 0 && tag == null) {
-                       throw new java.lang.IllegalArgumentException(
-                                       "A tag version cannot be used without a tag");
-               }
-
-               this.major = major;
-               this.minor = minor;
-               this.patch = patch;
-               this.tag = tag;
-               this.tagVersion = tagVersion;
-
-               this.version = generateVersion();
-       }
-
-       /**
-        * Create a new {@link Version} with the given value, which must be in the
-        * form <tt>MAJOR.MINOR.PATCH(-TAG(TAG_VERSION))</tt>.
-        * 
-        * @param version
-        *            the version (<tt>MAJOR.MINOR.PATCH</tt>,
-        *            <tt>MAJOR.MINOR.PATCH-TAG</tt> or
-        *            <tt>MAJOR.MINOR.PATCH-TAGVERSIONTAG</tt>)
-        */
-       public Version(String version) {
-               try {
-                       String[] tab = version.split("\\.");
-                       this.major = Integer.parseInt(tab[0].trim());
-                       this.minor = Integer.parseInt(tab[1].trim());
-                       if (tab[2].contains("-")) {
-                               int posInVersion = version.indexOf('.');
-                               posInVersion = version.indexOf('.', posInVersion + 1);
-                               String rest = version.substring(posInVersion + 1);
-
-                               int posInRest = rest.indexOf('-');
-                               this.patch = Integer.parseInt(rest.substring(0, posInRest)
-                                               .trim());
-
-                               posInVersion = version.indexOf('-');
-                               this.tag = version.substring(posInVersion + 1).trim();
-                               this.tagVersion = -1;
-
-                               StringBuilder str = new StringBuilder();
-                               while (!tag.isEmpty() && tag.charAt(tag.length() - 1) >= '0'
-                                               && tag.charAt(tag.length() - 1) <= '9') {
-                                       str.insert(0, tag.charAt(tag.length() - 1));
-                                       tag = tag.substring(0, tag.length() - 1);
-                               }
-
-                               if (str.length() > 0) {
-                                       this.tagVersion = Integer.parseInt(str.toString());
-                               }
-                       } else {
-                               this.patch = Integer.parseInt(tab[2].trim());
-                               this.tag = null;
-                               this.tagVersion = -1;
-                       }
-
-                       this.version = generateVersion();
-               } catch (Exception e) {
-                       this.major = 0;
-                       this.minor = 0;
-                       this.patch = 0;
-                       this.tag = null;
-                       this.tagVersion = -1;
-                       this.version = null;
-               }
-       }
-
-       /**
-        * The 'major' version.
-        * <p>
-        * This version should only change when API-incompatible changes are made to
-        * the program.
-        * 
-        * @return the major version
-        */
-       public int getMajor() {
-               return major;
-       }
-
-       /**
-        * The 'minor' version.
-        * <p>
-        * This version should only change when new, backwards-compatible
-        * functionality has been added to the program.
-        * 
-        * @return the minor version
-        */
-       public int getMinor() {
-               return minor;
-       }
-
-       /**
-        * The 'patch' version.
-        * <p>
-        * This version should change when backwards-compatible bugfixes have been
-        * added to the program.
-        * 
-        * @return the patch version
-        */
-       public int getPatch() {
-               return patch;
-       }
-
-       /**
-        * A tag name for this version.
-        * 
-        * @return the tag
-        */
-       public String getTag() {
-               return tag;
-       }
-
-       /**
-        * The version of the tag, or -1 for no version.
-        * 
-        * @return the tag version
-        */
-       public int getTagVersion() {
-               return tagVersion;
-       }
-
-       /**
-        * Check if this {@link Version} is "empty" (i.e., the version was not
-        * parse-able or not given).
-        * 
-        * @return TRUE if it is empty
-        */
-       public boolean isEmpty() {
-               return version == null;
-       }
-
-       /**
-        * Check if we are more recent than the given {@link Version}.
-        * <p>
-        * Note that a tagged version is considered newer than a non-tagged version,
-        * but two tagged versions with different tags are not comparable.
-        * <p>
-        * Also, an empty version is always considered older.
-        * 
-        * @param o
-        *            the other {@link Version}
-        * @return TRUE if this {@link Version} is more recent than the given one
-        */
-       public boolean isNewerThan(Version o) {
-               if (isEmpty()) {
-                       return false;
-               } else if (o.isEmpty()) {
-                       return true;
-               }
-
-               if (major > o.major) {
-                       return true;
-               }
-
-               if (major == o.major && minor > o.minor) {
-                       return true;
-               }
-
-               if (major == o.major && minor == o.minor && patch > o.patch) {
-                       return true;
-               }
-
-               // a tagged version is considered newer than a non-tagged one
-               if (major == o.major && minor == o.minor && patch == o.patch
-                               && tag != null && o.tag == null) {
-                       return true;
-               }
-
-               // 2 <> tagged versions are not comparable
-               boolean sameTag = (tag == null && o.tag == null)
-                               || (tag != null && tag.equals(o.tag));
-               if (major == o.major && minor == o.minor && patch == o.patch && sameTag
-                               && tagVersion > o.tagVersion) {
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
-        * Check if we are older than the given {@link Version}.
-        * <p>
-        * Note that a tagged version is considered newer than a non-tagged version,
-        * but two tagged versions with different tags are not comparable.
-        * <p>
-        * Also, an empty version is always considered older.
-        * 
-        * @param o
-        *            the other {@link Version}
-        * @return TRUE if this {@link Version} is older than the given one
-        */
-       public boolean isOlderThan(Version o) {
-               if (o.isEmpty()) {
-                       return false;
-               } else if (isEmpty()) {
-                       return true;
-               }
-
-               // 2 <> tagged versions are not comparable
-               boolean sameTag = (tag == null && o.tag == null)
-                               || (tag != null && tag.equals(o.tag));
-               if (major == o.major && minor == o.minor && patch == o.patch
-                               && !sameTag) {
-                       return false;
-               }
-
-               return !equals(o) && !isNewerThan(o);
-       }
-
-       /**
-        * Return the version of the running program if it follows the VERSION
-        * convention (i.e., if it has a file called VERSION containing the version
-        * as a {@link String} in its binary root, and if this {@link String}
-        * follows the Major/Minor/Patch convention).
-        * <p>
-        * If it does not, return an empty {@link Version} object.
-        * 
-        * @return the {@link Version} of the program, or an empty {@link Version}
-        *         (does not return NULL)
-        */
-       public static Version getCurrentVersion() {
-               String version = null;
-
-               InputStream in = IOUtils.openResource("VERSION");
-               if (in != null) {
-                       try {
-                               ByteArrayOutputStream ba = new ByteArrayOutputStream();
-                               IOUtils.write(in, ba);
-                               in.close();
-
-                               version = ba.toString("UTF-8").trim();
-                       } catch (IOException e) {
-                       }
-               }
-
-               return new Version(version);
-       }
-
-       @Override
-       public int compareTo(Version o) {
-               if (equals(o)) {
-                       return 0;
-               } else if (isNewerThan(o)) {
-                       return 1;
-               } else {
-                       return -1;
-               }
-       }
-
-       @Override
-       public boolean equals(Object obj) {
-               if (obj instanceof Version) {
-                       Version o = (Version) obj;
-                       if (isEmpty()) {
-                               return o.isEmpty();
-                       }
-
-                       boolean sameTag = (tag == null && o.tag == null)
-                                       || (tag != null && tag.equals(o.tag));
-                       return o.major == major && o.minor == minor && o.patch == patch
-                                       && sameTag && o.tagVersion == tagVersion;
-               }
-
-               return false;
-       }
-
-       @Override
-       public int hashCode() {
-               return version == null ? 0 : version.hashCode();
-       }
-
-       /**
-        * Return a user-readable form of this {@link Version}.
-        */
-       @Override
-       public String toString() {
-               return version == null ? "[unknown]" : version;
-       }
-
-       /**
-        * Generate the clean version {@link String} from the current values.
-        * 
-        * @return the clean version string
-        */
-       private String generateVersion() {
-               String tagSuffix = "";
-               if (tag != null) {
-                       tagSuffix = "-" + tag
-                                       + (tagVersion >= 0 ? Integer.toString(tagVersion) : "");
-               }
-
-               return String.format("%d.%d.%d%s", major, minor, patch, tagSuffix);
-       }
-}
diff --git a/src/be/nikiroo/utils/VersionCheck.java b/src/be/nikiroo/utils/VersionCheck.java
deleted file mode 100644 (file)
index 0e16c2b..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-package be.nikiroo.utils;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * Version checker: can check the current version of the program against a
- * remote changelog, and list the missed updates and their description.
- * 
- * @author niki
- */
-public class VersionCheck {
-       private static final String base = "https://github.com/${PROJECT}/raw/master/changelog${LANG}.md";
-       private static Downloader downloader = new Downloader(null);
-
-       private Version current;
-       private List<Version> newer;
-       private Map<Version, List<String>> changes;
-
-       /**
-        * Create a new {@link VersionCheck}.
-        * 
-        * @param current
-        *            the current version of the program
-        * @param newer
-        *            the list of available {@link Version}s newer the current one
-        * @param changes
-        *            the list of changes
-        */
-       private VersionCheck(Version current, List<Version> newer,
-                       Map<Version, List<String>> changes) {
-               this.current = current;
-               this.newer = newer;
-               this.changes = changes;
-       }
-
-       /**
-        * Check if there are more recent {@link Version}s of this program
-        * available.
-        * 
-        * @return TRUE if there is at least one
-        */
-       public boolean isNewVersionAvailable() {
-               return !newer.isEmpty();
-       }
-
-       /**
-        * The current {@link Version} of the program.
-        * 
-        * @return the current {@link Version}
-        */
-       public Version getCurrentVersion() {
-               return current;
-       }
-
-       /**
-        * The list of available {@link Version}s newer than the current one.
-        * 
-        * @return the newer {@link Version}s
-        */
-       public List<Version> getNewer() {
-               return newer;
-       }
-
-       /**
-        * The list of changes for each available {@link Version} newer than the
-        * current one.
-        * 
-        * @return the list of changes
-        */
-       public Map<Version, List<String>> getChanges() {
-               return changes;
-       }
-
-       /**
-        * Check if there are available {@link Version}s of this program more recent
-        * than the current one.
-        * 
-        * @param githubProject
-        *            the GitHub project to check on, for instance "nikiroo/fanfix"
-        * @param lang
-        *            the current locale, so we can try to get the changelog in the
-        *            correct language (can be NULL, will fetch the default
-        *            changelog)
-        * 
-        * @return a {@link VersionCheck}
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public static VersionCheck check(String githubProject, Locale lang)
-                       throws IOException {
-               Version current = Version.getCurrentVersion();
-               List<Version> newer = new ArrayList<Version>();
-               Map<Version, List<String>> changes = new HashMap<Version, List<String>>();
-
-               // Use the right project:
-               String base = VersionCheck.base.replace("${PROJECT}", githubProject);
-
-               // Prepare the URLs according to the user's language (we take here
-               // "-fr_BE" as an example):
-               String fr = lang == null ? "" : "-" + lang.getLanguage();
-               String BE = lang == null ? ""
-                               : "_" + lang.getCountry().replace(".UTF8", "");
-               String urlFrBE = base.replace("${LANG}", fr + BE);
-               String urlFr = base.replace("${LANG}", "-" + fr);
-               String urlDefault = base.replace("${LANG}", "");
-
-               InputStream in = null;
-               for (String url : new String[] { urlFrBE, urlFr, urlDefault }) {
-                       try {
-                               in = downloader.open(new URL(url), false);
-                               break;
-                       } catch (IOException e) {
-                       }
-               }
-
-               if (in == null) {
-                       throw new IOException("No changelog found");
-               }
-
-               BufferedReader reader = new BufferedReader(
-                               new InputStreamReader(in, "UTF-8"));
-               try {
-                       Version version = new Version();
-                       for (String line = reader.readLine(); line != null; line = reader
-                                       .readLine()) {
-                               if (line.startsWith("## Version ")) {
-                                       version = new Version(
-                                                       line.substring("## Version ".length()));
-                                       if (version.isNewerThan(current)) {
-                                               newer.add(version);
-                                               changes.put(version, new ArrayList<String>());
-                                       } else {
-                                               version = new Version();
-                                       }
-                               } else if (!version.isEmpty() && !newer.isEmpty()
-                                               && !line.isEmpty()) {
-                                       List<String> ch = changes.get(newer.get(newer.size() - 1));
-                                       if (!ch.isEmpty() && !line.startsWith("- ")) {
-                                               int i = ch.size() - 1;
-                                               ch.set(i, ch.get(i) + " " + line.trim());
-                                       } else {
-                                               ch.add(line.substring("- ".length()).trim());
-                                       }
-                               }
-                       }
-               } finally {
-                       reader.close();
-               }
-
-               return new VersionCheck(current, newer, changes);
-       }
-       
-       @Override
-       public String toString() {
-               return String.format(
-                               "Version checker: version [%s], %d releases behind latest version [%s]", //
-                               current, //
-                               newer.size(), //
-                               newer.isEmpty() ? current : newer.get(newer.size() - 1)//
-               );
-       }
-}
diff --git a/src/be/nikiroo/utils/android/ImageUtilsAndroid.java b/src/be/nikiroo/utils/android/ImageUtilsAndroid.java
deleted file mode 100644 (file)
index c2e269c..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-package be.nikiroo.utils.android;
-
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.stream.Stream;
-
-import be.nikiroo.utils.Image;
-import be.nikiroo.utils.ImageUtils;
-import be.nikiroo.utils.StringUtils;
-
-/**
- * This class offer some utilities based around images and uses the Android
- * framework.
- * 
- * @author niki
- */
-public class ImageUtilsAndroid extends ImageUtils {
-       @Override
-       protected boolean check() {
-               // If we can get the class, it means we have access to it
-               Config c = Config.ALPHA_8;
-               return true;
-       }
-
-       @Override
-       public void saveAsImage(Image img, File target, String format)
-                       throws IOException {
-               FileOutputStream fos = new FileOutputStream(target);
-               try {
-                       Bitmap image = fromImage(img);
-
-                       boolean ok = false;
-                       try {
-                               ok = image.compress(
-                                               Bitmap.CompressFormat.valueOf(format.toUpperCase()),
-                                               90, fos);
-                       } catch (Exception e) {
-                               ok = false;
-                       }
-
-                       // Some formats are not reliable
-                       // Second chance: PNG
-                       if (!ok && !format.equals("png")) {
-                               ok = image.compress(Bitmap.CompressFormat.PNG, 90, fos);
-                       }
-
-                       if (!ok) {
-                               throw new IOException(
-                                               "Cannot find a writer for this image and format: "
-                                                               + format);
-                       }
-               } catch (IOException e) {
-                       throw new IOException("Cannot write image to " + target, e);
-               } finally {
-                       fos.close();
-               }
-       }
-
-       /**
-        * Convert the given {@link Image} into a {@link Bitmap} object.
-        * 
-        * @param img
-        *            the {@link Image}
-        * @return the {@link Image} object
-        * @throws IOException
-        *             in case of IO error
-        */
-       static public Bitmap fromImage(Image img) throws IOException {
-               InputStream stream = img.newInputStream();
-               try {
-                       Bitmap image = BitmapFactory.decodeStream(stream);
-                       if (image == null) {
-                               String extra = "";
-                               if (img.getSize() <= 2048) {
-                                       try {
-                                               extra = ", content: "
-                                                               + new String(img.getData(), "UTF-8");
-                                       } catch (Exception e) {
-                                               extra = ", content unavailable";
-                                       }
-                               }
-                               String ssize = StringUtils.formatNumber(img.getSize());
-                               throw new IOException(
-                                               "Failed to convert input to image, size was: " + ssize
-                                                               + extra);
-                       }
-
-                       return image;
-               } finally {
-                       stream.close();
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/android/test/TestAndroid.java b/src/be/nikiroo/utils/android/test/TestAndroid.java
deleted file mode 100644 (file)
index 2ded4e1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-package be.nikiroo.utils.android.test;
-
-import be.nikiroo.utils.android.ImageUtilsAndroid;
-
-public class TestAndroid {
-       ImageUtilsAndroid a = new ImageUtilsAndroid();
-}
diff --git a/src/be/nikiroo/utils/main/bridge.java b/src/be/nikiroo/utils/main/bridge.java
deleted file mode 100644 (file)
index 1b7ab85..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-package be.nikiroo.utils.main;
-
-import be.nikiroo.utils.TraceHandler;
-import be.nikiroo.utils.serial.server.ServerBridge;
-
-/**
- * Serialiser bridge (starts a {@link ServerBridge} and can thus intercept
- * communication between a client and a server).
- * 
- * @author niki
- */
-public class bridge {
-       /**
-        * The optional options that can be passed to the program.
-        * 
-        * @author niki
-        */
-       private enum Option {
-               /**
-                * The encryption key for the input data (optional, but can also be
-                * empty <b>which is different</b> (it will then use an empty encryption
-                * key)).
-                */
-               KEY,
-               /**
-                * The encryption key for the output data (optional, but can also be
-                * empty <b>which is different</b> (it will then use an empty encryption
-                * key)).
-                */
-               FORWARD_KEY,
-               /** The trace level (1, 2, 3.. default is 1). */
-               TRACE_LEVEL,
-               /**
-                * The maximum length after which to truncate data to display (the whole
-                * data will still be sent).
-                */
-               MAX_DISPLAY_SIZE,
-               /** The help message. */
-               HELP,
-       }
-
-       static private String getSyntax() {
-               return "Syntax: (--options) (--) [NAME] [PORT] [FORWARD_HOST] [FORWARD_PORT]\n"//
-                               + "\tNAME         : the bridge name for display/debug purposes\n"//
-                               + "\tPORT         : the port to listen on\n"//
-                               + "\tFORWARD_HOST : the host to connect to\n"//
-                               + "\tFORWARD_PORT : the port to connect to\n"//
-                               + "\n" //
-                               + "\tOptions: \n" //
-                               + "\t--                 : no more options in the rest of the parameters\n" //
-                               + "\t--help             : this help message\n" //
-                               + "\t--key              : the INCOMING encryption key\n" //
-                               + "\t--forward-key      : the OUTGOING encryption key\n" //
-                               + "\t--trace-level      : the trace level (1, 2, 3... default is 1)\n" //
-                               + "\t--max-display-size : the maximum size after which to \n"//
-                               + "\t        truncate the messages to display (the full message will still be sent)\n" //
-               ;
-       }
-
-       /**
-        * Start a bridge between 2 servers.
-        * 
-        * @param args
-        *            the parameters, which can be seen by passing "--help" or just
-        *            calling the program without parameters
-        */
-       public static void main(String[] args) {
-               final TraceHandler tracer = new TraceHandler(true, false, 0);
-               try {
-                       if (args.length == 0) {
-                               tracer.error(getSyntax());
-                               System.exit(0);
-                       }
-
-                       String key = null;
-                       String fkey = null;
-                       int traceLevel = 1;
-                       int maxPrintSize = 0;
-
-                       int i = 0;
-                       while (args[i].startsWith("--")) {
-                               String arg = args[i];
-                               i++;
-
-                               if (arg.equals("--")) {
-                                       break;
-                               }
-
-                               arg = arg.substring(2).toUpperCase().replace("-", "_");
-                               try {
-                                       Option opt = Enum.valueOf(Option.class, arg);
-                                       switch (opt) {
-                                       case HELP:
-                                               tracer.trace(getSyntax());
-                                               System.exit(0);
-                                               break;
-                                       case FORWARD_KEY:
-                                               fkey = args[i++];
-                                               break;
-                                       case KEY:
-                                               key = args[i++];
-                                               break;
-                                       case MAX_DISPLAY_SIZE:
-                                               maxPrintSize = Integer.parseInt(args[i++]);
-                                               break;
-                                       case TRACE_LEVEL:
-                                               traceLevel = Integer.parseInt(args[i++]);
-                                               break;
-                                       }
-                               } catch (Exception e) {
-                                       tracer.error(getSyntax());
-                                       System.exit(1);
-                               }
-                       }
-
-                       if ((args.length - i) != 4) {
-                               tracer.error(getSyntax());
-                               System.exit(2);
-                       }
-
-                       String name = args[i++];
-                       int port = Integer.parseInt(args[i++]);
-                       String fhost = args[i++];
-                       int fport = Integer.parseInt(args[i++]);
-
-                       ServerBridge bridge = new ServerBridge(name, port, key, fhost,
-                                       fport, fkey);
-                       bridge.setTraceHandler(new TraceHandler(true, true, traceLevel,
-                                       maxPrintSize));
-                       bridge.run();
-               } catch (Exception e) {
-                       tracer.error(e);
-                       System.exit(42);
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/main/img2aa.java b/src/be/nikiroo/utils/main/img2aa.java
deleted file mode 100644 (file)
index 9cc6f0c..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-package be.nikiroo.utils.main;
-
-import java.awt.Dimension;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import be.nikiroo.utils.IOUtils;
-import be.nikiroo.utils.Image;
-import be.nikiroo.utils.ui.ImageTextAwt;
-import be.nikiroo.utils.ui.ImageTextAwt.Mode;
-import be.nikiroo.utils.ui.ImageUtilsAwt;
-
-/**
- * Image to ASCII conversion.
- * 
- * @author niki
- */
-public class img2aa {
-       /**
-        * Syntax: (--mode=MODE) (--width=WIDTH) (--height=HEIGHT) (--size=SIZE)
-        * (--output=OUTPUT) (--invert) (--help)
-        * <p>
-        * See "--help".
-        * 
-        * @param args
-        */
-       public static void main(String[] args) {
-               Dimension size = null;
-               Mode mode = null;
-               boolean invert = false;
-               List<String> inputs = new ArrayList<String>();
-               File output = null;
-
-               String lastArg = "";
-               try {
-                       int height = -1;
-                       int width = -1;
-
-                       for (String arg : args) {
-                               lastArg = arg;
-
-                               if (arg.startsWith("--mode=")) {
-                                       mode = Mode.valueOf(arg.substring("--mode=".length()));
-                               } else if (arg.startsWith("--width=")) {
-                                       width = Integer
-                                                       .parseInt(arg.substring("--width=".length()));
-                               } else if (arg.startsWith("--height=")) {
-                                       height = Integer.parseInt(arg.substring("--height="
-                                                       .length()));
-                               } else if (arg.startsWith("--size=")) {
-                                       String content = arg.substring("--size=".length()).replace(
-                                                       "X", "x");
-                                       width = Integer.parseInt(content.split("x")[0]);
-                                       height = Integer.parseInt(content.split("x")[1]);
-                               } else if (arg.startsWith("--ouput=")) {
-                                       if (!arg.equals("--output=-")) {
-                                               output = new File(arg.substring("--output=".length()));
-                                       }
-                               } else if (arg.equals("--invert")) {
-                                       invert = true;
-                               } else if (arg.equals("--help")) {
-                                       System.out
-                                                       .println("Syntax: (--mode=MODE) (--width=WIDTH) (--height=HEIGHT) (--size=SIZE) (--output=OUTPUT) (--invert) (--help)");
-                                       System.out.println("\t --help: will show this screen");
-                                       System.out
-                                                       .println("\t --invert: will invert the 'colours'");
-                                       System.out
-                                                       .println("\t --mode: will select the rendering mode (default: ASCII):");
-                                       System.out
-                                                       .println("\t\t ASCII: ASCI output mode, that is, characters \" .-+=o8#\"");
-                                       System.out
-                                                       .println("\t\t DITHERING: Use 5 different \"colours\" which are actually"
-                                                                       + "\n\t\t Unicode characters \" ░▒▓█\"");
-                                       System.out
-                                                       .println("\t\t DOUBLE_RESOLUTION: Use \"block\" Unicode characters up to quarter"
-                                                                       + "\n\t\t blocks, thus in effect doubling the resolution both in vertical"
-                                                                       + "\n\t\t and horizontal space."
-                                                                       + "\n\t\t Note that since 2 characters next to each other are square,"
-                                                                       + "\n\t\t 4 blocks per 2 blocks for w/h resolution.");
-                                       System.out
-                                                       .println("\t\t DOUBLE_DITHERING: Use characters from both DOUBLE_RESOLUTION"
-                                                                       + "\n\t\t and DITHERING");
-                                       return;
-                               } else {
-                                       inputs.add(arg);
-                               }
-                       }
-
-                       size = new Dimension(width, height);
-                       if (inputs.size() == 0) {
-                               inputs.add("-"); // by default, stdin
-                       }
-               } catch (Exception e) {
-                       System.err.println("Syntax error: \"" + lastArg + "\" is invalid");
-                       System.exit(1);
-               }
-
-               try {
-                       if (mode == null) {
-                               mode = Mode.ASCII;
-                       }
-
-                       for (String input : inputs) {
-                               InputStream in = null;
-
-                               try {
-                                       if (input.equals("-")) {
-                                               in = System.in;
-                                       } else {
-                                               in = new FileInputStream(input);
-                                       }
-                                       BufferedImage image = ImageUtilsAwt
-                                                       .fromImage(new Image(in));
-                                       ImageTextAwt img = new ImageTextAwt(image, size, mode,
-                                                       invert);
-                                       if (output == null) {
-                                               System.out.println(img.getText());
-                                       } else {
-                                               IOUtils.writeSmallFile(output, img.getText());
-                                       }
-                               } finally {
-                                       if (!input.equals("-")) {
-                                               in.close();
-                                       }
-                               }
-                       }
-               } catch (IOException e) {
-                       e.printStackTrace();
-                       System.exit(2);
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/main/justify.java b/src/be/nikiroo/utils/main/justify.java
deleted file mode 100644 (file)
index 2a83389..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package be.nikiroo.utils.main;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Scanner;
-
-import be.nikiroo.utils.StringUtils;
-import be.nikiroo.utils.StringUtils.Alignment;
-
-/**
- * Text justification (left, right, center, justify).
- * 
- * @author niki
- */
-public class justify {
-       /**
-        * Syntax: $0 ([left|right|center|justify]) (max width)
-        * <p>
-        * <ul>
-        * <li>mode: left, right, center or full justification (defaults to left)</li>
-        * <li>max width: the maximum width of a line, or "" for "no maximum"
-        * (defaults to "no maximum")</li>
-        * </ul>
-        * 
-        * @param args
-        */
-       public static void main(String[] args) {
-               int width = -1;
-               StringUtils.Alignment align = Alignment.LEFT;
-
-               if (args.length >= 1) {
-                       align = Alignment.valueOf(args[0].toUpperCase());
-               }
-               if (args.length >= 2) {
-                       width = Integer.parseInt(args[1]);
-               }
-
-               Scanner scan = new Scanner(System.in);
-               scan.useDelimiter("\r\n|[\r\n]");
-               try {
-                       List<String> lines = new ArrayList<String>();
-                       while (scan.hasNext()) {
-                               lines.add(scan.next());
-                       }
-
-                       for (String line : StringUtils.justifyText(lines, width, align)) {
-                               System.out.println(line);
-                       }
-               } finally {
-                       scan.close();
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/resources/Bundle.java b/src/be/nikiroo/utils/resources/Bundle.java
deleted file mode 100644 (file)
index fe3ac1a..0000000
+++ /dev/null
@@ -1,1313 +0,0 @@
-package be.nikiroo.utils.resources;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.PropertyResourceBundle;
-import java.util.ResourceBundle;
-
-import be.nikiroo.utils.resources.Meta.Format;
-
-/**
- * This class encapsulate a {@link ResourceBundle} in UTF-8. It allows to
- * retrieve values associated to an enumeration, and allows some additional
- * methods.
- * <p>
- * It also sports a writable change map, and you can save back the
- * {@link Bundle} to file with {@link Bundle#updateFile(String)}.
- * 
- * @param <E>
- *            the enum to use to get values out of this class
- * 
- * @author niki
- */
-
-public class Bundle<E extends Enum<E>> {
-       /** The type of E. */
-       protected Class<E> type;
-       /**
-        * The {@link Enum} associated to this {@link Bundle} (all the keys used in
-        * this {@link Bundle} will be of this type).
-        */
-       protected Enum<?> keyType;
-
-       private TransBundle<E> descriptionBundle;
-
-       /** R/O map */
-       private Map<String, String> map;
-       /** R/W map */
-       private Map<String, String> changeMap;
-
-       /**
-        * Create a new {@link Bundles} of the given name.
-        * 
-        * @param type
-        *            a runtime instance of the class of E
-        * @param name
-        *            the name of the {@link Bundles}
-        * @param descriptionBundle
-        *            the description {@link TransBundle}, that is, a
-        *            {@link TransBundle} dedicated to the description of the values
-        *            of the given {@link Bundle} (can be NULL)
-        */
-       protected Bundle(Class<E> type, Enum<?> name,
-                       TransBundle<E> descriptionBundle) {
-               this.type = type;
-               this.keyType = name;
-               this.descriptionBundle = descriptionBundle;
-
-               this.map = new HashMap<String, String>();
-               this.changeMap = new HashMap<String, String>();
-               setBundle(name, Locale.getDefault(), false);
-       }
-
-       /**
-        * Check if the setting is set into this {@link Bundle}.
-        * 
-        * @param id
-        *            the id of the setting to check
-        * @param includeDefaultValue
-        *            TRUE to only return false when the setting is not set AND
-        *            there is no default value
-        * 
-        * @return TRUE if the setting is set
-        */
-       public boolean isSet(E id, boolean includeDefaultValue) {
-               return isSet(id.name(), includeDefaultValue);
-       }
-
-       /**
-        * Check if the setting is set into this {@link Bundle}.
-        * 
-        * @param name
-        *            the id of the setting to check
-        * @param includeDefaultValue
-        *            TRUE to only return false when the setting is explicitly set
-        *            to NULL (and not just "no set") in the change maps
-        * 
-        * @return TRUE if the setting is set
-        */
-       protected boolean isSet(String name, boolean includeDefaultValue) {
-               if (getString(name, null) == null) {
-                       if (!includeDefaultValue || getString(name, "") == null) {
-                               return false;
-                       }
-               }
-
-               return true;
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link String}.
-        * 
-        * @param id
-        *            the id of the value to get
-        * 
-        * @return the associated value, or NULL if not found (not present in the
-        *         resource file)
-        */
-       public String getString(E id) {
-               return getString(id, null);
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link String}.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file
-        * 
-        * @return the associated value, or <tt>def</tt> if not found (not present
-        *         in the resource file)
-        */
-       public String getString(E id, String def) {
-               return getString(id, def, -1);
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link String}.
-        * <p>
-        * If no value is associated (or if it is empty!), take the default one if
-        * any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the associated value, <tt>def</tt> if not found (not present in
-        *         the resource file) or NULL if the item is specified (not -1) and
-        *         does not exist
-        */
-       public String getString(E id, String def, int item) {
-               String rep = getString(id.name(), null);
-               if (rep == null) {
-                       rep = getMetaDef(id.name());
-               }
-
-               if (rep.isEmpty()) {
-                       return def;
-               }
-
-               if (item >= 0) {
-                       List<String> values = BundleHelper.parseList(rep, item);
-                       if (values != null && item < values.size()) {
-                               return values.get(item);
-                       }
-
-                       return null;
-               }
-
-               return rep;
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link String}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * 
-        */
-       public void setString(E id, String value) {
-               setString(id.name(), value);
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link String}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        */
-       public void setString(E id, String value, int item) {
-               if (item < 0) {
-                       setString(id.name(), value);
-               } else {
-                       List<String> values = getList(id);
-                       setString(id.name(), BundleHelper.fromList(values, value, item));
-               }
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link String} suffixed
-        * with the runtime value "_suffix" (that is, "_" and suffix).
-        * <p>
-        * Will only accept suffixes that form an existing id.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param suffix
-        *            the runtime suffix
-        * 
-        * @return the associated value, or NULL if not found (not present in the
-        *         resource file)
-        */
-       public String getStringX(E id, String suffix) {
-               return getStringX(id, suffix, null, -1);
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link String} suffixed
-        * with the runtime value "_suffix" (that is, "_" and suffix).
-        * <p>
-        * Will only accept suffixes that form an existing id.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param suffix
-        *            the runtime suffix
-        * @param def
-        *            the default value when it is not present in the config file
-        * 
-        * @return the associated value, or NULL if not found (not present in the
-        *         resource file)
-        */
-       public String getStringX(E id, String suffix, String def) {
-               return getStringX(id, suffix, def, -1);
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link String} suffixed
-        * with the runtime value "_suffix" (that is, "_" and suffix).
-        * <p>
-        * Will only accept suffixes that form an existing id.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param suffix
-        *            the runtime suffix
-        * @param def
-        *            the default value when it is not present in the config file
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the associated value, <tt>def</tt> if not found (not present in
-        *         the resource file), NULL if the item is specified (not -1) but
-        *         does not exist and NULL if bad key
-        */
-       public String getStringX(E id, String suffix, String def, int item) {
-               String key = id.name()
-                               + (suffix == null ? "" : "_" + suffix.toUpperCase());
-
-               try {
-                       id = Enum.valueOf(type, key);
-                       return getString(id, def, item);
-               } catch (IllegalArgumentException e) {
-               }
-
-               return null;
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link String} suffixed
-        * with the runtime value "_suffix" (that is, "_" and suffix).
-        * <p>
-        * Will only accept suffixes that form an existing id.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param suffix
-        *            the runtime suffix
-        * @param value
-        *            the value
-        */
-       public void setStringX(E id, String suffix, String value) {
-               setStringX(id, suffix, value, -1);
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link String} suffixed
-        * with the runtime value "_suffix" (that is, "_" and suffix).
-        * <p>
-        * Will only accept suffixes that form an existing id.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param suffix
-        *            the runtime suffix
-        * @param value
-        *            the value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        */
-       public void setStringX(E id, String suffix, String value, int item) {
-               String key = id.name()
-                               + (suffix == null ? "" : "_" + suffix.toUpperCase());
-
-               try {
-                       id = Enum.valueOf(type, key);
-                       setString(id, value, item);
-               } catch (IllegalArgumentException e) {
-               }
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link Boolean}.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * 
-        * @return the associated value
-        */
-       public Boolean getBoolean(E id) {
-               return BundleHelper.parseBoolean(getString(id), -1);
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link Boolean}.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a boolean value
-        * 
-        * @return the associated value
-        */
-       public boolean getBoolean(E id, boolean def) {
-               Boolean value = getBoolean(id);
-               if (value != null) {
-                       return value;
-               }
-
-               return def;
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link Boolean}.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a boolean value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the associated value
-        */
-       public Boolean getBoolean(E id, boolean def, int item) {
-               String value = getString(id);
-               if (value != null) {
-                       return BundleHelper.parseBoolean(value, item);
-               }
-
-               return def;
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link Boolean}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * 
-        */
-       public void setBoolean(E id, boolean value) {
-               setBoolean(id, value, -1);
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link Boolean}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        */
-       public void setBoolean(E id, boolean value, int item) {
-               setString(id, BundleHelper.fromBoolean(value), item);
-       }
-
-       /**
-        * Return the value associated to the given id as an {@link Integer}.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * 
-        * @return the associated value
-        */
-       public Integer getInteger(E id) {
-               String value = getString(id);
-               if (value != null) {
-                       return BundleHelper.parseInteger(value, -1);
-               }
-
-               return null;
-       }
-
-       /**
-        * Return the value associated to the given id as an int.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a int value
-        * 
-        * @return the associated value
-        */
-       public int getInteger(E id, int def) {
-               Integer value = getInteger(id);
-               if (value != null) {
-                       return value;
-               }
-
-               return def;
-       }
-
-       /**
-        * Return the value associated to the given id as an int.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a int value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the associated value
-        */
-       public Integer getInteger(E id, int def, int item) {
-               String value = getString(id);
-               if (value != null) {
-                       return BundleHelper.parseInteger(value, item);
-               }
-
-               return def;
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link Integer}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * 
-        */
-       public void setInteger(E id, int value) {
-               setInteger(id, value, -1);
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link Integer}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        */
-       public void setInteger(E id, int value, int item) {
-               setString(id, BundleHelper.fromInteger(value), item);
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link Character}.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * 
-        * @return the associated value
-        */
-       public Character getCharacter(E id) {
-               return BundleHelper.parseCharacter(getString(id), -1);
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link Character}.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a char value
-        * 
-        * @return the associated value
-        */
-       public char getCharacter(E id, char def) {
-               Character value = getCharacter(id);
-               if (value != null) {
-                       return value;
-               }
-
-               return def;
-       }
-
-       /**
-        * Return the value associated to the given id as a {@link Character}.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a char value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the associated value
-        */
-       public Character getCharacter(E id, char def, int item) {
-               String value = getString(id);
-               if (value != null) {
-                       return BundleHelper.parseCharacter(value, item);
-               }
-
-               return def;
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link Character}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * 
-        */
-       public void setCharacter(E id, char value) {
-               setCharacter(id, value, -1);
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link Character}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        */
-       public void setCharacter(E id, char value, int item) {
-               setString(id, BundleHelper.fromCharacter(value), item);
-       }
-
-       /**
-        * Return the value associated to the given id as a colour if it is found
-        * and can be parsed.
-        * <p>
-        * The returned value is an ARGB value.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * 
-        * @return the associated value
-        */
-       public Integer getColor(E id) {
-               return BundleHelper.parseColor(getString(id), -1);
-       }
-
-       /**
-        * Return the value associated to the given id as a colour if it is found
-        * and can be parsed.
-        * <p>
-        * The returned value is an ARGB value.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a char value
-        * 
-        * @return the associated value
-        */
-       public int getColor(E id, int def) {
-               Integer value = getColor(id);
-               if (value != null) {
-                       return value;
-               }
-
-               return def;
-       }
-
-       /**
-        * Return the value associated to the given id as a colour if it is found
-        * and can be parsed.
-        * <p>
-        * The returned value is an ARGB value.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a char value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the associated value
-        */
-       public Integer getColor(E id, int def, int item) {
-               String value = getString(id);
-               if (value != null) {
-                       return BundleHelper.parseColor(value, item);
-               }
-
-               return def;
-       }
-
-       /**
-        * Set the value associated to the given id as a colour.
-        * <p>
-        * The value is a BGRA value.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param color
-        *            the new colour
-        */
-       public void setColor(E id, Integer color) {
-               setColor(id, color, -1);
-       }
-
-       /**
-        * Set the value associated to the given id as a Color.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        */
-       public void setColor(E id, int value, int item) {
-               setString(id, BundleHelper.fromColor(value), item);
-       }
-
-       /**
-        * Return the value associated to the given id as a list of values if it is
-        * found and can be parsed.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * 
-        * @return the associated list, empty if the value is empty, NULL if it is
-        *         not found or cannot be parsed as a list
-        */
-       public List<String> getList(E id) {
-               return BundleHelper.parseList(getString(id), -1);
-       }
-
-       /**
-        * Return the value associated to the given id as a list of values if it is
-        * found and can be parsed.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a char value
-        * 
-        * @return the associated list, empty if the value is empty, NULL if it is
-        *         not found or cannot be parsed as a list
-        */
-       public List<String> getList(E id, List<String> def) {
-               List<String> value = getList(id);
-               if (value != null) {
-                       return value;
-               }
-
-               return def;
-       }
-
-       /**
-        * Return the value associated to the given id as a list of values if it is
-        * found and can be parsed.
-        * <p>
-        * If no value is associated, take the default one if any.
-        * 
-        * @param id
-        *            the id of the value to get
-        * @param def
-        *            the default value when it is not present in the config file or
-        *            if it is not a char value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the associated list, empty if the value is empty, NULL if it is
-        *         not found or cannot be parsed as a list
-        */
-       public List<String> getList(E id, List<String> def, int item) {
-               String value = getString(id);
-               if (value != null) {
-                       return BundleHelper.parseList(value, item);
-               }
-
-               return def;
-       }
-
-       /**
-        * Set the value associated to the given id as a list of values.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param list
-        *            the new list of values
-        */
-       public void setList(E id, List<String> list) {
-               setList(id, list, -1);
-       }
-
-       /**
-        * Set the value associated to the given id as a {@link List}.
-        * 
-        * @param id
-        *            the id of the value to set
-        * @param value
-        *            the value
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays
-        * 
-        */
-       public void setList(E id, List<String> value, int item) {
-               setString(id, BundleHelper.fromList(value), item);
-       }
-
-       /**
-        * Create/update the .properties file.
-        * <p>
-        * Will use the most likely candidate as base if the file does not already
-        * exists and this resource is translatable (for instance, "en_US" will use
-        * "en" as a base if the resource is a translation file).
-        * <p>
-        * Will update the files in {@link Bundles#getDirectory()}; it <b>MUST</b>
-        * be set.
-        * 
-        * @throws IOException
-        *             in case of IO errors
-        */
-       public void updateFile() throws IOException {
-               updateFile(Bundles.getDirectory());
-       }
-
-       /**
-        * Create/update the .properties file.
-        * <p>
-        * Will use the most likely candidate as base if the file does not already
-        * exists and this resource is translatable (for instance, "en_US" will use
-        * "en" as a base if the resource is a translation file).
-        * 
-        * @param path
-        *            the path where the .properties files are, <b>MUST NOT</b> be
-        *            NULL
-        * 
-        * @throws IOException
-        *             in case of IO errors
-        */
-       public void updateFile(String path) throws IOException {
-               File file = getUpdateFile(path);
-
-               BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
-                               new FileOutputStream(file), "UTF-8"));
-
-               writeHeader(writer);
-               writer.write("\n");
-               writer.write("\n");
-
-               for (Field field : type.getDeclaredFields()) {
-                       Meta meta = field.getAnnotation(Meta.class);
-                       if (meta != null) {
-                               E id = Enum.valueOf(type, field.getName());
-                               String info = getMetaInfo(meta);
-
-                               if (info != null) {
-                                       writer.write(info);
-                                       writer.write("\n");
-                               }
-
-                               writeValue(writer, id);
-                       }
-               }
-
-               writer.close();
-       }
-
-       /**
-        * Delete the .properties file.
-        * <p>
-        * Will use the most likely candidate as base if the file does not already
-        * exists and this resource is translatable (for instance, "en_US" will use
-        * "en" as a base if the resource is a translation file).
-        * <p>
-        * Will delete the files in {@link Bundles#getDirectory()}; it <b>MUST</b>
-        * be set.
-        * 
-        * @return TRUE if the file was deleted
-        */
-       public boolean deleteFile() {
-               return deleteFile(Bundles.getDirectory());
-       }
-
-       /**
-        * Delete the .properties file.
-        * <p>
-        * Will use the most likely candidate as base if the file does not already
-        * exists and this resource is translatable (for instance, "en_US" will use
-        * "en" as a base if the resource is a translation file).
-        * 
-        * @param path
-        *            the path where the .properties files are, <b>MUST NOT</b> be
-        *            NULL
-        * 
-        * @return TRUE if the file was deleted
-        */
-       public boolean deleteFile(String path) {
-               File file = getUpdateFile(path);
-               return file.delete();
-       }
-
-       /**
-        * The description {@link TransBundle}, that is, a {@link TransBundle}
-        * dedicated to the description of the values of the given {@link Bundle}
-        * (can be NULL).
-        * 
-        * @return the description {@link TransBundle}
-        */
-       public TransBundle<E> getDescriptionBundle() {
-               return descriptionBundle;
-       }
-
-       /**
-        * Reload the {@link Bundle} data files.
-        * 
-        * @param resetToDefault
-        *            reset to the default configuration (do not look into the
-        *            possible user configuration files, only take the original
-        *            configuration)
-        */
-       public void reload(boolean resetToDefault) {
-               setBundle(keyType, Locale.getDefault(), resetToDefault);
-       }
-
-       /**
-        * Check if the internal map contains the given key.
-        * 
-        * @param key
-        *            the key to check for
-        * 
-        * @return true if it does
-        */
-       protected boolean containsKey(String key) {
-               return changeMap.containsKey(key) || map.containsKey(key);
-       }
-
-       /**
-        * The default {@link Meta#def()} value for the given enumeration name.
-        * 
-        * @param id
-        *            the enumeration name (the "id")
-        * 
-        * @return the def value in the {@link MetaInfo} or "" if none (never NULL)
-        */
-       protected String getMetaDef(String id) {
-               String rep = "";
-               try {
-                       Meta meta = type.getDeclaredField(id).getAnnotation(Meta.class);
-                       rep = meta.def();
-               } catch (NoSuchFieldException e) {
-               } catch (SecurityException e) {
-               }
-
-               if (rep == null) {
-                       rep = "";
-               }
-
-               return rep;
-       }
-
-       /**
-        * Get the value for the given key if it exists in the internal map, or
-        * <tt>def</tt> if not.
-        * <p>
-        * DO NOT get the default meta value (MetaInfo.def()).
-        * 
-        * @param key
-        *            the key to check for
-        * @param def
-        *            the default value when it is not present in the internal map
-        * 
-        * @return the value, or <tt>def</tt> if not found
-        */
-       protected String getString(String key, String def) {
-               if (changeMap.containsKey(key)) {
-                       return changeMap.get(key);
-               }
-
-               if (map.containsKey(key)) {
-                       return map.get(key);
-               }
-
-               return def;
-       }
-
-       /**
-        * Set the value for this key, in the change map (it is kept in memory, not
-        * yet on disk).
-        * 
-        * @param key
-        *            the key
-        * @param value
-        *            the associated value
-        */
-       protected void setString(String key, String value) {
-               changeMap.put(key, value == null ? null : value.trim());
-       }
-
-       /**
-        * Return formated, display-able information from the {@link Meta} field
-        * given. Each line will always starts with a "#" character.
-        * 
-        * @param meta
-        *            the {@link Meta} field
-        * 
-        * @return the information to display or NULL if none
-        */
-       protected String getMetaInfo(Meta meta) {
-               String desc = meta.description();
-               boolean group = meta.group();
-               Meta.Format format = meta.format();
-               String[] list = meta.list();
-               boolean nullable = meta.nullable();
-               String def = meta.def();
-               boolean array = meta.array();
-
-               // Default, empty values -> NULL
-               if (desc.length() + list.length + def.length() == 0 && !group
-                               && nullable && format == Format.STRING) {
-                       return null;
-               }
-
-               StringBuilder builder = new StringBuilder();
-               for (String line : desc.split("\n")) {
-                       builder.append("# ").append(line).append("\n");
-               }
-
-               if (group) {
-                       builder.append("# This item is used as a group, its content is not expected to be used.");
-               } else {
-                       builder.append("# (FORMAT: ").append(format)
-                                       .append(nullable ? "" : ", required");
-                       builder.append(") ");
-
-                       if (list.length > 0) {
-                               builder.append("\n# ALLOWED VALUES: ");
-                               boolean first = true;
-                               for (String value : list) {
-                                       if (!first) {
-                                               builder.append(", ");
-                                       }
-                                       builder.append(BundleHelper.escape(value));
-                                       first = false;
-                               }
-                       }
-
-                       if (array) {
-                               builder.append("\n# (This item accepts a list of ^escaped comma-separated values)");
-                       }
-               }
-
-               return builder.toString();
-       }
-
-       /**
-        * The display name used in the <tt>.properties file</tt>.
-        * 
-        * @return the name
-        */
-       protected String getBundleDisplayName() {
-               return keyType.toString();
-       }
-
-       /**
-        * Write the header found in the configuration <tt>.properties</tt> file of
-        * this {@link Bundles}.
-        * 
-        * @param writer
-        *            the {@link Writer} to write the header in
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       protected void writeHeader(Writer writer) throws IOException {
-               writer.write("# " + getBundleDisplayName() + "\n");
-               writer.write("#\n");
-       }
-
-       /**
-        * Write the given data to the config file, i.e., "MY_ID = my_curent_value"
-        * followed by a new line.
-        * <p>
-        * Will prepend a # sign if the is is not set (see
-        * {@link Bundle#isSet(Enum, boolean)}).
-        * 
-        * @param writer
-        *            the {@link Writer} to write into
-        * @param id
-        *            the id to write
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       protected void writeValue(Writer writer, E id) throws IOException {
-               boolean set = isSet(id, false);
-               writeValue(writer, id.name(), getString(id), set);
-       }
-
-       /**
-        * Write the given data to the config file, i.e., "MY_ID = my_curent_value"
-        * followed by a new line.
-        * <p>
-        * Will prepend a # sign if the is is not set.
-        * 
-        * @param writer
-        *            the {@link Writer} to write into
-        * @param id
-        *            the id to write
-        * @param value
-        *            the id's value
-        * @param set
-        *            the value is set in this {@link Bundle}
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       protected void writeValue(Writer writer, String id, String value,
-                       boolean set) throws IOException {
-
-               if (!set) {
-                       writer.write('#');
-               }
-
-               writer.write(id);
-               writer.write(" = ");
-
-               if (value == null) {
-                       value = "";
-               }
-
-               String[] lines = value.replaceAll("\t", "\\\\\\t").split("\n");
-               for (int i = 0; i < lines.length; i++) {
-                       writer.write(lines[i]);
-                       if (i < lines.length - 1) {
-                               writer.write("\\n\\");
-                       }
-                       writer.write("\n");
-               }
-       }
-
-       /**
-        * Return the source file for this {@link Bundles} from the given path.
-        * 
-        * @param path
-        *            the path where the .properties files are
-        * 
-        * @return the source {@link File}
-        */
-       protected File getUpdateFile(String path) {
-               return new File(path, keyType.name() + ".properties");
-       }
-
-       /**
-        * Change the currently used bundle, and reset all changes.
-        * 
-        * @param name
-        *            the name of the bundle to load
-        * @param locale
-        *            the {@link Locale} to use
-        * @param resetToDefault
-        *            reset to the default configuration (do not look into the
-        *            possible user configuration files, only take the original
-        *            configuration)
-        */
-       protected void setBundle(Enum<?> name, Locale locale, boolean resetToDefault) {
-               changeMap.clear();
-               String dir = Bundles.getDirectory();
-               String bname = type.getPackage().getName() + "." + name.name();
-
-               boolean found = false;
-               if (!resetToDefault && dir != null) {
-                       try {
-                               // Look into Bundles.getDirectory() for .properties files
-                               File file = getPropertyFile(dir, name.name(), locale);
-                               if (file != null) {
-                                       InputStream in = new FileInputStream(file);
-                                       try {
-                                               Reader reader = new InputStreamReader(in, "UTF-8");
-                                               try {
-                                                       resetMap(new PropertyResourceBundle(reader));
-                                               } finally {
-                                                       reader.close();
-                                               }
-                                       } finally {
-                                               in.close();
-                                       }
-                                       found = true;
-                               }
-                       } catch (IOException e) {
-                               e.printStackTrace();
-                       }
-               }
-
-               if (!found) {
-                       // Look into the package itself for resources
-                       try {
-                               resetMap(ResourceBundle
-                                               .getBundle(bname, locale, type.getClassLoader(),
-                                                               new FixedResourceBundleControl()));
-                               found = true;
-                       } catch (MissingResourceException e) {
-                       } catch (Exception e) {
-                               e.printStackTrace();
-                       }
-               }
-
-               if (!found) {
-                       // We have no bundle for this Bundle
-                       System.err.println("No bundle found for: " + bname);
-                       resetMap(null);
-               }
-       }
-
-       /**
-        * Reset the backing map to the content of the given bundle, or with NULL
-        * values if bundle is NULL.
-        * 
-        * @param bundle
-        *            the bundle to copy
-        */
-       protected void resetMap(ResourceBundle bundle) {
-               this.map.clear();
-               if (bundle != null) {
-                       for (Field field : type.getDeclaredFields()) {
-                               try {
-                                       Meta meta = field.getAnnotation(Meta.class);
-                                       if (meta != null) {
-                                               E id = Enum.valueOf(type, field.getName());
-                                               String value = bundle.getString(id.name());
-                                               this.map.put(id.name(),
-                                                               value == null ? null : value.trim());
-                                       }
-                               } catch (MissingResourceException e) {
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Take a snapshot of the changes in memory in this {@link Bundle} made by
-        * the "set" methods ( {@link Bundle#setString(Enum, String)}...) at the
-        * current time.
-        * 
-        * @return a snapshot to use with {@link Bundle#restoreSnapshot(Object)}
-        */
-       public Object takeSnapshot() {
-               return new HashMap<String, String>(changeMap);
-       }
-
-       /**
-        * Restore a snapshot taken with {@link Bundle}, or reset the current
-        * changes if the snapshot is NULL.
-        * 
-        * @param snap
-        *            the snapshot or NULL
-        */
-       @SuppressWarnings("unchecked")
-       public void restoreSnapshot(Object snap) {
-               if (snap == null) {
-                       changeMap.clear();
-               } else {
-                       if (snap instanceof Map) {
-                               changeMap = (Map<String, String>) snap;
-                       } else {
-                               throw new RuntimeException(
-                                               "Restoring changes in a Bundle must be done on a changes snapshot, "
-                                                               + "or NULL to discard current changes");
-                       }
-               }
-       }
-
-       /**
-        * Return the resource file that is closer to the {@link Locale}.
-        * 
-        * @param dir
-        *            the directory to look into
-        * @param name
-        *            the file base name (without <tt>.properties</tt>)
-        * @param locale
-        *            the {@link Locale}
-        * 
-        * @return the closest match or NULL if none
-        */
-       private File getPropertyFile(String dir, String name, Locale locale) {
-               List<String> locales = new ArrayList<String>();
-               if (locale != null) {
-                       String country = locale.getCountry() == null ? "" : locale
-                                       .getCountry();
-                       String language = locale.getLanguage() == null ? "" : locale
-                                       .getLanguage();
-                       if (!language.isEmpty() && !country.isEmpty()) {
-                               locales.add("_" + language + "-" + country);
-                       }
-                       if (!language.isEmpty()) {
-                               locales.add("_" + language);
-                       }
-               }
-
-               locales.add("");
-
-               File file = null;
-               for (String loc : locales) {
-                       file = new File(dir, name + loc + ".properties");
-                       if (file.exists()) {
-                               break;
-                       }
-
-                       file = null;
-               }
-
-               return file;
-       }
-}
diff --git a/src/be/nikiroo/utils/resources/BundleHelper.java b/src/be/nikiroo/utils/resources/BundleHelper.java
deleted file mode 100644 (file)
index c6b26c7..0000000
+++ /dev/null
@@ -1,589 +0,0 @@
-package be.nikiroo.utils.resources;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Internal class used to convert data to/from {@link String}s in the context of
- * {@link Bundle}s.
- * 
- * @author niki
- */
-class BundleHelper {
-       /**
-        * Convert the given {@link String} into a {@link Boolean} if it represents
-        * a {@link Boolean}, or NULL if it doesn't.
-        * <p>
-        * Note: null, "strange text", ""... will all be converted to NULL.
-        * 
-        * @param str
-        *            the input {@link String}
-        * @param item
-        *            the item number to use for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the converted {@link Boolean} or NULL
-        */
-       static public Boolean parseBoolean(String str, int item) {
-               str = getItem(str, item);
-               if (str == null) {
-                       return null;
-               }
-
-               if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("on")
-                               || str.equalsIgnoreCase("yes"))
-                       return true;
-               if (str.equalsIgnoreCase("false") || str.equalsIgnoreCase("off")
-                               || str.equalsIgnoreCase("no"))
-                       return false;
-
-               return null;
-       }
-
-       /**
-        * Return a {@link String} representation of the given {@link Boolean}.
-        * 
-        * @param value
-        *            the input value
-        * 
-        * @return the raw {@link String} value that correspond to it
-        */
-       static public String fromBoolean(boolean value) {
-               return Boolean.toString(value);
-       }
-
-       /**
-        * Convert the given {@link String} into a {@link Integer} if it represents
-        * a {@link Integer}, or NULL if it doesn't.
-        * <p>
-        * Note: null, "strange text", ""... will all be converted to NULL.
-        * 
-        * @param str
-        *            the input {@link String}
-        * @param item
-        *            the item number to use for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the converted {@link Integer} or NULL
-        */
-       static public Integer parseInteger(String str, int item) {
-               str = getItem(str, item);
-               if (str == null) {
-                       return null;
-               }
-
-               try {
-                       return Integer.parseInt(str);
-               } catch (Exception e) {
-               }
-
-               return null;
-       }
-
-       /**
-        * Return a {@link String} representation of the given {@link Integer}.
-        * 
-        * @param value
-        *            the input value
-        * 
-        * @return the raw {@link String} value that correspond to it
-        */
-       static public String fromInteger(int value) {
-               return Integer.toString(value);
-       }
-
-       /**
-        * Convert the given {@link String} into a {@link Character} if it
-        * represents a {@link Character}, or NULL if it doesn't.
-        * <p>
-        * Note: null, "strange text", ""... will all be converted to NULL
-        * (remember: any {@link String} whose length is not 1 is <b>not</b> a
-        * {@link Character}).
-        * 
-        * @param str
-        *            the input {@link String}
-        * @param item
-        *            the item number to use for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the converted {@link Character} or NULL
-        */
-       static public Character parseCharacter(String str, int item) {
-               str = getItem(str, item);
-               if (str == null) {
-                       return null;
-               }
-
-               String s = str.trim();
-               if (s.length() == 1) {
-                       return s.charAt(0);
-               }
-
-               return null;
-       }
-
-       /**
-        * Return a {@link String} representation of the given {@link Boolean}.
-        * 
-        * @param value
-        *            the input value
-        * 
-        * @return the raw {@link String} value that correspond to it
-        */
-       static public String fromCharacter(char value) {
-               return Character.toString(value);
-       }
-
-       /**
-        * Convert the given {@link String} into a colour (represented here as an
-        * {@link Integer}) if it represents a colour, or NULL if it doesn't.
-        * <p>
-        * The returned colour value is an ARGB value.
-        * 
-        * @param str
-        *            the input {@link String}
-        * @param item
-        *            the item number to use for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the converted colour as an {@link Integer} value or NULL
-        */
-       static Integer parseColor(String str, int item) {
-               str = getItem(str, item);
-               if (str == null) {
-                       return null;
-               }
-
-               Integer rep = null;
-
-               str = str.trim();
-               int r = 0, g = 0, b = 0, a = -1;
-               if (str.startsWith("#") && (str.length() == 7 || str.length() == 9)) {
-                       try {
-                               r = Integer.parseInt(str.substring(1, 3), 16);
-                               g = Integer.parseInt(str.substring(3, 5), 16);
-                               b = Integer.parseInt(str.substring(5, 7), 16);
-                               if (str.length() == 9) {
-                                       a = Integer.parseInt(str.substring(7, 9), 16);
-                               } else {
-                                       a = 255;
-                               }
-
-                       } catch (NumberFormatException e) {
-                               // no changes
-                       }
-               }
-
-               // Try by name if still not found
-               if (a == -1) {
-                       if ("black".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 0;
-                               g = 0;
-                               b = 0;
-                       } else if ("white".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 255;
-                               g = 255;
-                               b = 255;
-                       } else if ("red".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 255;
-                               g = 0;
-                               b = 0;
-                       } else if ("green".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 0;
-                               g = 255;
-                               b = 0;
-                       } else if ("blue".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 0;
-                               g = 0;
-                               b = 255;
-                       } else if ("grey".equalsIgnoreCase(str)
-                                       || "gray".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 128;
-                               g = 128;
-                               b = 128;
-                       } else if ("cyan".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 0;
-                               g = 255;
-                               b = 255;
-                       } else if ("magenta".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 255;
-                               g = 0;
-                               b = 255;
-                       } else if ("yellow".equalsIgnoreCase(str)) {
-                               a = 255;
-                               r = 255;
-                               g = 255;
-                               b = 0;
-                       }
-               }
-
-               if (a != -1) {
-                       rep = ((a & 0xFF) << 24) //
-                                       | ((r & 0xFF) << 16) //
-                                       | ((g & 0xFF) << 8) //
-                                       | ((b & 0xFF) << 0);
-               }
-
-               return rep;
-       }
-
-       /**
-        * Return a {@link String} representation of the given colour.
-        * <p>
-        * The colour value is interpreted as an ARGB value.
-        * 
-        * @param color
-        *            the ARGB colour value
-        * @return the raw {@link String} value that correspond to it
-        */
-       static public String fromColor(int color) {
-               int a = (color >> 24) & 0xFF;
-               int r = (color >> 16) & 0xFF;
-               int g = (color >> 8) & 0xFF;
-               int b = (color >> 0) & 0xFF;
-
-               String rs = Integer.toString(r, 16);
-               String gs = Integer.toString(g, 16);
-               String bs = Integer.toString(b, 16);
-               String as = "";
-               if (a < 255) {
-                       as = Integer.toString(a, 16);
-               }
-
-               return "#" + rs + gs + bs + as;
-       }
-
-       /**
-        * The size of this raw list (note than a NULL list is of size 0).
-        * 
-        * @param raw
-        *            the raw list
-        * 
-        * @return its size if it is a list (NULL is an empty list), -1 if it is not
-        *         a list
-        */
-       static public int getListSize(String raw) {
-               if (raw == null) {
-                       return 0;
-               }
-
-               List<String> list = parseList(raw, -1);
-               if (list == null) {
-                       return -1;
-               }
-
-               return list.size();
-       }
-
-       /**
-        * Return a {@link String} representation of the given list of values.
-        * <p>
-        * The list of values is comma-separated and each value is surrounded by
-        * double-quotes; caret (^) and double-quotes (") are escaped by a caret.
-        * 
-        * @param str
-        *            the input value
-        * @param item
-        *            the item number to use for an array of values, or -1 for
-        *            non-arrays
-        * 
-        * @return the raw {@link String} value that correspond to it
-        */
-       static public List<String> parseList(String str, int item) {
-               if (str == null) {
-                       return null;
-               }
-
-               if (item >= 0) {
-                       str = getItem(str, item);
-               }
-
-               List<String> list = new ArrayList<String>();
-               try {
-                       boolean inQuote = false;
-                       boolean prevIsBackSlash = false;
-                       StringBuilder builder = new StringBuilder();
-                       for (int i = 0; i < str.length(); i++) {
-                               char car = str.charAt(i);
-
-                               if (prevIsBackSlash) {
-                                       // We don't process it here
-                                       builder.append(car);
-                                       prevIsBackSlash = false;
-                               } else {
-                                       switch (car) {
-                                       case '"':
-                                               // We don't process it here
-                                               builder.append(car);
-
-                                               if (inQuote) {
-                                                       list.add(unescape(builder.toString()));
-                                                       builder.setLength(0);
-                                               }
-
-                                               inQuote = !inQuote;
-                                               break;
-                                       case '^':
-                                               // We don't process it here
-                                               builder.append(car);
-                                               prevIsBackSlash = true;
-                                               break;
-                                       case ' ':
-                                       case '\n':
-                                       case '\r':
-                                               if (inQuote) {
-                                                       builder.append(car);
-                                               }
-                                               break;
-
-                                       case ',':
-                                               if (!inQuote) {
-                                                       break;
-                                               }
-                                               // continue to default
-                                       default:
-                                               if (!inQuote) {
-                                                       // Bad format!
-                                                       return null;
-                                               }
-
-                                               builder.append(car);
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (inQuote || prevIsBackSlash) {
-                               // Bad format!
-                               return null;
-                       }
-
-               } catch (Exception e) {
-                       return null;
-               }
-
-               return list;
-       }
-
-       /**
-        * Return a {@link String} representation of the given list of values.
-        * <p>
-        * NULL will be assimilated to an empty {@link String} if later non-null
-        * values exist, or just ignored if not.
-        * <p>
-        * Example:
-        * <ul>
-        * <li><tt>1</tt>,<tt>NULL</tt>, <tt>3</tt> will become <tt>1</tt>,
-        * <tt>""</tt>, <tt>3</tt></li>
-        * <li><tt>1</tt>,<tt>NULL</tt>, <tt>NULL</tt> will become <tt>1</tt></li>
-        * <li><tt>NULL</tt>, <tt>NULL</tt>, <tt>NULL</tt> will become an empty list
-        * </li>
-        * </ul>
-        * 
-        * @param list
-        *            the input value
-        * 
-        * @return the raw {@link String} value that correspond to it
-        */
-       static public String fromList(List<String> list) {
-               if (list == null) {
-                       list = new ArrayList<String>();
-               }
-
-               int last = list.size() - 1;
-               for (int i = 0; i < list.size(); i++) {
-                       if (list.get(i) != null) {
-                               last = i;
-                       }
-               }
-
-               StringBuilder builder = new StringBuilder();
-               for (int i = 0; i <= last; i++) {
-                       String item = list.get(i);
-                       if (item == null) {
-                               item = "";
-                       }
-
-                       if (builder.length() > 0) {
-                               builder.append(", ");
-                       }
-                       builder.append(escape(item));
-               }
-
-               return builder.toString();
-       }
-
-       /**
-        * Return a {@link String} representation of the given list of values.
-        * <p>
-        * NULL will be assimilated to an empty {@link String} if later non-null
-        * values exist, or just ignored if not.
-        * <p>
-        * Example:
-        * <ul>
-        * <li><tt>1</tt>,<tt>NULL</tt>, <tt>3</tt> will become <tt>1</tt>,
-        * <tt>""</tt>, <tt>3</tt></li>
-        * <li><tt>1</tt>,<tt>NULL</tt>, <tt>NULL</tt> will become <tt>1</tt></li>
-        * <li><tt>NULL</tt>, <tt>NULL</tt>, <tt>NULL</tt> will become an empty list
-        * </li>
-        * </ul>
-        * 
-        * @param list
-        *            the input value
-        * @param value
-        *            the value to insert
-        * @param item
-        *            the position to insert it at
-        * 
-        * @return the raw {@link String} value that correspond to it
-        */
-       static public String fromList(List<String> list, String value, int item) {
-               if (list == null) {
-                       list = new ArrayList<String>();
-               }
-
-               while (item >= list.size()) {
-                       list.add(null);
-               }
-               list.set(item, value);
-
-               return fromList(list);
-       }
-
-       /**
-        * Return a {@link String} representation of the given list of values.
-        * <p>
-        * NULL will be assimilated to an empty {@link String} if later non-null
-        * values exist, or just ignored if not.
-        * <p>
-        * Example:
-        * <ul>
-        * <li><tt>1</tt>,<tt>NULL</tt>, <tt>3</tt> will become <tt>1</tt>,
-        * <tt>""</tt>, <tt>3</tt></li>
-        * <li><tt>1</tt>,<tt>NULL</tt>, <tt>NULL</tt> will become <tt>1</tt></li>
-        * <li><tt>NULL</tt>, <tt>NULL</tt>, <tt>NULL</tt> will become an empty list
-        * </li>
-        * </ul>
-        * 
-        * @param list
-        *            the input value
-        * @param value
-        *            the value to insert
-        * @param item
-        *            the position to insert it at
-        * 
-        * @return the raw {@link String} value that correspond to it
-        */
-       static public String fromList(String list, String value, int item) {
-               return fromList(parseList(list, -1), value, item);
-       }
-
-       /**
-        * Escape the given value for list formating (no carets, no NEWLINES...).
-        * <p>
-        * You can unescape it with {@link BundleHelper#unescape(String)}
-        * 
-        * @param value
-        *            the value to escape
-        * 
-        * @return an escaped value that can unquoted by the reverse operation
-        *         {@link BundleHelper#unescape(String)}
-        */
-       static public String escape(String value) {
-               return '"' + value//
-                               .replace("^", "^^") //
-                               .replace("\"", "^\"") //
-                               .replace("\n", "^\n") //
-                               .replace("\r", "^\r") //
-               + '"';
-       }
-
-       /**
-        * Unescape the given value for list formating (change ^n into NEWLINE and
-        * so on).
-        * <p>
-        * You can escape it with {@link BundleHelper#escape(String)}
-        * 
-        * @param value
-        *            the value to escape
-        * 
-        * @return an unescaped value that can reverted by the reverse operation
-        *         {@link BundleHelper#escape(String)}, or NULL if it was badly
-        *         formated
-        */
-       static public String unescape(String value) {
-               if (value.length() < 2 || !value.startsWith("\"")
-                               || !value.endsWith("\"")) {
-                       // Bad format
-                       return null;
-               }
-
-               value = value.substring(1, value.length() - 1);
-
-               boolean prevIsBackslash = false;
-               StringBuilder builder = new StringBuilder();
-               for (char car : value.toCharArray()) {
-                       if (prevIsBackslash) {
-                               switch (car) {
-                               case 'n':
-                               case 'N':
-                                       builder.append('\n');
-                                       break;
-                               case 'r':
-                               case 'R':
-                                       builder.append('\r');
-                                       break;
-                               default: // includes ^ and "
-                                       builder.append(car);
-                                       break;
-                               }
-                               prevIsBackslash = false;
-                       } else {
-                               if (car == '^') {
-                                       prevIsBackslash = true;
-                               } else {
-                                       builder.append(car);
-                               }
-                       }
-               }
-
-               if (prevIsBackslash) {
-                       // Bad format
-                       return null;
-               }
-
-               return builder.toString();
-       }
-
-       /**
-        * Retrieve the specific item in the given value, assuming it is an array.
-        * 
-        * @param value
-        *            the value to look into
-        * @param item
-        *            the item number to get for an array of values, or -1 for
-        *            non-arrays (in that case, simply return the value as-is)
-        * 
-        * @return the value as-is for non arrays, the item <tt>item</tt> if found,
-        *         NULL if not
-        */
-       static private String getItem(String value, int item) {
-               if (item >= 0) {
-                       value = null;
-                       List<String> values = parseList(value, -1);
-                       if (values != null && item < values.size()) {
-                               value = values.get(item);
-                       }
-               }
-
-               return value;
-       }
-}
diff --git a/src/be/nikiroo/utils/resources/Bundles.java b/src/be/nikiroo/utils/resources/Bundles.java
deleted file mode 100644 (file)
index ad7b99d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-package be.nikiroo.utils.resources;
-
-import java.util.ResourceBundle;
-
-/**
- * This class help you get UTF-8 bundles for this application.
- * 
- * @author niki
- */
-public class Bundles {
-       /**
-        * The configuration directory where we try to get the <tt>.properties</tt>
-        * in priority, or NULL to get the information from the compiled resources.
-        */
-       static private String confDir = null;
-
-       /**
-        * Set the primary configuration directory to look for <tt>.properties</tt>
-        * files in.
-        * 
-        * All {@link ResourceBundle}s returned by this class after that point will
-        * respect this new directory.
-        * 
-        * @param confDir
-        *            the new directory
-        */
-       static public void setDirectory(String confDir) {
-               Bundles.confDir = confDir;
-       }
-
-       /**
-        * Get the primary configuration directory to look for <tt>.properties</tt>
-        * files in.
-        * 
-        * @return the directory
-        */
-       static public String getDirectory() {
-               return Bundles.confDir;
-       }
-}
diff --git a/src/be/nikiroo/utils/resources/FixedResourceBundleControl.java b/src/be/nikiroo/utils/resources/FixedResourceBundleControl.java
deleted file mode 100644 (file)
index b53da9d..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-package be.nikiroo.utils.resources;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Locale;
-import java.util.PropertyResourceBundle;
-import java.util.ResourceBundle;
-import java.util.ResourceBundle.Control;
-
-/**
- * Fixed ResourceBundle.Control class. It will use UTF-8 for the files to load.
- * 
- * Also support an option to first check into the given path before looking into
- * the resources.
- * 
- * @author niki
- * 
- */
-class FixedResourceBundleControl extends Control {
-       @Override
-       public ResourceBundle newBundle(String baseName, Locale locale,
-                       String format, ClassLoader loader, boolean reload)
-                       throws IllegalAccessException, InstantiationException, IOException {
-               // The below is a copy of the default implementation.
-               String bundleName = toBundleName(baseName, locale);
-               String resourceName = toResourceName(bundleName, "properties");
-
-               ResourceBundle bundle = null;
-               InputStream stream = null;
-               if (reload) {
-                       URL url = loader.getResource(resourceName);
-                       if (url != null) {
-                               URLConnection connection = url.openConnection();
-                               if (connection != null) {
-                                       connection.setUseCaches(false);
-                                       stream = connection.getInputStream();
-                               }
-                       }
-               } else {
-                       stream = loader.getResourceAsStream(resourceName);
-               }
-
-               if (stream != null) {
-                       try {
-                               // This line is changed to make it to read properties files
-                               // as UTF-8.
-                               // How can someone use an archaic encoding such as ISO 8859-1 by
-                               // *DEFAULT* is beyond me...
-                               bundle = new PropertyResourceBundle(new InputStreamReader(
-                                               stream, "UTF-8"));
-                       } finally {
-                               stream.close();
-                       }
-               }
-               return bundle;
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/resources/Meta.java b/src/be/nikiroo/utils/resources/Meta.java
deleted file mode 100644 (file)
index fb4d491..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-package be.nikiroo.utils.resources;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation used to give some information about the translation keys, so the
- * translation .properties file can be created programmatically.
- * 
- * @author niki
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.FIELD)
-public @interface Meta {
-       /**
-        * The format of an item (the values it is expected to be of).
-        * <p>
-        * Note that the INI file can contain arbitrary data, but it is expected to
-        * be valid.
-        * 
-        * @author niki
-        */
-       public enum Format {
-               /** An integer value, can be negative. */
-               INT,
-               /** true or false. */
-               BOOLEAN,
-               /** Any text String. */
-               STRING,
-               /** A password field. */
-               PASSWORD,
-               /** A colour (either by name or #rrggbb or #aarrggbb). */
-               COLOR,
-               /** A locale code (e.g., fr-BE, en-GB, es...). */
-               LOCALE,
-               /** A path to a file. */
-               FILE,
-               /** A path to a directory. */
-               DIRECTORY,
-               /** A fixed list of values (see {@link Meta#list()} for the values). */
-               FIXED_LIST,
-               /**
-                * A fixed list of values (see {@link Meta#list()} for the values) OR a
-                * custom String value (basically, a {@link Format#FIXED_LIST} with an
-                * option to enter a not accounted for value).
-                */
-               COMBO_LIST,
-       }
-
-       /**
-        * A description for this item: what it is or does, how to explain that item
-        * to the user including what can be used here (i.e., %s = file name, %d =
-        * file size...).
-        * <p>
-        * For group, the first line ('\\n'-separated) will be used as a title while
-        * the rest will be the description.
-        * 
-        * @return what it is
-        */
-       String description() default "";
-       
-       /**
-        * This item should be hidden from the user (she will still be able to
-        * modify it if she opens the file manually).
-        * <p>
-        * Defaults to FALSE (visible).
-        * 
-        * @return TRUE if it should stay hidden
-        */
-       boolean hidden() default false;
-
-       /**
-        * This item is only used as a group, not as an option.
-        * <p>
-        * For instance, you could have LANGUAGE_CODE as a group for which you won't
-        * use the value in the program, and LANGUAGE_CODE_FR, LANGUAGE_CODE_EN
-        * inside for which the value must be set.
-        * 
-        * @return TRUE if it is a group
-        */
-       boolean group() default false;
-
-       /**
-        * What format should/must this key be in.
-        * 
-        * @return the format it is in
-        */
-       Format format() default Format.STRING;
-
-       /**
-        * The list of fixed values this item can be (either for
-        * {@link Format#FIXED_LIST} or {@link Format#COMBO_LIST}).
-        * 
-        * @return the list of values
-        */
-       String[] list() default {};
-
-       /**
-        * This item can be left unspecified.
-        * 
-        * @return TRUE if it can
-        */
-       boolean nullable() default true;
-
-       /**
-        * The default value of this item.
-        * 
-        * @return the value
-        */
-       String def() default "";
-
-       /**
-        * This item is a comma-separated list of values instead of a single value.
-        * <p>
-        * The list items are separated by a comma, each surrounded by
-        * double-quotes, with backslashes and double-quotes escaped by a backslash.
-        * <p>
-        * Example: <tt>"un", "deux"</tt>
-        * 
-        * @return TRUE if it is
-        */
-       boolean array() default false;
-
-       /**
-        * @deprecated add the info into the description, as only the description
-        *             will be translated.
-        */
-       @Deprecated
-       String info() default "";
-}
diff --git a/src/be/nikiroo/utils/resources/MetaInfo.java b/src/be/nikiroo/utils/resources/MetaInfo.java
deleted file mode 100644 (file)
index 70c6c43..0000000
+++ /dev/null
@@ -1,770 +0,0 @@
-package be.nikiroo.utils.resources;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import be.nikiroo.utils.resources.Meta.Format;
-
-/**
- * A graphical item that reflect a configuration option from the given
- * {@link Bundle}.
- * 
- * @author niki
- * 
- * @param <E>
- *            the type of {@link Bundle} to edit
- */
-public class MetaInfo<E extends Enum<E>> implements Iterable<MetaInfo<E>> {
-       private final Bundle<E> bundle;
-       private final E id;
-
-       private Meta meta;
-       private List<MetaInfo<E>> children = new ArrayList<MetaInfo<E>>();
-
-       private String value;
-       private List<Runnable> reloadedListeners = new ArrayList<Runnable>();
-       private List<Runnable> saveListeners = new ArrayList<Runnable>();
-
-       private String name;
-       private boolean hidden;
-       private String description;
-
-       private boolean dirty;
-
-       /**
-        * Create a new {@link MetaInfo} from a value (without children).
-        * <p>
-        * For instance, you can call
-        * <tt>new MetaInfo(Config.class, configBundle, Config.MY_VALUE)</tt>.
-        * 
-        * @param type
-        *            the type of enum the value is
-        * @param bundle
-        *            the bundle this value belongs to
-        * @param id
-        *            the value itself
-        */
-       public MetaInfo(Class<E> type, Bundle<E> bundle, E id) {
-               this.bundle = bundle;
-               this.id = id;
-
-               try {
-                       this.meta = type.getDeclaredField(id.name()).getAnnotation(
-                                       Meta.class);
-               } catch (NoSuchFieldException e) {
-               } catch (SecurityException e) {
-               }
-
-               // We consider that if a description bundle is used, everything is in it
-
-               String description = null;
-               if (bundle.getDescriptionBundle() != null) {
-                       description = bundle.getDescriptionBundle().getString(id);
-                       if (description != null && description.trim().isEmpty()) {
-                               description = null;
-                       }
-               }
-               if (description == null) {
-                       description = meta.description();
-                       if (description == null) {
-                               description = "";
-                       }
-               }
-
-               String name = idToName(id, null);
-
-               // Special rules for groups:
-               if (meta.group()) {
-                       String groupName = description.split("\n")[0];
-                       description = description.substring(groupName.length()).trim();
-                       if (!groupName.isEmpty()) {
-                               name = groupName;
-                       }
-               }
-
-               if (meta.def() != null && !meta.def().isEmpty()) {
-                       if (!description.isEmpty()) {
-                               description += "\n\n";
-                       }
-                       description += "(Default value: " + meta.def() + ")";
-               }
-
-               this.name = name;
-               this.hidden = meta.hidden();
-               this.description = description;
-
-               reload();
-       }
-
-       /**
-        * For normal items, this is the name of this item, deduced from its ID (or
-        * in other words, it is the ID but presented in a displayable form).
-        * <p>
-        * For group items, this is the first line of the description if it is not
-        * empty (else, it is the ID in the same way as normal items).
-        * <p>
-        * Never NULL.
-        * 
-        * 
-        * @return the name, never NULL
-        */
-       public String getName() {
-               return name;
-       }
-       
-       /**
-        * This item should be hidden from the user (she will still be able to
-        * modify it if she opens the file manually).
-        * 
-        * @return TRUE if it should stay hidden
-        */
-       public boolean isHidden() {
-               return hidden;
-       }
-
-       /**
-        * A description for this item: what it is or does, how to explain that item
-        * to the user including what can be used here (i.e., %s = file name, %d =
-        * file size...).
-        * <p>
-        * For group, the first line ('\\n'-separated) will be used as a title while
-        * the rest will be the description.
-        * <p>
-        * If a default value is known, it will be specified here, too.
-        * <p>
-        * Never NULL.
-        * 
-        * @return the description, not NULL
-        */
-       public String getDescription() {
-               return description;
-       }
-
-       /**
-        * The format this item is supposed to follow
-        * 
-        * @return the format
-        */
-       public Format getFormat() {
-               return meta.format();
-       }
-
-       /**
-        * The allowed list of values that a {@link Format#FIXED_LIST} item is
-        * allowed to be, or a list of suggestions for {@link Format#COMBO_LIST}
-        * items. Also works for {@link Format#LOCALE}.
-        * <p>
-        * Will always allow an empty string in addition to the rest.
-        * 
-        * @return the list of values
-        */
-       public String[] getAllowedValues() {
-               String[] list = meta.list();
-
-               String[] withEmpty = new String[list.length + 1];
-               withEmpty[0] = "";
-               for (int i = 0; i < list.length; i++) {
-                       withEmpty[i + 1] = list[i];
-               }
-
-               return withEmpty;
-       }
-
-       /**
-        * Return all the languages known by the program for this bundle.
-        * <p>
-        * This only works for {@link TransBundle}, and will return an empty list if
-        * this is not a {@link TransBundle}.
-        * 
-        * @return the known language codes
-        */
-       public List<String> getKnownLanguages() {
-               if (bundle instanceof TransBundle) {
-                       return ((TransBundle<E>) bundle).getKnownLanguages();
-               }
-
-               return new ArrayList<String>();
-       }
-
-       /**
-        * This item is a comma-separated list of values instead of a single value.
-        * <p>
-        * The list items are separated by a comma, each surrounded by
-        * double-quotes, with backslashes and double-quotes escaped by a backslash.
-        * <p>
-        * Example: <tt>"un", "deux"</tt>
-        * 
-        * @return TRUE if it is
-        */
-       public boolean isArray() {
-               return meta.array();
-       }
-
-       /**
-        * A manual flag to specify if the data has been changed or not, which can
-        * be used by {@link MetaInfo#save(boolean)}.
-        * 
-        * @return TRUE if it is dirty (if it has changed)
-        */
-       public boolean isDirty() {
-               return dirty;
-       }
-
-       /**
-        * A manual flag to specify that the data has been changed, which can be
-        * used by {@link MetaInfo#save(boolean)}.
-        */
-       public void setDirty() {
-               this.dirty = true;
-       }
-
-       /**
-        * The number of items in this item if it {@link MetaInfo#isArray()}, or -1
-        * if not.
-        * 
-        * @param useDefaultIfEmpty
-        *            check the size of the default list instead if the list is
-        *            empty
-        * 
-        * @return -1 or the number of items
-        */
-       public int getListSize(boolean useDefaultIfEmpty) {
-               if (!isArray()) {
-                       return -1;
-               }
-
-               return BundleHelper.getListSize(getString(-1, useDefaultIfEmpty));
-       }
-
-       /**
-        * This item is only used as a group, not as an option.
-        * <p>
-        * For instance, you could have LANGUAGE_CODE as a group for which you won't
-        * use the value in the program, and LANGUAGE_CODE_FR, LANGUAGE_CODE_EN
-        * inside for which the value must be set.
-        * 
-        * @return TRUE if it is a group
-        */
-       public boolean isGroup() {
-               return meta.group();
-       }
-
-       /**
-        * The value stored by this item, as a {@link String}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * @param useDefaultIfEmpty
-        *            use the default value instead of NULL if the setting is not
-        *            set
-        * 
-        * @return the value
-        */
-       public String getString(int item, boolean useDefaultIfEmpty) {
-               if (isArray() && item >= 0) {
-                       List<String> values = BundleHelper.parseList(value, -1);
-                       if (values != null && item < values.size()) {
-                               return values.get(item);
-                       }
-
-                       if (useDefaultIfEmpty) {
-                               return getDefaultString(item);
-                       }
-
-                       return null;
-               }
-
-               if (value == null && useDefaultIfEmpty) {
-                       return getDefaultString(item);
-               }
-
-               return value;
-       }
-
-       /**
-        * The default value of this item, as a {@link String}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the default value
-        */
-       public String getDefaultString(int item) {
-               if (isArray() && item >= 0) {
-                       List<String> values = BundleHelper.parseList(meta.def(), item);
-                       if (values != null && item < values.size()) {
-                               return values.get(item);
-                       }
-
-                       return null;
-               }
-
-               return meta.def();
-       }
-
-       /**
-        * The value stored by this item, as a {@link Boolean}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * @param useDefaultIfEmpty
-        *            use the default value instead of NULL if the setting is not
-        *            set
-        * 
-        * @return the value
-        */
-       public Boolean getBoolean(int item, boolean useDefaultIfEmpty) {
-               return BundleHelper
-                               .parseBoolean(getString(item, useDefaultIfEmpty), -1);
-       }
-
-       /**
-        * The default value of this item, as a {@link Boolean}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the default value
-        */
-       public Boolean getDefaultBoolean(int item) {
-               return BundleHelper.parseBoolean(getDefaultString(item), -1);
-       }
-
-       /**
-        * The value stored by this item, as a {@link Character}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * @param useDefaultIfEmpty
-        *            use the default value instead of NULL if the setting is not
-        *            set
-        * 
-        * @return the value
-        */
-       public Character getCharacter(int item, boolean useDefaultIfEmpty) {
-               return BundleHelper.parseCharacter(getString(item, useDefaultIfEmpty),
-                               -1);
-       }
-
-       /**
-        * The default value of this item, as a {@link Character}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the default value
-        */
-       public Character getDefaultCharacter(int item) {
-               return BundleHelper.parseCharacter(getDefaultString(item), -1);
-       }
-
-       /**
-        * The value stored by this item, as an {@link Integer}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * @param useDefaultIfEmpty
-        *            use the default value instead of NULL if the setting is not
-        *            set
-        * 
-        * @return the value
-        */
-       public Integer getInteger(int item, boolean useDefaultIfEmpty) {
-               return BundleHelper
-                               .parseInteger(getString(item, useDefaultIfEmpty), -1);
-       }
-
-       /**
-        * The default value of this item, as an {@link Integer}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the default value
-        */
-       public Integer getDefaultInteger(int item) {
-               return BundleHelper.parseInteger(getDefaultString(item), -1);
-       }
-
-       /**
-        * The value stored by this item, as a colour (represented here as an
-        * {@link Integer}) if it represents a colour, or NULL if it doesn't.
-        * <p>
-        * The returned colour value is an ARGB value.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * @param useDefaultIfEmpty
-        *            use the default value instead of NULL if the setting is not
-        *            set
-        * 
-        * @return the value
-        */
-       public Integer getColor(int item, boolean useDefaultIfEmpty) {
-               return BundleHelper.parseColor(getString(item, useDefaultIfEmpty), -1);
-       }
-
-       /**
-        * The default value stored by this item, as a colour (represented here as
-        * an {@link Integer}) if it represents a colour, or NULL if it doesn't.
-        * <p>
-        * The returned colour value is an ARGB value.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the value
-        */
-       public Integer getDefaultColor(int item) {
-               return BundleHelper.parseColor(getDefaultString(item), -1);
-       }
-
-       /**
-        * A {@link String} representation of the list of values.
-        * <p>
-        * The list of values is comma-separated and each value is surrounded by
-        * double-quotes; backslashes and double-quotes are escaped by a backslash.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * @param useDefaultIfEmpty
-        *            use the default value instead of NULL if the setting is not
-        *            set
-        * 
-        * @return the value
-        */
-       public List<String> getList(int item, boolean useDefaultIfEmpty) {
-               return BundleHelper.parseList(getString(item, useDefaultIfEmpty), -1);
-       }
-
-       /**
-        * A {@link String} representation of the default list of values.
-        * <p>
-        * The list of values is comma-separated and each value is surrounded by
-        * double-quotes; backslashes and double-quotes are escaped by a backslash.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the value
-        */
-       public List<String> getDefaultList(int item) {
-               return BundleHelper.parseList(getDefaultString(item), -1);
-       }
-
-       /**
-        * The value stored by this item, as a {@link String}.
-        * 
-        * @param value
-        *            the new value
-        * @param item
-        *            the item number to set for an array of values, or -1 to set
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       public void setString(String value, int item) {
-               if (isArray() && item >= 0) {
-                       this.value = BundleHelper.fromList(this.value, value, item);
-               } else {
-                       this.value = value;
-               }
-       }
-
-       /**
-        * The value stored by this item, as a {@link Boolean}.
-        * 
-        * @param value
-        *            the new value
-        * @param item
-        *            the item number to set for an array of values, or -1 to set
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       public void setBoolean(boolean value, int item) {
-               setString(BundleHelper.fromBoolean(value), item);
-       }
-
-       /**
-        * The value stored by this item, as a {@link Character}.
-        * 
-        * @param value
-        *            the new value
-        * @param item
-        *            the item number to set for an array of values, or -1 to set
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       public void setCharacter(char value, int item) {
-               setString(BundleHelper.fromCharacter(value), item);
-       }
-
-       /**
-        * The value stored by this item, as an {@link Integer}.
-        * 
-        * @param value
-        *            the new value
-        * @param item
-        *            the item number to set for an array of values, or -1 to set
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       public void setInteger(int value, int item) {
-               setString(BundleHelper.fromInteger(value), item);
-       }
-
-       /**
-        * The value stored by this item, as a colour (represented here as an
-        * {@link Integer}) if it represents a colour, or NULL if it doesn't.
-        * <p>
-        * The colour value is an ARGB value.
-        * 
-        * @param value
-        *            the value
-        * @param item
-        *            the item number to set for an array of values, or -1 to set
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       public void setColor(int value, int item) {
-               setString(BundleHelper.fromColor(value), item);
-       }
-
-       /**
-        * A {@link String} representation of the default list of values.
-        * <p>
-        * The list of values is comma-separated and each value is surrounded by
-        * double-quotes; backslashes and double-quotes are escaped by a backslash.
-        * 
-        * @param value
-        *            the {@link String} representation
-        * @param item
-        *            the item number to set for an array of values, or -1 to set
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       public void setList(List<String> value, int item) {
-               setString(BundleHelper.fromList(value), item);
-       }
-
-       /**
-        * Reload the value from the {@link Bundle}, so the last value that was
-        * saved will be used.
-        */
-       public void reload() {
-               if (bundle.isSet(id, false)) {
-                       value = bundle.getString(id);
-               } else {
-                       value = null;
-               }
-
-               // Copy the list so we can create new listener in a listener
-               for (Runnable listener : new ArrayList<Runnable>(reloadedListeners)) {
-                       try {
-                               listener.run();
-                       } catch (Exception e) {
-                               e.printStackTrace();
-                       }
-               }
-       }
-
-       /**
-        * Add a listener that will be called <b>after</b> a reload operation.
-        * <p>
-        * You could use it to refresh the UI for instance.
-        * 
-        * @param listener
-        *            the listener
-        */
-       public void addReloadedListener(Runnable listener) {
-               reloadedListeners.add(listener);
-       }
-
-       /**
-        * Save the current value to the {@link Bundle}.
-        * <p>
-        * Note that listeners will be called <b>before</b> the dirty check and
-        * <b>before</b> saving the value.
-        * 
-        * @param onlyIfDirty
-        *            only save the data if the dirty flag is set (will reset the
-        *            dirty flag)
-        */
-       public void save(boolean onlyIfDirty) {
-               // Copy the list so we can create new listener in a listener
-               for (Runnable listener : new ArrayList<Runnable>(saveListeners)) {
-                       try {
-                               listener.run();
-                       } catch (Exception e) {
-                               e.printStackTrace();
-                       }
-               }
-
-               if (!onlyIfDirty || isDirty()) {
-                       bundle.setString(id, value);
-               }
-       }
-
-       /**
-        * Add a listener that will be called <b>before</b> a save operation.
-        * <p>
-        * You could use it to make some modification to the stored value before it
-        * is saved.
-        * 
-        * @param listener
-        *            the listener
-        */
-       public void addSaveListener(Runnable listener) {
-               saveListeners.add(listener);
-       }
-
-       /**
-        * The sub-items if any (if no sub-items, will return an empty list).
-        * <p>
-        * Sub-items are declared when a {@link Meta} has an ID that starts with the
-        * ID of a {@link Meta#group()} {@link MetaInfo}.
-        * <p>
-        * For instance:
-        * <ul>
-        * <li>{@link Meta} <tt>MY_PREFIX</tt> is a {@link Meta#group()}</li>
-        * <li>{@link Meta} <tt>MY_PREFIX_DESCRIPTION</tt> is another {@link Meta}</li>
-        * <li><tt>MY_PREFIX_DESCRIPTION</tt> will be a child of <tt>MY_PREFIX</tt></li>
-        * </ul>
-        * 
-        * @return the sub-items if any
-        */
-       public List<MetaInfo<E>> getChildren() {
-               return children;
-       }
-
-       /**
-        * The number of sub-items, if any.
-        * 
-        * @return the number or 0
-        */
-       public int size() {
-               return children.size();
-       }
-
-       @Override
-       public Iterator<MetaInfo<E>> iterator() {
-               return children.iterator();
-       }
-
-       /**
-        * Create a list of {@link MetaInfo}, one for each of the item in the given
-        * {@link Bundle}.
-        * 
-        * @param <E>
-        *            the type of {@link Bundle} to edit
-        * @param type
-        *            a class instance of the item type to work on
-        * @param bundle
-        *            the {@link Bundle} to sort through
-        * 
-        * @return the list
-        */
-       static public <E extends Enum<E>> List<MetaInfo<E>> getItems(Class<E> type,
-                       Bundle<E> bundle) {
-               List<MetaInfo<E>> list = new ArrayList<MetaInfo<E>>();
-               List<MetaInfo<E>> shadow = new ArrayList<MetaInfo<E>>();
-               for (E id : type.getEnumConstants()) {
-                       MetaInfo<E> info = new MetaInfo<E>(type, bundle, id);
-                       if (!info.hidden) {
-                               list.add(info);
-                               shadow.add(info);
-                       }
-               }
-
-               for (int i = 0; i < list.size(); i++) {
-                       MetaInfo<E> info = list.get(i);
-
-                       MetaInfo<E> parent = findParent(info, shadow);
-                       if (parent != null) {
-                               list.remove(i--);
-                               parent.children.add(info);
-                               info.name = idToName(info.id, parent.id);
-                       }
-               }
-
-               return list;
-       }
-
-       /**
-        * Find the longest parent of the given {@link MetaInfo}, which means:
-        * <ul>
-        * <li>the parent is a {@link Meta#group()}</li>
-        * <li>the parent Id is a substring of the Id of the given {@link MetaInfo}</li>
-        * <li>there is no other parent sharing a substring for this
-        * {@link MetaInfo} with a longer Id</li>
-        * </ul>
-        * 
-        * @param <E>
-        *            the kind of enum
-        * @param info
-        *            the info to look for a parent for
-        * @param candidates
-        *            the list of potential parents
-        * 
-        * @return the longest parent or NULL if no parent is found
-        */
-       static private <E extends Enum<E>> MetaInfo<E> findParent(MetaInfo<E> info,
-                       List<MetaInfo<E>> candidates) {
-               String id = info.id.toString();
-               MetaInfo<E> group = null;
-               for (MetaInfo<E> pcandidate : candidates) {
-                       if (pcandidate.isGroup()) {
-                               String candidateId = pcandidate.id.toString();
-                               if (!id.equals(candidateId) && id.startsWith(candidateId)) {
-                                       if (group == null
-                                                       || group.id.toString().length() < candidateId
-                                                                       .length()) {
-                                               group = pcandidate;
-                                       }
-                               }
-                       }
-               }
-
-               return group;
-       }
-
-       static private <E extends Enum<E>> String idToName(E id, E prefix) {
-               String name = id.toString();
-               if (prefix != null && name.startsWith(prefix.toString())) {
-                       name = name.substring(prefix.toString().length());
-               }
-
-               if (name.length() > 0) {
-                       name = name.substring(0, 1).toUpperCase()
-                                       + name.substring(1).toLowerCase();
-               }
-
-               name = name.replace("_", " ");
-
-               return name.trim();
-       }
-}
diff --git a/src/be/nikiroo/utils/resources/TransBundle.java b/src/be/nikiroo/utils/resources/TransBundle.java
deleted file mode 100644 (file)
index 7b2edb1..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
-package be.nikiroo.utils.resources;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Pattern;
-
-/**
- * This class manages a translation-dedicated Bundle.
- * <p>
- * Two special cases are handled for the used enum:
- * <ul>
- * <li>NULL will always will return an empty {@link String}</li>
- * <li>DUMMY will return "[DUMMY]" (maybe with a suffix and/or "NOUTF")</li>
- * </ul>
- * 
- * @param <E>
- *            the enum to use to get values out of this class
- * 
- * @author niki
- */
-public class TransBundle<E extends Enum<E>> extends Bundle<E> {
-       private boolean utf = true;
-       private Locale locale;
-       private boolean defaultLocale = false;
-
-       /**
-        * Create a translation service with the default language.
-        * 
-        * @param type
-        *            a runtime instance of the class of E
-        * @param name
-        *            the name of the {@link Bundles}
-        */
-       public TransBundle(Class<E> type, Enum<?> name) {
-               this(type, name, (Locale) null);
-       }
-
-       /**
-        * Create a translation service for the given language (will fall back to
-        * the default one i not found).
-        * 
-        * @param type
-        *            a runtime instance of the class of E
-        * @param name
-        *            the name of the {@link Bundles}
-        * @param language
-        *            the language to use, can be NULL for default
-        */
-       public TransBundle(Class<E> type, Enum<?> name, String language) {
-               super(type, name, null);
-               setLocale(language);
-       }
-
-       /**
-        * Create a translation service for the given language (will fall back to
-        * the default one i not found).
-        * 
-        * @param type
-        *            a runtime instance of the class of E
-        * @param name
-        *            the name of the {@link Bundles}
-        * @param language
-        *            the language to use, can be NULL for default
-        */
-       public TransBundle(Class<E> type, Enum<?> name, Locale language) {
-               super(type, name, null);
-               setLocale(language);
-       }
-
-       /**
-        * Translate the given id into user text.
-        * 
-        * @param stringId
-        *            the ID to translate
-        * @param values
-        *            the values to insert instead of the place holders in the
-        *            translation
-        * 
-        * @return the translated text with the given value where required or NULL
-        *         if not found (not present in the resource file)
-        */
-       public String getString(E stringId, Object... values) {
-               return getStringX(stringId, "", values);
-       }
-
-       /**
-        * Translate the given id into user text.
-        * 
-        * @param stringId
-        *            the ID to translate
-        * @param values
-        *            the values to insert instead of the place holders in the
-        *            translation
-        * 
-        * @return the translated text with the given value where required or NULL
-        *         if not found (not present in the resource file)
-        */
-       public String getStringNOUTF(E stringId, Object... values) {
-               return getStringX(stringId, "NOUTF", values);
-       }
-
-       /**
-        * Translate the given id suffixed with the runtime value "_suffix" (that
-        * is, "_" and suffix) into user text.
-        * 
-        * @param stringId
-        *            the ID to translate
-        * @param values
-        *            the values to insert instead of the place holders in the
-        *            translation
-        * @param suffix
-        *            the runtime suffix
-        * 
-        * @return the translated text with the given value where required or NULL
-        *         if not found (not present in the resource file)
-        */
-       public String getStringX(E stringId, String suffix, Object... values) {
-               E id = stringId;
-               String result = "";
-
-               String key = id.name()
-                               + ((suffix == null || suffix.isEmpty()) ? "" : "_"
-                                               + suffix.toUpperCase());
-
-               if (!isUnicode()) {
-                       if (containsKey(key + "_NOUTF")) {
-                               key += "_NOUTF";
-                       }
-               }
-
-               if ("NULL".equals(id.name().toUpperCase())) {
-                       result = "";
-               } else if ("DUMMY".equals(id.name().toUpperCase())) {
-                       result = "[" + key.toLowerCase() + "]";
-               } else if (containsKey(key)) {
-                       result = getString(key, null);
-                       if (result == null) {
-                               result = getMetaDef(id.name());
-                       }
-               } else {
-                       result = null;
-               }
-
-               if (values != null && values.length > 0 && result != null) {
-                       return String.format(locale, result, values);
-               }
-
-               return result;
-       }
-
-       /**
-        * Check if unicode characters should be used.
-        * 
-        * @return TRUE to allow unicode
-        */
-       public boolean isUnicode() {
-               return utf;
-       }
-
-       /**
-        * Allow or disallow unicode characters in the program.
-        * 
-        * @param utf
-        *            TRUE to allow unuciode, FALSE to only allow ASCII characters
-        */
-       public void setUnicode(boolean utf) {
-               this.utf = utf;
-       }
-
-       /**
-        * Return all the languages known by the program for this bundle.
-        * 
-        * @return the known language codes
-        */
-       public List<String> getKnownLanguages() {
-               return getKnownLanguages(keyType);
-       }
-
-       /**
-        * The current language (which can be the default one, but NOT NULL).
-        * 
-        * @return the language, not NULL
-        */
-       public Locale getLocale() {
-               return locale;
-       }
-
-       /**
-        * The current language (which can be the default one, but NOT NULL).
-        * 
-        * @return the language, not NULL, in a display format (fr-BE, en-GB, es,
-        *         de...)
-        */
-       public String getLocaleString() {
-               String lang = locale.getLanguage();
-               String country = locale.getCountry();
-               if (country != null && !country.isEmpty()) {
-                       return lang + "-" + country;
-               }
-               return lang;
-       }
-
-       /**
-        * Initialise the translation mappings for the given language.
-        * 
-        * @param language
-        *            the language to initialise, in the form "en-GB" or "fr" for
-        *            instance
-        */
-       private void setLocale(String language) {
-               setLocale(getLocaleFor(language));
-       }
-
-       /**
-        * Initialise the translation mappings for the given language.
-        * 
-        * @param language
-        *            the language to initialise, or NULL for default
-        */
-       private void setLocale(Locale language) {
-               if (language != null) {
-                       defaultLocale = false;
-                       locale = language;
-               } else {
-                       defaultLocale = true;
-                       locale = Locale.getDefault();
-               }
-
-               setBundle(keyType, locale, false);
-       }
-
-       @Override
-       public void reload(boolean resetToDefault) {
-               setBundle(keyType, locale, resetToDefault);
-       }
-
-       @Override
-       public String getString(E id) {
-               return getString(id, (Object[]) null);
-       }
-
-       /**
-        * Create/update the .properties files for each supported language and for
-        * the default language.
-        * <p>
-        * Note: this method is <b>NOT</b> thread-safe.
-        * 
-        * @param path
-        *            the path where the .properties files are
-        * 
-        * @throws IOException
-        *             in case of IO errors
-        */
-       @Override
-       public void updateFile(String path) throws IOException {
-               String prev = locale.getLanguage();
-               Object status = takeSnapshot();
-
-               // default locale
-               setLocale((Locale) null);
-               if (prev.equals(Locale.getDefault().getLanguage())) {
-                       // restore snapshot if default locale = current locale
-                       restoreSnapshot(status);
-               }
-               super.updateFile(path);
-
-               for (String lang : getKnownLanguages()) {
-                       setLocale(lang);
-                       if (lang.equals(prev)) {
-                               restoreSnapshot(status);
-                       }
-                       super.updateFile(path);
-               }
-
-               setLocale(prev);
-               restoreSnapshot(status);
-       }
-
-       @Override
-       protected File getUpdateFile(String path) {
-               String code = locale.toString();
-               File file = null;
-               if (!defaultLocale && code.length() > 0) {
-                       file = new File(path, keyType.name() + "_" + code + ".properties");
-               } else {
-                       // Default properties file:
-                       file = new File(path, keyType.name() + ".properties");
-               }
-
-               return file;
-       }
-
-       @Override
-       protected void writeHeader(Writer writer) throws IOException {
-               String code = locale.toString();
-               String name = locale.getDisplayCountry(locale);
-
-               if (name.length() == 0) {
-                       name = locale.getDisplayLanguage(locale);
-               }
-
-               if (name.length() == 0) {
-                       name = "default";
-               }
-
-               if (code.length() > 0) {
-                       name = name + " (" + code + ")";
-               }
-
-               name = (name + " " + getBundleDisplayName()).trim();
-
-               writer.write("# " + name + " translation file (UTF-8)\n");
-               writer.write("# \n");
-               writer.write("# Note that any key can be doubled with a _NOUTF suffix\n");
-               writer.write("# to use when the NOUTF env variable is set to 1\n");
-               writer.write("# \n");
-               writer.write("# Also, the comments always refer to the key below them.\n");
-               writer.write("# \n");
-       }
-
-       @Override
-       protected void writeValue(Writer writer, E id) throws IOException {
-               super.writeValue(writer, id);
-
-               String name = id.name() + "_NOUTF";
-               if (containsKey(name)) {
-                       String value = getString(name, null);
-                       if (value == null) {
-                               value = getMetaDef(id.name());
-                       }
-                       boolean set = isSet(id, false);
-                       writeValue(writer, name, value, set);
-               }
-       }
-
-       /**
-        * Return the {@link Locale} representing the given language.
-        * 
-        * @param language
-        *            the language to initialise, in the form "en-GB" or "fr" for
-        *            instance
-        * 
-        * @return the corresponding {@link Locale} or NULL if it is not known
-        */
-       static private Locale getLocaleFor(String language) {
-               Locale locale;
-
-               if (language == null || language.trim().isEmpty()) {
-                       return null;
-               }
-
-               language = language.replaceAll("_", "-");
-               String lang = language;
-               String country = null;
-               if (language.contains("-")) {
-                       lang = language.split("-")[0];
-                       country = language.split("-")[1];
-               }
-
-               if (country != null)
-                       locale = new Locale(lang, country);
-               else
-                       locale = new Locale(lang);
-
-               return locale;
-       }
-
-       /**
-        * Return all the languages known by the program.
-        * 
-        * @param name
-        *            the enumeration on which we translate
-        * 
-        * @return the known language codes
-        */
-       static protected List<String> getKnownLanguages(Enum<?> name) {
-               List<String> resources = new LinkedList<String>();
-
-               String regex = ".*" + name.name() + "[_a-zA-Za]*\\.properties$";
-
-               for (String res : TransBundle_ResourceList.getResources(Pattern
-                               .compile(regex))) {
-                       String resource = res;
-                       int index = resource.lastIndexOf('/');
-                       if (index >= 0 && index < (resource.length() - 1))
-                               resource = resource.substring(index + 1);
-                       if (resource.startsWith(name.name())) {
-                               resource = resource.substring(0, resource.length()
-                                               - ".properties".length());
-                               resource = resource.substring(name.name().length());
-                               if (resource.startsWith("_")) {
-                                       resource = resource.substring(1);
-                                       resources.add(resource);
-                               }
-                       }
-               }
-
-               return resources;
-       }
-}
diff --git a/src/be/nikiroo/utils/resources/TransBundle_ResourceList.java b/src/be/nikiroo/utils/resources/TransBundle_ResourceList.java
deleted file mode 100644 (file)
index 9983b8b..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-package be.nikiroo.utils.resources;
-
-// code copied from from:
-//             http://forums.devx.com/showthread.php?t=153784,
-// via:
-//             http://stackoverflow.com/questions/3923129/get-a-list-of-resources-from-classpath-directory
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
-
-/**
- * list resources available from the classpath @ *
- */
-class TransBundle_ResourceList {
-
-       /**
-        * for all elements of java.class.path get a Collection of resources Pattern
-        * pattern = Pattern.compile(".*"); gets all resources
-        * 
-        * @param pattern
-        *            the pattern to match
-        * @return the resources in the order they are found
-        */
-       public static Collection<String> getResources(final Pattern pattern) {
-               final ArrayList<String> retval = new ArrayList<String>();
-               final String classPath = System.getProperty("java.class.path", ".");
-               final String[] classPathElements = classPath.split(System
-                               .getProperty("path.separator"));
-               for (final String element : classPathElements) {
-                       retval.addAll(getResources(element, pattern));
-               }
-
-               return retval;
-       }
-
-       private static Collection<String> getResources(final String element,
-                       final Pattern pattern) {
-               final ArrayList<String> retval = new ArrayList<String>();
-               final File file = new File(element);
-               if (file.isDirectory()) {
-                       retval.addAll(getResourcesFromDirectory(file, pattern));
-               } else {
-                       retval.addAll(getResourcesFromJarFile(file, pattern));
-               }
-
-               return retval;
-       }
-
-       private static Collection<String> getResourcesFromJarFile(final File file,
-                       final Pattern pattern) {
-               final ArrayList<String> retval = new ArrayList<String>();
-               ZipFile zf;
-               try {
-                       zf = new ZipFile(file);
-               } catch (final ZipException e) {
-                       throw new Error(e);
-               } catch (final IOException e) {
-                       throw new Error(e);
-               }
-               final Enumeration<? extends ZipEntry> e = zf.entries();
-               while (e.hasMoreElements()) {
-                       final ZipEntry ze = e.nextElement();
-                       final String fileName = ze.getName();
-                       final boolean accept = pattern.matcher(fileName).matches();
-                       if (accept) {
-                               retval.add(fileName);
-                       }
-               }
-               try {
-                       zf.close();
-               } catch (final IOException e1) {
-                       throw new Error(e1);
-               }
-
-               return retval;
-       }
-
-       private static Collection<String> getResourcesFromDirectory(
-                       final File directory, final Pattern pattern) {
-               List<String> acc = new ArrayList<String>();
-               List<File> dirs = new ArrayList<File>();
-               getResourcesFromDirectory(acc, dirs, directory, pattern);
-
-               List<String> rep = new ArrayList<String>();
-               for (String value : acc) {
-                       if (pattern.matcher(value).matches()) {
-                               rep.add(value);
-                       }
-               }
-
-               return rep;
-       }
-
-       private static void getResourcesFromDirectory(List<String> acc,
-                       List<File> dirs, final File directory, final Pattern pattern) {
-               final File[] fileList = directory.listFiles();
-               if (fileList != null) {
-                       for (final File file : fileList) {
-                               if (!dirs.contains(file)) {
-                                       try {
-                                               String key = file.getCanonicalPath();
-                                               if (!acc.contains(key)) {
-                                                       if (file.isDirectory()) {
-                                                               dirs.add(file);
-                                                               getResourcesFromDirectory(acc, dirs, file,
-                                                                               pattern);
-                                                       } else {
-                                                               acc.add(key);
-                                                       }
-                                               }
-                                       } catch (IOException e) {
-                                       }
-                               }
-                       }
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/resources/package-info.java b/src/be/nikiroo/utils/resources/package-info.java
deleted file mode 100644 (file)
index bda940b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * This package encloses the classes needed to use 
- * {@link be.nikiroo.utils.resources.Bundle}s
- * <p>
- * Those are basically a <tt>.properties</tt> resource linked to an enumeration
- * listing all the fields you can use. The classes can also be used to update
- * the linked <tt>.properties</tt> files (or export them, which is useful when
- * you work from a JAR file).
- * <p>
- * All those classes expect UTF-8 content only.
- * 
- * @author niki
- */
-package be.nikiroo.utils.resources;
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/serial/CustomSerializer.java b/src/be/nikiroo/utils/serial/CustomSerializer.java
deleted file mode 100644 (file)
index e58ccf2..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-package be.nikiroo.utils.serial;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import be.nikiroo.utils.streams.BufferedInputStream;
-import be.nikiroo.utils.streams.ReplaceInputStream;
-import be.nikiroo.utils.streams.ReplaceOutputStream;
-
-/**
- * A {@link CustomSerializer} supports and generates values in the form:
- * <ul>
- * <li><tt>custom^<i>TYPE</i>^<i>ENCODED_VALUE</i></tt></li>
- * </ul>
- * <p>
- * In this scheme, the values are:
- * <ul>
- * <li><tt>custom</tt>: a fixed keyword</li>
- * <li><tt>^</tt>: a fixed separator character (the
- * <tt><i>ENCODED_VALUE</i></tt> can still use it inside its content, though</li>
- * <li><tt><i>TYPE</i></tt>: the object type of this value</li>
- * <li><tt><i>ENCODED_VALUE</i></tt>: the custom encoded value</li>
- * </ul>
- * <p>
- * To create a new {@link CustomSerializer}, you are expected to implement the
- * abstract methods of this class. The rest should be taken care of bythe
- * system.
- * 
- * @author niki
- */
-public abstract class CustomSerializer {
-       /**
-        * Generate the custom <tt><i>ENCODED_VALUE</i></tt> from this
-        * <tt>value</tt>.
-        * <p>
-        * The <tt>value</tt> will always be of the supported type.
-        * 
-        * @param out
-        *            the {@link OutputStream} to write the value to
-        * @param value
-        *            the value to serialize
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       protected abstract void toStream(OutputStream out, Object value)
-                       throws IOException;
-
-       /**
-        * Regenerate the value from the custom <tt><i>ENCODED_VALUE</i></tt>.
-        * <p>
-        * The value in the {@link InputStream} <tt>in</tt> will always be of the
-        * supported type.
-        * 
-        * @param in
-        *            the {@link InputStream} containing the
-        *            <tt><i>ENCODED_VALUE</i></tt>
-        * 
-        * @return the regenerated object
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       protected abstract Object fromStream(InputStream in) throws IOException;
-
-       /**
-        * Return the supported type name.
-        * <p>
-        * It <b>must</b> be the name returned by {@link Object#getClass()
-        * #getCanonicalName()}.
-        * 
-        * @return the supported class name
-        */
-       protected abstract String getType();
-
-       /**
-        * Encode the object into the given {@link OutputStream}, i.e., generate the
-        * <tt><i>ENCODED_VALUE</i></tt> part.
-        * <p>
-        * Use whatever scheme you wish, the system shall ensure that the content is
-        * correctly encoded and that you will receive the same content at decode
-        * time.
-        * 
-        * @param out
-        *            the builder to append to
-        * @param value
-        *            the object to encode
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public void encode(OutputStream out, Object value) throws IOException {
-               ReplaceOutputStream replace = new ReplaceOutputStream(out, //
-                               new String[] { "\\", "\n" }, //
-                               new String[] { "\\\\", "\\n" });
-
-               try {
-                       SerialUtils.write(replace, "custom^");
-                       SerialUtils.write(replace, getType());
-                       SerialUtils.write(replace, "^");
-                       toStream(replace, value);
-               } finally {
-                       replace.close(false);
-               }
-       }
-
-       /**
-        * Decode the value back into the supported object type.
-        * <p>
-        * We do <b>not</b> expect the full content here but only:
-        * <ul>
-        * <li>ENCODED_VALUE
-        * <li>
-        * </ul>
-        * That is, we do not expect the "<tt>custom</tt>^<tt><i>TYPE</i></tt>^"
-        * part.
-        * 
-        * @param in
-        *            the encoded value
-        * 
-        * @return the object
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public Object decode(InputStream in) throws IOException {
-               ReplaceInputStream replace = new ReplaceInputStream(in, //
-                               new String[] { "\\\\", "\\n" }, //
-                               new String[] { "\\", "\n" });
-
-               try {
-                       return fromStream(replace);
-               } finally {
-                       replace.close(false);
-               }
-       }
-
-       public static boolean isCustom(BufferedInputStream in) throws IOException {
-               return in.startsWith("custom^");
-       }
-
-       public static String typeOf(String encodedValue) {
-               int pos1 = encodedValue.indexOf('^');
-               int pos2 = encodedValue.indexOf('^', pos1 + 1);
-               String type = encodedValue.substring(pos1 + 1, pos2);
-
-               return type;
-       }
-}
diff --git a/src/be/nikiroo/utils/serial/Exporter.java b/src/be/nikiroo/utils/serial/Exporter.java
deleted file mode 100644 (file)
index 2470bde..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-package be.nikiroo.utils.serial;
-
-import java.io.IOException;
-import java.io.NotSerializableException;
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A simple class to serialise objects to {@link String}.
- * <p>
- * This class does not support inner classes (it does support nested classes,
- * though).
- * 
- * @author niki
- */
-public class Exporter {
-       private Map<Integer, Object> map;
-       private OutputStream out;
-
-       /**
-        * Create a new {@link Exporter}.
-        * 
-        * @param out
-        *            export the data to this stream
-        */
-       public Exporter(OutputStream out) {
-               if (out == null) {
-                       throw new NullPointerException(
-                                       "Cannot create an be.nikiroo.utils.serials.Exporter that will export to NULL");
-               }
-
-               this.out = out;
-               map = new HashMap<Integer, Object>();
-       }
-
-       /**
-        * Serialise the given object and add it to the list.
-        * <p>
-        * <b>Important: </b>If the operation fails (with a
-        * {@link NotSerializableException}), the {@link Exporter} will be corrupted
-        * (will contain bad, most probably not importable data).
-        * 
-        * @param o
-        *            the object to serialise
-        * @return this (for easier appending of multiple values)
-        * 
-        * @throws NotSerializableException
-        *             if the object cannot be serialised (in this case, the
-        *             {@link Exporter} can contain bad, most probably not
-        *             importable data)
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public Exporter append(Object o) throws NotSerializableException,
-                       IOException {
-               SerialUtils.append(out, o, map);
-               return this;
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/serial/Importer.java b/src/be/nikiroo/utils/serial/Importer.java
deleted file mode 100644 (file)
index 81814df..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-package be.nikiroo.utils.serial;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.zip.GZIPInputStream;
-
-import be.nikiroo.utils.IOUtils;
-import be.nikiroo.utils.streams.Base64InputStream;
-import be.nikiroo.utils.streams.BufferedInputStream;
-import be.nikiroo.utils.streams.NextableInputStream;
-import be.nikiroo.utils.streams.NextableInputStreamStep;
-
-/**
- * A simple class that can accept the output of {@link Exporter} to recreate
- * objects as they were sent to said exporter.
- * <p>
- * This class requires the objects (and their potential enclosing objects) to
- * have an empty constructor, and does not support inner classes (it does
- * support nested classes, though).
- * 
- * @author niki
- */
-public class Importer {
-       private Boolean link;
-       private Object me;
-       private Importer child;
-       private Map<String, Object> map;
-
-       private String currentFieldName;
-
-       /**
-        * Create a new {@link Importer}.
-        */
-       public Importer() {
-               map = new HashMap<String, Object>();
-               map.put("NULL", null);
-       }
-
-       private Importer(Map<String, Object> map) {
-               this.map = map;
-       }
-
-       /**
-        * Read some data into this {@link Importer}: it can be the full serialised
-        * content, or a number of lines of it (any given line <b>MUST</b> be
-        * complete though) and accumulate it with the already present data.
-        * 
-        * @param in
-        *            the data to parse
-        * 
-        * @return itself so it can be chained
-        * 
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        * @throws IOException
-        *             if the content cannot be read (for instance, corrupt data)
-        * @throws NullPointerException
-        *             if the stream is empty
-        */
-       public Importer read(InputStream in) throws NoSuchFieldException,
-                       NoSuchMethodException, ClassNotFoundException, IOException,
-                       NullPointerException {
-
-               NextableInputStream stream = new NextableInputStream(in,
-                               new NextableInputStreamStep('\n'));
-
-               try {
-                       if (in == null) {
-                               throw new NullPointerException("InputStream is null");
-                       }
-
-                       boolean first = true;
-                       while (stream.next()) {
-                               if (stream.eof()) {
-                                       if (first) {
-                                               throw new NullPointerException(
-                                                               "InputStream empty, normal termination");
-                                       }
-                                       return this;
-                               }
-                               first = false;
-
-                               boolean zip = stream.startsWith("ZIP:");
-                               boolean b64 = stream.startsWith("B64:");
-
-                               if (zip || b64) {
-                                       stream.skip("XXX:".length());
-
-                                       InputStream decoded = stream.open();
-                                       if (zip) {
-                                               decoded = new GZIPInputStream(decoded);
-                                       }
-                                       decoded = new Base64InputStream(decoded, false);
-
-                                       try {
-                                               read(decoded);
-                                       } finally {
-                                               decoded.close();
-                                       }
-                               } else {
-                                       processLine(stream);
-                               }
-                       }
-               } finally {
-                       stream.close(false);
-               }
-
-               return this;
-       }
-
-       /**
-        * Read a single (whole) line of serialised data into this {@link Importer}
-        * and accumulate it with the already present data.
-        * 
-        * @param in
-        *            the line to parse
-        * 
-        * @return TRUE if we are just done with one object or sub-object
-        * 
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        * @throws IOException
-        *             if the content cannot be read (for instance, corrupt data)
-        */
-       private boolean processLine(BufferedInputStream in)
-                       throws NoSuchFieldException, NoSuchMethodException,
-                       ClassNotFoundException, IOException {
-
-               // Defer to latest child if any
-               if (child != null) {
-                       if (child.processLine(in)) {
-                               if (currentFieldName != null) {
-                                       setField(currentFieldName, child.getValue());
-                                       currentFieldName = null;
-                               }
-                               child = null;
-                       }
-
-                       return false;
-               }
-
-               // Start/Stop object
-               if (in.is("{")) { // START: new child if needed
-                       if (link != null) {
-                               child = new Importer(map);
-                       }
-                       in.end();
-                       return false;
-               } else if (in.is("}")) { // STOP: report self to parent
-                       in.end();
-                       return true;
-               }
-
-               // Custom objects
-               if (CustomSerializer.isCustom(in)) {
-                       // not a field value but a direct value
-                       me = SerialUtils.decode(in);
-                       return false;
-               }
-
-               // REF: (object)
-               if (in.startsWith("REF ")) { // REF: create/link self
-                       // here, line is REF type@999:xxx
-                       // xxx is optional
-
-                       NextableInputStream stream = new NextableInputStream(in,
-                                       new NextableInputStreamStep(':'));
-                       try {
-                               stream.next();
-
-                               stream.skip("REF ".length());
-                               String header = IOUtils.readSmallStream(stream);
-
-                               String[] tab = header.split("@");
-                               if (tab.length != 2) {
-                                       throw new IOException("Bad import header line: " + header);
-                               }
-                               String type = tab[0];
-                               String ref = tab[1];
-
-                               stream.nextAll();
-
-                               link = map.containsKey(ref);
-                               if (link) {
-                                       me = map.get(ref);
-                                       stream.end();
-                               } else {
-                                       if (stream.eof()) {
-                                               // construct
-                                               me = SerialUtils.createObject(type);
-                                       } else {
-                                               // direct value
-                                               me = SerialUtils.decode(stream);
-                                       }
-                                       map.put(ref, me);
-                               }
-                       } finally {
-                               stream.close(false);
-                       }
-
-                       return false;
-               }
-
-               if (SerialUtils.isDirectValue(in)) {
-                       // not a field value but a direct value
-                       me = SerialUtils.decode(in);
-                       return false;
-               }
-
-               if (in.startsWith("^")) {
-                       in.skip(1);
-
-                       NextableInputStream nameThenContent = new NextableInputStream(in,
-                                       new NextableInputStreamStep(':'));
-
-                       try {
-                               nameThenContent.next();
-                               String fieldName = IOUtils.readSmallStream(nameThenContent);
-
-                               if (nameThenContent.nextAll() && !nameThenContent.eof()) {
-                                       // field value is direct or custom
-                                       Object value = null;
-                                       value = SerialUtils.decode(nameThenContent);
-
-                                       // To support simple types directly:
-                                       if (me == null) {
-                                               me = value;
-                                       } else {
-                                               setField(fieldName, value);
-                                       }
-                               } else {
-                                       // field value is compound
-                                       currentFieldName = fieldName;
-                               }
-                       } finally {
-                               nameThenContent.close(false);
-                       }
-
-                       return false;
-               }
-
-               String line = IOUtils.readSmallStream(in);
-               throw new IOException("Line cannot be processed: <" + line + ">");
-       }
-
-       private void setField(String name, Object value)
-                       throws NoSuchFieldException {
-
-               try {
-                       Field field = me.getClass().getDeclaredField(name);
-
-                       field.setAccessible(true);
-                       field.set(me, value);
-               } catch (NoSuchFieldException e) {
-                       throw new NoSuchFieldException(String.format(
-                                       "Field \"%s\" was not found in object of type \"%s\".",
-                                       name, me.getClass().getCanonicalName()));
-               } catch (Exception e) {
-                       throw new NoSuchFieldException(String.format(
-                                       "Internal error when setting \"%s.%s\": %s", me.getClass()
-                                                       .getCanonicalName(), name, e.getMessage()));
-               }
-       }
-
-       /**
-        * Return the current deserialised value.
-        * 
-        * @return the current value
-        */
-       public Object getValue() {
-               return me;
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/serial/SerialUtils.java b/src/be/nikiroo/utils/serial/SerialUtils.java
deleted file mode 100644 (file)
index ad3b5d4..0000000
+++ /dev/null
@@ -1,733 +0,0 @@
-package be.nikiroo.utils.serial;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.NotSerializableException;
-import java.io.OutputStream;
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UnknownFormatConversionException;
-
-import be.nikiroo.utils.IOUtils;
-import be.nikiroo.utils.Image;
-import be.nikiroo.utils.StringUtils;
-import be.nikiroo.utils.streams.Base64InputStream;
-import be.nikiroo.utils.streams.Base64OutputStream;
-import be.nikiroo.utils.streams.BufferedInputStream;
-import be.nikiroo.utils.streams.NextableInputStream;
-import be.nikiroo.utils.streams.NextableInputStreamStep;
-
-/**
- * Small class to help with serialisation.
- * <p>
- * Note that we do not support inner classes (but we do support nested classes)
- * and all objects require an empty constructor to be deserialised.
- * <p>
- * It is possible to add support to custom types (both the encoder and the
- * decoder will require the custom classes) -- see {@link CustomSerializer}.
- * <p>
- * Default supported types are:
- * <ul>
- * <li>NULL (as a null value)</li>
- * <li>String</li>
- * <li>Boolean</li>
- * <li>Byte</li>
- * <li>Character</li>
- * <li>Short</li>
- * <li>Long</li>
- * <li>Float</li>
- * <li>Double</li>
- * <li>Integer</li>
- * <li>Enum (any enum whose name and value is known by the caller)</li>
- * <li>java.awt.image.BufferedImage (as a {@link CustomSerializer})</li>
- * <li>An array of the above (as a {@link CustomSerializer})</li>
- * <li>URL</li>
- * </ul>
- * 
- * @author niki
- */
-public class SerialUtils {
-       private static Map<String, CustomSerializer> customTypes;
-
-       static {
-               customTypes = new HashMap<String, CustomSerializer>();
-
-               // Array types:
-               customTypes.put("[]", new CustomSerializer() {
-                       @Override
-                       protected void toStream(OutputStream out, Object value)
-                                       throws IOException {
-
-                               String type = value.getClass().getCanonicalName();
-                               type = type.substring(0, type.length() - 2); // remove the []
-
-                               write(out, type);
-                               try {
-                                       for (int i = 0; true; i++) {
-                                               Object item = Array.get(value, i);
-
-                                               // encode it normally if direct value
-                                               write(out, "\r");
-                                               if (!SerialUtils.encode(out, item)) {
-                                                       try {
-                                                               write(out, "B64:");
-                                                               OutputStream out64 = new Base64OutputStream(
-                                                                               out, true);
-                                                               new Exporter(out64).append(item);
-                                                               out64.flush();
-                                                       } catch (NotSerializableException e) {
-                                                               throw new UnknownFormatConversionException(e
-                                                                               .getMessage());
-                                                       }
-                                               }
-                                       }
-                               } catch (ArrayIndexOutOfBoundsException e) {
-                                       // Done.
-                               }
-                       }
-
-                       @Override
-                       protected Object fromStream(InputStream in) throws IOException {
-                               NextableInputStream stream = new NextableInputStream(in,
-                                               new NextableInputStreamStep('\r'));
-
-                               try {
-                                       List<Object> list = new ArrayList<Object>();
-                                       stream.next();
-                                       String type = IOUtils.readSmallStream(stream);
-
-                                       while (stream.next()) {
-                                               Object value = new Importer().read(stream).getValue();
-                                               list.add(value);
-                                       }
-
-                                       Object array = Array.newInstance(
-                                                       SerialUtils.getClass(type), list.size());
-                                       for (int i = 0; i < list.size(); i++) {
-                                               Array.set(array, i, list.get(i));
-                                       }
-
-                                       return array;
-                               } catch (Exception e) {
-                                       if (e instanceof IOException) {
-                                               throw (IOException) e;
-                                       }
-                                       throw new IOException(e.getMessage());
-                               }
-                       }
-
-                       @Override
-                       protected String getType() {
-                               return "[]";
-                       }
-               });
-
-               // URL:
-               customTypes.put("java.net.URL", new CustomSerializer() {
-                       @Override
-                       protected void toStream(OutputStream out, Object value)
-                                       throws IOException {
-                               String val = "";
-                               if (value != null) {
-                                       val = ((URL) value).toString();
-                               }
-
-                               out.write(StringUtils.getBytes(val));
-                       }
-
-                       @Override
-                       protected Object fromStream(InputStream in) throws IOException {
-                               String val = IOUtils.readSmallStream(in);
-                               if (!val.isEmpty()) {
-                                       return new URL(val);
-                               }
-
-                               return null;
-                       }
-
-                       @Override
-                       protected String getType() {
-                               return "java.net.URL";
-                       }
-               });
-
-               // Images (this is currently the only supported image type by default)
-               customTypes.put("be.nikiroo.utils.Image", new CustomSerializer() {
-                       @Override
-                       protected void toStream(OutputStream out, Object value)
-                                       throws IOException {
-                               Image img = (Image) value;
-                               OutputStream encoded = new Base64OutputStream(out, true);
-                               try {
-                                       InputStream in = img.newInputStream();
-                                       try {
-                                               IOUtils.write(in, encoded);
-                                       } finally {
-                                               in.close();
-                                       }
-                               } finally {
-                                       encoded.flush();
-                                       // Cannot close!
-                               }
-                       }
-
-                       @Override
-                       protected String getType() {
-                               return "be.nikiroo.utils.Image";
-                       }
-
-                       @Override
-                       protected Object fromStream(InputStream in) throws IOException {
-                               try {
-                                       // Cannot close it!
-                                       InputStream decoded = new Base64InputStream(in, false);
-                                       return new Image(decoded);
-                               } catch (IOException e) {
-                                       throw new UnknownFormatConversionException(e.getMessage());
-                               }
-                       }
-               });
-       }
-
-       /**
-        * Create an empty object of the given type.
-        * 
-        * @param type
-        *            the object type (its class name)
-        * 
-        * @return the new object
-        * 
-        * @throws ClassNotFoundException
-        *             if the class cannot be found
-        * @throws NoSuchMethodException
-        *             if the given class is not compatible with this code
-        */
-       public static Object createObject(String type)
-                       throws ClassNotFoundException, NoSuchMethodException {
-
-               String desc = null;
-               try {
-                       Class<?> clazz = getClass(type);
-                       String className = clazz.getName();
-                       List<Object> args = new ArrayList<Object>();
-                       List<Class<?>> classes = new ArrayList<Class<?>>();
-                       Constructor<?> ctor = null;
-                       if (className.contains("$")) {
-                               for (String parentName = className.substring(0,
-                                               className.lastIndexOf('$'));; parentName = parentName
-                                               .substring(0, parentName.lastIndexOf('$'))) {
-                                       Object parent = createObject(parentName);
-                                       args.add(parent);
-                                       classes.add(parent.getClass());
-
-                                       if (!parentName.contains("$")) {
-                                               break;
-                                       }
-                               }
-
-                               // Better error description in case there is no empty
-                               // constructor:
-                               desc = "";
-                               String end = "";
-                               for (Class<?> parent = clazz; parent != null
-                                               && !parent.equals(Object.class); parent = parent
-                                               .getSuperclass()) {
-                                       if (!desc.isEmpty()) {
-                                               desc += " [:";
-                                               end += "]";
-                                       }
-                                       desc += parent;
-                               }
-                               desc += end;
-                               //
-
-                               try {
-                                       ctor = clazz.getDeclaredConstructor(classes
-                                                       .toArray(new Class[] {}));
-                               } catch (NoSuchMethodException nsme) {
-                                       // TODO: it seems we do not always need a parameter for each
-                                       // level, so we currently try "ALL" levels or "FIRST" level
-                                       // only -> we should check the actual rule and use it
-                                       ctor = clazz.getDeclaredConstructor(classes.get(0));
-                                       Object firstParent = args.get(0);
-                                       args.clear();
-                                       args.add(firstParent);
-                               }
-                               desc = null;
-                       } else {
-                               ctor = clazz.getDeclaredConstructor();
-                       }
-
-                       ctor.setAccessible(true);
-                       return ctor.newInstance(args.toArray());
-               } catch (ClassNotFoundException e) {
-                       throw e;
-               } catch (NoSuchMethodException e) {
-                       if (desc != null) {
-                               throw new NoSuchMethodException("Empty constructor not found: "
-                                               + desc);
-                       }
-                       throw e;
-               } catch (Exception e) {
-                       throw new NoSuchMethodException("Cannot instantiate: " + type);
-               }
-       }
-
-       /**
-        * Insert a custom serialiser that will take precedence over the default one
-        * or the target class.
-        * 
-        * @param serializer
-        *            the custom serialiser
-        */
-       static public void addCustomSerializer(CustomSerializer serializer) {
-               customTypes.put(serializer.getType(), serializer);
-       }
-
-       /**
-        * Serialise the given object into this {@link OutputStream}.
-        * <p>
-        * <b>Important: </b>If the operation fails (with a
-        * {@link NotSerializableException}), the {@link StringBuilder} will be
-        * corrupted (will contain bad, most probably not importable data).
-        * 
-        * @param out
-        *            the output {@link OutputStream} to serialise to
-        * @param o
-        *            the object to serialise
-        * @param map
-        *            the map of already serialised objects (if the given object or
-        *            one of its descendant is already present in it, only an ID
-        *            will be serialised)
-        * 
-        * @throws NotSerializableException
-        *             if the object cannot be serialised (in this case, the
-        *             {@link StringBuilder} can contain bad, most probably not
-        *             importable data)
-        * @throws IOException
-        *             in case of I/O errors
-        */
-       static void append(OutputStream out, Object o, Map<Integer, Object> map)
-                       throws NotSerializableException, IOException {
-
-               Field[] fields = new Field[] {};
-               String type = "";
-               String id = "NULL";
-
-               if (o != null) {
-                       int hash = System.identityHashCode(o);
-                       fields = o.getClass().getDeclaredFields();
-                       type = o.getClass().getCanonicalName();
-                       if (type == null) {
-                               // Anonymous inner classes support
-                               type = o.getClass().getName();
-                       }
-                       id = Integer.toString(hash);
-                       if (map.containsKey(hash)) {
-                               fields = new Field[] {};
-                       } else {
-                               map.put(hash, o);
-                       }
-               }
-
-               write(out, "{\nREF ");
-               write(out, type);
-               write(out, "@");
-               write(out, id);
-               write(out, ":");
-
-               if (!encode(out, o)) { // check if direct value
-                       try {
-                               for (Field field : fields) {
-                                       field.setAccessible(true);
-
-                                       if (field.getName().startsWith("this$")
-                                                       || field.isSynthetic()
-                                                       || (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
-                                               // Do not keep this links of nested classes
-                                               // Do not keep synthetic fields
-                                               // Do not keep final fields
-                                               continue;
-                                       }
-
-                                       write(out, "\n^");
-                                       write(out, field.getName());
-                                       write(out, ":");
-
-                                       Object value = field.get(o);
-
-                                       if (!encode(out, value)) {
-                                               write(out, "\n");
-                                               append(out, value, map);
-                                       }
-                               }
-                       } catch (IllegalArgumentException e) {
-                               e.printStackTrace(); // should not happen (see
-                                                                               // setAccessible)
-                       } catch (IllegalAccessException e) {
-                               e.printStackTrace(); // should not happen (see
-                                                                               // setAccessible)
-                       }
-
-                       write(out, "\n}");
-               }
-       }
-
-       /**
-        * Encode the object into the given {@link OutputStream} if possible and if
-        * supported.
-        * <p>
-        * A supported object in this context means an object we can directly
-        * encode, like an Integer or a String. Custom objects and arrays are also
-        * considered supported, but <b>compound objects are not supported here</b>.
-        * <p>
-        * For compound objects, you should use {@link Exporter}.
-        * 
-        * @param out
-        *            the {@link OutputStream} to append to
-        * @param value
-        *            the object to encode (can be NULL, which will be encoded)
-        * 
-        * @return TRUE if success, FALSE if not (the content of the
-        *         {@link OutputStream} won't be changed in case of failure)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       static boolean encode(OutputStream out, Object value) throws IOException {
-               if (value == null) {
-                       write(out, "NULL");
-               } else if (value.getClass().getSimpleName().endsWith("[]")) {
-                       // Simple name does support [] suffix and do not return NULL for
-                       // inner anonymous classes
-                       customTypes.get("[]").encode(out, value);
-               } else if (customTypes.containsKey(value.getClass().getCanonicalName())) {
-                       customTypes.get(value.getClass().getCanonicalName())//
-                                       .encode(out, value);
-               } else if (value instanceof String) {
-                       encodeString(out, (String) value);
-               } else if (value instanceof Boolean) {
-                       write(out, value);
-               } else if (value instanceof Byte) {
-                       write(out, "b");
-                       write(out, value);
-               } else if (value instanceof Character) {
-                       write(out, "c");
-                       encodeString(out, "" + value);
-               } else if (value instanceof Short) {
-                       write(out, "s");
-                       write(out, value);
-               } else if (value instanceof Integer) {
-                       write(out, "i");
-                       write(out, value);
-               } else if (value instanceof Long) {
-                       write(out, "l");
-                       write(out, value);
-               } else if (value instanceof Float) {
-                       write(out, "f");
-                       write(out, value);
-               } else if (value instanceof Double) {
-                       write(out, "d");
-                       write(out, value);
-               } else if (value instanceof Enum) {
-                       write(out, "E:");
-                       String type = value.getClass().getCanonicalName();
-                       write(out, type);
-                       write(out, ".");
-                       write(out, ((Enum<?>) value).name());
-                       write(out, ";");
-               } else {
-                       return false;
-               }
-
-               return true;
-       }
-
-       static boolean isDirectValue(BufferedInputStream encodedValue)
-                       throws IOException {
-               if (CustomSerializer.isCustom(encodedValue)) {
-                       return false;
-               }
-
-               for (String fullValue : new String[] { "NULL", "null", "true", "false" }) {
-                       if (encodedValue.is(fullValue)) {
-                               return true;
-                       }
-               }
-
-               for (String prefix : new String[] { "c\"", "\"", "b", "s", "i", "l",
-                               "f", "d", "E:" }) {
-                       if (encodedValue.startsWith(prefix)) {
-                               return true;
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Decode the data into an equivalent supported source object.
-        * <p>
-        * A supported object in this context means an object we can directly
-        * encode, like an Integer or a String (see
-        * {@link SerialUtils#decode(String)}.
-        * <p>
-        * Custom objects and arrays are also considered supported here, but
-        * <b>compound objects are not</b>.
-        * <p>
-        * For compound objects, you should use {@link Importer}.
-        * 
-        * @param encodedValue
-        *            the encoded data, cannot be NULL
-        * 
-        * @return the object (can be NULL for NULL encoded values)
-        * 
-        * @throws IOException
-        *             if the content cannot be converted
-        */
-       static Object decode(BufferedInputStream encodedValue) throws IOException {
-               if (CustomSerializer.isCustom(encodedValue)) {
-                       // custom^TYPE^ENCODED_VALUE
-                       NextableInputStream content = new NextableInputStream(encodedValue,
-                                       new NextableInputStreamStep('^'));
-                       try {
-                               content.next();
-                               @SuppressWarnings("unused")
-                               String custom = IOUtils.readSmallStream(content);
-                               content.next();
-                               String type = IOUtils.readSmallStream(content);
-                               content.nextAll();
-                               if (customTypes.containsKey(type)) {
-                                       return customTypes.get(type).decode(content);
-                               }
-                               content.end();
-                               throw new IOException("Unknown custom type: " + type);
-                       } finally {
-                               content.close(false);
-                               encodedValue.end();
-                       }
-               }
-
-               String encodedString = IOUtils.readSmallStream(encodedValue);
-               return decode(encodedString);
-       }
-
-       /**
-        * Decode the data into an equivalent supported source object.
-        * <p>
-        * A supported object in this context means an object we can directly
-        * encode, like an Integer or a String.
-        * <p>
-        * For custom objects and arrays, you should use
-        * {@link SerialUtils#decode(InputStream)} or directly {@link Importer}.
-        * <p>
-        * For compound objects, you should use {@link Importer}.
-        * 
-        * @param encodedValue
-        *            the encoded data, cannot be NULL
-        * 
-        * @return the object (can be NULL for NULL encoded values)
-        * 
-        * @throws IOException
-        *             if the content cannot be converted
-        */
-       static Object decode(String encodedValue) throws IOException {
-               try {
-                       String cut = "";
-                       if (encodedValue.length() > 1) {
-                               cut = encodedValue.substring(1);
-                       }
-
-                       if (encodedValue.equals("NULL") || encodedValue.equals("null")) {
-                               return null;
-                       } else if (encodedValue.startsWith("\"")) {
-                               return decodeString(encodedValue);
-                       } else if (encodedValue.equals("true")) {
-                               return true;
-                       } else if (encodedValue.equals("false")) {
-                               return false;
-                       } else if (encodedValue.startsWith("b")) {
-                               return Byte.parseByte(cut);
-                       } else if (encodedValue.startsWith("c")) {
-                               return decodeString(cut).charAt(0);
-                       } else if (encodedValue.startsWith("s")) {
-                               return Short.parseShort(cut);
-                       } else if (encodedValue.startsWith("l")) {
-                               return Long.parseLong(cut);
-                       } else if (encodedValue.startsWith("f")) {
-                               return Float.parseFloat(cut);
-                       } else if (encodedValue.startsWith("d")) {
-                               return Double.parseDouble(cut);
-                       } else if (encodedValue.startsWith("i")) {
-                               return Integer.parseInt(cut);
-                       } else if (encodedValue.startsWith("E:")) {
-                               cut = cut.substring(1);
-                               return decodeEnum(cut);
-                       } else {
-                               throw new IOException("Unrecognized value: " + encodedValue);
-                       }
-               } catch (Exception e) {
-                       if (e instanceof IOException) {
-                               throw (IOException) e;
-                       }
-                       throw new IOException(e.getMessage(), e);
-               }
-       }
-
-       /**
-        * Write the given {@link String} into the given {@link OutputStream} in
-        * UTF-8.
-        * 
-        * @param out
-        *            the {@link OutputStream}
-        * @param data
-        *            the data to write, cannot be NULL
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       static void write(OutputStream out, Object data) throws IOException {
-               out.write(StringUtils.getBytes(data.toString()));
-       }
-
-       /**
-        * Return the corresponding class or throw an {@link Exception} if it
-        * cannot.
-        * 
-        * @param type
-        *            the class name to look for
-        * 
-        * @return the class (will never be NULL)
-        * 
-        * @throws ClassNotFoundException
-        *             if the class cannot be found
-        * @throws NoSuchMethodException
-        *             if the class cannot be created (usually because it or its
-        *             enclosing class doesn't have an empty constructor)
-        */
-       static private Class<?> getClass(String type)
-                       throws ClassNotFoundException, NoSuchMethodException {
-               Class<?> clazz = null;
-               try {
-                       clazz = Class.forName(type);
-               } catch (ClassNotFoundException e) {
-                       int pos = type.length();
-                       pos = type.lastIndexOf(".", pos);
-                       if (pos >= 0) {
-                               String parentType = type.substring(0, pos);
-                               String nestedType = type.substring(pos + 1);
-                               Class<?> javaParent = null;
-                               try {
-                                       javaParent = getClass(parentType);
-                                       parentType = javaParent.getName();
-                                       clazz = Class.forName(parentType + "$" + nestedType);
-                               } catch (Exception ee) {
-                               }
-
-                               if (javaParent == null) {
-                                       throw new NoSuchMethodException(
-                                                       "Class not found: "
-                                                                       + type
-                                                                       + " (the enclosing class cannot be created: maybe it doesn't have an empty constructor?)");
-                               }
-                       }
-               }
-
-               if (clazz == null) {
-                       throw new ClassNotFoundException("Class not found: " + type);
-               }
-
-               return clazz;
-       }
-
-       @SuppressWarnings({ "unchecked", "rawtypes" })
-       static private Enum<?> decodeEnum(String escaped) {
-               // escaped: be.xxx.EnumType.VALUE;
-               int pos = escaped.lastIndexOf(".");
-               String type = escaped.substring(0, pos);
-               String name = escaped.substring(pos + 1, escaped.length() - 1);
-
-               try {
-                       return Enum.valueOf((Class<Enum>) getClass(type), name);
-               } catch (Exception e) {
-                       throw new UnknownFormatConversionException("Unknown enum: <" + type
-                                       + "> " + name);
-               }
-       }
-
-       // aa bb -> "aa\tbb"
-       static void encodeString(OutputStream out, String raw) throws IOException {
-               // TODO: not. efficient.
-               out.write('\"');
-               for (char car : raw.toCharArray()) {
-                       encodeString(out, car);
-               }
-               out.write('\"');
-       }
-
-       // for encoding string, NOT to encode a char by itself!
-       static void encodeString(OutputStream out, char raw) throws IOException {
-               switch (raw) {
-               case '\\':
-                       out.write('\\');
-                       out.write('\\');
-                       break;
-               case '\r':
-                       out.write('\\');
-                       out.write('r');
-                       break;
-               case '\n':
-                       out.write('\\');
-                       out.write('n');
-                       break;
-               case '"':
-                       out.write('\\');
-                       out.write('\"');
-                       break;
-               default:
-                       out.write(raw);
-                       break;
-               }
-       }
-
-       // "aa\tbb" -> aa bb
-       static String decodeString(String escaped) {
-               StringBuilder builder = new StringBuilder();
-
-               boolean escaping = false;
-               for (char car : escaped.toCharArray()) {
-                       if (!escaping) {
-                               if (car == '\\') {
-                                       escaping = true;
-                               } else {
-                                       builder.append(car);
-                               }
-                       } else {
-                               switch (car) {
-                               case '\\':
-                                       builder.append('\\');
-                                       break;
-                               case 'r':
-                                       builder.append('\r');
-                                       break;
-                               case 'n':
-                                       builder.append('\n');
-                                       break;
-                               case '"':
-                                       builder.append('"');
-                                       break;
-                               }
-                               escaping = false;
-                       }
-               }
-
-               return builder.substring(1, builder.length() - 1);
-       }
-}
diff --git a/src/be/nikiroo/utils/serial/server/ConnectAction.java b/src/be/nikiroo/utils/serial/server/ConnectAction.java
deleted file mode 100644 (file)
index 6a19368..0000000
+++ /dev/null
@@ -1,474 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import javax.net.ssl.SSLException;
-
-import be.nikiroo.utils.CryptUtils;
-import be.nikiroo.utils.IOUtils;
-import be.nikiroo.utils.StringUtils;
-import be.nikiroo.utils.Version;
-import be.nikiroo.utils.serial.Exporter;
-import be.nikiroo.utils.serial.Importer;
-import be.nikiroo.utils.streams.BufferedOutputStream;
-import be.nikiroo.utils.streams.NextableInputStream;
-import be.nikiroo.utils.streams.NextableInputStreamStep;
-import be.nikiroo.utils.streams.ReplaceInputStream;
-import be.nikiroo.utils.streams.ReplaceOutputStream;
-
-/**
- * Base class used for the client/server basic handling.
- * <p>
- * It represents a single action: a client is expected to only execute one
- * action, while a server is expected to execute one action for each client
- * action.
- * 
- * @author niki
- */
-abstract class ConnectAction {
-       // We separate each "packet" we send with this character and make sure it
-       // does not occurs in the message itself.
-       static private char STREAM_SEP = '\b';
-       static private String[] STREAM_RAW = new String[] { "\\", "\b" };
-       static private String[] STREAM_CODED = new String[] { "\\\\", "\\b" };
-
-       private Socket s;
-       private boolean server;
-
-       private Version clientVersion;
-       private Version serverVersion;
-
-       private CryptUtils crypt;
-
-       private Object lock = new Object();
-       private NextableInputStream in;
-       private BufferedOutputStream out;
-       private boolean contentToSend;
-
-       /**
-        * Method that will be called when an action is performed on either the
-        * client or server this {@link ConnectAction} represent.
-        * 
-        * @param version
-        *            the version on the other side of the communication (client or
-        *            server)
-        * 
-        * @throws Exception
-        *             in case of I/O error
-        */
-       abstract protected void action(Version version) throws Exception;
-
-       /**
-        * Method called when we negotiate the version with the client.
-        * <p>
-        * Thus, it is only called on the server.
-        * <p>
-        * Will return the actual server version by default.
-        * 
-        * @param clientVersion
-        *            the client version
-        * 
-        * @return the version to send to the client
-        */
-       abstract protected Version negotiateVersion(Version clientVersion);
-
-       /**
-        * Handler called when an unexpected error occurs in the code.
-        * 
-        * @param e
-        *            the exception that occurred, SSLException usually denotes a
-        *            crypt error
-        */
-       abstract protected void onError(Exception e);
-
-       /**
-        * Create a new {@link ConnectAction}.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param server
-        *            TRUE for a server action, FALSE for a client action (will
-        *            impact the process)
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param version
-        *            the client-or-server version (depending upon the boolean
-        *            parameter <tt>server</tt>)
-        */
-       protected ConnectAction(Socket s, boolean server, String key,
-                       Version version) {
-               this.s = s;
-               this.server = server;
-               if (key != null) {
-                       crypt = new CryptUtils(key);
-               }
-
-               if (version == null) {
-                       version = new Version();
-               }
-
-               if (server) {
-                       serverVersion = version;
-               } else {
-                       clientVersion = version;
-               }
-       }
-
-       /**
-        * The version of this client-or-server.
-        * 
-        * @return the version
-        */
-       public Version getVersion() {
-               if (server) {
-                       return serverVersion;
-               }
-
-               return clientVersion;
-       }
-
-       /**
-        * The total amount of bytes received.
-        * 
-        * @return the amount of bytes received
-        */
-       public long getBytesReceived() {
-               return in.getBytesRead();
-       }
-
-       /**
-        * The total amount of bytes sent.
-        * 
-        * @return the amount of bytes sent
-        */
-       public long getBytesWritten() {
-               return out.getBytesWritten();
-       }
-
-       /**
-        * Actually start the process (this is synchronous).
-        */
-       public void connect() {
-               try {
-                       in = new NextableInputStream(s.getInputStream(),
-                                       new NextableInputStreamStep(STREAM_SEP));
-                       try {
-                               out = new BufferedOutputStream(s.getOutputStream());
-                               try {
-                                       // Negotiate version
-                                       Version version;
-                                       if (server) {
-                                               String HELLO = recString();
-                                               if (HELLO == null || !HELLO.startsWith("VERSION ")) {
-                                                       throw new SSLException(
-                                                                       "Client used bad encryption key");
-                                               }
-                                               version = negotiateVersion(new Version(
-                                                               HELLO.substring("VERSION ".length())));
-                                               sendString("VERSION " + version);
-                                       } else {
-                                               String HELLO = sendString("VERSION " + clientVersion);
-                                               if (HELLO == null || !HELLO.startsWith("VERSION ")) {
-                                                       throw new SSLException(
-                                                                       "Server did not accept the encryption key");
-                                               }
-                                               version = new Version(HELLO.substring("VERSION "
-                                                               .length()));
-                                       }
-
-                                       // Actual code
-                                       action(version);
-                               } finally {
-                                       out.close();
-                               }
-                       } finally {
-                               in.close();
-                       }
-               } catch (Exception e) {
-                       onError(e);
-               } finally {
-                       try {
-                               s.close();
-                       } catch (Exception e) {
-                               onError(e);
-                       }
-               }
-       }
-
-       /**
-        * Serialise and send the given object to the counter part (and, only for
-        * client, return the deserialised answer -- the server will always receive
-        * NULL).
-        * 
-        * @param data
-        *            the data to send
-        * 
-        * @return the answer (which can be NULL if no answer, or NULL for an answer
-        *         which is NULL) if this action is a client, always NULL if it is a
-        *         server
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        */
-       protected Object sendObject(Object data) throws IOException,
-                       NoSuchFieldException, NoSuchMethodException, ClassNotFoundException {
-               return send(out, data, false);
-       }
-
-       /**
-        * Reserved for the server: flush the data to the client and retrieve its
-        * answer.
-        * <p>
-        * Also used internally for the client (only do something if there is
-        * contentToSend).
-        * <p>
-        * Will only flush the data if there is contentToSend.
-        * 
-        * @return the deserialised answer (which can actually be NULL)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        * @throws java.lang.NullPointerException
-        *             if the counter part has no data to send
-        */
-       protected Object recObject() throws IOException, NoSuchFieldException,
-                       NoSuchMethodException, ClassNotFoundException,
-                       java.lang.NullPointerException {
-               return rec(false);
-       }
-
-       /**
-        * Send the given string to the counter part (and, only for client, return
-        * the answer -- the server will always receive NULL).
-        * 
-        * @param line
-        *            the data to send (we will add a line feed)
-        * 
-        * @return the answer if this action is a client (without the added line
-        *         feed), NULL if it is a server
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws SSLException
-        *             in case of crypt error
-        */
-       protected String sendString(String line) throws IOException {
-               try {
-                       return (String) send(out, line, true);
-               } catch (NoSuchFieldException e) {
-                       // Cannot happen
-                       e.printStackTrace();
-               } catch (NoSuchMethodException e) {
-                       // Cannot happen
-                       e.printStackTrace();
-               } catch (ClassNotFoundException e) {
-                       // Cannot happen
-                       e.printStackTrace();
-               }
-
-               return null;
-       }
-
-       /**
-        * Reserved for the server (externally): flush the data to the client and
-        * retrieve its answer.
-        * <p>
-        * Also used internally for the client (only do something if there is
-        * contentToSend).
-        * <p>
-        * Will only flush the data if there is contentToSend.
-        * 
-        * @return the answer (which can be NULL if no more content)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws SSLException
-        *             in case of crypt error
-        */
-       protected String recString() throws IOException {
-               try {
-                       return (String) rec(true);
-               } catch (NoSuchFieldException e) {
-                       // Cannot happen
-                       e.printStackTrace();
-               } catch (NoSuchMethodException e) {
-                       // Cannot happen
-                       e.printStackTrace();
-               } catch (ClassNotFoundException e) {
-                       // Cannot happen
-                       e.printStackTrace();
-               } catch (NullPointerException e) {
-                       // Should happen
-                       e.printStackTrace();
-               }
-
-               return null;
-       }
-
-       /**
-        * Serialise and send the given object to the counter part (and, only for
-        * client, return the deserialised answer -- the server will always receive
-        * NULL).
-        * 
-        * @param out
-        *            the stream to write to
-        * @param data
-        *            the data to write
-        * @param asString
-        *            TRUE to write it as a String, FALSE to write it as an Object
-        * 
-        * @return the answer (which can be NULL if no answer, or NULL for an answer
-        *         which is NULL) if this action is a client, always NULL if it is a
-        *         server
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws SSLException
-        *             in case of crypt error
-        * @throws IOException
-        *             in case of I/O error
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        */
-       private Object send(BufferedOutputStream out, Object data, boolean asString)
-                       throws IOException, NoSuchFieldException, NoSuchMethodException,
-                       ClassNotFoundException, java.lang.NullPointerException {
-
-               synchronized (lock) {
-                       OutputStream sub;
-                       if (crypt != null) {
-                               sub = crypt.encrypt64(out.open());
-                       } else {
-                               sub = out.open();
-                       }
-
-                       sub = new ReplaceOutputStream(sub, STREAM_RAW, STREAM_CODED);
-                       try {
-                               if (asString) {
-                                       sub.write(StringUtils.getBytes(data.toString()));
-                               } else {
-                                       new Exporter(sub).append(data);
-                               }
-                       } finally {
-                               sub.close();
-                       }
-
-                       out.write(STREAM_SEP);
-
-                       if (server) {
-                               out.flush();
-                               return null;
-                       }
-
-                       contentToSend = true;
-                       try {
-                               return rec(asString);
-                       } catch (NullPointerException e) {
-                               // We accept no data here for Objects
-                       }
-
-                       return null;
-               }
-       }
-
-       /**
-        * Reserved for the server: flush the data to the client and retrieve its
-        * answer.
-        * <p>
-        * Also used internally for the client (only do something if there is
-        * contentToSend).
-        * <p>
-        * Will only flush the data if there is contentToSend.
-        * <p>
-        * Note that the behaviour is slightly different for String and Object
-        * reading regarding exceptions:
-        * <ul>
-        * <li>NULL means that the counter part has no more data to send</li>
-        * <li>All the exceptions except {@link IOException} are there for Object
-        * conversion</li>
-        * </ul>
-        * 
-        * @param asString
-        *            TRUE for String reading, FALSE for Object reading (which can
-        *            still be a String)
-        * 
-        * @return the deserialised answer (which can actually be NULL)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        * @throws java.lang.NullPointerException
-        *             for Objects only: if the counter part has no data to send
-        */
-       @SuppressWarnings("resource")
-       private Object rec(boolean asString) throws IOException,
-                       NoSuchFieldException, NoSuchMethodException,
-                       ClassNotFoundException, java.lang.NullPointerException {
-
-               synchronized (lock) {
-                       if (server || contentToSend) {
-                               if (contentToSend) {
-                                       out.flush();
-                                       contentToSend = false;
-                               }
-
-                               if (in.next() && !in.eof()) {
-                                       InputStream read = new ReplaceInputStream(in.open(),
-                                                       STREAM_CODED, STREAM_RAW);
-                                       try {
-                                               if (crypt != null) {
-                                                       read = crypt.decrypt64(read);
-                                               }
-
-                                               if (asString) {
-                                                       return IOUtils.readSmallStream(read);
-                                               }
-
-                                               return new Importer().read(read).getValue();
-                                       } finally {
-                                               read.close();
-                                       }
-                               }
-
-                               if (!asString) {
-                                       throw new NullPointerException();
-                               }
-                       }
-
-                       return null;
-               }
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/serial/server/ConnectActionClient.java b/src/be/nikiroo/utils/serial/server/ConnectActionClient.java
deleted file mode 100644 (file)
index cb6bef3..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import be.nikiroo.utils.Version;
-
-/**
- * Base class used for the client basic handling.
- * <p>
- * It represents a single action: a client is expected to only execute one
- * action.
- * 
- * @author niki
- */
-abstract class ConnectActionClient {
-       /**
-        * The underlying {@link ConnectAction}.
-        * <p>
-        * Cannot be NULL.
-        */
-       protected ConnectAction action;
-
-       /**
-        * Create a new {@link ConnectActionClient}, using the current version of
-        * the program.
-        * 
-        * @param host
-        *            the host to bind to
-        * @param port
-        *            the port to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the host is not known
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ConnectActionClient(String host, int port, String key)
-                       throws IOException {
-               this(host, port, key, Version.getCurrentVersion());
-       }
-
-       /**
-        * Create a new {@link ConnectActionClient}.
-        * 
-        * @param host
-        *            the host to bind to
-        * @param port
-        *            the port to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param clientVersion
-        *            the client version
-        * 
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the host is not known
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ConnectActionClient(String host, int port, String key,
-                       Version clientVersion) throws IOException {
-               this(new Socket(host, port), key, clientVersion);
-       }
-
-       /**
-        * Create a new {@link ConnectActionClient}, using the current version of
-        * the program.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        */
-       public ConnectActionClient(Socket s, String key) {
-               this(s, key, Version.getCurrentVersion());
-       }
-
-       /**
-        * Create a new {@link ConnectActionClient}.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param clientVersion
-        *            the client version
-        */
-       public ConnectActionClient(Socket s, String key, Version clientVersion) {
-               action = new ConnectAction(s, false, key, clientVersion) {
-                       @Override
-                       protected void action(Version serverVersion) throws Exception {
-                               ConnectActionClient.this.action(serverVersion);
-                       }
-
-                       @Override
-                       protected void onError(Exception e) {
-                               ConnectActionClient.this.onError(e);
-                       }
-
-                       @Override
-                       protected Version negotiateVersion(Version clientVersion) {
-                               new Exception("Should never be called on a client")
-                                               .printStackTrace();
-                               return null;
-                       }
-               };
-       }
-
-       /**
-        * Actually start the process and call the action (synchronous).
-        */
-       public void connect() {
-               action.connect();
-       }
-
-       /**
-        * Actually start the process and call the action (asynchronous).
-        */
-       public void connectAsync() {
-               new Thread(new Runnable() {
-                       @Override
-                       public void run() {
-                               connect();
-                       }
-               }).start();
-       }
-
-       /**
-        * Method that will be called when an action is performed on the client.
-        * 
-        * @param serverVersion
-        *            the version of the server connected to this client
-        * 
-        * @throws Exception
-        *             in case of I/O error
-        */
-       @SuppressWarnings("unused")
-       public void action(Version serverVersion) throws Exception {
-       }
-
-       /**
-        * Handler called when an unexpected error occurs in the code.
-        * <p>
-        * Will just ignore the error by default.
-        * 
-        * @param e
-        *            the exception that occurred
-        */
-       protected void onError(@SuppressWarnings("unused") Exception e) {
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/serial/server/ConnectActionClientObject.java b/src/be/nikiroo/utils/serial/server/ConnectActionClientObject.java
deleted file mode 100644 (file)
index 9385645..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import be.nikiroo.utils.Version;
-
-/**
- * Class used for the client basic handling.
- * <p>
- * It represents a single action: a client is expected to only execute one
- * action.
- * 
- * @author niki
- */
-public class ConnectActionClientObject extends ConnectActionClient {
-       /**
-        * Create a new {@link ConnectActionClientObject} .
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        */
-       public ConnectActionClientObject(Socket s, String key) {
-               super(s, key);
-       }
-
-       /**
-        * Create a new {@link ConnectActionClientObject} .
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param clientVersion
-        *            the version of the client
-        */
-       public ConnectActionClientObject(Socket s, String key, Version clientVersion) {
-               super(s, key, clientVersion);
-       }
-
-       /**
-        * Create a new {@link ConnectActionClientObject}.
-        * 
-        * @param host
-        *            the host to bind to
-        * @param port
-        *            the port to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ConnectActionClientObject(String host, int port, String key)
-                       throws IOException {
-               super(host, port, key);
-       }
-
-       /**
-        * Create a new {@link ConnectActionClientObject}.
-        * 
-        * @param host
-        *            the host to bind to
-        * @param port
-        *            the port to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param clientVersion
-        *            the version of the client
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ConnectActionClientObject(String host, int port, String key,
-                       Version clientVersion) throws IOException {
-               super(host, port, key, clientVersion);
-       }
-
-       /**
-        * Serialise and send the given object to the server (and return the
-        * deserialised answer).
-        * 
-        * @param data
-        *            the data to send
-        * 
-        * @return the answer, which can be NULL
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        */
-       public Object send(Object data) throws IOException, NoSuchFieldException,
-                       NoSuchMethodException, ClassNotFoundException {
-               return action.sendObject(data);
-       }
-
-       // Deprecated //
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @Deprecated
-       public ConnectActionClientObject(String host, int port, boolean ssl)
-                       throws IOException {
-               this(host, port, ssl ? "" : null);
-       }
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @Deprecated
-       public ConnectActionClientObject(String host, int port, boolean ssl,
-                       Version version) throws IOException {
-               this(host, port, ssl ? "" : null, version);
-       }
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @SuppressWarnings("unused")
-       @Deprecated
-       public ConnectActionClientObject(Socket s, boolean ssl) throws IOException {
-               this(s, ssl ? "" : null);
-       }
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @SuppressWarnings("unused")
-       @Deprecated
-       public ConnectActionClientObject(Socket s, boolean ssl, Version version)
-                       throws IOException {
-               this(s, ssl ? "" : null, version);
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/serial/server/ConnectActionClientString.java b/src/be/nikiroo/utils/serial/server/ConnectActionClientString.java
deleted file mode 100644 (file)
index 3005cee..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import be.nikiroo.utils.Version;
-
-/**
- * Class used for the client basic handling.
- * <p>
- * It represents a single action: a client is expected to only execute one
- * action.
- * 
- * @author niki
- */
-public class ConnectActionClientString extends ConnectActionClient {
-       /**
-        * Create a new {@link ConnectActionClientString}.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        */
-       public ConnectActionClientString(Socket s, String key) {
-               super(s, key);
-       }
-
-       /**
-        * Create a new {@link ConnectActionClientString}.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param clientVersion
-        *            the version of this client
-        */
-       public ConnectActionClientString(Socket s, String key, Version clientVersion) {
-               super(s, key, clientVersion);
-       }
-
-       /**
-        * Create a new {@link ConnectActionClientString}.
-        * 
-        * @param host
-        *            the host to bind to
-        * @param port
-        *            the port to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ConnectActionClientString(String host, int port, String key)
-                       throws IOException {
-               super(host, port, key);
-       }
-
-       /**
-        * Create a new {@link ConnectActionClientString}.
-        * 
-        * @param host
-        *            the host to bind to
-        * @param port
-        *            the port to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param clientVersion
-        *            the version of this client
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ConnectActionClientString(String host, int port, String key,
-                       Version clientVersion) throws IOException {
-               super(host, port, key, clientVersion);
-       }
-
-       /**
-        * Send the given object to the server (and return the answer).
-        * 
-        * @param data
-        *            the data to send
-        * 
-        * @return the answer, which can be NULL
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public String send(String data) throws IOException {
-               return action.sendString(data);
-       }
-
-       // Deprecated //
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @Deprecated
-       public ConnectActionClientString(String host, int port, boolean ssl)
-                       throws IOException {
-               this(host, port, ssl ? "" : null);
-       }
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @Deprecated
-       public ConnectActionClientString(String host, int port, boolean ssl,
-                       Version version) throws IOException {
-               this(host, port, ssl ? "" : null, version);
-       }
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @SuppressWarnings("unused")
-       @Deprecated
-       public ConnectActionClientString(Socket s, boolean ssl) throws IOException {
-               this(s, ssl ? "" : null);
-       }
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @SuppressWarnings("unused")
-       @Deprecated
-       public ConnectActionClientString(Socket s, boolean ssl, Version version)
-                       throws IOException {
-               this(s, ssl ? "" : null, version);
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/serial/server/ConnectActionServer.java b/src/be/nikiroo/utils/serial/server/ConnectActionServer.java
deleted file mode 100644 (file)
index 350d3fe..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.net.Socket;
-
-import be.nikiroo.utils.Version;
-
-/**
- * Base class used for the server basic handling.
- * <p>
- * It represents a single action: a server is expected to execute one action for
- * each client action.
- * 
- * @author niki
- */
-abstract class ConnectActionServer {
-       private boolean closing;
-
-       /**
-        * The underlying {@link ConnectAction}.
-        * <p>
-        * Cannot be NULL.
-        */
-       protected ConnectAction action;
-
-       /**
-        * Create a new {@link ConnectActionServer}, using the current version.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        */
-       public ConnectActionServer(Socket s, String key) {
-               this(s, key, Version.getCurrentVersion());
-       }
-
-       /**
-        * Create a new {@link ConnectActionServer}.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param serverVersion
-        *            the version of this server,that will be sent to the client
-        */
-       public ConnectActionServer(Socket s, String key, Version serverVersion) {
-               action = new ConnectAction(s, true, key, serverVersion) {
-                       @Override
-                       protected void action(Version clientVersion) throws Exception {
-                               ConnectActionServer.this.action(clientVersion);
-                       }
-
-                       @Override
-                       protected void onError(Exception e) {
-                               ConnectActionServer.this.onError(e);
-                       }
-
-                       @Override
-                       protected Version negotiateVersion(Version clientVersion) {
-                               return ConnectActionServer.this.negotiateVersion(clientVersion);
-                       }
-               };
-       }
-
-       /**
-        * Actually start the process and call the action (synchronous).
-        */
-       public void connect() {
-               action.connect();
-       }
-
-       /**
-        * Actually start the process and call the action (asynchronous).
-        */
-       public void connectAsync() {
-               new Thread(new Runnable() {
-                       @Override
-                       public void run() {
-                               connect();
-                       }
-               }).start();
-       }
-
-       /**
-        * Stop the client/server connection on behalf of the server (usually, the
-        * client connects then is allowed to send as many requests as it wants; in
-        * some cases, though, the server may wish to forcefully close the
-        * connection and can do via this value, when it is set to TRUE).
-        * <p>
-        * Example of usage: the client failed an authentication check, cut the
-        * connection here and now.
-        * 
-        * @return TRUE when it is
-        */
-       public boolean isClosing() {
-               return closing;
-       }
-
-       /**
-        * Can be called to stop the client/server connection on behalf of the
-        * server (usually, the client connects then is allowed to send as many
-        * requests as it wants; in some cases, though, the server may wish to
-        * forcefully close the connection and can do so by calling this method).
-        * <p>
-        * Example of usage: the client failed an authentication check, cut the
-        * connection here and now.
-        */
-       public void close() {
-               closing = true;
-       }
-
-       /**
-        * The total amount of bytes received.
-        * 
-        * @return the amount of bytes received
-        */
-       public long getBytesReceived() {
-               return action.getBytesReceived();
-       }
-
-       /**
-        * The total amount of bytes sent.
-        * 
-        * @return the amount of bytes sent
-        */
-       public long getBytesSent() {
-               return action.getBytesWritten();
-       }
-
-       /**
-        * Method that will be called when an action is performed on the server.
-        * 
-        * @param clientVersion
-        *            the version of the client connected to this server
-        * 
-        * @throws Exception
-        *             in case of I/O error
-        */
-       @SuppressWarnings("unused")
-       public void action(Version clientVersion) throws Exception {
-       }
-
-       /**
-        * Handler called when an unexpected error occurs in the code.
-        * <p>
-        * Will just ignore the error by default.
-        * 
-        * @param e
-        *            the exception that occurred
-        */
-       protected void onError(@SuppressWarnings("unused") Exception e) {
-       }
-
-       /**
-        * Method called when we negotiate the version with the client.
-        * <p>
-        * Will return the actual server version by default.
-        * 
-        * @param clientVersion
-        *            the client version
-        * 
-        * @return the version to send to the client
-        */
-       protected Version negotiateVersion(
-                       @SuppressWarnings("unused") Version clientVersion) {
-               return action.getVersion();
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/serial/server/ConnectActionServerObject.java b/src/be/nikiroo/utils/serial/server/ConnectActionServerObject.java
deleted file mode 100644 (file)
index 07d9867..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.net.Socket;
-
-/**
- * Class used for the server basic handling.
- * <p>
- * It represents a single action: a server is expected to execute one action for
- * each client action.
- * 
- * @author niki
- */
-public class ConnectActionServerObject extends ConnectActionServer {
-       /**
-        * Create a new {@link ConnectActionServerObject} as the server version.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        */
-       public ConnectActionServerObject(Socket s, String key) {
-               super(s, key);
-       }
-
-       /**
-        * Serialise and send the given object to the client.
-        * 
-        * @param data
-        *            the data to send
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        */
-       public void send(Object data) throws IOException, NoSuchFieldException,
-                       NoSuchMethodException, ClassNotFoundException {
-               action.sendObject(data);
-       }
-
-       /**
-        * (Flush the data to the client if needed and) retrieve its answer.
-        * 
-        * @return the deserialised answer (which can actually be NULL)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws NoSuchFieldException
-        *             if the serialised data contains information about a field
-        *             which does actually not exist in the class we know of
-        * @throws NoSuchMethodException
-        *             if a class described in the serialised data cannot be created
-        *             because it is not compatible with this code
-        * @throws ClassNotFoundException
-        *             if a class described in the serialised data cannot be found
-        * @throws java.lang.NullPointerException
-        *             if the counter part has no data to send
-        */
-       public Object rec() throws NoSuchFieldException, NoSuchMethodException,
-                       ClassNotFoundException, IOException, java.lang.NullPointerException {
-               return action.recObject();
-       }
-}
diff --git a/src/be/nikiroo/utils/serial/server/ConnectActionServerString.java b/src/be/nikiroo/utils/serial/server/ConnectActionServerString.java
deleted file mode 100644 (file)
index 8d113c1..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.net.Socket;
-
-/**
- * Class used for the server basic handling.
- * <p>
- * It represents a single action: a server is expected to execute one action for
- * each client action.
- * 
- * @author niki
- */
-public class ConnectActionServerString extends ConnectActionServer {
-       /**
-        * Create a new {@link ConnectActionServerString} as the server version.
-        * 
-        * @param s
-        *            the socket to bind to
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        */
-       public ConnectActionServerString(Socket s, String key) {
-               super(s, key);
-       }
-
-       /**
-        * Serialise and send the given object to the client.
-        * 
-        * @param data
-        *            the data to send
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public void send(String data) throws IOException {
-               action.sendString(data);
-       }
-
-       /**
-        * (Flush the data to the client if needed and) retrieve its answer.
-        * 
-        * @return the answer if it is available, or NULL if not
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public String rec() throws IOException {
-               return action.recString();
-       }
-}
diff --git a/src/be/nikiroo/utils/serial/server/Server.java b/src/be/nikiroo/utils/serial/server/Server.java
deleted file mode 100644 (file)
index 0470159..0000000
+++ /dev/null
@@ -1,419 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import be.nikiroo.utils.TraceHandler;
-
-/**
- * This class implements a simple server that can listen for connections and
- * send/receive objects.
- * <p>
- * Note: this {@link Server} has to be discarded after use (cannot be started
- * twice).
- * 
- * @author niki
- */
-abstract class Server implements Runnable {
-       protected final String key;
-       protected long id = 0;
-
-       private final String name;
-       private final Object lock = new Object();
-       private final Object counterLock = new Object();
-
-       private ServerSocket ss;
-       private int port;
-
-       private boolean started;
-       private boolean exiting = false;
-       private int counter;
-
-       private long bytesReceived;
-       private long bytesSent;
-
-       private TraceHandler tracer = new TraceHandler();
-
-       /**
-        * Create a new {@link ConnectActionServer} to handle a request.
-        * 
-        * @param s
-        *            the socket to service
-        * 
-        * @return the action
-        */
-       abstract ConnectActionServer createConnectActionServer(Socket s);
-
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link Server#start()} is called.
-        * 
-        * @param port
-        *            the port to listen on, or 0 to assign any unallocated port
-        *            found (which can later on be queried via
-        *            {@link Server#getPort()}
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public Server(int port, String key) throws IOException {
-               this((String) null, port, key);
-       }
-
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link Server#start()} is called.
-        * <p>
-        * All the communications will happen in plain text.
-        * 
-        * @param name
-        *            the server name (only used for debug info and traces)
-        * @param port
-        *            the port to listen on
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public Server(String name, int port) throws IOException {
-               this(name, port, null);
-       }
-
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link Server#start()} is called.
-        * 
-        * @param name
-        *            the server name (only used for debug info and traces)
-        * @param port
-        *            the port to listen on
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public Server(String name, int port, String key) throws IOException {
-               this.name = name;
-               this.port = port;
-               this.key = key;
-               this.ss = new ServerSocket(port);
-
-               if (this.port == 0) {
-                       this.port = this.ss.getLocalPort();
-               }
-       }
-
-       /**
-        * The traces handler for this {@link Server}.
-        * 
-        * @return the traces handler
-        */
-       public TraceHandler getTraceHandler() {
-               return tracer;
-       }
-
-       /**
-        * The traces handler for this {@link Server}.
-        * 
-        * @param tracer
-        *            the new traces handler
-        */
-       public void setTraceHandler(TraceHandler tracer) {
-               if (tracer == null) {
-                       tracer = new TraceHandler(false, false, false);
-               }
-
-               this.tracer = tracer;
-       }
-
-       /**
-        * The name of this {@link Server} if any.
-        * <p>
-        * Used for traces and debug purposes only.
-        * 
-        * @return the name or NULL
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * Return the assigned port.
-        * 
-        * @return the assigned port
-        */
-       public int getPort() {
-               return port;
-       }
-
-       /**
-        * The total amount of bytes received.
-        * 
-        * @return the amount of bytes received
-        */
-       public long getBytesReceived() {
-               return bytesReceived;
-       }
-
-       /**
-        * The total amount of bytes sent.
-        * 
-        * @return the amount of bytes sent
-        */
-       public long getBytesSent() {
-               return bytesSent;
-       }
-
-       /**
-        * Start the server (listen on the network for new connections).
-        * <p>
-        * Can only be called once.
-        * <p>
-        * This call is asynchronous, and will just start a new {@link Thread} on
-        * itself (see {@link Server#run()}).
-        */
-       public void start() {
-               new Thread(this).start();
-       }
-
-       /**
-        * Start the server (listen on the network for new connections).
-        * <p>
-        * Can only be called once.
-        * <p>
-        * You may call it via {@link Server#start()} for an asynchronous call, too.
-        */
-       @Override
-       public void run() {
-               ServerSocket ss = null;
-               boolean alreadyStarted = false;
-               synchronized (lock) {
-                       ss = this.ss;
-                       if (!started && ss != null) {
-                               started = true;
-                       } else {
-                               alreadyStarted = started;
-                       }
-               }
-
-               if (alreadyStarted) {
-                       tracer.error(name + ": cannot start server on port " + port
-                                       + ", it is already started");
-                       return;
-               }
-
-               if (ss == null) {
-                       tracer.error(name + ": cannot start server on port " + port
-                                       + ", it has already been used");
-                       return;
-               }
-
-               try {
-                       tracer.trace(name + ": server starting on port " + port + " ("
-                                       + (key != null ? "encrypted" : "plain text") + ")");
-
-                       while (started && !exiting) {
-                               count(1);
-                               final Socket s = ss.accept();
-                               new Thread(new Runnable() {
-                                       @Override
-                                       public void run() {
-                                               ConnectActionServer action = null;
-                                               try {
-                                                       action = createConnectActionServer(s);
-                                                       action.connect();
-                                               } finally {
-                                                       count(-1);
-                                                       if (action != null) {
-                                                               bytesReceived += action.getBytesReceived();
-                                                               bytesSent += action.getBytesSent();
-                                                       }
-                                               }
-                                       }
-                               }).start();
-                       }
-
-                       // Will be covered by @link{Server#stop(long)} for timeouts
-                       while (counter > 0) {
-                               Thread.sleep(10);
-                       }
-               } catch (Exception e) {
-                       if (counter > 0) {
-                               onError(e);
-                       }
-               } finally {
-                       try {
-                               ss.close();
-                       } catch (Exception e) {
-                               onError(e);
-                       }
-
-                       this.ss = null;
-
-                       started = false;
-                       exiting = false;
-                       counter = 0;
-
-                       tracer.trace(name + ": client terminated on port " + port);
-               }
-       }
-
-       /**
-        * Will stop the server, synchronously and without a timeout.
-        */
-       public void stop() {
-               tracer.trace(name + ": stopping server");
-               stop(0, true);
-       }
-
-       /**
-        * Stop the server.
-        * 
-        * @param timeout
-        *            the maximum timeout to wait for existing actions to complete,
-        *            or 0 for "no timeout"
-        * @param wait
-        *            wait for the server to be stopped before returning
-        *            (synchronous) or not (asynchronous)
-        */
-       public void stop(final long timeout, final boolean wait) {
-               if (wait) {
-                       stop(timeout);
-               } else {
-                       new Thread(new Runnable() {
-                               @Override
-                               public void run() {
-                                       stop(timeout);
-                               }
-                       }).start();
-               }
-       }
-
-       /**
-        * Stop the server (synchronous).
-        * 
-        * @param timeout
-        *            the maximum timeout to wait for existing actions to complete,
-        *            or 0 for "no timeout"
-        */
-       private void stop(long timeout) {
-               tracer.trace(name + ": server stopping on port " + port);
-               synchronized (lock) {
-                       if (started && !exiting) {
-                               exiting = true;
-
-                               try {
-                                       getConnectionToMe().connect();
-                                       long time = 0;
-                                       while (ss != null && timeout > 0 && timeout > time) {
-                                               Thread.sleep(10);
-                                               time += 10;
-                                       }
-                               } catch (Exception e) {
-                                       if (ss != null) {
-                                               counter = 0; // will stop the main thread
-                                               onError(e);
-                                       }
-                               }
-                       }
-               }
-
-               // return only when stopped
-               while (started || exiting) {
-                       try {
-                               Thread.sleep(10);
-                       } catch (InterruptedException e) {
-                       }
-               }
-       }
-
-       /**
-        * Return a connection to this server (used by the Exit code to send an exit
-        * message).
-        * 
-        * @return the connection
-        * 
-        * @throws UnknownHostException
-        *             the host should always be NULL (localhost)
-        * @throws IOException
-        *             in case of I/O error
-        */
-       abstract protected ConnectActionClient getConnectionToMe()
-                       throws UnknownHostException, IOException;
-
-       /**
-        * Change the number of currently serviced actions.
-        * 
-        * @param change
-        *            the number to increase or decrease
-        * 
-        * @return the current number after this operation
-        */
-       private int count(int change) {
-               synchronized (counterLock) {
-                       counter += change;
-                       return counter;
-               }
-       }
-
-       /**
-        * This method will be called on errors.
-        * <p>
-        * By default, it will only call the trace handler (so you may want to call
-        * super {@link Server#onError} if you override it).
-        * 
-        * @param e
-        *            the error
-        */
-       protected void onError(Exception e) {
-               tracer.error(e);
-       }
-
-       /**
-        * Return the next ID to use.
-        * 
-        * @return the next ID
-        */
-       protected synchronized long getNextId() {
-               return id++;
-       }
-
-       /**
-        * Method called when
-        * {@link ServerObject#onRequest(ConnectActionServerObject, Object, long)}
-        * has successfully finished.
-        * <p>
-        * Can be used to know how much data was transmitted.
-        * 
-        * @param id
-        *            the ID used to identify the request
-        * @param bytesReceived
-        *            the bytes received during the request
-        * @param bytesSent
-        *            the bytes sent during the request
-        */
-       @SuppressWarnings("unused")
-       protected void onRequestDone(long id, long bytesReceived, long bytesSent) {
-       }
-}
diff --git a/src/be/nikiroo/utils/serial/server/ServerBridge.java b/src/be/nikiroo/utils/serial/server/ServerBridge.java
deleted file mode 100644 (file)
index 0b734c6..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Array;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import be.nikiroo.utils.StringUtils;
-import be.nikiroo.utils.TraceHandler;
-import be.nikiroo.utils.Version;
-import be.nikiroo.utils.serial.Importer;
-
-/**
- * This class implements a simple server that can bridge two other
- * {@link Server}s.
- * <p>
- * It can, of course, inspect the data that goes through it (by default, it
- * prints traces of the data).
- * <p>
- * Note: this {@link ServerBridge} has to be discarded after use (cannot be
- * started twice).
- * 
- * @author niki
- */
-public class ServerBridge extends Server {
-       private final String forwardToHost;
-       private final int forwardToPort;
-       private final String forwardToKey;
-
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link ServerBridge#start()} is called.
-        * 
-        * @param port
-        *            the port to listen on, or 0 to assign any unallocated port
-        *            found (which can later on be queried via
-        *            {@link ServerBridge#getPort()}
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param forwardToHost
-        *            the host server to forward the calls to
-        * @param forwardToPort
-        *            the host port to forward the calls to
-        * @param forwardToKey
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ServerBridge(int port, String key, String forwardToHost,
-                       int forwardToPort, String forwardToKey) throws IOException {
-               super(port, key);
-               this.forwardToHost = forwardToHost;
-               this.forwardToPort = forwardToPort;
-               this.forwardToKey = forwardToKey;
-       }
-
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link ServerBridge#start()} is called.
-        * 
-        * @param name
-        *            the server name (only used for debug info and traces)
-        * @param port
-        *            the port to listen on
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * @param forwardToHost
-        *            the host server to forward the calls to
-        * @param forwardToPort
-        *            the host port to forward the calls to
-        * @param forwardToKey
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text) use an SSL connection
-        *            for the forward server or not
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ServerBridge(String name, int port, String key,
-                       String forwardToHost, int forwardToPort, String forwardToKey)
-                       throws IOException {
-               super(name, port, key);
-               this.forwardToHost = forwardToHost;
-               this.forwardToPort = forwardToPort;
-               this.forwardToKey = forwardToKey;
-       }
-
-       /**
-        * The traces handler for this {@link Server}.
-        * <p>
-        * The trace levels are handled as follow:
-        * <ul>
-        * <li>1: it will only print basic IN/OUT messages with length</li>
-        * <li>2: it will try to interpret it as an object (SLOW) and print the
-        * object class if possible</li>
-        * <li>3: it will try to print the {@link Object#toString()} value, or the
-        * data if it is not an object</li>
-        * <li>4: it will also print the unzipped serialised value if it is an
-        * object</li>
-        * </ul>
-        * 
-        * @param tracer
-        *            the new traces handler
-        */
-       @Override
-       public void setTraceHandler(TraceHandler tracer) {
-               super.setTraceHandler(tracer);
-       }
-
-       @Override
-       protected ConnectActionServer createConnectActionServer(Socket s) {
-               // Bad impl, not up to date (should work, but not efficient)
-               return new ConnectActionServerString(s, key) {
-                       @Override
-                       public void action(Version clientVersion) throws Exception {
-                               onClientContact(clientVersion);
-                               final ConnectActionServerString bridge = this;
-
-                               try {
-                                       new ConnectActionClientString(forwardToHost, forwardToPort,
-                                                       forwardToKey) {
-                                               @Override
-                                               public void action(Version serverVersion)
-                                                               throws Exception {
-                                                       onServerContact(serverVersion);
-
-                                                       for (String fromClient = bridge.rec(); fromClient != null; fromClient = bridge
-                                                                       .rec()) {
-                                                               onRec(fromClient);
-                                                               String fromServer = send(fromClient);
-                                                               onSend(fromServer);
-                                                               bridge.send(fromServer);
-                                                       }
-
-                                                       getTraceHandler().trace("=== DONE", 1);
-                                                       getTraceHandler().trace("", 1);
-                                               }
-
-                                               @Override
-                                               protected void onError(Exception e) {
-                                                       ServerBridge.this.onError(e);
-                                               }
-                                       }.connect();
-                               } catch (Exception e) {
-                                       ServerBridge.this.onError(e);
-                               }
-                       }
-               };
-       }
-
-       /**
-        * This is the method that is called each time a client contact us.
-        */
-       protected void onClientContact(Version clientVersion) {
-               getTraceHandler().trace(">>> CLIENT " + clientVersion);
-       }
-
-       /**
-        * This is the method that is called each time a client contact us.
-        */
-       protected void onServerContact(Version serverVersion) {
-               getTraceHandler().trace("<<< SERVER " + serverVersion);
-               getTraceHandler().trace("");
-       }
-
-       /**
-        * This is the method that is called each time a client contact us.
-        * 
-        * @param data
-        *            the data sent by the client
-        */
-       protected void onRec(String data) {
-               trace(">>> CLIENT", data);
-       }
-
-       /**
-        * This is the method that is called each time the forwarded server contact
-        * us.
-        * 
-        * @param data
-        *            the data sent by the client
-        */
-       protected void onSend(String data) {
-               trace("<<< SERVER", data);
-       }
-
-       @Override
-       protected ConnectActionClient getConnectionToMe()
-                       throws UnknownHostException, IOException {
-               return new ConnectActionClientString(new Socket((String) null,
-                               getPort()), key);
-       }
-
-       @Override
-       public void run() {
-               getTraceHandler().trace(
-                               getName() + ": will forward to " + forwardToHost + ":"
-                                               + forwardToPort + " ("
-                                               + (forwardToKey != null ? "encrypted" : "plain text")
-                                               + ")");
-               super.run();
-       }
-
-       /**
-        * Trace the data with the given prefix.
-        * 
-        * @param prefix
-        *            the prefix (client, server, version...)
-        * @param data
-        *            the data to trace
-        */
-       private void trace(String prefix, String data) {
-               int size = data == null ? 0 : data.length();
-               String ssize = StringUtils.formatNumber(size) + "bytes";
-
-               getTraceHandler().trace(prefix + ": " + ssize, 1);
-
-               if (getTraceHandler().getTraceLevel() >= 2) {
-                       try {
-                               while (data.startsWith("ZIP:") || data.startsWith("B64:")) {
-                                       if (data.startsWith("ZIP:")) {
-                                               data = StringUtils.unzip64s(data.substring(4));
-                                       } else if (data.startsWith("B64:")) {
-                                               data = StringUtils.unzip64s(data.substring(4));
-                                       }
-                               }
-
-                               InputStream stream = new ByteArrayInputStream(
-                                               StringUtils.getBytes(data));
-                               try {
-                                       Object obj = new Importer().read(stream).getValue();
-                                       if (obj == null) {
-                                               getTraceHandler().trace("NULL", 2);
-                                               getTraceHandler().trace("NULL", 3);
-                                               getTraceHandler().trace("NULL", 4);
-                                       } else {
-                                               if (obj.getClass().isArray()) {
-                                                       getTraceHandler().trace(
-                                                                       "(" + obj.getClass() + ") with "
-                                                                                       + Array.getLength(obj)
-                                                                                       + "element(s)", 3);
-                                               } else {
-                                                       getTraceHandler().trace("(" + obj.getClass() + ")",
-                                                                       2);
-                                               }
-                                               getTraceHandler().trace("" + obj.toString(), 3);
-                                               getTraceHandler().trace(data, 4);
-                                       }
-                               } finally {
-                                       stream.close();
-                               }
-                       } catch (NoSuchMethodException e) {
-                               getTraceHandler().trace("(not an object)", 2);
-                               getTraceHandler().trace(data, 3);
-                               getTraceHandler().trace("", 4);
-                       } catch (NoSuchFieldException e) {
-                               getTraceHandler().trace(
-                                               "(incompatible: " + e.getMessage() + ")", 2);
-                               getTraceHandler().trace(data, 3);
-                               getTraceHandler().trace("", 4);
-                       } catch (ClassNotFoundException e) {
-                               getTraceHandler().trace(
-                                               "(unknown object: " + e.getMessage() + ")", 2);
-                               getTraceHandler().trace(data, 3);
-                               getTraceHandler().trace("", 4);
-                       } catch (Exception e) {
-                               getTraceHandler().trace(
-                                               "(decode error: " + e.getMessage() + ")", 2);
-                               getTraceHandler().trace(data, 3);
-                               getTraceHandler().trace("", 4);
-                       }
-
-                       getTraceHandler().trace("", 2);
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/serial/server/ServerObject.java b/src/be/nikiroo/utils/serial/server/ServerObject.java
deleted file mode 100644 (file)
index a6a5dd1..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import be.nikiroo.utils.Version;
-
-/**
- * This class implements a simple server that can listen for connections and
- * send/receive objects.
- * <p>
- * Note: this {@link ServerObject} has to be discarded after use (cannot be
- * started twice).
- * 
- * @author niki
- */
-abstract public class ServerObject extends Server {
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link ServerObject#start()} is called.
-        * 
-        * @param port
-        *            the port to listen on, or 0 to assign any unallocated port
-        *            found (which can later on be queried via
-        *            {@link ServerObject#getPort()}
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ServerObject(int port, String key) throws IOException {
-               super(port, key);
-       }
-
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link ServerObject#start()} is called.
-        * 
-        * @param name
-        *            the server name (only used for debug info and traces)
-        * @param port
-        *            the port to listen on
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ServerObject(String name, int port, String key) throws IOException {
-               super(name, port, key);
-       }
-
-       @Override
-       protected ConnectActionServer createConnectActionServer(Socket s) {
-               return new ConnectActionServerObject(s, key) {
-                       @Override
-                       public void action(Version clientVersion) throws Exception {
-                               long id = getNextId();
-                               try {
-                                       for (Object data = rec(); true; data = rec()) {
-                                               Object rep = null;
-                                               try {
-                                                       rep = onRequest(this, clientVersion, data, id);
-                                                       if (isClosing()) {
-                                                               return;
-                                                       }
-                                               } catch (Exception e) {
-                                                       onError(e);
-                                               }
-
-                                               send(rep);
-                                       }
-                               } catch (NullPointerException e) {
-                                       // Client has no data any more, we quit
-                                       onRequestDone(id, getBytesReceived(), getBytesSent());
-                               }
-                       }
-
-                       @Override
-                       protected void onError(Exception e) {
-                               ServerObject.this.onError(e);
-                       }
-               };
-       }
-
-       @Override
-       protected ConnectActionClient getConnectionToMe()
-                       throws UnknownHostException, IOException {
-               return new ConnectActionClientObject(new Socket((String) null,
-                               getPort()), key);
-       }
-
-       /**
-        * This is the method that is called on each client request.
-        * <p>
-        * You are expected to react to it and return an answer (which can be NULL).
-        * 
-        * @param action
-        *            the client action
-        * @param data
-        *            the data sent by the client (which can be NULL)
-        * @param id
-        *            an ID to identify this request (will also be re-used for
-        *            {@link ServerObject#onRequestDone(long, long, long)}.
-        * 
-        * @return the answer to return to the client (which can be NULL)
-        * 
-        * @throws Exception
-        *             in case of an exception, the error will only be logged
-        */
-       protected Object onRequest(ConnectActionServerObject action,
-                       Version clientVersion, Object data,
-                       @SuppressWarnings("unused") long id) throws Exception {
-               // TODO: change to abstract when deprecated method is removed
-               // Default implementation for compat
-               return onRequest(action, clientVersion, data);
-       }
-
-       // Deprecated //
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @Deprecated
-       public ServerObject(int port, boolean ssl) throws IOException {
-               this(port, ssl ? "" : null);
-       }
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @Deprecated
-       public ServerObject(String name, int port, boolean ssl) throws IOException {
-               this(name, port, ssl ? "" : null);
-       }
-
-       /**
-        * Will be called if the correct version is not overrided.
-        * 
-        * @deprecated use the version with the id.
-        * 
-        * @param action
-        *            the client action
-        * @param data
-        *            the data sent by the client
-        * 
-        * @return the answer to return to the client
-        * 
-        * @throws Exception
-        *             in case of an exception, the error will only be logged
-        */
-       @Deprecated
-       @SuppressWarnings("unused")
-       protected Object onRequest(ConnectActionServerObject action,
-                       Version version, Object data) throws Exception {
-               return null;
-       }
-}
diff --git a/src/be/nikiroo/utils/serial/server/ServerString.java b/src/be/nikiroo/utils/serial/server/ServerString.java
deleted file mode 100644 (file)
index 3c982fd..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-package be.nikiroo.utils.serial.server;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import be.nikiroo.utils.Version;
-
-/**
- * This class implements a simple server that can listen for connections and
- * send/receive Strings.
- * <p>
- * Note: this {@link ServerString} has to be discarded after use (cannot be
- * started twice).
- * 
- * @author niki
- */
-abstract public class ServerString extends Server {
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link ServerString#start()} is called.
-        * 
-        * @param port
-        *            the port to listen on, or 0 to assign any unallocated port
-        *            found (which can later on be queried via
-        *            {@link ServerString#getPort()}
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ServerString(int port, String key) throws IOException {
-               super(port, key);
-       }
-
-       /**
-        * Create a new server that will start listening on the network when
-        * {@link ServerString#start()} is called.
-        * 
-        * @param name
-        *            the server name (only used for debug info and traces)
-        * @param port
-        *            the port to listen on
-        * @param key
-        *            an optional key to encrypt all the communications (if NULL,
-        *            everything will be sent in clear text)
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        * @throws UnknownHostException
-        *             if the IP address of the host could not be determined
-        * @throws IllegalArgumentException
-        *             if the port parameter is outside the specified range of valid
-        *             port values, which is between 0 and 65535, inclusive
-        */
-       public ServerString(String name, int port, String key) throws IOException {
-               super(name, port, key);
-       }
-
-       @Override
-       protected ConnectActionServer createConnectActionServer(Socket s) {
-               return new ConnectActionServerString(s, key) {
-                       @Override
-                       public void action(Version clientVersion) throws Exception {
-                               long id = getNextId();
-                               for (String data = rec(); data != null; data = rec()) {
-                                       String rep = null;
-                                       try {
-                                               rep = onRequest(this, clientVersion, data, id);
-                                               if (isClosing()) {
-                                                       return;
-                                               }
-                                       } catch (Exception e) {
-                                               onError(e);
-                                       }
-
-                                       if (rep == null) {
-                                               rep = "";
-                                       }
-                                       send(rep);
-                               }
-
-                               onRequestDone(id, getBytesReceived(), getBytesSent());
-                       }
-
-                       @Override
-                       protected void onError(Exception e) {
-                               ServerString.this.onError(e);
-                       }
-               };
-       }
-
-       @Override
-       protected ConnectActionClient getConnectionToMe()
-                       throws UnknownHostException, IOException {
-               return new ConnectActionClientString(new Socket((String) null,
-                               getPort()), key);
-       }
-
-       /**
-        * This is the method that is called on each client request.
-        * <p>
-        * You are expected to react to it and return an answer (NULL will be
-        * converted to an empty {@link String}).
-        * 
-        * @param action
-        *            the client action
-        * @param clientVersion
-        *            the client version
-        * @param data
-        *            the data sent by the client
-        * @param id
-        *            an ID to identify this request (will also be re-used for
-        *            {@link ServerObject#onRequestDone(long, long, long)}.
-        * 
-        * @return the answer to return to the client
-        * 
-        * @throws Exception
-        *             in case of an exception, the error will only be logged
-        */
-       protected String onRequest(ConnectActionServerString action,
-                       Version clientVersion, String data,
-                       @SuppressWarnings("unused") long id) throws Exception {
-               // TODO: change to abstract when deprecated method is removed
-               // Default implementation for compat
-               return onRequest(action, clientVersion, data);
-       }
-
-       // Deprecated //
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @Deprecated
-       public ServerString(int port, boolean ssl) throws IOException {
-               this(port, ssl ? "" : null);
-       }
-
-       /**
-        * @deprecated SSL support has been replaced by key-based encryption.
-        *             <p>
-        *             Please use the version with key encryption (this deprecated
-        *             version uses an empty key when <tt>ssl</tt> is TRUE and no
-        *             key (NULL) when <tt>ssl</tt> is FALSE).
-        */
-       @Deprecated
-       public ServerString(String name, int port, boolean ssl) throws IOException {
-               this(name, port, ssl ? "" : null);
-       }
-
-       /**
-        * Will be called if the correct version is not overrided.
-        * 
-        * @deprecated use the version with the id.
-        * 
-        * @param action
-        *            the client action
-        * @param data
-        *            the data sent by the client
-        * 
-        * @return the answer to return to the client
-        * 
-        * @throws Exception
-        *             in case of an exception, the error will only be logged
-        */
-       @Deprecated
-       @SuppressWarnings("unused")
-       protected String onRequest(ConnectActionServerString action,
-                       Version version, String data) throws Exception {
-               return null;
-       }
-}
diff --git a/src/be/nikiroo/utils/streams/Base64.java b/src/be/nikiroo/utils/streams/Base64.java
deleted file mode 100644 (file)
index d54794b..0000000
+++ /dev/null
@@ -1,752 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Changes (@author niki):
- * - default charset -> UTF-8
- */
-
-package be.nikiroo.utils.streams;
-
-import java.io.UnsupportedEncodingException;
-
-import be.nikiroo.utils.StringUtils;
-
-/**
- * Utilities for encoding and decoding the Base64 representation of
- * binary data.  See RFCs <a
- * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a
- * href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>.
- */
-class Base64 {
-    /**
-     * Default values for encoder/decoder flags.
-     */
-    public static final int DEFAULT = 0;
-
-    /**
-     * Encoder flag bit to omit the padding '=' characters at the end
-     * of the output (if any).
-     */
-    public static final int NO_PADDING = 1;
-
-    /**
-     * Encoder flag bit to omit all line terminators (i.e., the output
-     * will be on one long line).
-     */
-    public static final int NO_WRAP = 2;
-
-    /**
-     * Encoder flag bit to indicate lines should be terminated with a
-     * CRLF pair instead of just an LF.  Has no effect if {@code
-     * NO_WRAP} is specified as well.
-     */
-    public static final int CRLF = 4;
-
-    /**
-     * Encoder/decoder flag bit to indicate using the "URL and
-     * filename safe" variant of Base64 (see RFC 3548 section 4) where
-     * {@code -} and {@code _} are used in place of {@code +} and
-     * {@code /}.
-     */
-    public static final int URL_SAFE = 8;
-
-    /**
-     * Flag to pass to {@link Base64OutputStream} to indicate that it
-     * should not close the output stream it is wrapping when it
-     * itself is closed.
-     */
-    public static final int NO_CLOSE = 16;
-
-    //  --------------------------------------------------------
-    //  shared code
-    //  --------------------------------------------------------
-
-    /* package */ static abstract class Coder {
-        public byte[] output;
-        public int op;
-
-        /**
-         * Encode/decode another block of input data.  this.output is
-         * provided by the caller, and must be big enough to hold all
-         * the coded data.  On exit, this.opwill be set to the length
-         * of the coded data.
-         *
-         * @param finish true if this is the final call to process for
-         *        this object.  Will finalize the coder state and
-         *        include any final bytes in the output.
-         *
-         * @return true if the input so far is good; false if some
-         *         error has been detected in the input stream..
-         */
-        public abstract boolean process(byte[] input, int offset, int len, boolean finish);
-
-        /**
-         * @return the maximum number of bytes a call to process()
-         * could produce for the given number of input bytes.  This may
-         * be an overestimate.
-         */
-        public abstract int maxOutputSize(int len);
-    }
-
-    //  --------------------------------------------------------
-    //  decoding
-    //  --------------------------------------------------------
-
-    /**
-     * Decode the Base64-encoded data in input and return the data in
-     * a new byte array.
-     *
-     * <p>The padding '=' characters at the end are considered optional, but
-     * if any are present, there must be the correct number of them.
-     *
-     * @param str    the input String to decode, which is converted to
-     *               bytes using the default charset
-     * @param flags  controls certain features of the decoded output.
-     *               Pass {@code DEFAULT} to decode standard Base64.
-     *
-     * @throws IllegalArgumentException if the input contains
-     * incorrect padding
-     */
-    public static byte[] decode(String str, int flags) {
-               return decode(StringUtils.getBytes(str), flags);
-    }
-
-    /**
-     * Decode the Base64-encoded data in input and return the data in
-     * a new byte array.
-     *
-     * <p>The padding '=' characters at the end are considered optional, but
-     * if any are present, there must be the correct number of them.
-     *
-     * @param input the input array to decode
-     * @param flags  controls certain features of the decoded output.
-     *               Pass {@code DEFAULT} to decode standard Base64.
-     *
-     * @throws IllegalArgumentException if the input contains
-     * incorrect padding
-     */
-    public static byte[] decode(byte[] input, int flags) {
-        return decode(input, 0, input.length, flags);
-    }
-
-    /**
-     * Decode the Base64-encoded data in input and return the data in
-     * a new byte array.
-     *
-     * <p>The padding '=' characters at the end are considered optional, but
-     * if any are present, there must be the correct number of them.
-     *
-     * @param input  the data to decode
-     * @param offset the position within the input array at which to start
-     * @param len    the number of bytes of input to decode
-     * @param flags  controls certain features of the decoded output.
-     *               Pass {@code DEFAULT} to decode standard Base64.
-     *
-     * @throws IllegalArgumentException if the input contains
-     * incorrect padding
-     */
-    public static byte[] decode(byte[] input, int offset, int len, int flags) {
-        // Allocate space for the most data the input could represent.
-        // (It could contain less if it contains whitespace, etc.)
-        Decoder decoder = new Decoder(flags, new byte[len*3/4]);
-
-        if (!decoder.process(input, offset, len, true)) {
-            throw new IllegalArgumentException("bad base-64");
-        }
-
-        // Maybe we got lucky and allocated exactly enough output space.
-        if (decoder.op == decoder.output.length) {
-            return decoder.output;
-        }
-
-        // Need to shorten the array, so allocate a new one of the
-        // right size and copy.
-        byte[] temp = new byte[decoder.op];
-        System.arraycopy(decoder.output, 0, temp, 0, decoder.op);
-        return temp;
-    }
-
-    /* package */ static class Decoder extends Coder {
-        /**
-         * Lookup table for turning bytes into their position in the
-         * Base64 alphabet.
-         */
-        private static final int DECODE[] = {
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
-            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
-            -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-        };
-
-        /**
-         * Decode lookup table for the "web safe" variant (RFC 3548
-         * sec. 4) where - and _ replace + and /.
-         */
-        private static final int DECODE_WEBSAFE[] = {
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
-            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
-            -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
-            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-        };
-
-        /** Non-data values in the DECODE arrays. */
-        private static final int SKIP = -1;
-        private static final int EQUALS = -2;
-
-        /**
-         * States 0-3 are reading through the next input tuple.
-         * State 4 is having read one '=' and expecting exactly
-         * one more.
-         * State 5 is expecting no more data or padding characters
-         * in the input.
-         * State 6 is the error state; an error has been detected
-         * in the input and no future input can "fix" it.
-         */
-        private int state;   // state number (0 to 6)
-        private int value;
-
-        final private int[] alphabet;
-
-        public Decoder(int flags, byte[] output) {
-            this.output = output;
-
-            alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE;
-            state = 0;
-            value = 0;
-        }
-
-        /**
-         * @return an overestimate for the number of bytes {@code
-         * len} bytes could decode to.
-         */
-        @Override
-               public int maxOutputSize(int len) {
-            return len * 3/4 + 10;
-        }
-
-        /**
-         * Decode another block of input data.
-         *
-         * @return true if the state machine is still healthy.  false if
-         *         bad base-64 data has been detected in the input stream.
-         */
-        @Override
-               public boolean process(byte[] input, int offset, int len, boolean finish) {
-            if (this.state == 6) return false;
-
-            int p = offset;
-            len += offset;
-
-            // Using local variables makes the decoder about 12%
-            // faster than if we manipulate the member variables in
-            // the loop.  (Even alphabet makes a measurable
-            // difference, which is somewhat surprising to me since
-            // the member variable is final.)
-            int state = this.state;
-            int value = this.value;
-            int op = 0;
-            final byte[] output = this.output;
-            final int[] alphabet = this.alphabet;
-
-            while (p < len) {
-                // Try the fast path:  we're starting a new tuple and the
-                // next four bytes of the input stream are all data
-                // bytes.  This corresponds to going through states
-                // 0-1-2-3-0.  We expect to use this method for most of
-                // the data.
-                //
-                // If any of the next four bytes of input are non-data
-                // (whitespace, etc.), value will end up negative.  (All
-                // the non-data values in decode are small negative
-                // numbers, so shifting any of them up and or'ing them
-                // together will result in a value with its top bit set.)
-                //
-                // You can remove this whole block and the output should
-                // be the same, just slower.
-                if (state == 0) {
-                    while (p+4 <= len &&
-                           (value = ((alphabet[input[p] & 0xff] << 18) |
-                                     (alphabet[input[p+1] & 0xff] << 12) |
-                                     (alphabet[input[p+2] & 0xff] << 6) |
-                                     (alphabet[input[p+3] & 0xff]))) >= 0) {
-                        output[op+2] = (byte) value;
-                        output[op+1] = (byte) (value >> 8);
-                        output[op] = (byte) (value >> 16);
-                        op += 3;
-                        p += 4;
-                    }
-                    if (p >= len) break;
-                }
-
-                // The fast path isn't available -- either we've read a
-                // partial tuple, or the next four input bytes aren't all
-                // data, or whatever.  Fall back to the slower state
-                // machine implementation.
-
-                int d = alphabet[input[p++] & 0xff];
-
-                switch (state) {
-                case 0:
-                    if (d >= 0) {
-                        value = d;
-                        ++state;
-                    } else if (d != SKIP) {
-                        this.state = 6;
-                        return false;
-                    }
-                    break;
-
-                case 1:
-                    if (d >= 0) {
-                        value = (value << 6) | d;
-                        ++state;
-                    } else if (d != SKIP) {
-                        this.state = 6;
-                        return false;
-                    }
-                    break;
-
-                case 2:
-                    if (d >= 0) {
-                        value = (value << 6) | d;
-                        ++state;
-                    } else if (d == EQUALS) {
-                        // Emit the last (partial) output tuple;
-                        // expect exactly one more padding character.
-                        output[op++] = (byte) (value >> 4);
-                        state = 4;
-                    } else if (d != SKIP) {
-                        this.state = 6;
-                        return false;
-                    }
-                    break;
-
-                case 3:
-                    if (d >= 0) {
-                        // Emit the output triple and return to state 0.
-                        value = (value << 6) | d;
-                        output[op+2] = (byte) value;
-                        output[op+1] = (byte) (value >> 8);
-                        output[op] = (byte) (value >> 16);
-                        op += 3;
-                        state = 0;
-                    } else if (d == EQUALS) {
-                        // Emit the last (partial) output tuple;
-                        // expect no further data or padding characters.
-                        output[op+1] = (byte) (value >> 2);
-                        output[op] = (byte) (value >> 10);
-                        op += 2;
-                        state = 5;
-                    } else if (d != SKIP) {
-                        this.state = 6;
-                        return false;
-                    }
-                    break;
-
-                case 4:
-                    if (d == EQUALS) {
-                        ++state;
-                    } else if (d != SKIP) {
-                        this.state = 6;
-                        return false;
-                    }
-                    break;
-
-                case 5:
-                    if (d != SKIP) {
-                        this.state = 6;
-                        return false;
-                    }
-                    break;
-                }
-            }
-
-            if (!finish) {
-                // We're out of input, but a future call could provide
-                // more.
-                this.state = state;
-                this.value = value;
-                this.op = op;
-                return true;
-            }
-
-            // Done reading input.  Now figure out where we are left in
-            // the state machine and finish up.
-
-            switch (state) {
-            case 0:
-                // Output length is a multiple of three.  Fine.
-                break;
-            case 1:
-                // Read one extra input byte, which isn't enough to
-                // make another output byte.  Illegal.
-                this.state = 6;
-                return false;
-            case 2:
-                // Read two extra input bytes, enough to emit 1 more
-                // output byte.  Fine.
-                output[op++] = (byte) (value >> 4);
-                break;
-            case 3:
-                // Read three extra input bytes, enough to emit 2 more
-                // output bytes.  Fine.
-                output[op++] = (byte) (value >> 10);
-                output[op++] = (byte) (value >> 2);
-                break;
-            case 4:
-                // Read one padding '=' when we expected 2.  Illegal.
-                this.state = 6;
-                return false;
-            case 5:
-                // Read all the padding '='s we expected and no more.
-                // Fine.
-                break;
-            }
-
-            this.state = state;
-            this.op = op;
-            return true;
-        }
-    }
-
-    //  --------------------------------------------------------
-    //  encoding
-    //  --------------------------------------------------------
-
-    /**
-     * Base64-encode the given data and return a newly allocated
-     * String with the result.
-     *
-     * @param input  the data to encode
-     * @param flags  controls certain features of the encoded output.
-     *               Passing {@code DEFAULT} results in output that
-     *               adheres to RFC 2045.
-     */
-    public static String encodeToString(byte[] input, int flags) {
-        try {
-            return new String(encode(input, flags), "US-ASCII");
-        } catch (UnsupportedEncodingException e) {
-            // US-ASCII is guaranteed to be available.
-            throw new AssertionError(e);
-        }
-    }
-
-    /**
-     * Base64-encode the given data and return a newly allocated
-     * String with the result.
-     *
-     * @param input  the data to encode
-     * @param offset the position within the input array at which to
-     *               start
-     * @param len    the number of bytes of input to encode
-     * @param flags  controls certain features of the encoded output.
-     *               Passing {@code DEFAULT} results in output that
-     *               adheres to RFC 2045.
-     */
-    public static String encodeToString(byte[] input, int offset, int len, int flags) {
-        try {
-            return new String(encode(input, offset, len, flags), "US-ASCII");
-        } catch (UnsupportedEncodingException e) {
-            // US-ASCII is guaranteed to be available.
-            throw new AssertionError(e);
-        }
-    }
-
-    /**
-     * Base64-encode the given data and return a newly allocated
-     * byte[] with the result.
-     *
-     * @param input  the data to encode
-     * @param flags  controls certain features of the encoded output.
-     *               Passing {@code DEFAULT} results in output that
-     *               adheres to RFC 2045.
-     */
-    public static byte[] encode(byte[] input, int flags) {
-        return encode(input, 0, input.length, flags);
-    }
-
-    /**
-     * Base64-encode the given data and return a newly allocated
-     * byte[] with the result.
-     *
-     * @param input  the data to encode
-     * @param offset the position within the input array at which to
-     *               start
-     * @param len    the number of bytes of input to encode
-     * @param flags  controls certain features of the encoded output.
-     *               Passing {@code DEFAULT} results in output that
-     *               adheres to RFC 2045.
-     */
-    public static byte[] encode(byte[] input, int offset, int len, int flags) {
-        Encoder encoder = new Encoder(flags, null);
-
-        // Compute the exact length of the array we will produce.
-        int output_len = len / 3 * 4;
-
-        // Account for the tail of the data and the padding bytes, if any.
-        if (encoder.do_padding) {
-            if (len % 3 > 0) {
-                output_len += 4;
-            }
-        } else {
-            switch (len % 3) {
-                case 0: break;
-                case 1: output_len += 2; break;
-                case 2: output_len += 3; break;
-            }
-        }
-
-        // Account for the newlines, if any.
-        if (encoder.do_newline && len > 0) {
-            output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) *
-                (encoder.do_cr ? 2 : 1);
-        }
-
-        encoder.output = new byte[output_len];
-        encoder.process(input, offset, len, true);
-
-        assert encoder.op == output_len;
-
-        return encoder.output;
-    }
-
-    /* package */ static class Encoder extends Coder {
-        /**
-         * Emit a new line every this many output tuples.  Corresponds to
-         * a 76-character line length (the maximum allowable according to
-         * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>).
-         */
-        public static final int LINE_GROUPS = 19;
-
-        /**
-         * Lookup table for turning Base64 alphabet positions (6 bits)
-         * into output bytes.
-         */
-        private static final byte ENCODE[] = {
-            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
-            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
-            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
-            'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
-        };
-
-        /**
-         * Lookup table for turning Base64 alphabet positions (6 bits)
-         * into output bytes.
-         */
-        private static final byte ENCODE_WEBSAFE[] = {
-            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
-            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
-            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
-            'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_',
-        };
-
-        final private byte[] tail;
-        /* package */ int tailLen;
-        private int count;
-
-        final public boolean do_padding;
-        final public boolean do_newline;
-        final public boolean do_cr;
-        final private byte[] alphabet;
-
-        public Encoder(int flags, byte[] output) {
-            this.output = output;
-
-            do_padding = (flags & NO_PADDING) == 0;
-            do_newline = (flags & NO_WRAP) == 0;
-            do_cr = (flags & CRLF) != 0;
-            alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE;
-
-            tail = new byte[2];
-            tailLen = 0;
-
-            count = do_newline ? LINE_GROUPS : -1;
-        }
-
-        /**
-         * @return an overestimate for the number of bytes {@code
-         * len} bytes could encode to.
-         */
-        @Override
-               public int maxOutputSize(int len) {
-            return len * 8/5 + 10;
-        }
-
-        @Override
-               public boolean process(byte[] input, int offset, int len, boolean finish) {
-            // Using local variables makes the encoder about 9% faster.
-            final byte[] alphabet = this.alphabet;
-            final byte[] output = this.output;
-            int op = 0;
-            int count = this.count;
-
-            int p = offset;
-            len += offset;
-            int v = -1;
-
-            // First we need to concatenate the tail of the previous call
-            // with any input bytes available now and see if we can empty
-            // the tail.
-
-            switch (tailLen) {
-                case 0:
-                    // There was no tail.
-                    break;
-
-                case 1:
-                    if (p+2 <= len) {
-                        // A 1-byte tail with at least 2 bytes of
-                        // input available now.
-                        v = ((tail[0] & 0xff) << 16) |
-                            ((input[p++] & 0xff) << 8) |
-                            (input[p++] & 0xff);
-                        tailLen = 0;
-                    }
-                    break;
-
-                case 2:
-                    if (p+1 <= len) {
-                        // A 2-byte tail with at least 1 byte of input.
-                        v = ((tail[0] & 0xff) << 16) |
-                            ((tail[1] & 0xff) << 8) |
-                            (input[p++] & 0xff);
-                        tailLen = 0;
-                    }
-                    break;
-            }
-
-            if (v != -1) {
-                output[op++] = alphabet[(v >> 18) & 0x3f];
-                output[op++] = alphabet[(v >> 12) & 0x3f];
-                output[op++] = alphabet[(v >> 6) & 0x3f];
-                output[op++] = alphabet[v & 0x3f];
-                if (--count == 0) {
-                    if (do_cr) output[op++] = '\r';
-                    output[op++] = '\n';
-                    count = LINE_GROUPS;
-                }
-            }
-
-            // At this point either there is no tail, or there are fewer
-            // than 3 bytes of input available.
-
-            // The main loop, turning 3 input bytes into 4 output bytes on
-            // each iteration.
-            while (p+3 <= len) {
-                v = ((input[p] & 0xff) << 16) |
-                    ((input[p+1] & 0xff) << 8) |
-                    (input[p+2] & 0xff);
-                output[op] = alphabet[(v >> 18) & 0x3f];
-                output[op+1] = alphabet[(v >> 12) & 0x3f];
-                output[op+2] = alphabet[(v >> 6) & 0x3f];
-                output[op+3] = alphabet[v & 0x3f];
-                p += 3;
-                op += 4;
-                if (--count == 0) {
-                    if (do_cr) output[op++] = '\r';
-                    output[op++] = '\n';
-                    count = LINE_GROUPS;
-                }
-            }
-
-            if (finish) {
-                // Finish up the tail of the input.  Note that we need to
-                // consume any bytes in tail before any bytes
-                // remaining in input; there should be at most two bytes
-                // total.
-
-                if (p-tailLen == len-1) {
-                    int t = 0;
-                    v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4;
-                    tailLen -= t;
-                    output[op++] = alphabet[(v >> 6) & 0x3f];
-                    output[op++] = alphabet[v & 0x3f];
-                    if (do_padding) {
-                        output[op++] = '=';
-                        output[op++] = '=';
-                    }
-                    if (do_newline) {
-                        if (do_cr) output[op++] = '\r';
-                        output[op++] = '\n';
-                    }
-                } else if (p-tailLen == len-2) {
-                    int t = 0;
-                    v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) |
-                        (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2);
-                    tailLen -= t;
-                    output[op++] = alphabet[(v >> 12) & 0x3f];
-                    output[op++] = alphabet[(v >> 6) & 0x3f];
-                    output[op++] = alphabet[v & 0x3f];
-                    if (do_padding) {
-                        output[op++] = '=';
-                    }
-                    if (do_newline) {
-                        if (do_cr) output[op++] = '\r';
-                        output[op++] = '\n';
-                    }
-                } else if (do_newline && op > 0 && count != LINE_GROUPS) {
-                    if (do_cr) output[op++] = '\r';
-                    output[op++] = '\n';
-                }
-
-                assert tailLen == 0;
-                assert p == len;
-            } else {
-                // Save the leftovers in tail to be consumed on the next
-                // call to encodeInternal.
-
-                if (p == len-1) {
-                    tail[tailLen++] = input[p];
-                } else if (p == len-2) {
-                    tail[tailLen++] = input[p];
-                    tail[tailLen++] = input[p+1];
-                }
-            }
-
-            this.op = op;
-            this.count = count;
-
-            return true;
-        }
-    }
-
-    private Base64() { }   // don't instantiate
-}
diff --git a/src/be/nikiroo/utils/streams/Base64InputStream.java b/src/be/nikiroo/utils/streams/Base64InputStream.java
deleted file mode 100644 (file)
index a3afaef..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package be.nikiroo.utils.streams;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * An InputStream that does Base64 decoding on the data read through
- * it.
- */
-public class Base64InputStream extends FilterInputStream {
-    private final Base64.Coder coder;
-
-    private static byte[] EMPTY = new byte[0];
-
-    private static final int BUFFER_SIZE = 2048;
-    private boolean eof;
-    private byte[] inputBuffer;
-    private int outputStart;
-    private int outputEnd;
-
-    /**
-     * An InputStream that performs Base64 decoding on the data read
-     * from the wrapped stream.
-     *
-     * @param in the InputStream to read the source data from
-     */
-    public Base64InputStream(InputStream in) {
-        this(in, false);
-    }
-
-    /**
-     * Performs Base64 encoding or decoding on the data read from the
-     * wrapped InputStream.
-     *
-     * @param in the InputStream to read the source data from
-     * @param flags bit flags for controlling the decoder; see the
-     *        constants in {@link Base64}
-     * @param encode true to encode, false to decode
-     *
-     * @hide
-     */
-    public Base64InputStream(InputStream in, boolean encode) {
-        super(in);
-        eof = false;
-        inputBuffer = new byte[BUFFER_SIZE];
-        if (encode) {
-            coder = new Base64.Encoder(Base64.NO_WRAP, null);
-        } else {
-            coder = new Base64.Decoder(Base64.NO_WRAP, null);
-        }
-        coder.output = new byte[coder.maxOutputSize(BUFFER_SIZE)];
-        outputStart = 0;
-        outputEnd = 0;
-    }
-
-    @Override
-       public boolean markSupported() {
-        return false;
-    }
-
-    @SuppressWarnings("sync-override")
-       @Override
-       public void mark(int readlimit) {
-        throw new UnsupportedOperationException();
-    }
-
-    @SuppressWarnings("sync-override")
-       @Override
-       public void reset() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-       public void close() throws IOException {
-        in.close();
-        inputBuffer = null;
-    }
-
-    @Override
-       public int available() {
-        return outputEnd - outputStart;
-    }
-
-    @Override
-       public long skip(long n) throws IOException {
-        if (outputStart >= outputEnd) {
-            refill();
-        }
-        if (outputStart >= outputEnd) {
-            return 0;
-        }
-        long bytes = Math.min(n, outputEnd-outputStart);
-        outputStart += bytes;
-        return bytes;
-    }
-
-    @Override
-       public int read() throws IOException {
-        if (outputStart >= outputEnd) {
-            refill();
-        }
-        if (outputStart >= outputEnd) {
-            return -1;
-        }
-        
-        return coder.output[outputStart++] & 0xff;
-    }
-
-    @Override
-       public int read(byte[] b, int off, int len) throws IOException {
-        if (outputStart >= outputEnd) {
-            refill();
-        }
-        if (outputStart >= outputEnd) {
-            return -1;
-        }
-        int bytes = Math.min(len, outputEnd-outputStart);
-        System.arraycopy(coder.output, outputStart, b, off, bytes);
-        outputStart += bytes;
-        return bytes;
-    }
-
-    /**
-     * Read data from the input stream into inputBuffer, then
-     * decode/encode it into the empty coder.output, and reset the
-     * outputStart and outputEnd pointers.
-     */
-    private void refill() throws IOException {
-        if (eof) return;
-        int bytesRead = in.read(inputBuffer);
-        boolean success;
-        if (bytesRead == -1) {
-            eof = true;
-            success = coder.process(EMPTY, 0, 0, true);
-        } else {
-            success = coder.process(inputBuffer, 0, bytesRead, false);
-        }
-        if (!success) {
-            throw new IOException("bad base-64");
-        }
-        outputEnd = coder.op;
-        outputStart = 0;
-    }
-}
diff --git a/src/be/nikiroo/utils/streams/Base64OutputStream.java b/src/be/nikiroo/utils/streams/Base64OutputStream.java
deleted file mode 100644 (file)
index ab4e457..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package be.nikiroo.utils.streams;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An OutputStream that does Base64 encoding on the data written to
- * it, writing the resulting data to another OutputStream.
- */
-public class Base64OutputStream extends FilterOutputStream {
-    private final Base64.Coder coder;
-    private final int flags;
-
-    private byte[] buffer = null;
-    private int bpos = 0;
-
-    private static byte[] EMPTY = new byte[0];
-
-    /**
-     * Performs Base64 encoding on the data written to the stream,
-     * writing the encoded data to another OutputStream.
-     *
-     * @param out the OutputStream to write the encoded data to
-     */
-    public Base64OutputStream(OutputStream out) {
-        this(out, true);
-    }
-
-    /**
-     * Performs Base64 encoding or decoding on the data written to the
-     * stream, writing the encoded/decoded data to another
-     * OutputStream.
-     *
-     * @param out the OutputStream to write the encoded data to
-     * @param encode true to encode, false to decode
-     *
-     * @hide
-     */
-    public Base64OutputStream(OutputStream out, boolean encode) {
-        super(out);
-        this.flags = Base64.NO_WRAP;
-        if (encode) {
-            coder = new Base64.Encoder(flags, null);
-        } else {
-            coder = new Base64.Decoder(flags, null);
-        }
-    }
-
-    @Override
-       public void write(int b) throws IOException {
-        // To avoid invoking the encoder/decoder routines for single
-        // bytes, we buffer up calls to write(int) in an internal
-        // byte array to transform them into writes of decently-sized
-        // arrays.
-
-        if (buffer == null) {
-            buffer = new byte[1024];
-        }
-        if (bpos >= buffer.length) {
-            // internal buffer full; write it out.
-            internalWrite(buffer, 0, bpos, false);
-            bpos = 0;
-        }
-        buffer[bpos++] = (byte) b;
-    }
-
-    /**
-     * Flush any buffered data from calls to write(int).  Needed
-     * before doing a write(byte[], int, int) or a close().
-     */
-    private void flushBuffer() throws IOException {
-        if (bpos > 0) {
-            internalWrite(buffer, 0, bpos, false);
-            bpos = 0;
-        }
-    }
-
-    @Override
-       public void write(byte[] b, int off, int len) throws IOException {
-        if (len <= 0) return;
-        flushBuffer();
-        internalWrite(b, off, len, false);
-    }
-
-    @Override
-       public void close() throws IOException {
-        IOException thrown = null;
-        try {
-            flushBuffer();
-            internalWrite(EMPTY, 0, 0, true);
-        } catch (IOException e) {
-            thrown = e;
-        }
-
-        try {
-            if ((flags & Base64.NO_CLOSE) == 0) {
-                out.close();
-            } else {
-                out.flush();
-            }
-        } catch (IOException e) {
-            if (thrown != null) {
-                thrown = e;
-            }
-        }
-
-        if (thrown != null) {
-            throw thrown;
-        }
-    }
-
-    /**
-     * Write the given bytes to the encoder/decoder.
-     *
-     * @param finish true if this is the last batch of input, to cause
-     *        encoder/decoder state to be finalized.
-     */
-    private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
-        coder.output = embiggen(coder.output, coder.maxOutputSize(len));
-        if (!coder.process(b, off, len, finish)) {
-            throw new IOException("bad base-64");
-        }
-        out.write(coder.output, 0, coder.op);
-    }
-
-    /**
-     * If b.length is at least len, return b.  Otherwise return a new
-     * byte array of length len.
-     */
-    private byte[] embiggen(byte[] b, int len) {
-        if (b == null || b.length < len) {
-            return new byte[len];
-        }
-        return b;
-    }
-}
diff --git a/src/be/nikiroo/utils/streams/BufferedInputStream.java b/src/be/nikiroo/utils/streams/BufferedInputStream.java
deleted file mode 100644 (file)
index 3cad70d..0000000
+++ /dev/null
@@ -1,610 +0,0 @@
-package be.nikiroo.utils.streams;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map.Entry;
-
-import be.nikiroo.utils.StringUtils;
-
-/**
- * A simple {@link InputStream} that is buffered with a bytes array.
- * <p>
- * It is mostly intended to be used as a base class to create new
- * {@link InputStream}s with special operation modes, and to give some default
- * methods.
- * 
- * @author niki
- */
-public class BufferedInputStream extends InputStream {
-       /**
-        * The size of the internal buffer (can be different if you pass your own
-        * buffer, of course, and can also expand to search for longer "startsWith"
-        * data).
-        * <p>
-        * Note that special "push-back" buffers can also be created during the life
-        * of this stream.
-        */
-       static private final int BUFFER_SIZE = 4096;
-
-       /** The current position in the buffer. */
-       protected int start;
-       /** The index of the last usable position of the buffer. */
-       protected int stop;
-       /** The buffer itself. */
-       protected byte[] buffer;
-       /** An End-Of-File (or {@link InputStream}, here) marker. */
-       protected boolean eof;
-
-       private boolean closed;
-       private InputStream in;
-       private int openCounter;
-       private byte[] singleByteReader = new byte[1];
-
-       /** array + offset of pushed-back buffers */
-       private List<Entry<byte[], Integer>> backBuffers;
-
-       private long bytesRead;
-
-       /**
-        * Create a new {@link BufferedInputStream} that wraps the given
-        * {@link InputStream}.
-        * 
-        * @param in
-        *            the {@link InputStream} to wrap
-        */
-       public BufferedInputStream(InputStream in) {
-               this.in = in;
-
-               this.buffer = new byte[BUFFER_SIZE];
-               this.start = 0;
-               this.stop = 0;
-               this.backBuffers = new ArrayList<Entry<byte[], Integer>>();
-       }
-
-       /**
-        * Create a new {@link BufferedInputStream} that wraps the given bytes array
-        * as a data source.
-        * 
-        * @param in
-        *            the array to wrap, cannot be NULL
-        */
-       public BufferedInputStream(byte[] in) {
-               this(in, 0, in.length);
-       }
-
-       /**
-        * Create a new {@link BufferedInputStream} that wraps the given bytes array
-        * as a data source.
-        * 
-        * @param in
-        *            the array to wrap, cannot be NULL
-        * @param offset
-        *            the offset to start the reading at
-        * @param length
-        *            the number of bytes to take into account in the array,
-        *            starting from the offset
-        * 
-        * @throws NullPointerException
-        *             if the array is NULL
-        * @throws IndexOutOfBoundsException
-        *             if the offset and length do not correspond to the given array
-        */
-       public BufferedInputStream(byte[] in, int offset, int length) {
-               if (in == null) {
-                       throw new NullPointerException();
-               } else if (offset < 0 || length < 0 || length > in.length - offset) {
-                       throw new IndexOutOfBoundsException();
-               }
-
-               this.in = null;
-
-               this.buffer = in;
-               this.start = offset;
-               this.stop = length;
-               this.backBuffers = new ArrayList<Entry<byte[], Integer>>();
-       }
-
-       /**
-        * Return this very same {@link BufferedInputStream}, but keep a counter of
-        * how many streams were open this way. When calling
-        * {@link BufferedInputStream#close()}, decrease this counter if it is not
-        * already zero instead of actually closing the stream.
-        * <p>
-        * You are now responsible for it &mdash; you <b>must</b> close it.
-        * <p>
-        * This method allows you to use a wrapping stream around this one and still
-        * close the wrapping stream.
-        * 
-        * @return the same stream, but you are now responsible for closing it
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the stream is closed
-        */
-       public synchronized InputStream open() throws IOException {
-               checkClose();
-               openCounter++;
-               return this;
-       }
-
-       /**
-        * Check if the current content (until eof) is equal to the given search
-        * term.
-        * <p>
-        * Note: the search term size <b>must</b> be smaller or equal the internal
-        * buffer size.
-        * 
-        * @param search
-        *            the term to search for
-        * 
-        * @return TRUE if the content that will be read starts with it
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the size of the search term is
-        *             greater than the internal buffer
-        */
-       public boolean is(String search) throws IOException {
-               return is(StringUtils.getBytes(search));
-       }
-
-       /**
-        * Check if the current content (until eof) is equal to the given search
-        * term.
-        * <p>
-        * Note: the search term size <b>must</b> be smaller or equal the internal
-        * buffer size.
-        * 
-        * @param search
-        *            the term to search for
-        * 
-        * @return TRUE if the content that will be read starts with it
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the size of the search term is
-        *             greater than the internal buffer
-        */
-       public boolean is(byte[] search) throws IOException {
-               if (startsWith(search)) {
-                       return available() == search.length;
-               }
-
-               return false;
-       }
-
-       /**
-        * Check if the current content (what will be read next) starts with the
-        * given search term.
-        * <p>
-        * Note: the search term size <b>must</b> be smaller or equal the internal
-        * buffer size.
-        * 
-        * @param search
-        *            the term to search for
-        * 
-        * @return TRUE if the content that will be read starts with it
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the size of the search term is
-        *             greater than the internal buffer
-        */
-       public boolean startsWith(String search) throws IOException {
-               return startsWith(StringUtils.getBytes(search));
-       }
-
-       /**
-        * Check if the current content (what will be read next) starts with the
-        * given search term.
-        * <p>
-        * An empty string will always return true (unless the stream is closed,
-        * which would throw an {@link IOException}).
-        * <p>
-        * Note: the search term size <b>must</b> be smaller or equal the internal
-        * buffer size.
-        * 
-        * @param search
-        *            the term to search for
-        * 
-        * @return TRUE if the content that will be read starts with it
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the size of the search term is
-        *             greater than the internal buffer
-        */
-       public boolean startsWith(byte[] search) throws IOException {
-               checkClose();
-
-               while (consolidatePushBack(search.length) < search.length) {
-                       preRead();
-                       if (start >= stop) {
-                               // Not enough data left to start with that
-                               return false;
-                       }
-
-                       byte[] newBuffer = new byte[stop - start];
-                       System.arraycopy(buffer, start, newBuffer, 0, stop - start);
-                       pushback(newBuffer, 0);
-                       start = stop;
-               }
-
-               Entry<byte[], Integer> bb = backBuffers.get(backBuffers.size() - 1);
-               byte[] bbBuffer = bb.getKey();
-               int bbOffset = bb.getValue();
-
-               return StreamUtils.startsWith(search, bbBuffer, bbOffset,
-                               bbBuffer.length);
-       }
-
-       /**
-        * The number of bytes read from the under-laying {@link InputStream}.
-        * 
-        * @return the number of bytes
-        */
-       public long getBytesRead() {
-               return bytesRead;
-       }
-
-       /**
-        * Check if this stream is spent (no more data to read or to process).
-        * 
-        * @return TRUE if it is
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public boolean eof() throws IOException {
-               if (closed) {
-                       return true;
-               }
-
-               preRead();
-               return !hasMoreData();
-       }
-
-       /**
-        * Read the whole {@link InputStream} until the end and return the number of
-        * bytes read.
-        * 
-        * @return the number of bytes read
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public long end() throws IOException {
-               long skipped = 0;
-               while (hasMoreData()) {
-                       skipped += skip(buffer.length);
-               }
-
-               return skipped;
-       }
-
-       @Override
-       public int read() throws IOException {
-               if (read(singleByteReader) < 0) {
-                       return -1;
-               }
-
-               return singleByteReader[0];
-       }
-
-       @Override
-       public int read(byte[] b) throws IOException {
-               return read(b, 0, b.length);
-       }
-
-       @Override
-       public int read(byte[] b, int boff, int blen) throws IOException {
-               checkClose();
-
-               if (b == null) {
-                       throw new NullPointerException();
-               } else if (boff < 0 || blen < 0 || blen > b.length - boff) {
-                       throw new IndexOutOfBoundsException();
-               } else if (blen == 0) {
-                       return 0;
-               }
-
-               // Read from the pushed-back buffers if any
-               if (backBuffers.isEmpty()) {
-                       preRead(); // an implementation could pushback in preRead()
-               }
-               
-               if (!backBuffers.isEmpty()) {
-                       int read = 0;
-
-                       Entry<byte[], Integer> bb = backBuffers
-                                       .remove(backBuffers.size() - 1);
-                       byte[] bbBuffer = bb.getKey();
-                       int bbOffset = bb.getValue();
-                       int bbSize = bbBuffer.length - bbOffset;
-                       
-                       if (bbSize > blen) {
-                               read = blen;
-                               System.arraycopy(bbBuffer, bbOffset, b, boff, read);
-                               pushback(bbBuffer, bbOffset + read);
-                       } else {
-                               read = bbSize;
-                               System.arraycopy(bbBuffer, bbOffset, b, boff, read);
-                       }
-
-                       return read;
-               }
-
-               int done = 0;
-               while (hasMoreData() && done < blen) {
-                       preRead();
-                       if (hasMoreData()) {
-                               int now = Math.min(blen - done, stop - start);
-                               if (now > 0) {
-                                       System.arraycopy(buffer, start, b, boff + done, now);
-                                       start += now;
-                                       done += now;
-                               }
-                       }
-               }
-
-               return done > 0 ? done : -1;
-       }
-
-       @Override
-       public long skip(long n) throws IOException {
-               if (n <= 0) {
-                       return 0;
-               }
-
-               long skipped = 0;
-               while (!backBuffers.isEmpty() && n > 0) {
-                       Entry<byte[], Integer> bb = backBuffers
-                                       .remove(backBuffers.size() - 1);
-                       byte[] bbBuffer = bb.getKey();
-                       int bbOffset = bb.getValue();
-                       int bbSize = bbBuffer.length - bbOffset;
-
-                       int localSkip = 0;
-                       localSkip = (int) Math.min(n, bbSize);
-
-                       n -= localSkip;
-                       bbSize -= localSkip;
-
-                       if (bbSize > 0) {
-                               pushback(bbBuffer, bbOffset + localSkip);
-                       }
-               }
-               while (hasMoreData() && n > 0) {
-                       preRead();
-
-                       long inBuffer = Math.min(n, available());
-                       start += inBuffer;
-                       n -= inBuffer;
-                       skipped += inBuffer;
-               }
-
-               return skipped;
-       }
-
-       @Override
-       public int available() {
-               if (closed) {
-                       return 0;
-               }
-
-               int avail = 0;
-               for (Entry<byte[], Integer> entry : backBuffers) {
-                       avail += entry.getKey().length - entry.getValue();
-               }
-
-               return avail + Math.max(0, stop - start);
-       }
-
-       /**
-        * Closes this stream and releases any system resources associated with the
-        * stream.
-        * <p>
-        * Including the under-laying {@link InputStream}.
-        * <p>
-        * <b>Note:</b> if you called the {@link BufferedInputStream#open()} method
-        * prior to this one, it will just decrease the internal count of how many
-        * open streams it held and do nothing else. The stream will actually be
-        * closed when you have called {@link BufferedInputStream#close()} once more
-        * than {@link BufferedInputStream#open()}.
-        * 
-        * @exception IOException
-        *                in case of I/O error
-        */
-       @Override
-       public synchronized void close() throws IOException {
-               close(true);
-       }
-
-       /**
-        * Closes this stream and releases any system resources associated with the
-        * stream.
-        * <p>
-        * Including the under-laying {@link InputStream} if
-        * <tt>incudingSubStream</tt> is true.
-        * <p>
-        * You can call this method multiple times, it will not cause an
-        * {@link IOException} for subsequent calls.
-        * <p>
-        * <b>Note:</b> if you called the {@link BufferedInputStream#open()} method
-        * prior to this one, it will just decrease the internal count of how many
-        * open streams it held and do nothing else. The stream will actually be
-        * closed when you have called {@link BufferedInputStream#close()} once more
-        * than {@link BufferedInputStream#open()}.
-        * 
-        * @param includingSubStream
-        *            also close the under-laying stream
-        * 
-        * @exception IOException
-        *                in case of I/O error
-        */
-       public synchronized void close(boolean includingSubStream)
-                       throws IOException {
-               if (!closed) {
-                       if (openCounter > 0) {
-                               openCounter--;
-                       } else {
-                               closed = true;
-                               if (includingSubStream && in != null) {
-                                       in.close();
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Consolidate the push-back buffers so the last one is at least the given
-        * size, if possible.
-        * <p>
-        * If there is not enough data in the push-back buffers, they will all be
-        * consolidated.
-        * 
-        * @param size
-        *            the minimum size of the consolidated buffer, or -1 to force
-        *            the consolidation of all push-back buffers
-        * 
-        * @return the size of the last, consolidated buffer; can be less than the
-        *         requested size if not enough data
-        */
-       protected int consolidatePushBack(int size) {
-               int bbIndex = -1;
-               int bbUpToSize = 0;
-               for (Entry<byte[], Integer> entry : backBuffers) {
-                       bbIndex++;
-                       bbUpToSize += entry.getKey().length - entry.getValue();
-
-                       if (size >= 0 && bbUpToSize >= size) {
-                               break;
-                       }
-               }
-
-               // Index 0 means "the last buffer is already big enough"
-               if (bbIndex > 0) {
-                       byte[] consolidatedBuffer = new byte[bbUpToSize];
-                       int consolidatedPos = 0;
-                       for (int i = 0; i <= bbIndex; i++) {
-                               Entry<byte[], Integer> bb = backBuffers
-                                               .remove(backBuffers.size() - 1);
-                               byte[] bbBuffer = bb.getKey();
-                               int bbOffset = bb.getValue();
-                               int bbSize = bbBuffer.length - bbOffset;
-                               System.arraycopy(bbBuffer, bbOffset, consolidatedBuffer,
-                                               consolidatedPos, bbSize);
-                       }
-
-                       pushback(consolidatedBuffer, 0);
-               }
-
-               return bbUpToSize;
-       }
-
-       /**
-        * Check if we still have some data in the buffer and, if not, fetch some.
-        * 
-        * @return TRUE if we fetched some data, FALSE if there are still some in
-        *         the buffer
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       protected boolean preRead() throws IOException {
-               boolean hasRead = false;
-               if (in != null && !eof && start >= stop) {
-                       start = 0;
-                       stop = read(in, buffer);
-                       if (stop > 0) {
-                               bytesRead += stop;
-                       }
-
-                       hasRead = true;
-               }
-
-               if (start >= stop) {
-                       eof = true;
-               }
-
-               return hasRead;
-       }
-
-       /**
-        * Push back some data that will be read again at the next read call.
-        * 
-        * @param buffer
-        *            the buffer to push back
-        * @param offset
-        *            the offset at which to start reading in the buffer
-        */
-       protected void pushback(byte[] buffer, int offset) {
-               backBuffers.add(
-                               new AbstractMap.SimpleEntry<byte[], Integer>(buffer, offset));
-       }
-
-       /**
-        * Push back some data that will be read again at the next read call.
-        * 
-        * @param buffer
-        *            the buffer to push back
-        * @param offset
-        *            the offset at which to start reading in the buffer
-        * @param len
-        *            the length to copy
-        */
-       protected void pushback(byte[] buffer, int offset, int len) {
-               // TODO: not efficient!
-               if (buffer.length != len) {
-                       byte[] lenNotSupportedYet = new byte[len];
-                       System.arraycopy(buffer, offset, lenNotSupportedYet, 0, len);
-                       buffer = lenNotSupportedYet;
-                       offset = 0;
-               }
-
-               pushback(buffer, offset);
-       }
-
-       /**
-        * Read the under-laying stream into the given local buffer.
-        * 
-        * @param in
-        *            the under-laying {@link InputStream}
-        * @param buffer
-        *            the buffer we use in this {@link BufferedInputStream}
-        * 
-        * @return the number of bytes read
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       protected int read(InputStream in, byte[] buffer) throws IOException {
-               return in.read(buffer, 0, buffer.length);
-       }
-
-       /**
-        * We have more data available in the buffer <b>or</b> we can, maybe, fetch
-        * more.
-        * 
-        * @return TRUE if it is the case, FALSE if not
-        */
-       protected boolean hasMoreData() {
-               if (closed) {
-                       return false;
-               }
-
-               return !backBuffers.isEmpty() || (start < stop) || !eof;
-       }
-
-       /**
-        * Check that the stream was not closed, and throw an {@link IOException} if
-        * it was.
-        * 
-        * @throws IOException
-        *             if it was closed
-        */
-       protected void checkClose() throws IOException {
-               if (closed) {
-                       throw new IOException(
-                                       "This BufferedInputStream was closed, you cannot use it anymore.");
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/streams/BufferedOutputStream.java b/src/be/nikiroo/utils/streams/BufferedOutputStream.java
deleted file mode 100644 (file)
index 1442534..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-package be.nikiroo.utils.streams;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * A simple {@link OutputStream} that is buffered with a bytes array.
- * <p>
- * It is mostly intended to be used as a base class to create new
- * {@link OutputStream}s with special operation modes, and to give some default
- * methods.
- * 
- * @author niki
- */
-public class BufferedOutputStream extends OutputStream {
-       /** The current position in the buffer. */
-       protected int start;
-       /** The index of the last usable position of the buffer. */
-       protected int stop;
-       /** The buffer itself. */
-       protected byte[] buffer;
-       /** An End-Of-File (or buffer, here) marker. */
-       protected boolean eof;
-       /** The actual under-laying stream. */
-       protected OutputStream out;
-       /** The number of bytes written to the under-laying stream. */
-       protected long bytesWritten;
-       /**
-        * Can bypass the flush process for big writes (will directly write to the
-        * under-laying buffer if the array to write is &gt; the internal buffer
-        * size).
-        * <p>
-        * By default, this is true.
-        */
-       protected boolean bypassFlush = true;
-
-       private boolean closed;
-       private int openCounter;
-       private byte[] b1;
-
-       /**
-        * Create a new {@link BufferedInputStream} that wraps the given
-        * {@link InputStream}.
-        * 
-        * @param out
-        *            the {@link OutputStream} to wrap
-        */
-       public BufferedOutputStream(OutputStream out) {
-               this.out = out;
-
-               this.buffer = new byte[4096];
-               this.b1 = new byte[1];
-               this.start = 0;
-               this.stop = 0;
-       }
-
-       @Override
-       public void write(int b) throws IOException {
-               b1[0] = (byte) b;
-               write(b1, 0, 1);
-       }
-
-       @Override
-       public void write(byte[] b) throws IOException {
-               write(b, 0, b.length);
-       }
-
-       @Override
-       public void write(byte[] source, int sourceOffset, int sourceLength)
-                       throws IOException {
-
-               checkClose();
-
-               if (source == null) {
-                       throw new NullPointerException();
-               } else if ((sourceOffset < 0) || (sourceOffset > source.length)
-                               || (sourceLength < 0)
-                               || ((sourceOffset + sourceLength) > source.length)
-                               || ((sourceOffset + sourceLength) < 0)) {
-                       throw new IndexOutOfBoundsException();
-               } else if (sourceLength == 0) {
-                       return;
-               }
-
-               if (bypassFlush && sourceLength >= buffer.length) {
-                       /*
-                        * If the request length exceeds the size of the output buffer,
-                        * flush the output buffer and then write the data directly. In this
-                        * way buffered streams will cascade harmlessly.
-                        */
-                       flush(false);
-                       out.write(source, sourceOffset, sourceLength);
-                       bytesWritten += (sourceLength - sourceOffset);
-                       return;
-               }
-
-               int done = 0;
-               while (done < sourceLength) {
-                       if (available() <= 0) {
-                               flush(false);
-                       }
-
-                       int now = Math.min(sourceLength - done, available());
-                       System.arraycopy(source, sourceOffset + done, buffer, stop, now);
-                       stop += now;
-                       done += now;
-               }
-       }
-
-       /**
-        * The available space in the buffer.
-        * 
-        * @return the space in bytes
-        */
-       private int available() {
-               if (closed) {
-                       return 0;
-               }
-
-               return Math.max(0, buffer.length - stop - 1);
-       }
-
-       /**
-        * The number of bytes written to the under-laying {@link OutputStream}.
-        * 
-        * @return the number of bytes
-        */
-       public long getBytesWritten() {
-               return bytesWritten;
-       }
-
-       /**
-        * Return this very same {@link BufferedInputStream}, but keep a counter of
-        * how many streams were open this way. When calling
-        * {@link BufferedInputStream#close()}, decrease this counter if it is not
-        * already zero instead of actually closing the stream.
-        * <p>
-        * You are now responsible for it &mdash; you <b>must</b> close it.
-        * <p>
-        * This method allows you to use a wrapping stream around this one and still
-        * close the wrapping stream.
-        * 
-        * @return the same stream, but you are now responsible for closing it
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the stream is closed
-        */
-       public synchronized OutputStream open() throws IOException {
-               checkClose();
-               openCounter++;
-               return this;
-       }
-
-       /**
-        * Check that the stream was not closed, and throw an {@link IOException} if
-        * it was.
-        * 
-        * @throws IOException
-        *             if it was closed
-        */
-       protected void checkClose() throws IOException {
-               if (closed) {
-                       throw new IOException(
-                                       "This BufferedInputStream was closed, you cannot use it anymore.");
-               }
-       }
-
-       @Override
-       public void flush() throws IOException {
-               flush(true);
-       }
-
-       /**
-        * Flush the {@link BufferedOutputStream}, write the current buffered data
-        * to (and optionally also flush) the under-laying stream.
-        * <p>
-        * If {@link BufferedOutputStream#bypassFlush} is false, all writes to the
-        * under-laying stream are done in this method.
-        * <p>
-        * This can be used if you want to write some data in the under-laying
-        * stream yourself (in that case, flush this {@link BufferedOutputStream}
-        * with or without flushing the under-laying stream, then you can write to
-        * the under-laying stream).
-        * 
-        * @param includingSubStream
-        *            also flush the under-laying stream
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public void flush(boolean includingSubStream) throws IOException {
-               if (stop > start) {
-                       out.write(buffer, start, stop - start);
-                       bytesWritten += (stop - start);
-               }
-               start = 0;
-               stop = 0;
-
-               if (includingSubStream) {
-                       out.flush();
-               }
-       }
-
-       /**
-        * Closes this stream and releases any system resources associated with the
-        * stream.
-        * <p>
-        * Including the under-laying {@link InputStream}.
-        * <p>
-        * <b>Note:</b> if you called the {@link BufferedInputStream#open()} method
-        * prior to this one, it will just decrease the internal count of how many
-        * open streams it held and do nothing else. The stream will actually be
-        * closed when you have called {@link BufferedInputStream#close()} once more
-        * than {@link BufferedInputStream#open()}.
-        * 
-        * @exception IOException
-        *                in case of I/O error
-        */
-       @Override
-       public synchronized void close() throws IOException {
-               close(true);
-       }
-
-       /**
-        * Closes this stream and releases any system resources associated with the
-        * stream.
-        * <p>
-        * Including the under-laying {@link InputStream} if
-        * <tt>incudingSubStream</tt> is true.
-        * <p>
-        * You can call this method multiple times, it will not cause an
-        * {@link IOException} for subsequent calls.
-        * <p>
-        * <b>Note:</b> if you called the {@link BufferedInputStream#open()} method
-        * prior to this one, it will just decrease the internal count of how many
-        * open streams it held and do nothing else. The stream will actually be
-        * closed when you have called {@link BufferedInputStream#close()} once more
-        * than {@link BufferedInputStream#open()}.
-        * 
-        * @param includingSubStream
-        *            also close the under-laying stream
-        * 
-        * @exception IOException
-        *                in case of I/O error
-        */
-       public synchronized void close(boolean includingSubStream)
-                       throws IOException {
-               if (!closed) {
-                       if (openCounter > 0) {
-                               openCounter--;
-                       } else {
-                               closed = true;
-                               flush(includingSubStream);
-                               if (includingSubStream && out != null) {
-                                       out.close();
-                               }
-                       }
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/streams/MarkableFileInputStream.java b/src/be/nikiroo/utils/streams/MarkableFileInputStream.java
deleted file mode 100644 (file)
index 7622b24..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-package be.nikiroo.utils.streams;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.nio.channels.FileChannel;
-
-/**
- * This is a markable (and thus reset-able) stream that you can create from a
- * FileInputStream.
- * 
- * @author niki
- */
-public class MarkableFileInputStream extends FilterInputStream {
-       private FileChannel channel;
-       private long mark = 0;
-
-       /**
-        * Create a new {@link MarkableFileInputStream} from this file.
-        * 
-        * @param file
-        *            the {@link File} to wrap
-        * 
-        * @throws FileNotFoundException
-        *             if the {@link File} cannot be found
-        */
-       public MarkableFileInputStream(File file) throws FileNotFoundException {
-               this(new FileInputStream(file));
-       }
-
-       /**
-        * Create a new {@link MarkableFileInputStream} from this stream.
-        * 
-        * @param in
-        *            the original {@link FileInputStream} to wrap
-        */
-       public MarkableFileInputStream(FileInputStream in) {
-               super(in);
-               channel = in.getChannel();
-       }
-
-       @Override
-       public boolean markSupported() {
-               return true;
-       }
-
-       @Override
-       public synchronized void mark(int readlimit) {
-               try {
-                       mark = channel.position();
-               } catch (IOException ex) {
-                       ex.printStackTrace();
-                       mark = -1;
-               }
-       }
-
-       @Override
-       public synchronized void reset() throws IOException {
-               if (mark < 0) {
-                       throw new IOException("mark position not valid");
-               }
-               channel.position(mark);
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/streams/NextableInputStream.java b/src/be/nikiroo/utils/streams/NextableInputStream.java
deleted file mode 100644 (file)
index dcab472..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-package be.nikiroo.utils.streams;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
-
-/**
- * This {@link InputStream} can be separated into sub-streams (you can process
- * it as a normal {@link InputStream} but, when it is spent, you can call
- * {@link NextableInputStream#next()} on it to unlock new data).
- * <p>
- * The separation in sub-streams is done via {@link NextableInputStreamStep}.
- * 
- * @author niki
- */
-public class NextableInputStream extends BufferedInputStream {
-       private NextableInputStreamStep step;
-       private boolean started;
-       private boolean stopped;
-
-       /**
-        * Create a new {@link NextableInputStream} that wraps the given
-        * {@link InputStream}.
-        * 
-        * @param in
-        *            the {@link InputStream} to wrap
-        * @param step
-        *            how to separate it into sub-streams (can be NULL, but in that
-        *            case it will behave as a normal {@link InputStream})
-        */
-       public NextableInputStream(InputStream in, NextableInputStreamStep step) {
-               super(in);
-               this.step = step;
-       }
-
-       /**
-        * Create a new {@link NextableInputStream} that wraps the given bytes array
-        * as a data source.
-        * 
-        * @param in
-        *            the array to wrap, cannot be NULL
-        * @param step
-        *            how to separate it into sub-streams (can be NULL, but in that
-        *            case it will behave as a normal {@link InputStream})
-        */
-       public NextableInputStream(byte[] in, NextableInputStreamStep step) {
-               this(in, step, 0, in.length);
-       }
-
-       /**
-        * Create a new {@link NextableInputStream} that wraps the given bytes array
-        * as a data source.
-        * 
-        * @param in
-        *            the array to wrap, cannot be NULL
-        * @param step
-        *            how to separate it into sub-streams (can be NULL, but in that
-        *            case it will behave as a normal {@link InputStream})
-        * @param offset
-        *            the offset to start the reading at
-        * @param length
-        *            the number of bytes to take into account in the array,
-        *            starting from the offset
-        * 
-        * @throws NullPointerException
-        *             if the array is NULL
-        * @throws IndexOutOfBoundsException
-        *             if the offset and length do not correspond to the given array
-        */
-       public NextableInputStream(byte[] in, NextableInputStreamStep step,
-                       int offset, int length) {
-               super(in, offset, length);
-               this.step = step;
-               checkBuffer(true);
-       }
-
-       /**
-        * Unblock the processing of the next sub-stream.
-        * <p>
-        * It can only be called when the "current" stream is spent (i.e., you must
-        * first process the stream until it is spent).
-        * <p>
-        * {@link IOException}s can happen when we have no data available in the
-        * buffer; in that case, we fetch more data to know if we can have a next
-        * sub-stream or not.
-        * <p>
-        * This is can be a blocking call when data need to be fetched.
-        * 
-        * @return TRUE if we unblocked the next sub-stream, FALSE if not (i.e.,
-        *         FALSE when there are no more sub-streams to fetch)
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the stream is closed
-        */
-       public boolean next() throws IOException {
-               return next(false);
-       }
-
-       /**
-        * Unblock the next sub-stream as would have done
-        * {@link NextableInputStream#next()}, but disable the sub-stream systems.
-        * <p>
-        * That is, the next stream, if any, will be the last one and will not be
-        * subject to the {@link NextableInputStreamStep}.
-        * <p>
-        * This is can be a blocking call when data need to be fetched.
-        * 
-        * @return TRUE if we unblocked the next sub-stream, FALSE if not
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the stream is closed
-        */
-       public boolean nextAll() throws IOException {
-               return next(true);
-       }
-
-       /**
-        * Check if this stream is totally spent (no more data to read or to
-        * process).
-        * <p>
-        * Note: when the stream is divided into sub-streams, each sub-stream will
-        * report its own eof when spent.
-        * 
-        * @return TRUE if it is
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       @Override
-       public boolean eof() throws IOException {
-               return super.eof();
-       }
-
-       /**
-        * Check if we still have some data in the buffer and, if not, fetch some.
-        * 
-        * @return TRUE if we fetched some data, FALSE if there are still some in
-        *         the buffer
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       @Override
-       protected boolean preRead() throws IOException {
-               if (!stopped) {
-                       boolean bufferChanged = super.preRead();
-                       checkBuffer(bufferChanged);
-                       return bufferChanged;
-               }
-
-               if (start >= stop) {
-                       eof = true;
-               }
-
-               return false;
-       }
-
-       @Override
-       protected boolean hasMoreData() {
-               return started && super.hasMoreData();
-       }
-
-       /**
-        * Check that the buffer didn't overshot to the next item, and fix
-        * {@link NextableInputStream#stop} if needed.
-        * <p>
-        * If {@link NextableInputStream#stop} is fixed,
-        * {@link NextableInputStream#eof} and {@link NextableInputStream#stopped}
-        * are set to TRUE.
-        * 
-        * @param newBuffer
-        *            we changed the buffer, we need to clear some information in
-        *            the {@link NextableInputStreamStep}
-        */
-       private void checkBuffer(boolean newBuffer) {
-               if (step != null && stop >= 0) {
-                       if (newBuffer) {
-                               step.clearBuffer();
-                       }
-
-                       int stopAt = step.stop(buffer, start, stop, eof);
-                       if (stopAt >= 0) {
-                               stop = stopAt;
-                               eof = true;
-                               stopped = true;
-                       }
-               }
-       }
-
-       /**
-        * The implementation of {@link NextableInputStream#next()} and
-        * {@link NextableInputStream#nextAll()}.
-        * <p>
-        * This is can be a blocking call when data need to be fetched.
-        * 
-        * @param all
-        *            TRUE for {@link NextableInputStream#nextAll()}, FALSE for
-        *            {@link NextableInputStream#next()}
-        * 
-        * @return TRUE if we unblocked the next sub-stream, FALSE if not (i.e.,
-        *         FALSE when there are no more sub-streams to fetch)
-        * 
-        * @throws IOException
-        *             in case of I/O error or if the stream is closed
-        */
-       private boolean next(boolean all) throws IOException {
-               checkClose();
-
-               if (!started) {
-                       // First call before being allowed to read
-                       started = true;
-
-                       if (all) {
-                               step = null;
-                       }
-
-                       return true;
-               }
-
-               // If started, must be stopped and no more data to continue
-               // i.e., sub-stream must be spent
-               if (!stopped || hasMoreData()) {
-                       return false;
-               }
-
-               if (step != null) {
-                       stop = step.getResumeLen();
-                       start += step.getResumeSkip();
-                       eof = step.getResumeEof();
-                       stopped = false;
-
-                       if (all) {
-                               step = null;
-                       }
-
-                       checkBuffer(false);
-
-                       return true;
-               }
-
-               return false;
-
-               // // consider that if EOF, there is no next
-               // if (start >= stop) {
-               // // Make sure, block if necessary
-               // preRead();
-               //
-               // return hasMoreData();
-               // }
-               //
-               // return true;
-       }
-
-       /**
-        * Display a DEBUG {@link String} representation of this object.
-        * <p>
-        * Do <b>not</b> use for release code.
-        */
-       @Override
-       public String toString() {
-               String data = "";
-               if (stop > 0) {
-                       try {
-                               data = new String(Arrays.copyOfRange(buffer, 0, stop), "UTF-8");
-                       } catch (UnsupportedEncodingException e) {
-                       }
-                       if (data.length() > 200) {
-                               data = data.substring(0, 197) + "...";
-                       }
-               }
-               String rep = String.format(
-                               "Nextable %s: %d -> %d [eof: %s] [more data: %s]: %s",
-                               (stopped ? "stopped" : "running"), start, stop, "" + eof, ""
-                                               + hasMoreData(), data);
-
-               return rep;
-       }
-}
diff --git a/src/be/nikiroo/utils/streams/NextableInputStreamStep.java b/src/be/nikiroo/utils/streams/NextableInputStreamStep.java
deleted file mode 100644 (file)
index fda998d..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-package be.nikiroo.utils.streams;
-
-import java.io.InputStream;
-
-/**
- * Divide an {@link InputStream} into sub-streams.
- * 
- * @author niki
- */
-public class NextableInputStreamStep {
-       private int stopAt;
-       private int last = -1;
-       private int resumeLen;
-       private int resumeSkip;
-       private boolean resumeEof;
-
-       /**
-        * Create a new divider that will separate the sub-streams each time it sees
-        * this byte.
-        * <p>
-        * Note that the byte will be bypassed by the {@link InputStream} as far as
-        * the consumers will be aware.
-        * 
-        * @param byt
-        *            the byte at which to separate two sub-streams
-        */
-       public NextableInputStreamStep(int byt) {
-               stopAt = byt;
-       }
-
-       /**
-        * Check if we need to stop the {@link InputStream} reading at some point in
-        * the current buffer.
-        * <p>
-        * If we do, return the index at which to stop; if not, return -1.
-        * <p>
-        * This method will <b>not</b> return the same index a second time (unless
-        * we cleared the buffer).
-        * 
-        * @param buffer
-        *            the buffer to check
-        * @param pos
-        *            the current position of what was read in the buffer
-        * @param len
-        *            the maximum index to use in the buffer (anything above that is
-        *            not to be used)
-        * @param eof
-        *            the current state of the under-laying stream
-        * 
-        * @return the index at which to stop, or -1
-        */
-       public int stop(byte[] buffer, int pos, int len, boolean eof) {
-               for (int i = pos; i < len; i++) {
-                       if (buffer[i] == stopAt) {
-                               if (i > this.last) {
-                                       // we skip the sep
-                                       this.resumeSkip = 1;
-
-                                       this.resumeLen = len;
-                                       this.resumeEof = eof;
-                                       this.last = i;
-                                       return i;
-                               }
-                       }
-               }
-
-               return -1;
-       }
-
-       /**
-        * Get the maximum index to use in the buffer used in
-        * {@link NextableInputStreamStep#stop(byte[], int, int, boolean)} at resume
-        * time.
-        * 
-        * @return the index
-        */
-       public int getResumeLen() {
-               return resumeLen;
-       }
-
-       /**
-        * Get the number of bytes to skip at resume time.
-        * 
-        * @return the number of bytes to skip
-        */
-       public int getResumeSkip() {
-               return resumeSkip;
-       }
-
-       /**
-        * Get the under-laying stream state at resume time.
-        * 
-        * @return the EOF state
-        */
-       public boolean getResumeEof() {
-               return resumeEof;
-       }
-
-       /**
-        * Clear the information we may have kept about the current buffer
-        * <p>
-        * You should call this method each time you change the content of the
-        * buffer used in
-        * {@link NextableInputStreamStep#stop(byte[], int, int, boolean)}.
-        */
-       public void clearBuffer() {
-               this.last = -1;
-               this.resumeSkip = 0;
-               this.resumeLen = 0;
-               this.resumeEof = false;
-       }
-}
diff --git a/src/be/nikiroo/utils/streams/ReplaceInputStream.java b/src/be/nikiroo/utils/streams/ReplaceInputStream.java
deleted file mode 100644 (file)
index 0860f78..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-package be.nikiroo.utils.streams;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import be.nikiroo.utils.StringUtils;
-
-/**
- * This {@link InputStream} will change some of its content by replacing it with
- * something else.
- * 
- * @author niki
- */
-public class ReplaceInputStream extends BufferedInputStream {
-       /**
-        * The minimum size of the internal buffer (could be more if at least one of
-        * the 'FROM' bytes arrays is &gt; 2048 bytes &mdash; in that case the
-        * buffer will be twice the largest size of the 'FROM' bytes arrays).
-        * <p>
-        * This is a different buffer than the one from the inherited class.
-        */
-       static private final int MIN_BUFFER_SIZE = 4096;
-
-       private byte[][] froms;
-       private byte[][] tos;
-       private int bufferSize;
-       private int maxFromSize;
-
-       /**
-        * Create a {@link ReplaceInputStream} that will replace <tt>from</tt> with
-        * <tt>to</tt>.
-        * 
-        * @param in
-        *            the under-laying {@link InputStream}
-        * @param from
-        *            the {@link String} to replace
-        * @param to
-        *            the {@link String} to replace with
-        */
-       public ReplaceInputStream(InputStream in, String from, String to) {
-               this(in, StringUtils.getBytes(from), StringUtils.getBytes(to));
-       }
-
-       /**
-        * Create a {@link ReplaceInputStream} that will replace <tt>from</tt> with
-        * <tt>to</tt>.
-        * 
-        * @param in
-        *            the under-laying {@link InputStream}
-        * @param from
-        *            the value to replace
-        * @param to
-        *            the value to replace with
-        */
-       public ReplaceInputStream(InputStream in, byte[] from, byte[] to) {
-               this(in, new byte[][] { from }, new byte[][] { to });
-       }
-
-       /**
-        * Create a {@link ReplaceInputStream} that will replace all <tt>froms</tt>
-        * with <tt>tos</tt>.
-        * <p>
-        * Note that they will be replaced in order, and that for each <tt>from</tt>
-        * a <tt>to</tt> must correspond.
-        * 
-        * @param in
-        *            the under-laying {@link InputStream}
-        * @param froms
-        *            the values to replace
-        * @param tos
-        *            the values to replace with
-        */
-       public ReplaceInputStream(InputStream in, String[] froms, String[] tos) {
-               this(in, StreamUtils.getBytes(froms), StreamUtils.getBytes(tos));
-       }
-
-       /**
-        * Create a {@link ReplaceInputStream} that will replace all <tt>froms</tt>
-        * with <tt>tos</tt>.
-        * <p>
-        * Note that they will be replaced in order, and that for each <tt>from</tt>
-        * a <tt>to</tt> must correspond.
-        * 
-        * @param in
-        *            the under-laying {@link InputStream}
-        * @param froms
-        *            the values to replace
-        * @param tos
-        *            the values to replace with
-        */
-       public ReplaceInputStream(InputStream in, byte[][] froms, byte[][] tos) {
-               super(in);
-
-               if (froms.length != tos.length) {
-                       throw new IllegalArgumentException(
-                                       "For replacing, each FROM must have a corresponding TO");
-               }
-
-               this.froms = froms;
-               this.tos = tos;
-
-               maxFromSize = 0;
-               for (int i = 0; i < froms.length; i++) {
-                       maxFromSize = Math.max(maxFromSize, froms[i].length);
-               }
-
-               int maxToSize = 0;
-               for (int i = 0; i < tos.length; i++) {
-                       maxToSize = Math.max(maxToSize, tos[i].length);
-               }
-
-               // We need at least maxFromSize so we can iterate and replace
-               bufferSize = Math.max(4 * Math.max(maxToSize, maxFromSize),
-                               MIN_BUFFER_SIZE);
-       }
-
-       @Override
-       protected boolean preRead() throws IOException {
-               boolean rep = super.preRead();
-               start = stop;
-               return rep;
-       }
-
-       @Override
-       protected int read(InputStream in, byte[] buffer) throws IOException {
-               buffer = null; // do not use the buffer.
-
-               byte[] newBuffer = new byte[bufferSize];
-               int read = 0;
-               while (read < bufferSize / 2) {
-                       int thisTime = in.read(newBuffer, read, bufferSize / 2 - read);
-                       if (thisTime <= 0) {
-                               break;
-                       }
-                       read += thisTime;
-               }
-
-               List<byte[]> bbBuffers = new ArrayList<byte[]>();
-               List<Integer> bbOffsets = new ArrayList<Integer>();
-               List<Integer> bbLengths = new ArrayList<Integer>();
-
-               int offset = 0;
-               for (int i = 0; i < read; i++) {
-                       for (int fromIndex = 0; fromIndex < froms.length; fromIndex++) {
-                               byte[] from = froms[fromIndex];
-                               byte[] to = tos[fromIndex];
-
-                               if (from.length > 0
-                                               && StreamUtils.startsWith(from, newBuffer, i, read)) {
-                                       if (i - offset > 0) {
-                                               bbBuffers.add(newBuffer);
-                                               bbOffsets.add(offset);
-                                               bbLengths.add(i - offset);
-                                       }
-
-                                       if (to.length > 0) {
-                                               bbBuffers.add(to);
-                                               bbOffsets.add(0);
-                                               bbLengths.add(to.length);
-                                       }
-
-                                       i += from.length;
-                                       offset = i;
-                               }
-                       }
-               }
-
-               if (offset < read) {
-                       bbBuffers.add(newBuffer);
-                       bbOffsets.add(offset);
-                       bbLengths.add(read - offset);
-               }
-
-               for (int i = bbBuffers.size() - 1; i >= 0; i--) {
-                       // DEBUG("pushback", bbBuffers.get(i), bbOffsets.get(i),
-                       // bbLengths.get(i));
-                       pushback(bbBuffers.get(i), bbOffsets.get(i), bbLengths.get(i));
-               }
-
-               return read;
-       }
-
-       // static public void DEBUG(String title, byte[] b, int off, int len) {
-       // String str = new String(b,off,len);
-       // if(str.length()>20) {
-       // str=str.substring(0,10)+" ...
-       // "+str.substring(str.length()-10,str.length());
-       // }
-       // }
-
-       @Override
-       public String toString() {
-               StringBuilder rep = new StringBuilder();
-               rep.append(getClass().getSimpleName()).append("\n");
-
-               for (int i = 0; i < froms.length; i++) {
-                       byte[] from = froms[i];
-                       byte[] to = tos[i];
-
-                       rep.append("\t");
-                       rep.append("bytes[").append(from.length).append("]");
-                       if (from.length <= 20) {
-                               rep.append(" (").append(new String(from)).append(")");
-                       }
-                       rep.append(" -> ");
-                       rep.append("bytes[").append(to.length).append("]");
-                       if (to.length <= 20) {
-                               rep.append(" (").append(new String(to)).append(")");
-                       }
-                       rep.append("\n");
-               }
-
-               return "[" + rep + "]";
-       }
-}
diff --git a/src/be/nikiroo/utils/streams/ReplaceOutputStream.java b/src/be/nikiroo/utils/streams/ReplaceOutputStream.java
deleted file mode 100644 (file)
index c6679cc..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-package be.nikiroo.utils.streams;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-import be.nikiroo.utils.StringUtils;
-
-/**
- * This {@link OutputStream} will change some of its content by replacing it
- * with something else.
- * 
- * @author niki
- */
-public class ReplaceOutputStream extends BufferedOutputStream {
-       private byte[][] froms;
-       private byte[][] tos;
-
-       /**
-        * Create a {@link ReplaceOutputStream} that will replace <tt>from</tt> with
-        * <tt>to</tt>.
-        * 
-        * @param out
-        *            the under-laying {@link OutputStream}
-        * @param from
-        *            the {@link String} to replace
-        * @param to
-        *            the {@link String} to replace with
-        */
-       public ReplaceOutputStream(OutputStream out, String from, String to) {
-               this(out, StringUtils.getBytes(from), StringUtils.getBytes(to));
-       }
-
-       /**
-        * Create a {@link ReplaceOutputStream} that will replace <tt>from</tt> with
-        * <tt>to</tt>.
-        * 
-        * @param out
-        *            the under-laying {@link OutputStream}
-        * @param from
-        *            the value to replace
-        * @param to
-        *            the value to replace with
-        */
-       public ReplaceOutputStream(OutputStream out, byte[] from, byte[] to) {
-               this(out, new byte[][] { from }, new byte[][] { to });
-       }
-
-       /**
-        * Create a {@link ReplaceOutputStream} that will replace all <tt>froms</tt>
-        * with <tt>tos</tt>.
-        * <p>
-        * Note that they will be replaced in order, and that for each <tt>from</tt>
-        * a <tt>to</tt> must correspond.
-        * 
-        * @param out
-        *            the under-laying {@link OutputStream}
-        * @param froms
-        *            the values to replace
-        * @param tos
-        *            the values to replace with
-        */
-       public ReplaceOutputStream(OutputStream out, String[] froms, String[] tos) {
-               this(out, StreamUtils.getBytes(froms), StreamUtils.getBytes(tos));
-       }
-
-       /**
-        * Create a {@link ReplaceOutputStream} that will replace all <tt>froms</tt>
-        * with <tt>tos</tt>.
-        * <p>
-        * Note that they will be replaced in order, and that for each <tt>from</tt>
-        * a <tt>to</tt> must correspond.
-        * 
-        * @param out
-        *            the under-laying {@link OutputStream}
-        * @param froms
-        *            the values to replace
-        * @param tos
-        *            the values to replace with
-        */
-       public ReplaceOutputStream(OutputStream out, byte[][] froms, byte[][] tos) {
-               super(out);
-               bypassFlush = false;
-
-               if (froms.length != tos.length) {
-                       throw new IllegalArgumentException(
-                                       "For replacing, each FROM must have a corresponding TO");
-               }
-
-               this.froms = froms;
-               this.tos = tos;
-       }
-
-       /**
-        * Flush the {@link BufferedOutputStream}, write the current buffered data
-        * to (and optionally also flush) the under-laying stream.
-        * <p>
-        * If {@link BufferedOutputStream#bypassFlush} is false, all writes to the
-        * under-laying stream are done in this method.
-        * <p>
-        * This can be used if you want to write some data in the under-laying
-        * stream yourself (in that case, flush this {@link BufferedOutputStream}
-        * with or without flushing the under-laying stream, then you can write to
-        * the under-laying stream).
-        * <p>
-        * <b>But be careful!</b> If a replacement could be done with the end o the
-        * currently buffered data and the start of the data to come, we obviously
-        * will not be able to do it.
-        * 
-        * @param includingSubStream
-        *            also flush the under-laying stream
-        * @throws IOException
-        *             in case of I/O error
-        */
-       @Override
-       public void flush(boolean includingSubStream) throws IOException {
-               // Note: very simple, not efficient implementation; sorry.
-               while (start < stop) {
-                       boolean replaced = false;
-                       for (int i = 0; i < froms.length; i++) {
-                               if (froms[i] != null
-                                               && froms[i].length > 0
-                                               && StreamUtils
-                                                               .startsWith(froms[i], buffer, start, stop)) {
-                                       if (tos[i] != null && tos[i].length > 0) {
-                                               out.write(tos[i]);
-                                               bytesWritten += tos[i].length;
-                                       }
-
-                                       start += froms[i].length;
-                                       replaced = true;
-                                       break;
-                               }
-                       }
-
-                       if (!replaced) {
-                               out.write(buffer[start++]);
-                               bytesWritten++;
-                       }
-               }
-
-               start = 0;
-               stop = 0;
-
-               if (includingSubStream) {
-                       out.flush();
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/streams/StreamUtils.java b/src/be/nikiroo/utils/streams/StreamUtils.java
deleted file mode 100644 (file)
index dc75090..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-package be.nikiroo.utils.streams;
-
-import be.nikiroo.utils.StringUtils;
-
-/**
- * Some non-public utilities used in the stream classes.
- * 
- * @author niki
- */
-class StreamUtils {
-       /**
-        * Check if the buffer starts with the given search term (given as an array,
-        * a start position and an end position).
-        * <p>
-        * Note: the parameter <tt>stop</tt> is the <b>index</b> of the last
-        * position, <b>not</b> the length.
-        * <p>
-        * Note: the search term size <b>must</b> be smaller or equal the internal
-        * buffer size.
-        * 
-        * @param search
-        *            the term to search for
-        * @param buffer
-        *            the buffer to look into
-        * @param start
-        *            the offset at which to start the search
-        * @param stop
-        *            the maximum index of the data to check (this is <b>not</b> a
-        *            length, but an index)
-        * 
-        * @return TRUE if the search content is present at the given location and
-        *         does not exceed the <tt>len</tt> index
-        */
-       static public boolean startsWith(byte[] search, byte[] buffer, int start,
-                       int stop) {
-
-               // Check if there even is enough space for it
-               if (search.length > (stop - start)) {
-                       return false;
-               }
-
-               boolean same = true;
-               for (int i = 0; i < search.length; i++) {
-                       if (search[i] != buffer[start + i]) {
-                               same = false;
-                               break;
-                       }
-               }
-
-               return same;
-       }
-
-       /**
-        * Return the bytes array representation of the given {@link String} in
-        * UTF-8.
-        * 
-        * @param strs
-        *            the {@link String}s to transform into bytes
-        * @return the content in bytes
-        */
-       static public byte[][] getBytes(String[] strs) {
-               byte[][] bytes = new byte[strs.length][];
-               for (int i = 0; i < strs.length; i++) {
-                       bytes[i] = StringUtils.getBytes(strs[i]);
-               }
-
-               return bytes;
-       }
-}
diff --git a/src/be/nikiroo/utils/test/TestCase.java b/src/be/nikiroo/utils/test/TestCase.java
deleted file mode 100644 (file)
index fe7b9af..0000000
+++ /dev/null
@@ -1,535 +0,0 @@
-package be.nikiroo.utils.test;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import be.nikiroo.utils.IOUtils;
-
-/**
- * A {@link TestCase} that can be run with {@link TestLauncher}.
- * 
- * @author niki
- */
-abstract public class TestCase {
-       /**
-        * The type of {@link Exception} used to signal a failed assertion or a
-        * force-fail.
-        * 
-        * @author niki
-        */
-       class AssertException extends Exception {
-               private static final long serialVersionUID = 1L;
-
-               public AssertException(String reason, Exception source) {
-                       super(reason, source);
-               }
-
-               public AssertException(String reason) {
-                       super(reason);
-               }
-       }
-
-       private String name;
-
-       /**
-        * Create a new {@link TestCase}.
-        * 
-        * @param name
-        *            the test name
-        */
-       public TestCase(String name) {
-               this.name = name;
-       }
-
-       /**
-        * This constructor can be used if you require a no-param constructor. In
-        * this case, you are allowed to set the name manually via
-        * {@link TestCase#setName}.
-        */
-       protected TestCase() {
-               this("no name");
-       }
-
-       /**
-        * Setup the test (called before the test is run).
-        * 
-        * @throws Exception
-        *             in case of error
-        */
-       public void setUp() throws Exception {
-       }
-
-       /**
-        * Tear-down the test (called when the test has been ran).
-        * 
-        * @throws Exception
-        *             in case of error
-        */
-       public void tearDown() throws Exception {
-       }
-
-       /**
-        * The test name.
-        * 
-        * @return the name
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * The test name.
-        * 
-        * @param name
-        *            the new name (internal use only)
-        * 
-        * @return this (so we can chain and so we can initialize it in a member
-        *         variable if this is an anonymous inner class)
-        */
-       protected TestCase setName(String name) {
-               this.name = name;
-               return this;
-       }
-
-       /**
-        * Actually do the test.
-        * 
-        * @throws Exception
-        *             in case of error
-        */
-       abstract public void test() throws Exception;
-
-       /**
-        * Force a failure.
-        * 
-        * @throws AssertException
-        *             every time
-        */
-       public void fail() throws AssertException {
-               fail(null);
-       }
-
-       /**
-        * Force a failure.
-        * 
-        * @param reason
-        *            the failure reason
-        * 
-        * @throws AssertException
-        *             every time
-        */
-       public void fail(String reason) throws AssertException {
-               fail(reason, null);
-       }
-
-       /**
-        * Force a failure.
-        * 
-        * @param reason
-        *            the failure reason
-        * @param e
-        *            the exception that caused the failure (can be NULL)
-        * 
-        * @throws AssertException
-        *             every time
-        */
-       public void fail(String reason, Exception e) throws AssertException {
-               throw new AssertException("Failed!" + //
-                               reason != null ? "\n" + reason : "", e);
-       }
-
-       /**
-        * Check that 2 {@link Object}s are equals.
-        * 
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(Object expected, Object actual)
-                       throws AssertException {
-               assertEquals(null, expected, actual);
-       }
-
-       /**
-        * Check that 2 {@link Object}s are equals.
-        * 
-        * @param errorMessage
-        *            the error message to display if they differ
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(String errorMessage, Object expected, Object actual)
-                       throws AssertException {
-               if ((expected == null && actual != null)
-                               || (expected != null && !expected.equals(actual))) {
-                       if (errorMessage == null) {
-                               throw new AssertException(generateAssertMessage(expected,
-                                               actual));
-                       }
-
-                       throw new AssertException(errorMessage, new AssertException(
-                                       generateAssertMessage(expected, actual)));
-               }
-       }
-
-       /**
-        * Check that 2 longs are equals.
-        * 
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(long expected, long actual) throws AssertException {
-               assertEquals(Long.valueOf(expected), Long.valueOf(actual));
-       }
-
-       /**
-        * Check that 2 longs are equals.
-        * 
-        * @param errorMessage
-        *            the error message to display if they differ
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(String errorMessage, long expected, long actual)
-                       throws AssertException {
-               assertEquals(errorMessage, Long.valueOf(expected), Long.valueOf(actual));
-       }
-
-       /**
-        * Check that 2 booleans are equals.
-        * 
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(boolean expected, boolean actual)
-                       throws AssertException {
-               assertEquals(Boolean.valueOf(expected), Boolean.valueOf(actual));
-       }
-
-       /**
-        * Check that 2 booleans are equals.
-        * 
-        * @param errorMessage
-        *            the error message to display if they differ
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(String errorMessage, boolean expected,
-                       boolean actual) throws AssertException {
-               assertEquals(errorMessage, Boolean.valueOf(expected),
-                               Boolean.valueOf(actual));
-       }
-
-       /**
-        * Check that 2 doubles are equals.
-        * 
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(double expected, double actual)
-                       throws AssertException {
-               assertEquals(Double.valueOf(expected), Double.valueOf(actual));
-       }
-
-       /**
-        * Check that 2 doubles are equals.
-        * 
-        * @param errorMessage
-        *            the error message to display if they differ
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(String errorMessage, double expected, double actual)
-                       throws AssertException {
-               assertEquals(errorMessage, Double.valueOf(expected),
-                               Double.valueOf(actual));
-       }
-
-       /**
-        * Check that 2 {@link List}s are equals.
-        * 
-        * @param errorMessage
-        *            the error message to display if they differ
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(List<?> expected, List<?> actual)
-                       throws AssertException {
-               assertEquals("Assertion failed", expected, actual);
-       }
-
-       /**
-        * Check that 2 {@link List}s are equals.
-        * 
-        * @param errorMessage
-        *            the error message to display if they differ
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(String errorMessage, List<?> expected,
-                       List<?> actual) throws AssertException {
-
-               if (expected.size() != actual.size()) {
-                       assertEquals(errorMessage + ": not same number of items",
-                                       list(expected), list(actual));
-               }
-
-               int size = expected.size();
-               for (int i = 0; i < size; i++) {
-                       assertEquals(errorMessage + ": item " + i
-                                       + " (0-based) is not correct", expected.get(i),
-                                       actual.get(i));
-               }
-       }
-
-       /**
-        * Check that 2 {@link File}s are equals, by doing a line-by-line
-        * comparison.
-        * 
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * @param errorMessage
-        *            the error message to display if they differ
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(File expected, File actual) throws AssertException {
-               assertEquals(generateAssertMessage(expected, actual), expected, actual);
-       }
-
-       /**
-        * Check that 2 {@link File}s are equals, by doing a line-by-line
-        * comparison.
-        * 
-        * @param errorMessage
-        *            the error message to display if they differ
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(String errorMessage, File expected, File actual)
-                       throws AssertException {
-               assertEquals(errorMessage, expected, actual, null);
-       }
-
-       /**
-        * Check that 2 {@link File}s are equals, by doing a line-by-line
-        * comparison.
-        * 
-        * @param errorMessage
-        *            the error message to display if they differ
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * @param skipCompare
-        *            skip the lines starting with some values for the given files
-        *            (relative path from base directory in recursive mode)
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertEquals(String errorMessage, File expected, File actual,
-                       Map<String, List<String>> skipCompare) throws AssertException {
-               assertEquals(errorMessage, expected, actual, skipCompare, null);
-       }
-
-       private void assertEquals(String errorMessage, File expected, File actual,
-                       Map<String, List<String>> skipCompare, String removeFromName)
-                       throws AssertException {
-
-               if (expected.isDirectory() || actual.isDirectory()) {
-                       assertEquals(errorMessage + ": type mismatch: expected a "
-                                       + (expected.isDirectory() ? "directory" : "file")
-                                       + ", received a "
-                                       + (actual.isDirectory() ? "directory" : "file"),
-                                       expected.isDirectory(), actual.isDirectory());
-
-                       List<String> expectedFiles = Arrays.asList(expected.list());
-                       Collections.sort(expectedFiles);
-                       List<String> actualFiles = Arrays.asList(actual.list());
-                       Collections.sort(actualFiles);
-
-                       assertEquals(errorMessage, expectedFiles, actualFiles);
-                       for (int i = 0; i < actualFiles.size(); i++) {
-                               File expectedFile = new File(expected, expectedFiles.get(i));
-                               File actualFile = new File(actual, actualFiles.get(i));
-
-                               assertEquals(errorMessage, expectedFile, actualFile,
-                                               skipCompare, expected.getAbsolutePath());
-                       }
-               } else {
-                       try {
-                               List<String> expectedLines = Arrays.asList(IOUtils
-                                               .readSmallFile(expected).split("\n"));
-                               List<String> resultLines = Arrays.asList(IOUtils.readSmallFile(
-                                               actual).split("\n"));
-
-                               String name = expected.getAbsolutePath();
-                               if (removeFromName != null && name.startsWith(removeFromName)) {
-                                       name = expected.getName()
-                                                       + name.substring(removeFromName.length());
-                               }
-
-                               assertEquals(errorMessage + ": " + name
-                                               + ": the number of lines is not the same",
-                                               expectedLines.size(), resultLines.size());
-
-                               for (int j = 0; j < expectedLines.size(); j++) {
-                                       String expectedLine = expectedLines.get(j);
-                                       String resultLine = resultLines.get(j);
-
-                                       boolean skip = false;
-                                       if (skipCompare != null) {
-                                               for (Entry<String, List<String>> skipThose : skipCompare
-                                                               .entrySet()) {
-                                                       for (String skipStart : skipThose.getValue()) {
-                                                               if (name.endsWith(skipThose.getKey())
-                                                                               && expectedLine.startsWith(skipStart)
-                                                                               && resultLine.startsWith(skipStart)) {
-                                                                       skip = true;
-                                                               }
-                                                       }
-                                               }
-                                       }
-
-                                       if (skip) {
-                                               continue;
-                                       }
-
-                                       assertEquals(errorMessage + ": line " + (j + 1)
-                                                       + " is not the same in file " + name, expectedLine,
-                                                       resultLine);
-                               }
-                       } catch (Exception e) {
-                               throw new AssertException(errorMessage, e);
-                       }
-               }
-       }
-
-       /**
-        * Check that given {@link Object} is not NULL.
-        * 
-        * @param errorMessage
-        *            the error message to display if it is NULL
-        * @param actual
-        *            the actual value
-        * 
-        * @throws AssertException
-        *             in case they differ
-        */
-       public void assertNotNull(String errorMessage, Object actual)
-                       throws AssertException {
-               if (actual == null) {
-                       String defaultReason = String.format("" //
-                                       + "Assertion failed!%n" //
-                                       + "Object should not have been NULL");
-
-                       if (errorMessage == null) {
-                               throw new AssertException(defaultReason);
-                       }
-
-                       throw new AssertException(errorMessage, new AssertException(
-                                       defaultReason));
-               }
-       }
-
-       /**
-        * Generate the default assert message for 2 different values that were
-        * supposed to be equals.
-        * 
-        * @param expected
-        *            the expected value
-        * @param actual
-        *            the actual value
-        * 
-        * @return the message
-        */
-       public static String generateAssertMessage(Object expected, Object actual) {
-               return String.format("" //
-                               + "Assertion failed!%n" //
-                               + "Expected value: [%s]%n" //
-                               + "Actual value:   [%s]", expected, actual);
-       }
-
-       private static String list(List<?> items) {
-               StringBuilder builder = new StringBuilder();
-               for (Object item : items) {
-                       if (builder.length() == 0) {
-                               builder.append(items.size() + " item(s): ");
-                       } else {
-                               builder.append(", ");
-                       }
-
-                       builder.append("" + item);
-
-                       if (builder.length() > 60) {
-                               builder.setLength(57);
-                               builder.append("...");
-                               break;
-                       }
-               }
-
-               return builder.toString();
-       }
-}
diff --git a/src/be/nikiroo/utils/test/TestLauncher.java b/src/be/nikiroo/utils/test/TestLauncher.java
deleted file mode 100644 (file)
index 895b565..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-package be.nikiroo.utils.test;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A {@link TestLauncher} starts a series of {@link TestCase}s and displays the
- * result to the user.
- * 
- * @author niki
- */
-public class TestLauncher {
-       /**
-        * {@link Exception} happening during the setup process.
-        * 
-        * @author niki
-        */
-       private class SetupException extends Exception {
-               private static final long serialVersionUID = 1L;
-
-               public SetupException(Throwable e) {
-                       super(e);
-               }
-       }
-
-       /**
-        * {@link Exception} happening during the tear-down process.
-        * 
-        * @author niki
-        */
-       private class TearDownException extends Exception {
-               private static final long serialVersionUID = 1L;
-
-               public TearDownException(Throwable e) {
-                       super(e);
-               }
-       }
-
-       private List<TestLauncher> series;
-       private List<TestCase> tests;
-       private TestLauncher parent;
-
-       private int columns;
-       private String okString;
-       private String koString;
-       private String name;
-       private boolean cont;
-
-       protected int executed;
-       protected int total;
-
-       private int currentSeries = 0;
-       private boolean details = false;
-
-       /**
-        * Create a new {@link TestLauncher} with default parameters.
-        * 
-        * @param name
-        *            the test suite name
-        * @param args
-        *            the arguments to configure the number of columns and the ok/ko
-        *            {@link String}s
-        */
-       public TestLauncher(String name, String[] args) {
-               this.name = name;
-
-               int cols = 80;
-               if (args != null && args.length >= 1) {
-                       try {
-                               cols = Integer.parseInt(args[0]);
-                       } catch (NumberFormatException e) {
-                               System.err.println("Test configuration: given number "
-                                               + "of columns is not parseable: " + args[0]);
-                       }
-               }
-
-               setColumns(cols);
-
-               String okString = "[ ok ]";
-               String koString = "[ !! ]";
-               if (args != null && args.length >= 3) {
-                       okString = args[1];
-                       koString = args[2];
-               }
-
-               setOkString(okString);
-               setKoString(koString);
-
-               series = new ArrayList<TestLauncher>();
-               tests = new ArrayList<TestCase>();
-               cont = true;
-       }
-
-       /**
-        * Display the details of the errors
-        * 
-        * @return TRUE to display them, false to simply mark the test as failed
-        */
-       public boolean isDetails() {
-               if (parent != null) {
-                       return parent.isDetails();
-               }
-
-               return details;
-       }
-
-       /**
-        * Display the details of the errors
-        * 
-        * @param details
-        *            TRUE to display them, false to simply mark the test as failed
-        */
-       public void setDetails(boolean details) {
-               if (parent != null) {
-                       parent.setDetails(details);
-               }
-
-               this.details = details;
-       }
-
-       /**
-        * Called before actually starting the tests themselves.
-        * 
-        * @throws Exception
-        *             in case of error
-        */
-       protected void start() throws Exception {
-       }
-
-       /**
-        * Called when the tests are passed (or failed to do so).
-        * 
-        * @throws Exception
-        *             in case of error
-        */
-       protected void stop() throws Exception {
-       }
-
-       protected void addTest(TestCase test) {
-               tests.add(test);
-       }
-
-       protected void addSeries(TestLauncher series) {
-               this.series.add(series);
-               series.parent = this;
-       }
-
-       /**
-        * Launch the series of {@link TestCase}s and the {@link TestCase}s.
-        * 
-        * @return the number of errors
-        */
-       public int launch() {
-               return launch(0);
-       }
-
-       /**
-        * Launch the series of {@link TestCase}s and the {@link TestCase}s.
-        * 
-        * @param depth
-        *            the level at which is the launcher (0 = main launcher)
-        * 
-        * @return the number of errors
-        */
-       public int launch(int depth) {
-               int errors = 0;
-               executed = 0;
-               total = tests.size();
-
-               print(depth);
-
-               try {
-                       start();
-
-                       errors += launchTests(depth);
-                       if (tests.size() > 0 && depth == 0) {
-                               System.out.println("");
-                       }
-
-                       currentSeries = 0;
-                       for (TestLauncher serie : series) {
-                               errors += serie.launch(depth + 1);
-                               executed += serie.executed;
-                               total += serie.total;
-                               currentSeries++;
-                       }
-               } catch (Exception e) {
-                       print(depth, "__start");
-                       print(depth, e);
-               } finally {
-                       try {
-                               stop();
-                       } catch (Exception e) {
-                               print(depth, "__stop");
-                               print(depth, e);
-                       }
-               }
-
-               print(depth, executed, errors, total);
-
-               return errors;
-       }
-
-       /**
-        * Launch the {@link TestCase}s.
-        * 
-        * @param depth
-        *            the level at which is the launcher (0 = main launcher)
-        * 
-        * @return the number of errors
-        */
-       protected int launchTests(int depth) {
-               int errors = 0;
-               for (TestCase test : tests) {
-                       print(depth, test.getName());
-
-                       Throwable ex = null;
-                       try {
-                               try {
-                                       test.setUp();
-                               } catch (Throwable e) {
-                                       throw new SetupException(e);
-                               }
-                               test.test();
-                               try {
-                                       test.tearDown();
-                               } catch (Throwable e) {
-                                       throw new TearDownException(e);
-                               }
-                       } catch (Throwable e) {
-                               ex = e;
-                       }
-
-                       if (ex != null) {
-                               errors++;
-                       }
-
-                       print(depth, ex);
-
-                       executed++;
-
-                       if (ex != null && !cont) {
-                               break;
-                       }
-               }
-
-               return errors;
-       }
-
-       /**
-        * Specify a custom number of columns to use for the display of messages.
-        * 
-        * @param columns
-        *            the number of columns
-        */
-       public void setColumns(int columns) {
-               this.columns = columns;
-       }
-
-       /**
-        * Continue to run the tests when an error is detected.
-        * 
-        * @param cont
-        *            yes or no
-        */
-       public void setContinueAfterFail(boolean cont) {
-               this.cont = cont;
-       }
-
-       /**
-        * Set a custom "[ ok ]" {@link String} when a test passed.
-        * 
-        * @param okString
-        *            the {@link String} to display at the end of a success
-        */
-       public void setOkString(String okString) {
-               this.okString = okString;
-       }
-
-       /**
-        * Set a custom "[ !! ]" {@link String} when a test failed.
-        * 
-        * @param koString
-        *            the {@link String} to display at the end of a failure
-        */
-       public void setKoString(String koString) {
-               this.koString = koString;
-       }
-
-       /**
-        * Print the test suite header.
-        * 
-        * @param depth
-        *            the level at which is the launcher (0 = main launcher)
-        */
-       protected void print(int depth) {
-               if (depth == 0) {
-                       System.out.println("[ Test suite: " + name + " ]");
-                       System.out.println("");
-               } else {
-                       System.out.println(prefix(depth, false) + name + ":");
-               }
-       }
-
-       /**
-        * Print the name of the {@link TestCase} we will start immediately after.
-        * 
-        * @param depth
-        *            the level at which is the launcher (0 = main launcher)
-        * @param name
-        *            the {@link TestCase} name
-        */
-       protected void print(int depth, String name) {
-               name = prefix(depth, false)
-                               + (name == null ? "" : name).replace("\t", "    ");
-
-               StringBuilder dots = new StringBuilder();
-               while ((name.length() + dots.length()) < columns - 11) {
-                       dots.append('.');
-               }
-
-               System.out.print(name + dots.toString());
-       }
-
-       /**
-        * Print the result of the {@link TestCase} we just ran.
-        * 
-        * @param depth
-        *            the level at which is the launcher (0 = main launcher)
-        * @param error
-        *            the {@link Exception} it ran into if any
-        */
-       private void print(int depth, Throwable error) {
-               if (error != null) {
-                       System.out.println(" " + koString);
-                       if (isDetails()) {
-                               StringWriter sw = new StringWriter();
-                               PrintWriter pw = new PrintWriter(sw);
-                               error.printStackTrace(pw);
-                               String lines = sw.toString();
-                               for (String line : lines.split("\n")) {
-                                       System.out.println(prefix(depth, false) + "\t\t" + line);
-                               }
-                       }
-               } else {
-                       System.out.println(" " + okString);
-               }
-       }
-
-       /**
-        * Print the total result for this test suite.
-        * 
-        * @param depth
-        *            the level at which is the launcher (0 = main launcher)
-        * @param executed
-        *            the number of tests actually ran
-        * @param errors
-        *            the number of errors encountered
-        * @param total
-        *            the total number of tests in the suite
-        */
-       private void print(int depth, int executed, int errors, int total) {
-               int ok = executed - errors;
-               int pc = (int) ((100.0 * ok) / executed);
-               if (pc == 0 && ok > 0) {
-                       pc = 1;
-               }
-               int pcTotal = (int) ((100.0 * ok) / total);
-               if (pcTotal == 0 && ok > 0) {
-                       pcTotal = 1;
-               }
-
-               String resume = "Tests passed: " + ok + "/" + executed + " (" + pc
-                               + "%) on a total of " + total + " (" + pcTotal + "% total)";
-               if (depth == 0) {
-                       System.out.println(resume);
-               } else {
-                       String arrow = "┗▶ ";
-                       System.out.println(prefix(depth, currentSeries == 0) + arrow
-                                       + resume);
-                       System.out.println(prefix(depth, currentSeries == 0));
-               }
-       }
-
-       private int last = -1;
-
-       /**
-        * Return the prefix to print before the current line.
-        * 
-        * @param depth
-        *            the current depth
-        * @param first
-        *            this line is the first of its tabulation level
-        * 
-        * @return the prefix
-        */
-       private String prefix(int depth, boolean first) {
-               String space = tabs(depth - 1);
-
-               String line = "";
-               if (depth > 0) {
-                       if (depth > 1) {
-                               if (depth != last && first) {
-                                       line = "╻"; // first line
-                               } else {
-                                       line = "┃"; // continuation
-                               }
-                       }
-
-                       space += line + tabs(1);
-               }
-
-               last = depth;
-               return space;
-       }
-
-       /**
-        * Return the given number of space-converted tabs in a {@link String}.
-        * 
-        * @param depth
-        *            the number of tabs to return
-        * 
-        * @return the string
-        */
-       private String tabs(int depth) {
-               StringBuilder builder = new StringBuilder();
-               for (int i = 0; i < depth; i++) {
-                       builder.append("    ");
-               }
-               return builder.toString();
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/BreadCrumbsBar.java b/src/be/nikiroo/utils/ui/BreadCrumbsBar.java
deleted file mode 100644 (file)
index ed7e0bb..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-
-package be.nikiroo.utils.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.AbstractAction;
-import javax.swing.BoxLayout;
-import javax.swing.JPanel;
-import javax.swing.JPopupMenu;
-import javax.swing.JToggleButton;
-import javax.swing.SwingWorker;
-import javax.swing.event.PopupMenuEvent;
-import javax.swing.event.PopupMenuListener;
-
-public class BreadCrumbsBar<T> extends ListenerPanel {
-       private class BreadCrumb extends JPanel {
-               private JToggleButton button;
-               private JToggleButton down;
-
-               public BreadCrumb(final DataNode<T> node) {
-                       this.setLayout(new BorderLayout());
-
-                       if (!node.isRoot()) {
-                               button = new JToggleButton(node.toString());
-                               button.addActionListener(new ActionListener() {
-                                       @Override
-                                       public void actionPerformed(ActionEvent e) {
-                                               button.setSelected(false);
-                                               if (!node.isRoot()) {
-                                                       // TODO: allow clicking on root? option?
-                                                       setSelectedNode(node);
-                                               }
-                                       }
-                               });
-
-                               this.add(button, BorderLayout.CENTER);
-                       }
-
-                       if ((node.isRoot() && node.getChildren().isEmpty())
-                                       || !node.getChildren().isEmpty()) {
-                               // TODO allow an image or ">", viewer
-                               down = new JToggleButton(">");
-                               final JPopupMenu popup = new JPopupMenu();
-
-                               for (final DataNode<T> child : node.getChildren()) {
-                                       popup.add(new AbstractAction(child.toString()) {
-                                               private static final long serialVersionUID = 1L;
-
-                                               @Override
-                                               public void actionPerformed(ActionEvent e) {
-                                                       setSelectedNode(child);
-                                               }
-                                       });
-                               }
-
-                               down.addActionListener(new ActionListener() {
-                                       @Override
-                                       public void actionPerformed(ActionEvent ev) {
-                                               if (down.isSelected()) {
-                                                       popup.show(down, 0, down.getBounds().height);
-                                               } else {
-                                                       popup.setVisible(false);
-                                               }
-                                       }
-                               });
-
-                               popup.addPopupMenuListener(new PopupMenuListener() {
-                                       @Override
-                                       public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
-                                       }
-
-                                       @Override
-                                       public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
-                                               down.setSelected(false);
-                                       }
-
-                                       @Override
-                                       public void popupMenuCanceled(PopupMenuEvent e) {
-                                       }
-                               });
-
-                               this.add(down, BorderLayout.EAST);
-                       }
-               }
-       }
-
-       static public final String CHANGE_ACTION = "change";
-
-       private boolean vertical;
-       private DataNode<T> node;
-       private List<BreadCrumb> crumbs = new ArrayList<BreadCrumb>();
-
-       public BreadCrumbsBar(final DataTree<T> tree) {
-               vertical = true; // to force an update
-               setVertical(false);
-
-               addComponentListener(new ComponentAdapter() {
-                       @Override
-                       public void componentResized(ComponentEvent e) {
-                               super.componentResized(e);
-                               synchronized (crumbs) {
-                                       for (BreadCrumb crumb : crumbs) {
-                                               setCrumbSize(crumb);
-                                       }
-                               }
-                       }
-               });
-
-               setSelectedNode(new DataNode<T>(null, null));
-
-               new SwingWorker<DataNode<T>, Void>() {
-                       @Override
-                       protected DataNode<T> doInBackground() throws Exception {
-                               tree.loadData();
-                               return tree.getRoot();
-                       }
-
-                       @Override
-                       protected void done() {
-                               try {
-                                       DataNode<T> node = get();
-
-                                       setSelectedNode(null);
-                                       BreadCrumbsBar.this.node = node;
-                                       addCrumb(node);
-
-                                       // TODO: option?
-                                       if (node.size() > 0) {
-                                               setSelectedNode(node.getChildren().get(0));
-                                       } else {
-                                               revalidate();
-                                               repaint();
-                                       }
-                               } catch (Exception e) {
-                                       e.printStackTrace();
-                               }
-                       }
-               }.execute();
-       }
-
-       public void setVertical(boolean vertical) {
-               if (vertical != this.vertical) {
-                       synchronized (crumbs) {
-                               this.vertical = vertical;
-
-                               for (BreadCrumb crumb : crumbs) {
-                                       this.remove(crumb);
-                               }
-
-                               if (vertical) {
-                                       this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
-                               } else {
-                                       this.setLayout(new WrapLayout(WrapLayout.LEADING));
-                               }
-
-                               for (BreadCrumb crumb : crumbs) {
-                                       this.add(crumb);
-                                       setCrumbSize(crumb);
-                               }
-                       }
-
-                       this.revalidate();
-                       this.repaint();
-               }
-       }
-
-       public DataNode<T> getSelectedNode() {
-               return node;
-       }
-
-       public void setSelectedNode(DataNode<T> node) {
-               if (this.node == node) {
-                       return;
-               }
-
-               synchronized (crumbs) {
-                       // clear until common ancestor (can clear all!)
-                       while (this.node != null && !this.node.isParentOf(node)) {
-                               this.node = this.node.getParent();
-                               this.remove(crumbs.remove(crumbs.size() - 1));
-                       }
-
-                       // switch root if needed and possible
-                       if (this.node == null && node != null) {
-                               this.node = node.getRoot();
-                               addCrumb(this.node);
-                       }
-
-                       // re-create until node
-                       while (node != null && this.node != node) {
-                               DataNode<T> ancestorOrNode = node;
-                               for (DataNode<T> child : this.node.getChildren()) {
-                                       if (child.isParentOf(node))
-                                               ancestorOrNode = child;
-                               }
-
-                               this.node = ancestorOrNode;
-                               addCrumb(this.node);
-                       }
-               }
-
-               this.revalidate();
-               this.repaint();
-
-               fireActionPerformed(CHANGE_ACTION);
-       }
-
-       private void addCrumb(DataNode<T> node) {
-               BreadCrumb crumb = new BreadCrumb(node);
-               this.crumbs.add(crumb);
-               setCrumbSize(crumb);
-               this.add(crumb);
-       }
-
-       private void setCrumbSize(BreadCrumb crumb) {
-               if (vertical) {
-                       crumb.setMaximumSize(new Dimension(this.getWidth(),
-                                       crumb.getMinimumSize().height));
-               } else {
-                       crumb.setMaximumSize(null);
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigEditor.java b/src/be/nikiroo/utils/ui/ConfigEditor.java
deleted file mode 100644 (file)
index c687c98..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-import javax.swing.border.EmptyBorder;
-import javax.swing.border.TitledBorder;
-
-import be.nikiroo.utils.StringUtils;
-import be.nikiroo.utils.resources.Bundle;
-import be.nikiroo.utils.resources.MetaInfo;
-
-/**
- * A configuration panel for a {@link Bundle}.
- * <p>
- * All the items in the given {@link Bundle} will be displayed in editable
- * controls, with options to Save, Reset and/or Reset to the application default
- * values.
- * 
- * @author niki
- * 
- * @param <E>
- *            the type of {@link Bundle} to edit
- */
-public class ConfigEditor<E extends Enum<E>> extends JPanel {
-       private static final long serialVersionUID = 1L;
-       private List<MetaInfo<E>> items;
-
-       /**
-        * Create a new {@link ConfigEditor} for this {@link Bundle}.
-        * 
-        * @param type
-        *            a class instance of the item type to work on
-        * @param bundle
-        *            the {@link Bundle} to sort through
-        * @param title
-        *            the title to display before the options
-        */
-       public ConfigEditor(Class<E> type, final Bundle<E> bundle, String title) {
-               this.setLayout(new BorderLayout());
-
-               JPanel main = new JPanel();
-               main.setLayout(new BoxLayout(main, BoxLayout.PAGE_AXIS));
-               main.setBorder(new EmptyBorder(5, 5, 5, 5));
-
-               main.add(new JLabel(title));
-
-               items = new ArrayList<MetaInfo<E>>();
-               List<MetaInfo<E>> groupedItems = MetaInfo.getItems(type, bundle);
-               for (MetaInfo<E> item : groupedItems) {
-                       // will init this.items
-                       addItem(main, item, 0);
-               }
-
-               JPanel buttons = new JPanel();
-               buttons.setLayout(new BoxLayout(buttons, BoxLayout.PAGE_AXIS));
-               buttons.setBorder(new EmptyBorder(5, 5, 5, 5));
-
-               buttons.add(createButton("Reset", new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               for (MetaInfo<E> item : items) {
-                                       item.reload();
-                               }
-                       }
-               }));
-
-               buttons.add(createButton("Default", new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               Object snap = bundle.takeSnapshot();
-                               bundle.reload(true);
-                               for (MetaInfo<E> item : items) {
-                                       item.reload();
-                               }
-                               bundle.reload(false);
-                               bundle.restoreSnapshot(snap);
-                       }
-               }));
-
-               buttons.add(createButton("Save", new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               for (MetaInfo<E> item : items) {
-                                       item.save(true);
-                               }
-
-                               try {
-                                       bundle.updateFile();
-                               } catch (IOException e1) {
-                                       e1.printStackTrace();
-                               }
-                       }
-               }));
-
-               JScrollPane scroll = new JScrollPane(main);
-               scroll.getVerticalScrollBar().setUnitIncrement(16);
-
-               this.add(scroll, BorderLayout.CENTER);
-               this.add(buttons, BorderLayout.SOUTH);
-       }
-
-       private void addItem(JPanel main, MetaInfo<E> item, int nhgap) {
-               if (item.isGroup()) {
-                       JPanel bpane = new JPanel(new BorderLayout());
-                       bpane.setBorder(new TitledBorder(item.getName()));
-                       JPanel pane = new JPanel();
-                       pane.setBorder(new EmptyBorder(5, 5, 5, 5));
-                       pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
-
-                       String info = item.getDescription();
-                       info = StringUtils.justifyTexts(info, 100);
-                       if (!info.isEmpty()) {
-                               info = info + "\n";
-                               JTextArea text = new JTextArea(info);
-                               text.setWrapStyleWord(true);
-                               text.setOpaque(false);
-                               text.setForeground(new Color(100, 100, 180));
-                               text.setEditable(false);
-                               pane.add(text);
-                       }
-
-                       for (MetaInfo<E> subitem : item) {
-                               addItem(pane, subitem, nhgap + 11);
-                       }
-                       bpane.add(pane, BorderLayout.CENTER);
-                       main.add(bpane);
-               } else {
-                       items.add(item);
-                       main.add(ConfigItem.createItem(item, nhgap));
-               }
-       }
-
-       /**
-        * Add an action button for this action.
-        * 
-        * @param title
-        *            the action title
-        * @param listener
-        *            the action
-        */
-       private JComponent createButton(String title, ActionListener listener) {
-               JButton button = new JButton(title);
-               button.addActionListener(listener);
-
-               JPanel panel = new JPanel();
-               panel.setLayout(new BorderLayout());
-               panel.setBorder(new EmptyBorder(2, 10, 2, 10));
-               panel.add(button, BorderLayout.CENTER);
-
-               return panel;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItem.java b/src/be/nikiroo/utils/ui/ConfigItem.java
deleted file mode 100644 (file)
index 3ae029e..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Cursor;
-import java.awt.Dimension;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.util.List;
-
-import javax.swing.BoxLayout;
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
-
-import be.nikiroo.utils.Image;
-import be.nikiroo.utils.StringUtils;
-import be.nikiroo.utils.StringUtils.Alignment;
-import be.nikiroo.utils.resources.Bundle;
-import be.nikiroo.utils.resources.MetaInfo;
-
-/**
- * A graphical item that reflect a configuration option from the given
- * {@link Bundle}.
- * <p>
- * This graphical item can be edited, and the result will be saved back into the
- * linked {@link MetaInfo}; you still have to save the {@link MetaInfo} should
- * you wish to, of course.
- * 
- * @author niki
- * 
- * @param <E>
- *            the type of {@link Bundle} to edit
- */
-public abstract class ConfigItem<E extends Enum<E>> extends JPanel {
-       private static final long serialVersionUID = 1L;
-
-       private static int minimumHeight = -1;
-
-       /** A small 16x16 "?" blue in PNG, base64 encoded. */
-       private static String img64info = //
-       ""
-                       + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI"
-                       + "WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wURFRg6IrtcdgAAATdJREFUOMvtkj8sQ1EUxr9z/71G"
-                       + "m1RDogYxq7WDDYMYTSajSG4n6YRYzSaSLibWbiaDIGwdiLIYDFKDNJEgKu969xi8UNHy7H7LPcN3"
-                       + "v/Odcy+hG9oOIeIcBCJS9MAvlZtOMtHxsrFrJHGqe0RVGnHAHpcIbPlng8BS3HmKBJYzabGUzcrJ"
-                       + "XK+ckIrqANYR2JEv2nYDEVck0WKGfHzyq82Go+btxoX3XAcAIqTj8wPqOH6mtMeM4bGCLhyfhTMA"
-                       + "qlLhKHqujCfaweCAmV0p50dPzsNpEKpK01V/n55HIvTnfDC2odKlfeYadZN/T+AqDACUsnkhqaU1"
-                       + "LRIVuX1x7ciuSWQxVIrunONrfq3dI6oh+T94Z8453vEem/HTqT8ZpFJ0qDXtGkPbAGAMeSRngQCA"
-                       + "eUvgn195AwlZWyvjtQdhAAAAAElFTkSuQmCC";
-
-       /** A small 16x16 "+" image with colours */
-       private static String img64add = //
-       ""
-                       + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI"
-                       + "WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wUeES0QBFvvnAAAAB1pVFh0Q29tbWVudAAAAAAAQ3Jl"
-                       + "YXRlZCB3aXRoIEdJTVBkLmUHAAACH0lEQVQ4y42Tz0sVURTHP+fMmC7CQMpH1EjgIimCsEVBEIg/"
-                       + "qIbcBAW2Uai1m/oH2rlJXLQpeRJt2gQhTO0iTTKC1I2JBf5gKCJCRPvhPOed22LmvV70Fn7hwr3c"
-                       + "+z3ne+73HCFHEClxaASRHgduA91AW369BkwDI3Foy0GkEofmACQnSxyaCyItAkMClMzYdeCAJgVP"
-                       + "tJJrPA7tVoUjNZlngXMAiRmXClfoK/Tjq09x7T6LW+8RxOVJ5+LQzgSRojm5WCEDlMrQVbjIQNtN"
-                       + "rh0d5FTzaTLBmWKgM4h0Ig4NzWseohYCJUuqx123Sx0MBpF2+MAdyWUnlqX4lf4bIDHjR+rwJJPR"
-                       + "qNCgCjDsA10lM/oKIRcO9lByCYklnG/pqQa4euQ6J5tPoKI0yD6ef33Ku40Z80R7CSJNWyZxT+Ki"
-                       + "2ytGP911hyZxQaRp1RtPPPYKD4+sGJwPrDUp7Q9Xxnj9fYrUUnaszEAwQHfrZQAerT/g7cYMiuCp"
-                       + "z8LmLI0qBqz6wLQn2v5he57FrXkAtlPH2ZZOuskCzG2+4dnnx3iSuSgCKqLAlAIjmXPiVIRsgYjU"
-                       + "usrfO0Gq7cA9jUNbBsZrmiQnac1e6n3FeBzakpf39OSBG9IPHAZwzlFoagVg5edHXn57wZed9dpA"
-                       + "C3FoYRDpf8M0AQwKwu9yubxjeA7Y72ENqlp3mOqMcwcwDPQCx8gGchV4BYzGoS1V3gL8AVA5C5/0"
-                       + "oRFoAAAAAElFTkSuQmCC";
-
-       /** A small 32x32 "-" image with colours */
-       private static String img64remove = //
-       ""
-                       + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI"
-                       + "WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wUeESw5X/JGsQAAAB1pVFh0Q29tbWVudAAAAAAAQ3Jl"
-                       + "YXRlZCB3aXRoIEdJTVBkLmUHAAACKUlEQVQ4y5WTO2iTYRSG3+//v/+SJrG5SSABh1JQBHFJNUNR"
-                       + "YodCLoMoTkK0YKhQtBmsl01wKVZRBwcrgosg3SwFW9Cippe0VmlpB6uYqYIaNSZtbv/lOKRx0iR9"
-                       + "4YOzvOc8vOd8wLbG4nYGAKP9tshKr3Pq0zFXORt0UzbopvUeZ2ml1/niUcIWAYBzwwqr+xgAjCSt"
-                       + "wpXjWzx105Ha+1XsMgT8U6IJfPAacyfO50OXJi3VwbtbxMbidtZ3tiClbzi/eAuCmxgai4AfNvNn"
-                       + "KJn3X5xWKgwA0lHHYud3MdDUXMcmIOMx0oGJXJCN9tuiJ98p4//DbtTk2cFKhB/OSBcMgQHVMkir"
-                       + "AqwJBhGYrIIkCQc2eJK3aewI9Crko2FIh0K1Jo0mcwmV6XFUlmfRXhK7eXuRKaRVIYdiUGKnW8Kn"
-                       + "0ia0t6/hKHJVqCcLzncQgLhtIvBfbWbZZahq+cl96AuvQLre2Mw59NUlkCwjZ6USL0uYgSj26B/X"
-                       + "oK+vtkYgMAhMRF4x5oWlPdod0UQtfUFo7YEBBKz59BEGAAtRx1xHVgzu5AYyHmMmMJHrZolhhU3t"
-                       + "05XJe7s2PJuCq9k1MgKyNjOXiBf8kWW5JDy4XKHBl2ql6+pvX8ZjzDOqrcWsFQAAE/T3H3z2GG/6"
-                       + "zhT8sfdKeehWkUQAeJ7WcH23xTz1uPBwf1hclA3mBZjPojFOIOSsVPpmN1OznfpA+Gn+2kCHqg/d"
-                       + "LhIA/AFU5d0V6gTjtQAAAABJRU5ErkJggg==";
-
-       /** The code base */
-       private final ConfigItemBase<JComponent, E> base;
-
-       /** The main panel with all the fields in it. */
-       private JPanel main;
-
-       /**
-        * Prepare a new {@link ConfigItem} instance, linked to the given
-        * {@link MetaInfo}.
-        * 
-        * @param info
-        *            the info
-        * @param autoDirtyHandling
-        *            TRUE to automatically manage the setDirty/Save operations,
-        *            FALSE if you want to do it yourself via
-        *            {@link ConfigItem#setDirtyItem(int)}
-        */
-       protected ConfigItem(MetaInfo<E> info, boolean autoDirtyHandling) {
-               base = new ConfigItemBase<JComponent, E>(info, autoDirtyHandling) {
-                       @Override
-                       protected JComponent createEmptyField(int item) {
-                               return ConfigItem.this.createEmptyField(item);
-                       }
-
-                       @Override
-                       protected Object getFromInfo(int item) {
-                               return ConfigItem.this.getFromInfo(item);
-                       }
-
-                       @Override
-                       protected void setToInfo(Object value, int item) {
-                               ConfigItem.this.setToInfo(value, item);
-                       }
-
-                       @Override
-                       protected Object getFromField(int item) {
-                               return ConfigItem.this.getFromField(item);
-                       }
-
-                       @Override
-                       protected void setToField(Object value, int item) {
-                               ConfigItem.this.setToField(value, item);
-                       }
-
-                       @Override
-                       public JComponent createField(int item) {
-                               JComponent field = super.createField(item);
-
-                               int height = Math.max(getMinimumHeight(),
-                                               field.getMinimumSize().height);
-                               field.setPreferredSize(new Dimension(200, height));
-
-                               return field;
-                       }
-
-                       @Override
-                       public List<JComponent> reload() {
-                               List<JComponent> removed = base.reload();
-                               if (!removed.isEmpty()) {
-                                       for (JComponent c : removed) {
-                                               main.remove(c);
-                                       }
-                                       main.revalidate();
-                                       main.repaint();
-                               }
-
-                               return removed;
-                       }
-
-                       @Override
-                       protected JComponent removeItem(int item) {
-                               JComponent removed = super.removeItem(item);
-                               main.remove(removed);
-                               main.revalidate();
-                               main.repaint();
-
-                               return removed;
-                       }
-               };
-       }
-
-       /**
-        * Create a new {@link ConfigItem} for the given {@link MetaInfo}.
-        * 
-        * @param nhgap
-        *            negative horisontal gap in pixel to use for the label, i.e.,
-        *            the step lock sized labels will start smaller by that amount
-        *            (the use case would be to align controls that start at a
-        *            different horisontal position)
-        */
-       public void init(int nhgap) {
-               if (getInfo().isArray()) {
-                       this.setLayout(new BorderLayout());
-                       add(label(nhgap), BorderLayout.WEST);
-
-                       main = new JPanel();
-
-                       main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
-                       int size = getInfo().getListSize(false);
-                       for (int i = 0; i < size; i++) {
-                               addItemWithMinusPanel(i);
-                       }
-                       main.revalidate();
-                       main.repaint();
-
-                       final JButton add = new JButton();
-                       setImage(add, img64add, "+");
-
-                       add.addActionListener(new ActionListener() {
-                               @Override
-                               public void actionPerformed(ActionEvent e) {
-                                       addItemWithMinusPanel(base.getFieldsSize());
-                                       main.revalidate();
-                                       main.repaint();
-                               }
-                       });
-
-                       JPanel tmp = new JPanel(new BorderLayout());
-                       tmp.add(add, BorderLayout.WEST);
-
-                       JPanel mainPlus = new JPanel(new BorderLayout());
-                       mainPlus.add(main, BorderLayout.CENTER);
-                       mainPlus.add(tmp, BorderLayout.SOUTH);
-
-                       add(mainPlus, BorderLayout.CENTER);
-               } else {
-                       this.setLayout(new BorderLayout());
-                       add(label(nhgap), BorderLayout.WEST);
-
-                       JComponent field = base.createField(-1);
-                       add(field, BorderLayout.CENTER);
-               }
-       }
-
-       /** The {@link MetaInfo} linked to the field. */
-       public MetaInfo<E> getInfo() {
-               return base.getInfo();
-       }
-
-       /**
-        * Retrieve the associated graphical component that was created with
-        * {@link ConfigItemBase#createEmptyField(int)}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the graphical component
-        */
-       protected JComponent getField(int item) {
-               return base.getField(item);
-       }
-
-       /**
-        * Manually specify that the given item is "dirty" and thus should be saved
-        * when asked.
-        * <p>
-        * Has no effect if the class is using automatic dirty handling (see
-        * {@link ConfigItemBase#ConfigItem(MetaInfo, boolean)}).
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       protected void setDirtyItem(int item) {
-               base.setDirtyItem(item);
-       }
-
-       /**
-        * Check if the value changed since the last load/save into the linked
-        * {@link MetaInfo}.
-        * <p>
-        * Note that we consider NULL and an Empty {@link String} to be equals.
-        * 
-        * @param value
-        *            the value to test
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return TRUE if it has
-        */
-       protected boolean hasValueChanged(Object value, int item) {
-               return base.hasValueChanged(value, item);
-       }
-
-       private void addItemWithMinusPanel(int item) {
-               JPanel minusPanel = createMinusPanel(item);
-               JComponent field = base.addItem(item, minusPanel);
-               minusPanel.add(field, BorderLayout.CENTER);
-       }
-
-       private JPanel createMinusPanel(final int item) {
-               JPanel minusPanel = new JPanel(new BorderLayout());
-
-               final JButton remove = new JButton();
-               setImage(remove, img64remove, "-");
-
-               remove.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               base.removeItem(item);
-                       }
-               });
-
-               minusPanel.add(remove, BorderLayout.EAST);
-
-               main.add(minusPanel);
-               main.revalidate();
-               main.repaint();
-
-               return minusPanel;
-       }
-
-       /**
-        * Create an empty graphical component to be used later by
-        * {@link ConfigItem#createField(int)}.
-        * <p>
-        * Note that {@link ConfigItem#reload(int)} will be called after it was
-        * created by {@link ConfigItem#createField(int)}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the graphical component
-        */
-       abstract protected JComponent createEmptyField(int item);
-
-       /**
-        * Get the information from the {@link MetaInfo} in the subclass preferred
-        * format.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the information in the subclass preferred format
-        */
-       abstract protected Object getFromInfo(int item);
-
-       /**
-        * Set the value to the {@link MetaInfo}.
-        * 
-        * @param value
-        *            the value in the subclass preferred format
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       abstract protected void setToInfo(Object value, int item);
-
-       /**
-        * The value present in the given item's related field in the subclass
-        * preferred format.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the value present in the given item's related field in the
-        *         subclass preferred format
-        */
-       abstract protected Object getFromField(int item);
-
-       /**
-        * Set the value (in the subclass preferred format) into the field.
-        * 
-        * @param value
-        *            the value in the subclass preferred format
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       abstract protected void setToField(Object value, int item);
-
-       /**
-        * Create a label which width is constrained in lock steps.
-        * 
-        * @param nhgap
-        *            negative horisontal gap in pixel to use for the label, i.e.,
-        *            the step lock sized labels will start smaller by that amount
-        *            (the use case would be to align controls that start at a
-        *            different horisontal position)
-        * 
-        * @return the label
-        */
-       protected JComponent label(int nhgap) {
-               final JLabel label = new JLabel(getInfo().getName());
-
-               Dimension ps = label.getPreferredSize();
-               if (ps == null) {
-                       ps = label.getSize();
-               }
-
-               ps.height = Math.max(ps.height, getMinimumHeight());
-
-               int w = ps.width;
-               int step = 150;
-               for (int i = 2 * step - nhgap; i < 10 * step; i += step) {
-                       if (w < i) {
-                               w = i;
-                               break;
-                       }
-               }
-
-               final Runnable showInfo = new Runnable() {
-                       @Override
-                       public void run() {
-                               StringBuilder builder = new StringBuilder();
-                               String text = (getInfo().getDescription().replace("\\n", "\n"))
-                                               .trim();
-                               for (String line : StringUtils.justifyText(text, 80,
-                                               Alignment.LEFT)) {
-                                       if (builder.length() > 0) {
-                                               builder.append("\n");
-                                       }
-                                       builder.append(line);
-                               }
-                               text = builder.toString();
-                               JOptionPane.showMessageDialog(ConfigItem.this, text, getInfo()
-                                               .getName(), JOptionPane.INFORMATION_MESSAGE);
-                       }
-               };
-
-               JLabel help = new JLabel("");
-               help.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-               setImage(help, img64info, "?");
-
-               help.addMouseListener(new MouseAdapter() {
-                       @Override
-                       public void mouseClicked(MouseEvent e) {
-                               showInfo.run();
-                       }
-               });
-
-               JPanel pane2 = new JPanel(new BorderLayout());
-               pane2.add(help, BorderLayout.WEST);
-               pane2.add(new JLabel(" "), BorderLayout.CENTER);
-
-               JPanel contentPane = new JPanel(new BorderLayout());
-               contentPane.add(label, BorderLayout.WEST);
-               contentPane.add(pane2, BorderLayout.CENTER);
-
-               ps.width = w + 30; // 30 for the (?) sign
-               contentPane.setSize(ps);
-               contentPane.setPreferredSize(ps);
-
-               JPanel pane = new JPanel(new BorderLayout());
-               pane.add(contentPane, BorderLayout.NORTH);
-
-               return pane;
-       }
-
-       /**
-        * Create a new {@link ConfigItem} for the given {@link MetaInfo}.
-        * 
-        * @param <E>
-        *            the type of {@link Bundle} to edit
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        * @param nhgap
-        *            negative horisontal gap in pixel to use for the label, i.e.,
-        *            the step lock sized labels will start smaller by that amount
-        *            (the use case would be to align controls that start at a
-        *            different horisontal position)
-        * 
-        * @return the new {@link ConfigItem}
-        */
-       static public <E extends Enum<E>> ConfigItem<E> createItem(
-                       MetaInfo<E> info, int nhgap) {
-
-               ConfigItem<E> configItem;
-               switch (info.getFormat()) {
-               case BOOLEAN:
-                       configItem = new ConfigItemBoolean<E>(info);
-                       break;
-               case COLOR:
-                       configItem = new ConfigItemColor<E>(info);
-                       break;
-               case FILE:
-                       configItem = new ConfigItemBrowse<E>(info, false);
-                       break;
-               case DIRECTORY:
-                       configItem = new ConfigItemBrowse<E>(info, true);
-                       break;
-               case COMBO_LIST:
-                       configItem = new ConfigItemCombobox<E>(info, true);
-                       break;
-               case FIXED_LIST:
-                       configItem = new ConfigItemCombobox<E>(info, false);
-                       break;
-               case INT:
-                       configItem = new ConfigItemInteger<E>(info);
-                       break;
-               case PASSWORD:
-                       configItem = new ConfigItemPassword<E>(info);
-                       break;
-               case LOCALE:
-                       configItem = new ConfigItemLocale<E>(info);
-                       break;
-               case STRING:
-               default:
-                       configItem = new ConfigItemString<E>(info);
-                       break;
-               }
-
-               configItem.init(nhgap);
-               return configItem;
-       }
-
-       /**
-        * Set an image to the given {@link JButton}, with a fallback text if it
-        * fails.
-        * 
-        * @param button
-        *            the button to set
-        * @param image64
-        *            the image in BASE64 (should be PNG or similar)
-        * @param fallbackText
-        *            text to use in case the image cannot be created
-        */
-       static protected void setImage(JLabel button, String image64,
-                       String fallbackText) {
-               try {
-                       Image img = new Image(image64);
-                       try {
-                               BufferedImage bImg = ImageUtilsAwt.fromImage(img);
-                               button.setIcon(new ImageIcon(bImg));
-                       } finally {
-                               img.close();
-                       }
-               } catch (IOException e) {
-                       // This is an hard-coded image, should not happen
-                       button.setText(fallbackText);
-               }
-       }
-
-       /**
-        * Set an image to the given {@link JButton}, with a fallback text if it
-        * fails.
-        * 
-        * @param button
-        *            the button to set
-        * @param image64
-        *            the image in BASE64 (should be PNG or similar)
-        * @param fallbackText
-        *            text to use in case the image cannot be created
-        */
-       static protected void setImage(JButton button, String image64,
-                       String fallbackText) {
-               try {
-                       Image img = new Image(image64);
-                       try {
-                               BufferedImage bImg = ImageUtilsAwt.fromImage(img);
-                               button.setIcon(new ImageIcon(bImg));
-                       } finally {
-                               img.close();
-                       }
-               } catch (IOException e) {
-                       // This is an hard-coded image, should not happen
-                       button.setText(fallbackText);
-               }
-       }
-
-       static private int getMinimumHeight() {
-               if (minimumHeight < 0) {
-                       minimumHeight = new JTextField("Test").getMinimumSize().height;
-               }
-
-               return minimumHeight;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemBase.java b/src/be/nikiroo/utils/ui/ConfigItemBase.java
deleted file mode 100644 (file)
index 21b5755..0000000
+++ /dev/null
@@ -1,467 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import be.nikiroo.utils.resources.Bundle;
-import be.nikiroo.utils.resources.MetaInfo;
-
-/**
- * A graphical item that reflect a configuration option from the given
- * {@link Bundle}.
- * <p>
- * This graphical item can be edited, and the result will be saved back into the
- * linked {@link MetaInfo}; you still have to save the {@link MetaInfo} should
- * you wish to, of course.
- * 
- * @author niki
- * 
- * @param <T>
- *            the graphical base type to use (i.e., T or TWidget)
- * @param <E>
- *            the type of {@link Bundle} to edit
- */
-public abstract class ConfigItemBase<T, E extends Enum<E>> {
-       /** The original value before current changes. */
-       private Object orig;
-       private List<Object> origs = new ArrayList<Object>();
-       private List<Integer> dirtyBits;
-
-       /** The fields (one for non-array, a list for arrays). */
-       private T field;
-       private List<T> fields = new ArrayList<T>();
-
-       /** The fields to panel map to get the actual item added to 'main'. */
-       private Map<Integer, T> itemFields = new HashMap<Integer, T>();
-
-       /** The {@link MetaInfo} linked to the field. */
-       private MetaInfo<E> info;
-
-       /** The {@link MetaInfo} linked to the field. */
-       public MetaInfo<E> getInfo() {
-               return info;
-       }
-
-       /**
-        * The number of fields, for arrays.
-        * 
-        * @return
-        */
-       public int getFieldsSize() {
-               return fields.size();
-       }
-
-       /**
-        * The number of fields to panel map to get the actual item added to 'main'.
-        */
-       public int getItemFieldsSize() {
-               return itemFields.size();
-       }
-
-       /**
-        * Add a new item in an array-value {@link MetaInfo}.
-        * 
-        * @param item
-        *            the index of the new item
-        * @param panel
-        *            a linked T, if we want to link it into the itemFields (can be
-        *            NULL) -- that way, we can get it back later on
-        *            {@link ConfigItemBase#removeItem(int)}
-        * 
-        * @return the newly created graphical field
-        */
-       public T addItem(final int item, T panel) {
-               if (panel != null) {
-                       itemFields.put(item, panel);
-               }
-               return createField(item);
-       }
-
-       /**
-        * The counter-part to {@link ConfigItemBase#addItem(int, Object)}, to
-        * remove a specific item of an array-values {@link MetaInfo}; all the
-        * remaining items will be shifted as required (so, always the last
-        * graphical object will be removed).
-        * 
-        * @param item
-        *            the index of the item to remove
-        * 
-        * @return the linked graphical T to remove if any (always the latest
-        *         graphical object if any)
-        */
-       protected T removeItem(int item) {
-               int last = itemFields.size() - 1;
-
-               for (int i = item; i <= last; i++) {
-                       Object value = null;
-                       if (i < last) {
-                               value = getFromField(i + 1);
-                       }
-                       setToField(value, i);
-                       setToInfo(value, i);
-                       setDirtyItem(i);
-               }
-
-               return itemFields.remove(last);
-       }
-
-       /**
-        * Prepare a new {@link ConfigItemBase} instance, linked to the given
-        * {@link MetaInfo}.
-        * 
-        * @param info
-        *            the info
-        * @param autoDirtyHandling
-        *            TRUE to automatically manage the setDirty/Save operations,
-        *            FALSE if you want to do it yourself via
-        *            {@link ConfigItemBase#setDirtyItem(int)}
-        */
-       protected ConfigItemBase(MetaInfo<E> info, boolean autoDirtyHandling) {
-               this.info = info;
-               if (!autoDirtyHandling) {
-                       dirtyBits = new ArrayList<Integer>();
-               }
-       }
-
-       /**
-        * Create an empty graphical component to be used later by
-        * {@link ConfigItemBase#createField(int)}.
-        * <p>
-        * Note that {@link ConfigItemBase#reload(int)} will be called after it was
-        * created by {@link ConfigItemBase#createField(int)}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the graphical component
-        */
-       abstract protected T createEmptyField(int item);
-
-       /**
-        * Get the information from the {@link MetaInfo} in the subclass preferred
-        * format.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the information in the subclass preferred format
-        */
-       abstract protected Object getFromInfo(int item);
-
-       /**
-        * Set the value to the {@link MetaInfo}.
-        * 
-        * @param value
-        *            the value in the subclass preferred format
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       abstract protected void setToInfo(Object value, int item);
-
-       /**
-        * The value present in the given item's related field in the subclass
-        * preferred format.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the value present in the given item's related field in the
-        *         subclass preferred format
-        */
-       abstract protected Object getFromField(int item);
-
-       /**
-        * Set the value (in the subclass preferred format) into the field.
-        * 
-        * @param value
-        *            the value in the subclass preferred format
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       abstract protected void setToField(Object value, int item);
-
-       /**
-        * Create a new field for the given graphical component at the given index
-        * (note that the component is usually created by
-        * {@link ConfigItemBase#createEmptyField(int)}).
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * @param field
-        *            the graphical component
-        */
-       private void setField(int item, T field) {
-               if (item < 0) {
-                       this.field = field;
-                       return;
-               }
-
-               for (int i = fields.size(); i <= item; i++) {
-                       fields.add(null);
-               }
-
-               fields.set(item, field);
-       }
-
-       /**
-        * Retrieve the associated graphical component that was created with
-        * {@link ConfigItemBase#createEmptyField(int)}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the graphical component
-        */
-       public T getField(int item) {
-               if (item < 0) {
-                       return field;
-               }
-
-               if (item < fields.size()) {
-                       return fields.get(item);
-               }
-
-               return null;
-       }
-
-       /**
-        * The original value (before any changes to the {@link MetaInfo}) for this
-        * item.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the original value
-        */
-       private Object getOrig(int item) {
-               if (item < 0) {
-                       return orig;
-               }
-
-               if (item < origs.size()) {
-                       return origs.get(item);
-               }
-
-               return null;
-       }
-
-       /**
-        * The original value (before any changes to the {@link MetaInfo}) for this
-        * item.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * @param value
-        *            the new original value
-        */
-       private void setOrig(Object value, int item) {
-               if (item < 0) {
-                       orig = value;
-               } else {
-                       while (item >= origs.size()) {
-                               origs.add(null);
-                       }
-
-                       origs.set(item, value);
-               }
-       }
-
-       /**
-        * Manually specify that the given item is "dirty" and thus should be saved
-        * when asked.
-        * <p>
-        * Has no effect if the class is using automatic dirty handling (see
-        * {@link ConfigItemBase#ConfigItem(MetaInfo, boolean)}).
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       public void setDirtyItem(int item) {
-               if (dirtyBits != null) {
-                       dirtyBits.add(item);
-               }
-       }
-
-       /**
-        * Check if the value changed since the last load/save into the linked
-        * {@link MetaInfo}.
-        * <p>
-        * Note that we consider NULL and an Empty {@link String} to be equals.
-        * 
-        * @param value
-        *            the value to test
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return TRUE if it has
-        */
-       public boolean hasValueChanged(Object value, int item) {
-               // We consider "" and NULL to be equals
-               Object orig = getOrig(item);
-               if (orig == null) {
-                       orig = "";
-               }
-               return !orig.equals(value == null ? "" : value);
-       }
-
-       /**
-        * Reload the values to what they currently are in the {@link MetaInfo}.
-        * 
-        * @return for arrays, the list of graphical T objects we don't need any
-        *         more (never NULL, but can be empty)
-        */
-       public List<T> reload() {
-               List<T> removed = new ArrayList<T>();
-               if (info.isArray()) {
-                       while (!itemFields.isEmpty()) {
-                               removed.add(itemFields.remove(itemFields.size() - 1));
-                       }
-                       for (int item = 0; item < info.getListSize(false); item++) {
-                               reload(item);
-                       }
-               } else {
-                       reload(-1);
-               }
-
-               return removed;
-       }
-
-       /**
-        * Reload the values to what they currently are in the {@link MetaInfo}.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        */
-       private void reload(int item) {
-               if (item >= 0 && !itemFields.containsKey(item)) {
-                       addItem(item, null);
-               }
-
-               Object value = getFromInfo(item);
-               setToField(value, item);
-               setOrig(value == null ? "" : value, item);
-       }
-
-       /**
-        * If the item has been modified, set the {@link MetaInfo} to dirty then
-        * modify it to, reflect the changes so it can be saved later.
-        * <p>
-        * This method does <b>not</b> call {@link MetaInfo#save(boolean)}.
-        */
-       private void save() {
-               if (info.isArray()) {
-                       boolean dirty = itemFields.size() != info.getListSize(false);
-                       for (int item = 0; item < itemFields.size(); item++) {
-                               if (getDirtyBit(item)) {
-                                       dirty = true;
-                               }
-                       }
-
-                       if (dirty) {
-                               info.setDirty();
-                               info.setString(null, -1);
-
-                               for (int item = 0; item < itemFields.size(); item++) {
-                                       Object value = null;
-                                       if (getField(item) != null) {
-                                               value = getFromField(item);
-                                               if ("".equals(value)) {
-                                                       value = null;
-                                               }
-                                       }
-
-                                       setToInfo(value, item);
-                                       setOrig(value, item);
-                               }
-                       }
-               } else {
-                       if (getDirtyBit(-1)) {
-                               Object value = getFromField(-1);
-
-                               info.setDirty();
-                               setToInfo(value, -1);
-                               setOrig(value, -1);
-                       }
-               }
-       }
-
-       /**
-        * Check if the item is dirty, and clear the dirty bit if set.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return TRUE if it was dirty, FALSE if not
-        */
-       private boolean getDirtyBit(int item) {
-               if (dirtyBits != null) {
-                       return dirtyBits.remove((Integer) item);
-               }
-
-               Object value = null;
-               if (getField(item) != null) {
-                       value = getFromField(item);
-               }
-
-               return hasValueChanged(value, item);
-       }
-
-       /**
-        * Create a new field for the given item.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return the newly created field
-        */
-       public T createField(final int item) {
-               T field = createEmptyField(item);
-               setField(item, field);
-               reload(item);
-
-               info.addReloadedListener(new Runnable() {
-                       @Override
-                       public void run() {
-                               reload();
-                       }
-               });
-               info.addSaveListener(new Runnable() {
-                       @Override
-                       public void run() {
-                               save();
-                       }
-               });
-
-               return field;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemBoolean.java b/src/be/nikiroo/utils/ui/ConfigItemBoolean.java
deleted file mode 100644 (file)
index de89f68..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import javax.swing.JCheckBox;
-import javax.swing.JComponent;
-
-import be.nikiroo.utils.resources.MetaInfo;
-
-class ConfigItemBoolean<E extends Enum<E>> extends ConfigItem<E> {
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * Create a new {@link ConfigItemBoolean} for the given {@link MetaInfo}.
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        */
-       public ConfigItemBoolean(MetaInfo<E> info) {
-               super(info, true);
-       }
-
-       @Override
-       protected Object getFromField(int item) {
-               JCheckBox field = (JCheckBox) getField(item);
-               if (field != null) {
-                       return field.isSelected();
-               }
-
-               return null;
-       }
-
-       @Override
-       protected Object getFromInfo(int item) {
-               return getInfo().getBoolean(item, true);
-       }
-
-       @Override
-       protected void setToField(Object value, int item) {
-               JCheckBox field = (JCheckBox) getField(item);
-               if (field != null) {
-                       // Should not happen if config enum is correct
-                       // (but this is not enforced)
-                       if (value == null) {
-                               value = false;
-                       }
-
-                       field.setSelected((Boolean) value);
-               }
-       }
-
-       @Override
-       protected void setToInfo(Object value, int item) {
-               getInfo().setBoolean((Boolean) value, item);
-       }
-
-       @Override
-       protected JComponent createEmptyField(int item) {
-               // Should not happen!
-               if (getFromInfo(item) == null) {
-                       System.err
-                                       .println("No default value given for BOOLEAN parameter \""
-                                                       + getInfo().getName()
-                                                       + "\", we consider it is FALSE");
-               }
-
-               return new JCheckBox();
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemBrowse.java b/src/be/nikiroo/utils/ui/ConfigItemBrowse.java
deleted file mode 100644 (file)
index 9a54e52..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JFileChooser;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
-
-import be.nikiroo.utils.resources.MetaInfo;
-
-class ConfigItemBrowse<E extends Enum<E>> extends ConfigItem<E> {
-       private static final long serialVersionUID = 1L;
-
-       private boolean dir;
-       private Map<JComponent, JTextField> fields = new HashMap<JComponent, JTextField>();
-
-       /**
-        * Create a new {@link ConfigItemBrowse} for the given {@link MetaInfo}.
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        * @param dir
-        *            TRUE for directory browsing, FALSE for file browsing
-        */
-       public ConfigItemBrowse(MetaInfo<E> info, boolean dir) {
-               super(info, false);
-               this.dir = dir;
-       }
-
-       @Override
-       protected Object getFromField(int item) {
-               JTextField field = fields.get(getField(item));
-               if (field != null) {
-                       return new File(field.getText());
-               }
-
-               return null;
-       }
-
-       @Override
-       protected Object getFromInfo(int item) {
-               String path = getInfo().getString(item, false);
-               if (path != null && !path.isEmpty()) {
-                       return new File(path);
-               }
-
-               return null;
-       }
-
-       @Override
-       protected void setToField(Object value, int item) {
-               JTextField field = fields.get(getField(item));
-               if (field != null) {
-                       field.setText(value == null ? "" : ((File) value).getPath());
-               }
-       }
-
-       @Override
-       protected void setToInfo(Object value, int item) {
-               getInfo().setString(((File) value).getPath(), item);
-       }
-
-       @Override
-       protected JComponent createEmptyField(final int item) {
-               final JPanel pane = new JPanel(new BorderLayout());
-               final JTextField field = new JTextField();
-               field.addKeyListener(new KeyAdapter() {
-                       @Override
-                       public void keyTyped(KeyEvent e) {
-                               File file = null;
-                               if (!field.getText().isEmpty()) {
-                                       file = new File(field.getText());
-                               }
-
-                               if (hasValueChanged(file, item)) {
-                                       setDirtyItem(item);
-                               }
-                       }
-               });
-
-               final JButton browseButton = new JButton("...");
-               browseButton.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               JFileChooser chooser = new JFileChooser();
-                               chooser.setCurrentDirectory((File) getFromInfo(item));
-                               chooser.setFileSelectionMode(dir ? JFileChooser.DIRECTORIES_ONLY
-                                               : JFileChooser.FILES_ONLY);
-                               if (chooser.showOpenDialog(ConfigItemBrowse.this) == JFileChooser.APPROVE_OPTION) {
-                                       File file = chooser.getSelectedFile();
-                                       if (file != null) {
-                                               setToField(file, item);
-                                               if (hasValueChanged(file, item)) {
-                                                       setDirtyItem(item);
-                                               }
-                                       }
-                               }
-                       }
-               });
-
-               pane.add(browseButton, BorderLayout.WEST);
-               pane.add(field, BorderLayout.CENTER);
-
-               fields.put(pane, field);
-               return pane;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemColor.java b/src/be/nikiroo/utils/ui/ConfigItemColor.java
deleted file mode 100644 (file)
index 951ff45..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.image.BufferedImage;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.swing.Icon;
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
-import javax.swing.JColorChooser;
-import javax.swing.JComponent;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
-
-import be.nikiroo.utils.resources.MetaInfo;
-
-class ConfigItemColor<E extends Enum<E>> extends ConfigItem<E> {
-       private static final long serialVersionUID = 1L;
-
-       private Map<JComponent, JTextField> fields = new HashMap<JComponent, JTextField>();
-       private Map<JComponent, JButton> panels = new HashMap<JComponent, JButton>();
-
-       /**
-        * Create a new {@link ConfigItemColor} for the given {@link MetaInfo}.
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        */
-       public ConfigItemColor(MetaInfo<E> info) {
-               super(info, true);
-       }
-
-       @Override
-       protected Object getFromField(int item) {
-               JTextField field = fields.get(getField(item));
-               if (field != null) {
-                       return field.getText();
-               }
-
-               return null;
-       }
-
-       @Override
-       protected Object getFromInfo(int item) {
-               return getInfo().getString(item, true);
-       }
-
-       @Override
-       protected void setToField(Object value, int item) {
-               JTextField field = fields.get(getField(item));
-               if (field != null) {
-                       field.setText(value == null ? "" : value.toString());
-               }
-
-               JButton colorWheel = panels.get(getField(item));
-               if (colorWheel != null) {
-                       colorWheel.setIcon(getIcon(17, getFromInfoColor(item)));
-               }
-       }
-
-       @Override
-       protected void setToInfo(Object value, int item) {
-               getInfo().setString((String) value, item);
-       }
-
-       /**
-        * Get the colour currently present in the linked info for the given item.
-        * 
-        * @param item
-        *            the item number to get for an array of values, or -1 to get
-        *            the whole value (has no effect if {@link MetaInfo#isArray()}
-        *            is FALSE)
-        * 
-        * @return a colour
-        */
-       private int getFromInfoColor(int item) {
-               Integer color = getInfo().getColor(item, true);
-               if (color == null) {
-                       return new Color(255, 255, 255, 255).getRGB();
-               }
-
-               return color;
-       }
-
-       @Override
-       protected JComponent createEmptyField(final int item) {
-               final JPanel pane = new JPanel(new BorderLayout());
-               final JTextField field = new JTextField();
-
-               final JButton colorWheel = new JButton();
-               colorWheel.setIcon(getIcon(17, getFromInfoColor(item)));
-               colorWheel.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               int icol = getFromInfoColor(item);
-                               Color initialColor = new Color(icol, true);
-                               Color newColor = JColorChooser.showDialog(ConfigItemColor.this,
-                                               getInfo().getName(), initialColor);
-                               if (newColor != null) {
-                                       getInfo().setColor(newColor.getRGB(), item);
-                                       field.setText(getInfo().getString(item, false));
-                                       colorWheel.setIcon(getIcon(17,
-                                                       getInfo().getColor(item, true)));
-                               }
-                       }
-               });
-
-               field.addKeyListener(new KeyAdapter() {
-                       @Override
-                       public void keyTyped(KeyEvent e) {
-                               getInfo().setString(field.getText() + e.getKeyChar(), item);
-                               int color = getFromInfoColor(item);
-                               colorWheel.setIcon(getIcon(17, color));
-                       }
-               });
-
-               pane.add(colorWheel, BorderLayout.WEST);
-               pane.add(field, BorderLayout.CENTER);
-
-               fields.put(pane, field);
-               panels.put(pane, colorWheel);
-               return pane;
-       }
-
-       /**
-        * Return an {@link Icon} to use as a colour badge for the colour field
-        * controls.
-        * 
-        * @param size
-        *            the size of the badge
-        * @param color
-        *            the colour of the badge, which can be NULL (will return
-        *            transparent white)
-        * 
-        * @return the badge
-        */
-       static private Icon getIcon(int size, Integer color) {
-               // Allow null values
-               if (color == null) {
-                       color = new Color(255, 255, 255, 255).getRGB();
-               }
-
-               Color c = new Color(color, true);
-               int avg = (c.getRed() + c.getGreen() + c.getBlue()) / 3;
-               Color border = (avg >= 128 ? Color.BLACK : Color.WHITE);
-
-               BufferedImage img = new BufferedImage(size, size,
-                               BufferedImage.TYPE_4BYTE_ABGR);
-
-               Graphics2D g = img.createGraphics();
-               try {
-                       g.setColor(c);
-                       g.fillRect(0, 0, img.getWidth(), img.getHeight());
-                       g.setColor(border);
-                       g.drawRect(0, 0, img.getWidth() - 1, img.getHeight() - 1);
-               } finally {
-                       g.dispose();
-               }
-
-               return new ImageIcon(img);
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemCombobox.java b/src/be/nikiroo/utils/ui/ConfigItemCombobox.java
deleted file mode 100644 (file)
index 07a6115..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import javax.swing.JComboBox;
-import javax.swing.JComponent;
-
-import be.nikiroo.utils.resources.MetaInfo;
-
-class ConfigItemCombobox<E extends Enum<E>> extends ConfigItem<E> {
-       private static final long serialVersionUID = 1L;
-
-       private boolean editable;
-       private String[] allowedValues;
-
-       /**
-        * Create a new {@link ConfigItemCombobox} for the given {@link MetaInfo}.
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        * @param editable
-        *            allows the user to type in another value not in the list
-        */
-       public ConfigItemCombobox(MetaInfo<E> info, boolean editable) {
-               super(info, true);
-               this.editable = editable;
-               this.allowedValues = info.getAllowedValues();
-       }
-
-       @Override
-       protected Object getFromField(int item) {
-               // rawtypes for Java 1.6 (and 1.7 ?) support
-               @SuppressWarnings("rawtypes")
-               JComboBox field = (JComboBox) getField(item);
-               if (field != null) {
-                       return field.getSelectedItem();
-               }
-
-               return null;
-       }
-
-       @Override
-       protected Object getFromInfo(int item) {
-               return getInfo().getString(item, false);
-       }
-
-       @Override
-       protected void setToField(Object value, int item) {
-               // rawtypes for Java 1.6 (and 1.7 ?) support
-               @SuppressWarnings("rawtypes")
-               JComboBox field = (JComboBox) getField(item);
-               if (field != null) {
-                       field.setSelectedItem(value);
-               }
-       }
-
-       @Override
-       protected void setToInfo(Object value, int item) {
-               getInfo().setString((String) value, item);
-       }
-
-       // rawtypes for Java 1.6 (and 1.7 ?) support
-       @SuppressWarnings({ "unchecked", "rawtypes" })
-       @Override
-       protected JComponent createEmptyField(int item) {
-               JComboBox field = new JComboBox(allowedValues);
-               field.setEditable(editable);
-               return field;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemInteger.java b/src/be/nikiroo/utils/ui/ConfigItemInteger.java
deleted file mode 100644 (file)
index 10c5d9d..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import javax.swing.JComponent;
-import javax.swing.JSpinner;
-
-import be.nikiroo.utils.resources.MetaInfo;
-
-class ConfigItemInteger<E extends Enum<E>> extends ConfigItem<E> {
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * Create a new {@link ConfigItemInteger} for the given {@link MetaInfo}.
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        */
-       public ConfigItemInteger(MetaInfo<E> info) {
-               super(info, true);
-       }
-
-       @Override
-       protected Object getFromField(int item) {
-               JSpinner field = (JSpinner) getField(item);
-               if (field != null) {
-                       return field.getValue();
-               }
-
-               return null;
-       }
-
-       @Override
-       protected Object getFromInfo(int item) {
-               return getInfo().getInteger(item, true);
-       }
-
-       @Override
-       protected void setToField(Object value, int item) {
-               JSpinner field = (JSpinner) getField(item);
-               if (field != null) {
-                       field.setValue(value == null ? 0 : (Integer) value);
-               }
-       }
-
-       @Override
-       protected void setToInfo(Object value, int item) {
-               getInfo().setInteger((Integer) value, item);
-       }
-
-       @Override
-       protected JComponent createEmptyField(int item) {
-               return new JSpinner();
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemLocale.java b/src/be/nikiroo/utils/ui/ConfigItemLocale.java
deleted file mode 100644 (file)
index eef8da0..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.Component;
-import java.util.Locale;
-
-import javax.swing.DefaultListCellRenderer;
-import javax.swing.JComboBox;
-import javax.swing.JComponent;
-import javax.swing.JList;
-
-import be.nikiroo.utils.resources.MetaInfo;
-
-class ConfigItemLocale<E extends Enum<E>> extends ConfigItemCombobox<E> {
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * Create a new {@link ConfigItemLocale} for the given {@link MetaInfo}.
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        */
-       public ConfigItemLocale(MetaInfo<E> info) {
-               super(info, true);
-       }
-
-       // rawtypes for Java 1.6 (and 1.7 ?) support
-       @SuppressWarnings({ "unchecked", "rawtypes" })
-       @Override
-       protected JComponent createEmptyField(int item) {
-               JComboBox field = (JComboBox) super.createEmptyField(item);
-               field.setRenderer(new DefaultListCellRenderer() {
-                       private static final long serialVersionUID = 1L;
-
-                       @Override
-                       public Component getListCellRendererComponent(JList list,
-                                       Object value, int index, boolean isSelected,
-                                       boolean cellHasFocus) {
-
-                               String svalue = value == null ? "" : value.toString();
-                               String[] tab = svalue.split("-");
-                               Locale locale = null;
-                               if (tab.length == 1) {
-                                       locale = new Locale(tab[0]);
-                               } else if (tab.length == 2) {
-                                       locale = new Locale(tab[0], tab[1]);
-                               } else if (tab.length == 3) {
-                                       locale = new Locale(tab[0], tab[1], tab[2]);
-                               }
-
-                               String displayValue = svalue;
-                               if (locale != null) {
-                                       displayValue = locale.getDisplayName();
-                               }
-
-                               return super.getListCellRendererComponent(list, displayValue,
-                                               index, isSelected, cellHasFocus);
-                       }
-               });
-
-               return field;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemPassword.java b/src/be/nikiroo/utils/ui/ConfigItemPassword.java
deleted file mode 100644 (file)
index e8ad2f2..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JPanel;
-import javax.swing.JPasswordField;
-
-import be.nikiroo.utils.resources.MetaInfo;
-
-class ConfigItemPassword<E extends Enum<E>> extends ConfigItem<E> {
-       private static final long serialVersionUID = 1L;
-       /** A small 16x16 pass-protecet icon in PNG, base64 encoded. */
-       private static String img64passProtected = //
-       ""
-                       + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAAnNCSVQICFXsRgQAAAD5SURBVCjP"
-                       + "ndG9LoNxGIbxHxJTG9U0IsJAdCSNqZEa9BR87BaHYfW5ESYkmjQh4giwIU00MWFwAPWRSmpgaf6G"
-                       + "6ts36eZ+xuu+lvuhlTGjOFHAsXldWVDRa82WhE9pZFxrtmBeUY87+yqCH3UzMh4E1VYhp2ZVVfi7"
-                       + "C0PuBc9G2v6KoOlIQUoyhovyLb+uZla/TbsRHnOgJkfSi4YpbDiXjuwJDS+SlASLYC9mw5KgxJlg"
-                       + "CWJ4OyqckvKkIWswwmXrmPbl0QBkHcbsHRv6Fbz6MNnesWMnpMw51vRmphuXo7FujHf+cCt4NGza"
-                       + "lbp3l5b1xR/1rWrYf/MLWpplWwswQpMAAAAASUVORK5CYII=";
-
-       /** A small 16x16 pass-unprotecet icon in PNG, base64 encoded. */
-       private static String img64passUnprotected = //
-       ""
-                       + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAA"
-                       + "CxMAAAsTAQCanBgAAAAHdElNRQfjBR8MIilwhCwdAAABK0lEQVQoz5XQv0uUAQCH8c/7qod4nect"
-                       + "gop3BIKDFBIiRyiKtATmcEiBDW7+Ae5ODt5gW0SLigouKTg6SJvkjw4Co8mcNeWgc+o839dBBXPz"
-                       + "+Y7PM33r3NCpWcWKM1lfHapJq0B4G/TbEDoyZlyHQxuGtdw6eSMC33yyJxa79MW+wIj8TdDrxJSS"
-                       + "+N5KppQNEchrkrMosmzRT0/0eGdSaFrob6DXloSqgu9mNWlUNqPPpmYNJkg5UvEMResystYVpbwW"
-                       + "qWpjVWwcfNQqLS1rAXwQOw4N4SWoqZeUVFMGuzgg65/IqIw5a3LarZnDcxd+ScMrkcikhB8+m1eU"
-                       + "MODUua67q967EttR0KHFoCVX/nhxp1N4o/rfUTueekC332KRM9veqnuoAwQyHs81DiddylUvrecA"
-                       + "AAAASUVORK5CYII=";
-
-       private Map<JComponent, JPasswordField> fields = new HashMap<JComponent, JPasswordField>();
-
-       /**
-        * Create a new {@link ConfigItemPassword} for the given {@link MetaInfo}.
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        */
-       public ConfigItemPassword(MetaInfo<E> info) {
-               super(info, true);
-       }
-
-       @Override
-       protected Object getFromField(int item) {
-               JPasswordField field = fields.get(getField(item));
-               if (field != null) {
-                       return new String(field.getPassword());
-               }
-
-               return null;
-       }
-
-       @Override
-       protected Object getFromInfo(int item) {
-               return getInfo().getString(item, false);
-       }
-
-       @Override
-       protected void setToField(Object value, int item) {
-               JPasswordField field = fields.get(getField(item));
-               if (field != null) {
-                       field.setText(value == null ? "" : value.toString());
-               }
-       }
-
-       @Override
-       protected void setToInfo(Object value, int item) {
-               getInfo().setString((String) value, item);
-       }
-
-       @Override
-       protected JComponent createEmptyField(int item) {
-               JPanel pane = new JPanel(new BorderLayout());
-               final JPasswordField field = new JPasswordField();
-               field.setEchoChar('*');
-
-               final JButton show = new JButton();
-               final Boolean[] visible = new Boolean[] { false };
-               setImage(show, img64passProtected, "/");
-               show.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               visible[0] = !visible[0];
-                               if (visible[0]) {
-                                       field.setEchoChar((char) 0);
-                                       setImage(show, img64passUnprotected, "o");
-                               } else {
-                                       field.setEchoChar('*');
-                                       setImage(show, img64passProtected, "/");
-                               }
-                       }
-               });
-
-               pane.add(field, BorderLayout.CENTER);
-               pane.add(show, BorderLayout.EAST);
-
-               fields.put(pane, field);
-               return pane;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ConfigItemString.java b/src/be/nikiroo/utils/ui/ConfigItemString.java
deleted file mode 100644 (file)
index 46333c0..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import javax.swing.JComponent;
-import javax.swing.JTextField;
-
-import be.nikiroo.utils.resources.MetaInfo;
-
-class ConfigItemString<E extends Enum<E>> extends ConfigItem<E> {
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * Create a new {@link ConfigItemString} for the given {@link MetaInfo}.
-        * 
-        * @param info
-        *            the {@link MetaInfo}
-        */
-       public ConfigItemString(MetaInfo<E> info) {
-               super(info, true);
-       }
-
-       @Override
-       protected Object getFromField(int item) {
-               JTextField field = (JTextField) getField(item);
-               if (field != null) {
-                       return field.getText();
-               }
-
-               return null;
-       }
-
-       @Override
-       protected Object getFromInfo(int item) {
-               return getInfo().getString(item, false);
-       }
-
-       @Override
-       protected void setToField(Object value, int item) {
-               JTextField field = (JTextField) getField(item);
-               if (field != null) {
-                       field.setText(value == null ? "" : value.toString());
-               }
-       }
-
-       @Override
-       protected void setToInfo(Object value, int item) {
-               getInfo().setString((String) value, item);
-       }
-
-       @Override
-       protected JComponent createEmptyField(int item) {
-               return new JTextField();
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/DataNode.java b/src/be/nikiroo/utils/ui/DataNode.java
deleted file mode 100644 (file)
index b4dbe7b..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.swing.Icon;
-
-public class DataNode<T> {
-       private DataNode<T> parent;
-       private List<? extends DataNode<T>> children;
-       private T userData;
-
-       public DataNode(List<? extends DataNode<T>> children, T userData) {
-               if (children == null) {
-                       children = new ArrayList<DataNode<T>>();
-               }
-
-               this.children = children;
-               this.userData = userData;
-
-               for (DataNode<T> child : children) {
-                       child.parent = this;
-               }
-       }
-
-       public DataNode<T> getRoot() {
-               DataNode<T> root = this;
-               while (root.parent != null) {
-                       root = root.parent;
-               }
-
-               return root;
-       }
-
-       public DataNode<T> getParent() {
-               return parent;
-       }
-
-       public List<? extends DataNode<T>> getChildren() {
-               return children;
-       }
-
-       public int size() {
-               return children.size();
-       }
-
-       public boolean isRoot() {
-               return this == getRoot();
-       }
-
-       public boolean isSiblingOf(DataNode<T> node) {
-               if (this == node) {
-                       return true;
-               }
-
-               return node != null && parent != null && parent.children.contains(node);
-       }
-
-       public boolean isParentOf(DataNode<T> node) {
-               if (node == null || node.parent == null)
-                       return false;
-
-               if (this == node.parent)
-                       return true;
-
-               return isParentOf(node.parent);
-       }
-
-       public boolean isChildOf(DataNode<T> node) {
-               if (node == null || node.size() == 0)
-                       return false;
-
-               return node.isParentOf(this);
-       }
-
-       public T getUserData() {
-               return userData;
-       }
-
-       /**
-        * The total number of nodes present in this {@link DataNode} (including
-        * itself and descendants).
-        * 
-        * @return the number
-        */
-       public int count() {
-               int s = 1;
-               for (DataNode<T> child : children) {
-                       s += child.count();
-               }
-
-               return s;
-       }
-
-       @Override
-       public String toString() {
-               if (userData == null) {
-                       return "";
-               }
-
-               return userData.toString();
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/ui/DataTree.java b/src/be/nikiroo/utils/ui/DataTree.java
deleted file mode 100644 (file)
index 6b3657d..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.MutableTreeNode;
-
-public abstract class DataTree<E> {
-       protected DataNode<E> data;
-
-       public DataNode<E> loadData() throws IOException {
-               return this.data = extractData();
-       }
-
-       public DataNode<E> getRoot() {
-               return getRoot(null);
-       }
-
-       public DataNode<E> getRoot(String filter) {
-               return filterNode(data, filter);
-       }
-
-       protected abstract DataNode<E> extractData() throws IOException;
-
-       // filter cannot be null nor empty
-       protected abstract boolean checkFilter(String filter, E userData);
-
-       protected boolean checkFilter(DataNode<E> node, String filter) {
-               if (filter == null || filter.isEmpty()) {
-                       return true;
-               }
-
-               if (checkFilter(filter, node.getUserData()))
-                       return true;
-
-               for (DataNode<E> child : node.getChildren()) {
-                       if (checkFilter(child, filter))
-                               return true;
-               }
-
-               return false;
-       }
-
-       protected void sort(List<String> values) {
-               Collections.sort(values, new Comparator<String>() {
-                       @Override
-                       public int compare(String o1, String o2) {
-                               return ("" + o1).compareToIgnoreCase("" + o2);
-                       }
-               });
-       }
-
-       // note: we always send TAHT node, but filter children
-       private DataNode<E> filterNode(DataNode<E> source, String filter) {
-               List<DataNode<E>> children = new ArrayList<DataNode<E>>();
-               for (DataNode<E> child : source.getChildren()) {
-                       if (checkFilter(child, filter)) {
-                               children.add(filterNode(child, filter));
-                       }
-               }
-
-               return new DataNode<E>(children, source.getUserData());
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/DelayWorker.java b/src/be/nikiroo/utils/ui/DelayWorker.java
deleted file mode 100644 (file)
index 2a16c98..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeSet;
-
-import javax.swing.SwingWorker;
-
-/**
- * This class helps you delay some graphical actions and execute the most recent
- * ones when under contention.
- * <p>
- * How does it work?
- * <ul>
- * <li>it takes an ID and an associated {@link SwingWorker} and will call
- * {@link SwingWorker#execute()} after a small delay (see
- * {@link DelayWorker#DelayWorker(int)})</li>
- * <li>if a second call to {@link DelayWorker#delay(String, SwingWorker)} comes
- * with the same ID before the first one is done, it will be put on a waiting
- * queue</li>
- * <li>if a third call still with the same ID comes, its associated worker will
- * <b>replace</b> the one in the queue (only one worker per ID in the queue,
- * always the latest one)</li>
- * <li>when the first worker is done, it will check the waiting queue and
- * execute that latest worker if any</li>
- * </ul>
- * 
- * @author niki
- *
- */
-@SuppressWarnings("rawtypes")
-public class DelayWorker {
-       private Map<String, SwingWorker> lazyEnCours;
-       private Object lazyEnCoursLock;
-
-       private TreeSet<String> wip;
-
-       private Object waiter;
-
-       private boolean cont;
-       private boolean paused;
-       private Thread loop;
-
-       /**
-        * Create a new {@link DelayWorker} with the given delay (in milliseconds)
-        * before each drain of the queue.
-        * 
-        * @param delayMs
-        *            the delay in milliseconds (can be 0, cannot be negative)
-        */
-       public DelayWorker(final int delayMs) {
-               if (delayMs < 0) {
-                       throw new IllegalArgumentException(
-                                       "A waiting delay cannot be negative");
-               }
-
-               lazyEnCours = new HashMap<String, SwingWorker>();
-               lazyEnCoursLock = new Object();
-               wip = new TreeSet<String>();
-               waiter = new Object();
-               cont = true;
-               paused = false;
-
-               loop = new Thread(new Runnable() {
-                       @Override
-                       public void run() {
-                               while (cont) {
-                                       try {
-                                               Thread.sleep(delayMs);
-                                       } catch (InterruptedException e) {
-                                       }
-
-                                       Map<String, SwingWorker> workers = new HashMap<String, SwingWorker>();
-                                       synchronized (lazyEnCoursLock) {
-                                               for (String key : new ArrayList<String>(
-                                                               lazyEnCours.keySet())) {
-                                                       if (!wip.contains(key)) {
-                                                               workers.put(key, lazyEnCours.remove(key));
-                                                       }
-                                               }
-                                       }
-
-                                       for (final String key : workers.keySet()) {
-                                               SwingWorker worker = workers.get(key);
-
-                                               synchronized (lazyEnCoursLock) {
-                                                       wip.add(key);
-                                               }
-
-                                               worker.addPropertyChangeListener(
-                                                               new PropertyChangeListener() {
-                                                                       @Override
-                                                                       public void propertyChange(
-                                                                                       PropertyChangeEvent evt) {
-                                                                               synchronized (lazyEnCoursLock) {
-                                                                                       wip.remove(key);
-                                                                               }
-                                                                               wakeup();
-                                                                       }
-                                                               });
-
-                                               // Start it, at last
-                                               worker.execute();
-                                       }
-
-                                       synchronized (waiter) {
-                                               do {
-                                                       try {
-                                                               if (cont)
-                                                                       waiter.wait();
-                                                       } catch (InterruptedException e) {
-                                                       }
-                                               } while (cont && paused);
-                                       }
-                               }
-                       }
-               });
-
-               loop.setDaemon(true);
-               loop.setName("Loop for DelayWorker");
-       }
-
-       /**
-        * Start the internal loop that will drain the processing queue. <b>MUST
-        * NOT</b> be started twice (but see {@link DelayWorker#pause()} and
-        * {@link DelayWorker#resume()} instead).
-        */
-       public void start() {
-               loop.start();
-       }
-
-       /**
-        * Pause the system until {@link DelayWorker#resume()} is called -- note
-        * that it will still continue on the processes currently scheduled to run,
-        * but will pause after that.
-        * <p>
-        * Can be called even if already paused, will just do nothing in that
-        * context.
-        */
-       public void pause() {
-               paused = true;
-       }
-
-       /**
-        * Check if the {@link DelayWorker} is currently paused.
-        * 
-        * @return TRUE if it is
-        */
-       public boolean isPaused() {
-               return paused;
-       }
-
-       /**
-        * Resume the system after a pause.
-        * <p>
-        * Can be called even if already running, will just do nothing in that
-        * context.
-        */
-       public void resume() {
-               synchronized (waiter) {
-                       paused = false;
-                       wakeup();
-               }
-       }
-
-       /**
-        * Stop the system.
-        * <p>
-        * Note: this is final, you <b>MUST NOT</b> call {@link DelayWorker#start()}
-        * a second time (but see {@link DelayWorker#pause()} and
-        * {@link DelayWorker#resume()} instead).
-        */
-       public void stop() {
-               synchronized (waiter) {
-                       cont = false;
-                       wakeup();
-               }
-       }
-
-       /**
-        * Clear all the processes that were put on the queue but not yet scheduled
-        * to be executed -- note that it will still continue on the processes
-        * currently scheduled to run.
-        */
-       public void clear() {
-               synchronized (lazyEnCoursLock) {
-                       lazyEnCours.clear();
-                       wip.clear();
-               }
-       }
-
-       /**
-        * Put a new process in the delay queue.
-        * 
-        * @param id
-        *            the ID of this process (if you want to skip workers when they
-        *            are superseded by a new one, you need to use the same ID key)
-        * @param worker
-        *            the process to delay
-        */
-       public void delay(String id, SwingWorker worker) {
-               synchronized (lazyEnCoursLock) {
-                       lazyEnCours.put(id, worker);
-               }
-
-               wakeup();
-       }
-
-       /**
-        * Wake up the loop thread.
-        */
-       private void wakeup() {
-               synchronized (waiter) {
-                       waiter.notifyAll();
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ImageTextAwt.java b/src/be/nikiroo/utils/ui/ImageTextAwt.java
deleted file mode 100644 (file)
index 4c0c824..0000000
+++ /dev/null
@@ -1,512 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Graphics;
-import java.awt.Image;
-import java.awt.image.BufferedImage;
-import java.awt.image.ImageObserver;
-
-/**
- * This class converts an {@link Image} into a textual representation that can
- * be displayed to the user in a TUI.
- * 
- * @author niki
- */
-public class ImageTextAwt {
-       private Image image;
-       private Dimension size;
-       private String text;
-       private boolean ready;
-       private Mode mode;
-       private boolean invert;
-
-       /**
-        * The rendering modes supported by this {@link ImageTextAwt} to convert
-        * {@link Image}s into text.
-        * 
-        * @author niki
-        * 
-        */
-       public enum Mode {
-               /**
-                * Use 5 different "colours" which are actually Unicode
-                * {@link Character}s representing
-                * <ul>
-                * <li>space (blank)</li>
-                * <li>low shade (░)</li>
-                * <li>medium shade (▒)</li>
-                * <li>high shade (▓)</li>
-                * <li>full block (█)</li>
-                * </ul>
-                */
-               DITHERING,
-               /**
-                * Use "block" Unicode {@link Character}s up to quarter blocks, thus in
-                * effect doubling the resolution both in vertical and horizontal space.
-                * Note that since 2 {@link Character}s next to each other are square,
-                * we will use 4 blocks per 2 blocks for w/h resolution.
-                */
-               DOUBLE_RESOLUTION,
-               /**
-                * Use {@link Character}s from both {@link Mode#DOUBLE_RESOLUTION} and
-                * {@link Mode#DITHERING}.
-                */
-               DOUBLE_DITHERING,
-               /**
-                * Only use ASCII {@link Character}s.
-                */
-               ASCII,
-       }
-
-       /**
-        * Create a new {@link ImageTextAwt} with the given parameters. Defaults to
-        * {@link Mode#DOUBLE_DITHERING} and no colour inversion.
-        * 
-        * @param image
-        *            the source {@link Image}
-        * @param size
-        *            the final text size to target
-        */
-       public ImageTextAwt(Image image, Dimension size) {
-               this(image, size, Mode.DOUBLE_DITHERING, false);
-       }
-
-       /**
-        * Create a new {@link ImageTextAwt} with the given parameters.
-        * 
-        * @param image
-        *            the source {@link Image}
-        * @param size
-        *            the final text size to target
-        * @param mode
-        *            the mode of conversion
-        * @param invert
-        *            TRUE to invert colours rendering
-        */
-       public ImageTextAwt(Image image, Dimension size, Mode mode, boolean invert) {
-               setImage(image);
-               setSize(size);
-               setMode(mode);
-               setColorInvert(invert);
-       }
-
-       /**
-        * Change the source {@link Image}.
-        * 
-        * @param image
-        *            the new {@link Image}
-        */
-       public void setImage(Image image) {
-               this.text = null;
-               this.ready = false;
-               this.image = image;
-       }
-
-       /**
-        * Change the target size of this {@link ImageTextAwt}.
-        * 
-        * @param size
-        *            the new size
-        */
-       public void setSize(Dimension size) {
-               this.text = null;
-               this.ready = false;
-               this.size = size;
-       }
-
-       /**
-        * Change the image-to-text mode.
-        * 
-        * @param mode
-        *            the new {@link Mode}
-        */
-       public void setMode(Mode mode) {
-               this.mode = mode;
-               this.text = null;
-               this.ready = false;
-       }
-
-       /**
-        * Set the colour-invert mode.
-        * 
-        * @param invert
-        *            TRUE to inverse the colours
-        */
-       public void setColorInvert(boolean invert) {
-               this.invert = invert;
-               this.text = null;
-               this.ready = false;
-       }
-
-       /**
-        * Check if the colours are inverted.
-        * 
-        * @return TRUE if the colours are inverted
-        */
-       public boolean isColorInvert() {
-               return invert;
-       }
-
-       /**
-        * Return the textual representation of the included {@link Image}.
-        * 
-        * @return the {@link String} representation
-        */
-       public String getText() {
-               if (text == null) {
-                       if (image == null || size == null || size.width == 0
-                                       || size.height == 0) {
-                               return "";
-                       }
-
-                       int mult = 1;
-                       if (mode == Mode.DOUBLE_RESOLUTION || mode == Mode.DOUBLE_DITHERING) {
-                               mult = 2;
-                       }
-
-                       Dimension srcSize = getSize(image);
-                       srcSize = new Dimension(srcSize.width * 2, srcSize.height);
-                       int x = 0;
-                       int y = 0;
-
-                       int w = size.width * mult;
-                       int h = size.height * mult;
-
-                       // Default = original ratio or original size if none
-                       if (w < 0 || h < 0) {
-                               if (w < 0 && h < 0) {
-                                       w = srcSize.width * mult;
-                                       h = srcSize.height * mult;
-                               } else {
-                                       double ratioSrc = (double) srcSize.width
-                                                       / (double) srcSize.height;
-                                       if (w < 0) {
-                                               w = (int) Math.round(h * ratioSrc);
-                                       } else {
-                                               h = (int) Math.round(w / ratioSrc);
-                                       }
-                               }
-                       }
-
-                       // Fail safe: we consider this to be too much
-                       if (w > 1000 || h > 1000) {
-                               return "[IMAGE TOO BIG]";
-                       }
-
-                       BufferedImage buff = new BufferedImage(w, h,
-                                       BufferedImage.TYPE_INT_ARGB);
-
-                       Graphics gfx = buff.getGraphics();
-
-                       double ratioAsked = (double) (w) / (double) (h);
-                       double ratioSrc = (double) srcSize.height / (double) srcSize.width;
-                       double ratio = ratioAsked * ratioSrc;
-                       if (srcSize.width < srcSize.height) {
-                               h = (int) Math.round(ratio * h);
-                               y = (buff.getHeight() - h) / 2;
-                       } else {
-                               w = (int) Math.round(w / ratio);
-                               x = (buff.getWidth() - w) / 2;
-                       }
-
-                       if (gfx.drawImage(image, x, y, w, h, new ImageObserver() {
-                               @Override
-                               public boolean imageUpdate(Image img, int infoflags, int x,
-                                               int y, int width, int height) {
-                                       ImageTextAwt.this.ready = true;
-                                       return true;
-                               }
-                       })) {
-                               ready = true;
-                       }
-
-                       while (!ready) {
-                               try {
-                                       Thread.sleep(100);
-                               } catch (InterruptedException e) {
-                               }
-                       }
-
-                       gfx.dispose();
-
-                       StringBuilder builder = new StringBuilder();
-
-                       for (int row = 0; row + (mult - 1) < buff.getHeight(); row += mult) {
-                               if (row > 0) {
-                                       builder.append('\n');
-                               }
-
-                               for (int col = 0; col + (mult - 1) < buff.getWidth(); col += mult) {
-                                       if (mult == 1) {
-                                               char car = ' ';
-                                               float brightness = getBrightness(buff.getRGB(col, row));
-                                               if (mode == Mode.DITHERING)
-                                                       car = getDitheringChar(brightness, " ░▒▓█");
-                                               if (mode == Mode.ASCII)
-                                                       car = getDitheringChar(brightness, " .-+=o8#");
-
-                                               builder.append(car);
-                                       } else if (mult == 2) {
-                                               builder.append(getBlockChar( //
-                                                               buff.getRGB(col, row),//
-                                                               buff.getRGB(col + 1, row),//
-                                                               buff.getRGB(col, row + 1),//
-                                                               buff.getRGB(col + 1, row + 1),//
-                                                               mode == Mode.DOUBLE_DITHERING//
-                                               ));
-                                       }
-                               }
-                       }
-
-                       text = builder.toString();
-               }
-
-               return text;
-       }
-
-       @Override
-       public String toString() {
-               return getText();
-       }
-
-       /**
-        * Return the size of the given {@link Image}.
-        * 
-        * @param img
-        *            the image to measure
-        * 
-        * @return the size
-        */
-       static private Dimension getSize(Image img) {
-               Dimension size = null;
-               while (size == null) {
-                       int w = img.getWidth(null);
-                       int h = img.getHeight(null);
-                       if (w > -1 && h > -1) {
-                               size = new Dimension(w, h);
-                       } else {
-                               try {
-                                       Thread.sleep(100);
-                               } catch (InterruptedException e) {
-                               }
-                       }
-               }
-
-               return size;
-       }
-
-       /**
-        * Return the {@link Character} corresponding to the given brightness level
-        * from the evenly-separated given {@link Character}s.
-        * 
-        * @param brightness
-        *            the brightness level
-        * @param cars
-        *            the {@link Character}s to choose from, from less bright to
-        *            most bright; <b>MUST</b> contain at least one
-        *            {@link Character}
-        * 
-        * @return the {@link Character} to use
-        */
-       private char getDitheringChar(float brightness, String cars) {
-               int index = Math.round(brightness * (cars.length() - 1));
-               return cars.charAt(index);
-       }
-
-       /**
-        * Return the {@link Character} corresponding to the 4 given colours in
-        * {@link Mode#DOUBLE_RESOLUTION} or {@link Mode#DOUBLE_DITHERING} mode.
-        * 
-        * @param upperleft
-        *            the upper left colour
-        * @param upperright
-        *            the upper right colour
-        * @param lowerleft
-        *            the lower left colour
-        * @param lowerright
-        *            the lower right colour
-        * @param dithering
-        *            TRUE to use {@link Mode#DOUBLE_DITHERING}, FALSE for
-        *            {@link Mode#DOUBLE_RESOLUTION}
-        * 
-        * @return the {@link Character} to use
-        */
-       private char getBlockChar(int upperleft, int upperright, int lowerleft,
-                       int lowerright, boolean dithering) {
-               int choice = 0;
-
-               if (getBrightness(upperleft) > 0.5f) {
-                       choice += 1;
-               }
-               if (getBrightness(upperright) > 0.5f) {
-                       choice += 2;
-               }
-               if (getBrightness(lowerleft) > 0.5f) {
-                       choice += 4;
-               }
-               if (getBrightness(lowerright) > 0.5f) {
-                       choice += 8;
-               }
-
-               switch (choice) {
-               case 0:
-                       return ' ';
-               case 1:
-                       return '▘';
-               case 2:
-                       return '▝';
-               case 3:
-                       return '▀';
-               case 4:
-                       return '▖';
-               case 5:
-                       return '▌';
-               case 6:
-                       return '▞';
-               case 7:
-                       return '▛';
-               case 8:
-                       return '▗';
-               case 9:
-                       return '▚';
-               case 10:
-                       return '▐';
-               case 11:
-                       return '▜';
-               case 12:
-                       return '▄';
-               case 13:
-                       return '▙';
-               case 14:
-                       return '▟';
-               case 15:
-                       if (dithering) {
-                               float avg = 0;
-                               avg += getBrightness(upperleft);
-                               avg += getBrightness(upperright);
-                               avg += getBrightness(lowerleft);
-                               avg += getBrightness(lowerright);
-                               avg /= 4;
-
-                               // Since all the quarters are > 0.5, avg is between 0.5 and 1.0
-                               // So, expand the range of the value
-                               avg = (avg - 0.5f) * 2;
-
-                               // Do not use the " " char, as it would make a
-                               // "all quarters > 0.5" pixel go black
-                               return getDitheringChar(avg, "░▒▓█");
-                       }
-
-                       return '█';
-               }
-
-               return ' ';
-       }
-
-       /**
-        * Temporary array used so not to create a lot of new ones.
-        */
-       private float[] tmp = new float[4];
-
-       /**
-        * Return the brightness value to use from the given ARGB colour.
-        * 
-        * @param argb
-        *            the argb colour
-        * 
-        * @return the brightness to sue for computations
-        */
-       private float getBrightness(int argb) {
-               if (invert) {
-                       return 1 - rgb2hsb(argb, tmp)[2];
-               }
-
-               return rgb2hsb(argb, tmp)[2];
-       }
-
-       /**
-        * Convert the given ARGB colour in HSL/HSB, either into the supplied array
-        * or into a new one if array is NULL.
-        * 
-        * <p>
-        * ARGB pixels are given in 0xAARRGGBB format, while the returned array will
-        * contain Hue, Saturation, Lightness/Brightness, Alpha, in this order. H,
-        * S, L and A are all ranging from 0 to 1 (indeed, H is in 1/360th).
-        * </p>
-        * pixel
-        * 
-        * @param argb
-        *            the ARGB colour pixel to convert
-        * @param array
-        *            the array to convert into or NULL to create a new one
-        * 
-        * @return the array containing the HSL/HSB converted colour
-        */
-       static float[] rgb2hsb(int argb, float[] array) {
-               int a, r, g, b;
-               a = ((argb & 0xff000000) >> 24);
-               r = ((argb & 0x00ff0000) >> 16);
-               g = ((argb & 0x0000ff00) >> 8);
-               b = ((argb & 0x000000ff));
-
-               if (array == null) {
-                       array = new float[4];
-               }
-
-               Color.RGBtoHSB(r, g, b, array);
-
-               array[3] = a;
-
-               return array;
-
-               // // other implementation:
-               //
-               // float a, r, g, b;
-               // a = ((argb & 0xff000000) >> 24) / 255.0f;
-               // r = ((argb & 0x00ff0000) >> 16) / 255.0f;
-               // g = ((argb & 0x0000ff00) >> 8) / 255.0f;
-               // b = ((argb & 0x000000ff)) / 255.0f;
-               //
-               // float rgbMin, rgbMax;
-               // rgbMin = Math.min(r, Math.min(g, b));
-               // rgbMax = Math.max(r, Math.max(g, b));
-               //
-               // float l;
-               // l = (rgbMin + rgbMax) / 2;
-               //
-               // float s;
-               // if (rgbMin == rgbMax) {
-               // s = 0;
-               // } else {
-               // if (l <= 0.5) {
-               // s = (rgbMax - rgbMin) / (rgbMax + rgbMin);
-               // } else {
-               // s = (rgbMax - rgbMin) / (2.0f - rgbMax - rgbMin);
-               // }
-               // }
-               //
-               // float h;
-               // if (r > g && r > b) {
-               // h = (g - b) / (rgbMax - rgbMin);
-               // } else if (g > b) {
-               // h = 2.0f + (b - r) / (rgbMax - rgbMin);
-               // } else {
-               // h = 4.0f + (r - g) / (rgbMax - rgbMin);
-               // }
-               // h /= 6; // from 0 to 1
-               //
-               // return new float[] { h, s, l, a };
-               //
-               // // // natural mode:
-               // //
-               // // int aa = (int) Math.round(100 * a);
-               // // int hh = (int) (360 * h);
-               // // if (hh < 0)
-               // // hh += 360;
-               // // int ss = (int) Math.round(100 * s);
-               // // int ll = (int) Math.round(100 * l);
-               // //
-               // // return new int[] { hh, ss, ll, aa };
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ImageUtilsAwt.java b/src/be/nikiroo/utils/ui/ImageUtilsAwt.java
deleted file mode 100644 (file)
index c273e0d..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.geom.AffineTransform;
-import java.awt.image.AffineTransformOp;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.imageio.ImageIO;
-
-import be.nikiroo.utils.IOUtils;
-import be.nikiroo.utils.Image;
-import be.nikiroo.utils.ImageUtils;
-import be.nikiroo.utils.StringUtils;
-
-/**
- * This class offer some utilities based around images and uses java.awt.
- * 
- * @author niki
- */
-public class ImageUtilsAwt extends ImageUtils {
-       /**
-        * A rotation to perform on an image.
-        * 
-        * @author niki
-        */
-       public enum Rotation {
-               /** No rotation */
-               NONE,
-               /** Rotate the image to the right */
-               RIGHT,
-               /** Rotate the image to the left */
-               LEFT,
-               /** Rotate the image by 180° */
-               UTURN
-       }
-
-       @Override
-       protected boolean check() {
-               // Will not work if ImageIO is not available
-               ImageIO.getCacheDirectory();
-               return true;
-       }
-
-       @Override
-       public void saveAsImage(Image img, File target, String format)
-                       throws IOException {
-               try {
-                       BufferedImage image = fromImage(img);
-
-                       boolean ok = false;
-                       try {
-
-                               ok = ImageIO.write(image, format, target);
-                       } catch (IOException e) {
-                               ok = false;
-                       }
-
-                       // Some formats are not reliable
-                       // Second chance: PNG
-                       if (!ok && !format.equals("png")) {
-                               try {
-                                       ok = ImageIO.write(image, "png", target);
-                               } catch (IllegalArgumentException e) {
-                                       throw e;
-                               } catch (Exception e) {
-                                       throw new IOException("Undocumented exception occured, "
-                                                       + "converting to IOException", e);
-                               }
-                       }
-
-                       if (!ok) {
-                               throw new IOException(
-                                               "Cannot find a writer for this image and format: "
-                                                               + format);
-                       }
-               } catch (IOException e) {
-                       throw new IOException("Cannot write image to " + target, e);
-               }
-       }
-
-       /**
-        * Convert the given {@link Image} into a {@link BufferedImage} object,
-        * respecting the EXIF transformations if any.
-        * 
-        * @param img
-        *            the {@link Image}
-        * 
-        * @return the {@link Image} object
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       public static BufferedImage fromImage(Image img) throws IOException {
-               return fromImage(img, Rotation.NONE);
-       }
-
-       /**
-        * Convert the given {@link Image} into a {@link BufferedImage} object,
-        * respecting the EXIF transformations if any.
-        * 
-        * @param img
-        *            the {@link Image}
-        * @param rotation
-        *            the rotation to apply, if any (can be null, same as
-        *            {@link Rotation#NONE})
-        * 
-        * @return the {@link Image} object
-        * 
-        * @throws IOException
-        *             in case of IO error
-        */
-       public static BufferedImage fromImage(Image img, Rotation rotation)
-                       throws IOException {
-               InputStream in = img.newInputStream();
-               BufferedImage image;
-               try {
-                       int orientation;
-                       try {
-                               orientation = getExifTransorm(in);
-                       } catch (Exception e) {
-                               // no EXIF transform, ok
-                               orientation = -1;
-                       }
-
-                       in.reset();
-
-                       try {
-                               image = ImageIO.read(in);
-                       } catch (IllegalArgumentException e) {
-                               throw e;
-                       } catch (Exception e) {
-                               throw new IOException("Undocumented exception occured, "
-                                               + "converting to IOException", e);
-                       }
-
-                       if (image == null) {
-                               String extra = "";
-                               if (img.getSize() <= 2048) {
-                                       try {
-                                               byte[] data = null;
-                                               InputStream inData = img.newInputStream();
-                                               try {
-                                                       data = IOUtils.toByteArray(inData);
-                                               } finally {
-                                                       inData.close();
-                                               }
-                                               extra = ", content: " + new String(data, "UTF-8");
-                                       } catch (Exception e) {
-                                               extra = ", content unavailable";
-                                       }
-                               }
-                               String ssize = StringUtils.formatNumber(img.getSize());
-                               throw new IOException(
-                                               "Failed to convert input to image, size was: " + ssize
-                                                               + extra);
-                       }
-
-                       // Note: this code has been found on Internet;
-                       // thank you anonymous coder.
-                       int width = image.getWidth();
-                       int height = image.getHeight();
-                       AffineTransform affineTransform = new AffineTransform();
-
-                       switch (orientation) {
-                       case 1:
-                               affineTransform = null;
-                               break;
-                       case 2: // Flip X
-                               affineTransform.scale(-1.0, 1.0);
-                               affineTransform.translate(-width, 0);
-                               break;
-                       case 3: // PI rotation
-                               affineTransform.translate(width, height);
-                               affineTransform.rotate(Math.PI);
-                               break;
-                       case 4: // Flip Y
-                               affineTransform.scale(1.0, -1.0);
-                               affineTransform.translate(0, -height);
-                               break;
-                       case 5: // - PI/2 and Flip X
-                               affineTransform.rotate(-Math.PI / 2);
-                               affineTransform.scale(-1.0, 1.0);
-                               break;
-                       case 6: // -PI/2 and -width
-                               affineTransform.translate(height, 0);
-                               affineTransform.rotate(Math.PI / 2);
-                               break;
-                       case 7: // PI/2 and Flip
-                               affineTransform.scale(-1.0, 1.0);
-                               affineTransform.translate(-height, 0);
-                               affineTransform.translate(0, width);
-                               affineTransform.rotate(3 * Math.PI / 2);
-                               break;
-                       case 8: // PI / 2
-                               affineTransform.translate(0, width);
-                               affineTransform.rotate(3 * Math.PI / 2);
-                               break;
-                       default:
-                               affineTransform = null;
-                               break;
-                       }
-
-                       if (rotation == null)
-                               rotation = Rotation.NONE;
-
-                       switch (rotation) {
-                       case RIGHT:
-                               if (affineTransform == null) {
-                                       affineTransform = new AffineTransform();
-                               }
-                               affineTransform.translate(height, 0);
-                               affineTransform.rotate(Math.PI / 2);
-
-                               int tmp = width;
-                               width = height;
-                               height = tmp;
-
-                               break;
-                       case LEFT:
-                               if (affineTransform == null) {
-                                       affineTransform = new AffineTransform();
-                               }
-                               affineTransform.translate(0, width);
-                               affineTransform.rotate(3 * Math.PI / 2);
-
-                               int temp = width;
-                               width = height;
-                               height = temp;
-
-                               break;
-                       case UTURN:
-                               if (affineTransform == null) {
-                                       affineTransform = new AffineTransform();
-                               }
-                               affineTransform.translate(width, height);
-                               affineTransform.rotate(Math.PI);
-                               break;
-                       default:
-                               break;
-                       }
-
-                       if (affineTransform != null) {
-                               AffineTransformOp affineTransformOp = new AffineTransformOp(
-                                               affineTransform, AffineTransformOp.TYPE_BILINEAR);
-
-                               BufferedImage transformedImage = new BufferedImage(width,
-                                               height, image.getType());
-                               transformedImage = affineTransformOp.filter(image,
-                                               transformedImage);
-
-                               image = transformedImage;
-                       }
-                       //
-               } finally {
-                       in.close();
-               }
-
-               return image;
-       }
-
-       /**
-        * Scale a dimension.
-        * 
-        * @param imageSize
-        *            the actual image size
-        * @param areaSize
-        *            the base size of the target to get snap sizes for
-        * @param zoom
-        *            the zoom factor (ignored on snap mode)
-        * @param snapMode
-        *            NULL for no snap mode, TRUE to snap to width and FALSE for
-        *            snap to height)
-        * 
-        * @return the scaled (minimum is 1x1)
-        */
-       public static Dimension scaleSize(Dimension imageSize, Dimension areaSize,
-                       double zoom, Boolean snapMode) {
-               Integer[] sz = scaleSize(imageSize.width, imageSize.height,
-                               areaSize.width, areaSize.height, zoom, snapMode);
-               return new Dimension(sz[0], sz[1]);
-       }
-
-       /**
-        * Resize the given image.
-        * 
-        * @param image
-        *            the image to resize
-        * @param areaSize
-        *            the base size of the target dimension for snap sizes
-        * @param zoom
-        *            the zoom factor (ignored on snap mode)
-        * @param snapMode
-        *            NULL for no snap mode, TRUE to snap to width and FALSE for
-        *            snap to height)
-        * 
-        * @return a new, resized image
-        */
-       public static BufferedImage scaleImage(BufferedImage image,
-                       Dimension areaSize, double zoom, Boolean snapMode) {
-               Dimension scaledSize = scaleSize(
-                               new Dimension(image.getWidth(), image.getHeight()), areaSize,
-                               zoom, snapMode);
-
-               return scaleImage(image, scaledSize);
-       }
-
-       /**
-        * Resize the given image.
-        * 
-        * @param image
-        *            the image to resize
-        * @param targetSize
-        *            the target size
-        * 
-        * @return a new, resized image
-        */
-       public static BufferedImage scaleImage(BufferedImage image,
-                       Dimension targetSize) {
-               BufferedImage resizedImage = new BufferedImage(targetSize.width,
-                               targetSize.height, BufferedImage.TYPE_4BYTE_ABGR);
-               Graphics2D g = resizedImage.createGraphics();
-               try {
-                       g.drawImage(image, 0, 0, targetSize.width, targetSize.height, null);
-               } finally {
-                       g.dispose();
-               }
-
-               return resizedImage;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/Item.java b/src/be/nikiroo/utils/ui/Item.java
deleted file mode 100644 (file)
index c8afc7f..0000000
+++ /dev/null
@@ -1,479 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.swing.BorderFactory;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.SwingConstants;
-
-/**
- * A graphical item that can be presented in a list and supports user
- * interaction.
- * <p>
- * Can be selected, hovered...
- * 
- * @author niki
- */
-abstract public class Item extends JPanel {
-       static private final long serialVersionUID = 1L;
-
-       static private Map<Dimension, BufferedImage> empty = new HashMap<Dimension, BufferedImage>();
-       static private Map<Dimension, BufferedImage> error = new HashMap<Dimension, BufferedImage>();
-       static private Map<Color, JComponent> statuses = new HashMap<Color, JComponent>();
-
-       private String id;
-       private boolean selected;
-       private boolean hovered;
-
-       private String mainTemplate;
-       private String secondaryTemplate;
-
-       private boolean hasImage;
-       private JLabel title;
-       private JLabel secondary;
-       private JLabel statusIndicatorOn;
-       private JLabel statusIndicatorOff;
-       private JLabel statusIndicatorUnknown;
-       private Image image;
-       private boolean imageError;
-
-       private String cachedMain;
-       private String cachedOptSecondary;
-       private Integer cachedStatus;
-
-       /**
-        * Create a new {@link Item}
-        * 
-        * @param id
-        *            an ID that represents this {@link Item} (can be NULL)
-        * @param hasImage
-        *            this {@link Item} will contain an image
-        */
-       public Item(String id, boolean hasImage) {
-               this.id = id;
-               this.hasImage = hasImage;
-               init(hasImage);
-       }
-
-       // Configuration :
-
-       protected int getMaxDisplaySize() {
-               return 40;
-       }
-
-       protected int getCoverWidth() {
-               return 100;
-       }
-
-       protected int getCoverHeight() {
-               return 150;
-       }
-
-       protected int getTextWidth() {
-               return getCoverWidth() + 40;
-       }
-
-       protected int getTextHeight() {
-               return 50;
-       }
-
-       protected int getCoverVOffset() {
-               return 20;
-       }
-
-       protected int getCoverHOffset() {
-               return 0;
-       }
-
-       protected int getHGap() {
-               return 10;
-       }
-
-       /** Colour used for the secondary item (author/word count). */
-       protected Color getSecondaryColor() {
-               return new Color(128, 128, 128);
-       }
-
-       /**
-        * Return a display-ready version of the main information to show.
-        * <p>
-        * Note that you can make use of {@link Item#limit(String)}.
-        * 
-        * @return the main info in a ready-to-display version, cannot be NULL
-        */
-       abstract protected String getMainInfoDisplay();
-
-       /**
-        * Return a display-ready version of the secondary information to show.
-        * <p>
-        * Note that you can make use of {@link Item#limit(String)}.
-        * 
-        * @return the main info in a ready-to-display version, cannot be NULL
-        */
-       abstract protected String getSecondaryInfoDisplay();
-
-       /**
-        * The current status for the status indicator.
-        * <p>
-        * Note that NULL and negative values will create "hollow" indicators, while
-        * other values will create "filled" indicators.
-        * 
-        * @return the status which can be NULL, presumably for "Unknown"
-        */
-       abstract protected Integer getStatus();
-
-       /**
-        * Get the background colour to use according to the given state.
-        * <p>
-        * Since it is an overlay, an opaque colour will of course mask everything.
-        * 
-        * @param enabled
-        *            the item is enabled
-        * @param selected
-        *            the item is selected
-        * @param hovered
-        *            the mouse cursor currently hovers over the item
-        * 
-        * @return the correct background colour to use
-        */
-       abstract protected Color getOverlayColor(boolean enabled, boolean selected,
-                       boolean hovered);
-
-       /**
-        * Get the colour to use for the status indicator.
-        * <p>
-        * Return NULL if you don't want a status indicator for this state.
-        * 
-        * @param status
-        *            the current status as returned by {@link Item#getStatus()}
-        * 
-        * @return the base colour to use, or NULL for no status indicator
-        */
-       abstract protected Color getStatusIndicatorColor(Integer status);
-
-       /**
-        * Initialise this {@link Item}.
-        */
-       private void init(boolean hasImage) {
-               if (!hasImage) {
-                       title = new JLabel();
-                       mainTemplate = "${MAIN}";
-                       secondary = new JLabel();
-                       secondaryTemplate = "${SECONDARY}";
-                       secondary.setForeground(getSecondaryColor());
-
-                       JPanel idTitle = null;
-                       if (id != null && !id.isEmpty()) {
-                               JLabel idLabel = new JLabel(id);
-                               idLabel.setPreferredSize(new JLabel(" 999 ").getPreferredSize());
-                               idLabel.setForeground(Color.gray);
-                               idLabel.setHorizontalAlignment(SwingConstants.CENTER);
-
-                               idTitle = new JPanel(new BorderLayout());
-                               idTitle.setOpaque(false);
-                               idTitle.add(idLabel, BorderLayout.WEST);
-                               idTitle.add(title, BorderLayout.CENTER);
-                       }
-
-                       setLayout(new BorderLayout());
-                       if (idTitle != null)
-                               add(idTitle, BorderLayout.CENTER);
-                       add(secondary, BorderLayout.EAST);
-               } else {
-                       image = null;
-                       title = new JLabel();
-                       secondary = new JLabel();
-                       secondaryTemplate = "";
-
-                       String color = String.format("#%X%X%X", getSecondaryColor()
-                                       .getRed(), getSecondaryColor().getGreen(),
-                                       getSecondaryColor().getBlue());
-                       mainTemplate = String
-                                       .format("<html>"
-                                                       + "<body style='width: %d px; height: %d px; text-align: center;'>"
-                                                       + "${MAIN}" + "<br>" + "<span style='color: %s;'>"
-                                                       + "${SECONDARY}" + "</span>" + "</body>"
-                                                       + "</html>", getTextWidth(), getTextHeight(), color);
-
-                       int ww = Math.max(getCoverWidth(), getTextWidth());
-                       int hh = getCoverHeight() + getCoverVOffset() + getHGap()
-                                       + getTextHeight();
-
-                       JPanel placeholder = new JPanel();
-                       placeholder
-                                       .setPreferredSize(new Dimension(ww, hh - getTextHeight()));
-                       placeholder.setOpaque(false);
-
-                       JPanel titlePanel = new JPanel(new BorderLayout());
-                       titlePanel.setOpaque(false);
-                       titlePanel.add(title, BorderLayout.NORTH);
-
-                       titlePanel.setBorder(BorderFactory.createEmptyBorder());
-
-                       setLayout(new BorderLayout());
-                       add(placeholder, BorderLayout.NORTH);
-                       add(titlePanel, BorderLayout.CENTER);
-               }
-
-               // Cached values are NULL, so it will be updated
-               updateData();
-       }
-
-       /**
-        * The book current selection state.
-        * 
-        * @return the selection state
-        */
-       public boolean isSelected() {
-               return selected;
-       }
-
-       /**
-        * The book current selection state,
-        * 
-        * @param selected
-        *            TRUE if it is selected
-        */
-       public void setSelected(boolean selected) {
-               if (this.selected != selected) {
-                       this.selected = selected;
-                       repaint();
-               }
-       }
-
-       /**
-        * The item mouse-hover state.
-        * 
-        * @return TRUE if it is mouse-hovered
-        */
-       public boolean isHovered() {
-               return this.hovered;
-       }
-
-       /**
-        * The item mouse-hover state.
-        * 
-        * @param hovered
-        *            TRUE if it is mouse-hovered
-        */
-       public void setHovered(boolean hovered) {
-               if (this.hovered != hovered) {
-                       this.hovered = hovered;
-                       repaint();
-               }
-       }
-
-       /**
-        * Update the title, paint the item.
-        */
-       @Override
-       public void paint(Graphics g) {
-               Rectangle clip = g.getClipBounds();
-               if (clip == null || clip.getWidth() <= 0 || clip.getHeight() <= 0) {
-                       return;
-               }
-
-               updateData();
-
-               super.paint(g);
-               if (hasImage) {
-                       Image img = image == null ? getBlank(false) : image;
-                       if (isImageError())
-                               img = getBlank(true);
-
-                       int xOff = getCoverHOffset() + (getWidth() - getCoverWidth()) / 2;
-                       g.drawImage(img, xOff, getCoverVOffset(), null);
-
-                       Integer status = getStatus();
-                       boolean filled = status != null && status > 0;
-                       Color indicatorColor = getStatusIndicatorColor(status);
-                       if (indicatorColor != null) {
-                               UIUtils.drawEllipse3D(g, indicatorColor, getCoverWidth() + xOff
-                                               + 10, 10, 20, 20, filled);
-                       }
-               }
-
-               Color bg = getOverlayColor(isEnabled(), isSelected(), isHovered());
-               g.setColor(bg);
-               g.fillRect(clip.x, clip.y, clip.width, clip.height);
-       }
-
-       /**
-        * The image to display on image {@link Item} (NULL for non-image
-        * {@link Item}s).
-        * 
-        * @return the image or NULL for the empty image or for non image
-        *         {@link Item}s
-        */
-       public Image getImage() {
-               return hasImage ? image : null;
-       }
-
-       /**
-        * Change the image to display (does not work for non-image {@link Item}s).
-        * <p>
-        * NULL is allowed, an empty image will then be shown.
-        * 
-        * @param image
-        *            the new {@link Image} or NULL
-        * 
-        */
-       public void setImage(Image image) {
-               this.image = hasImage ? image : null;
-       }
-
-       /**
-        * Use the ERROR image instead of the real one or the empty one.
-        * 
-        * @return TRUE if we force use the error image
-        */
-       public boolean isImageError() {
-               return imageError;
-       }
-
-       /**
-        * Use the ERROR image instead of the real one or the empty one.
-        * 
-        * @param imageError
-        *            TRUE to force use the error image
-        */
-       public void setImageError(boolean imageError) {
-               this.imageError = imageError;
-       }
-
-       /**
-        * Make the given {@link String} display-ready (i.e., shorten it if it is
-        * too long).
-        * 
-        * @param value
-        *            the full value
-        * 
-        * @return the display-ready value
-        */
-       protected String limit(String value) {
-               if (value == null)
-                       value = "";
-
-               if (value.length() > getMaxDisplaySize()) {
-                       value = value.substring(0, getMaxDisplaySize() - 3) + "...";
-               }
-
-               return value;
-       }
-
-       /**
-        * Update the title with the currently registered information.
-        */
-       private void updateData() {
-               String main = getMainInfoDisplay();
-               String optSecondary = getSecondaryInfoDisplay();
-               Integer status = getStatus();
-
-               // Cached values can be NULL the first time
-               if (!main.equals(cachedMain)
-                               || !optSecondary.equals(cachedOptSecondary)
-                               || status != cachedStatus) {
-                       title.setText(mainTemplate //
-                                       .replace("${MAIN}", main) //
-                                       .replace("${SECONDARY}", optSecondary) //
-                       );
-                       secondary.setText(secondaryTemplate//
-                                       .replace("${MAIN}", main) //
-                                       .replace("${SECONDARY}", optSecondary) //
-                                       + " ");
-
-                       Color bg = getOverlayColor(isEnabled(), isSelected(), isHovered());
-                       setBackground(bg);
-
-                       if (!hasImage) {
-                               remove(statusIndicatorUnknown);
-                               remove(statusIndicatorOn);
-                               remove(statusIndicatorOff);
-
-                               Color k = getStatusIndicatorColor(getStatus());
-                               JComponent statusIndicator = statuses.get(k);
-                               if (!statuses.containsKey(k)) {
-                                       statusIndicator = generateStatusIndicator(k);
-                                       statuses.put(k, statusIndicator);
-                               }
-
-                               if (statusIndicator != null)
-                                       add(statusIndicator, BorderLayout.WEST);
-                       }
-
-                       validate();
-               }
-
-               this.cachedMain = main;
-               this.cachedOptSecondary = optSecondary;
-               this.cachedStatus = status;
-       }
-
-       /**
-        * Generate a status indicator for the given colour.
-        * 
-        * @param color
-        *            the colour to use
-        * 
-        * @return a status indicator ready to be used
-        */
-       private JLabel generateStatusIndicator(Color color) {
-               JLabel indicator = new JLabel("   ") {
-                       private static final long serialVersionUID = 1L;
-
-                       @Override
-                       public void paint(Graphics g) {
-                               super.paint(g);
-
-                               if (color != null) {
-                                       Dimension sz = statusIndicatorOn.getSize();
-                                       int s = Math.min(sz.width, sz.height);
-                                       int x = Math.max(0, (sz.width - sz.height) / 2);
-                                       int y = Math.max(0, (sz.height - sz.width) / 2);
-
-                                       UIUtils.drawEllipse3D(g, color, x, y, s, s, true);
-                               }
-                       }
-               };
-
-               indicator.setBackground(color);
-               return indicator;
-       }
-
-       private Image getBlank(boolean error) {
-               Dimension key = new Dimension(getCoverWidth(), getCoverHeight());
-               Map<Dimension, BufferedImage> images = error ? Item.error : Item.empty;
-
-               BufferedImage blank = images.get(key);
-               if (blank == null) {
-                       blank = new BufferedImage(getCoverWidth(), getCoverHeight(),
-                                       BufferedImage.TYPE_4BYTE_ABGR);
-
-                       Graphics2D g = blank.createGraphics();
-                       try {
-                               g.setColor(Color.white);
-                               g.fillRect(0, 0, getCoverWidth(), getCoverHeight());
-
-                               g.setColor(error ? Color.red : Color.black);
-                               g.drawLine(0, 0, getCoverWidth(), getCoverHeight());
-                               g.drawLine(getCoverWidth(), 0, 0, getCoverHeight());
-                       } finally {
-                               g.dispose();
-                       }
-                       images.put(key, blank);
-               }
-
-               return blank;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ListModel.java b/src/be/nikiroo/utils/ui/ListModel.java
deleted file mode 100644 (file)
index 7cc23b8..0000000
+++ /dev/null
@@ -1,570 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.Component;
-import java.awt.Point;
-import java.awt.Window;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import javax.swing.JList;
-import javax.swing.JPopupMenu;
-import javax.swing.ListCellRenderer;
-import javax.swing.SwingWorker;
-
-import be.nikiroo.utils.ui.compat.DefaultListModel6;
-import be.nikiroo.utils.ui.compat.JList6;
-import be.nikiroo.utils.ui.compat.ListCellRenderer6;
-
-/**
- * A {@link javax.swing.ListModel} that can maintain 2 lists; one with the
- * actual data (the elements), and a second one with the items that are
- * currently displayed (the items).
- * <p>
- * It also offers filter options, supports hovered changes and some more utility
- * functions.
- * 
- * @author niki
- *
- * @param <T>
- *            the type of elements and items (the same type)
- */
-public class ListModel<T> extends DefaultListModel6<T> {
-       private static final long serialVersionUID = 1L;
-
-       /** How long to wait before displaying a tooltip, in milliseconds. */
-       private static final int DELAY_TOOLTIP_MS = 1000;
-
-       /**
-        * A filter interface, to check for a condition (note that a Predicate class
-        * already exists in Java 1.8+, and is compatible with this one if you
-        * change the signatures -- but I support java 1.6+).
-        * 
-        * @author niki
-        *
-        * @param <T>
-        *            the type of elements and items (the same type)
-        */
-       public interface Predicate<T> {
-               /**
-                * Check if an item or an element pass a filter.
-                * 
-                * @param item
-                *            the item to test
-                * 
-                * @return TRUE if the test passed, FALSE if not
-                */
-               public boolean test(T item);
-       }
-
-       /**
-        * A simple interface your elements must implement if you want to use
-        * {@link ListModel#generateRenderer(ListModel)}.
-        * 
-        * @author niki
-        */
-       public interface Hoverable {
-               /**
-                * The element is currently selected.
-                * 
-                * @param selected
-                *            TRUE for selected, FALSE for unselected
-                */
-               public void setSelected(boolean selected);
-
-               /**
-                * The element is currently under the mouse cursor.
-                * 
-                * @param hovered
-                *            TRUE if it is, FALSE if not
-                */
-               public void setHovered(boolean hovered);
-       }
-
-       /**
-        * An interface required to support tooltips on this {@link ListModel}.
-        * 
-        * @author niki
-        *
-        * @param <T>
-        *            the type of elements and items (the same type)
-        */
-       public interface TooltipCreator<T> {
-               /**
-                * Generate a tooltip {@link Window} for this element.
-                * <p>
-                * Note that the tooltip can be of two modes: undecorated or standalone.
-                * An undecorated tooltip will be taken care of by this
-                * {@link ListModel}, but a standalone one is supposed to be its own
-                * Dialog or Frame (it won't be automatically closed).
-                * 
-                * @param t
-                *            the element to generate a tooltip for
-                * @param undecorated
-                *            TRUE for undecorated tooltip, FALSE for standalone
-                *            tooltips
-                * 
-                * @return the generated tooltip or NULL for none
-                */
-               public Window generateTooltip(T t, boolean undecorated);
-       }
-
-       private int hoveredIndex;
-       private List<T> items = new ArrayList<T>();
-       private boolean keepSelection = true;
-
-       private DelayWorker tooltipWatcher;
-       private JPopupMenu popup;
-       private TooltipCreator<T> tooltipCreator;
-       private Window tooltip;
-
-       @SuppressWarnings("rawtypes") // JList<?> not compatible Java 1.6
-       private JList list;
-
-       /**
-        * Create a new {@link ListModel}.
-        * 
-        * @param list
-        *            the {@link JList6} we will handle the data of (cannot be NULL)
-        */
-       @SuppressWarnings("rawtypes") // JList<?> not compatible Java 1.6
-       public ListModel(JList6<T> list) {
-               this((JList) list);
-       }
-
-       /**
-        * Create a new {@link ListModel}.
-        * <p>
-        * Note that you must take care of passing a {@link JList} that only handles
-        * elements of the type of this {@link ListModel} -- you can also use
-        * {@link ListModel#ListModel(JList6)} instead.
-        * 
-        * @param list
-        *            the {@link JList} we will handle the data of (cannot be NULL,
-        *            must only contain elements of the type of this
-        *            {@link ListModel})
-        */
-       @SuppressWarnings({ "unchecked", "rawtypes" }) // JList<?> not in Java 1.6
-       public ListModel(final JList list) {
-               this.list = list;
-
-               list.setModel(this);
-
-               // We always have it ready
-               tooltipWatcher = new DelayWorker(DELAY_TOOLTIP_MS);
-               tooltipWatcher.start();
-
-               list.addMouseMotionListener(new MouseAdapter() {
-                       @Override
-                       public void mouseMoved(final MouseEvent me) {
-                               if (ListModel.this.popup != null
-                                               && ListModel.this.popup.isShowing())
-                                       return;
-
-                               Point p = new Point(me.getX(), me.getY());
-                               final int index = list.locationToIndex(p);
-                               if (index != hoveredIndex) {
-                                       int oldIndex = hoveredIndex;
-                                       hoveredIndex = index;
-                                       fireElementChanged(oldIndex);
-                                       fireElementChanged(index);
-
-                                       if (ListModel.this.tooltipCreator != null) {
-                                               showTooltip(null);
-
-                                               tooltipWatcher.delay("tooltip",
-                                                               new SwingWorker<Void, Void>() {
-                                                                       @Override
-                                                                       protected Void doInBackground()
-                                                                                       throws Exception {
-                                                                               return null;
-                                                                       }
-
-                                                                       @Override
-                                                                       protected void done() {
-                                                                               showTooltip(null);
-
-                                                                               if (index < 0
-                                                                                               || index != hoveredIndex) {
-                                                                                       return;
-                                                                               }
-
-                                                                               if (ListModel.this.popup != null
-                                                                                               && ListModel.this.popup
-                                                                                                               .isShowing()) {
-                                                                                       return;
-                                                                               }
-
-                                                                               showTooltip(newTooltip(index, me));
-                                                                       }
-                                                               });
-                                       }
-                               }
-                       }
-               });
-
-               list.addMouseListener(new MouseAdapter() {
-                       @Override
-                       public void mousePressed(MouseEvent e) {
-                               check(e);
-                       }
-
-                       @Override
-                       public void mouseReleased(MouseEvent e) {
-                               check(e);
-                       }
-
-                       @Override
-                       public void mouseExited(MouseEvent e) {
-                               if (ListModel.this.popup != null
-                                               && ListModel.this.popup.isShowing())
-                                       return;
-
-                               if (hoveredIndex > -1) {
-                                       int oldIndex = hoveredIndex;
-                                       hoveredIndex = -1;
-                                       fireElementChanged(oldIndex);
-                               }
-                       }
-
-                       private void check(MouseEvent e) {
-                               if (ListModel.this.popup == null) {
-                                       return;
-                               }
-
-                               if (e.isPopupTrigger()) {
-                                       if (list.getSelectedIndices().length <= 1) {
-                                               list.setSelectedIndex(
-                                                               list.locationToIndex(e.getPoint()));
-                                       }
-
-                                       showTooltip(null);
-                                       ListModel.this.popup.show(list, e.getX(), e.getY());
-                               }
-                       }
-
-               });
-       }
-
-       /**
-        * (Try and) keep the elements that were selected when filtering.
-        * <p>
-        * This will use toString on the elements to identify them, and can be a bit
-        * resource intensive.
-        * 
-        * @return TRUE if we do
-        */
-       public boolean isKeepSelection() {
-               return keepSelection;
-       }
-
-       /**
-        * (Try and) keep the elements that were selected when filtering.
-        * <p>
-        * This will use toString on the elements to identify them, and can be a bit
-        * resource intensive.
-        * 
-        * @param keepSelection
-        *            TRUE to try and keep them selected
-        */
-       public void setKeepSelection(boolean keepSelection) {
-               this.keepSelection = keepSelection;
-       }
-
-       /**
-        * The popup to use and keep track of (can be NULL).
-        * 
-        * @return the current popup
-        */
-       public JPopupMenu getPopup() {
-               return popup;
-       }
-
-       /**
-        * The popup to use and keep track of (can be NULL).
-        * 
-        * @param popup
-        *            the new popup
-        */
-       public void setPopup(JPopupMenu popup) {
-               this.popup = popup;
-       }
-
-       /**
-        * You can use a {@link TooltipCreator} if you want the list to display
-        * tooltips on mouse hover (can be NULL).
-        * 
-        * @return the current {@link TooltipCreator}
-        */
-       public TooltipCreator<T> getTooltipCreator() {
-               return tooltipCreator;
-       }
-
-       /**
-        * You can use a {@link TooltipCreator} if you want the list to display
-        * tooltips on mouse hover (can be NULL).
-        * 
-        * @param tooltipCreator
-        *            the new {@link TooltipCreator}
-        */
-       public void setTooltipCreator(TooltipCreator<T> tooltipCreator) {
-               this.tooltipCreator = tooltipCreator;
-       }
-
-       /**
-        * Check if this element is currently under the mouse.
-        * 
-        * @param element
-        *            the element to check
-        * 
-        * @return TRUE if it is
-        */
-       public boolean isHovered(T element) {
-               return indexOf(element) == hoveredIndex;
-       }
-
-       /**
-        * Check if this element is currently under the mouse.
-        * 
-        * @param index
-        *            the index of the element to check
-        * 
-        * @return TRUE if it is
-        */
-       public boolean isHovered(int index) {
-               return index == hoveredIndex;
-       }
-
-       /**
-        * Add an item to the model.
-        * 
-        * @param item
-        *            the new item to add
-        */
-       public void addItem(T item) {
-               items.add(item);
-       }
-
-       /**
-        * Add items to the model.
-        * 
-        * @param items
-        *            the new items to add
-        */
-       public void addAllItems(Collection<T> items) {
-               this.items.addAll(items);
-       }
-
-       /**
-        * Removes the first occurrence of the specified element from this list, if
-        * it is present (optional operation).
-        * 
-        * @param item
-        *            the item to remove if possible (can be NULL)
-        * 
-        * @return TRUE if one element was removed, FALSE if not found
-        */
-       public boolean removeItem(T item) {
-               return items.remove(item);
-       }
-
-       /**
-        * Remove the items that pass the given filter (or all items if the filter
-        * is NULL).
-        * 
-        * @param filter
-        *            the filter (if the filter returns TRUE, the item will be
-        *            removed)
-        * 
-        * @return TRUE if at least one item was removed
-        */
-       public boolean removeItemIf(Predicate<T> filter) {
-               boolean changed = false;
-               if (filter == null) {
-                       changed = !items.isEmpty();
-                       clearItems();
-               } else {
-                       for (int i = 0; i < items.size(); i++) {
-                               if (filter.test(items.get(i))) {
-                                       items.remove(i--);
-                                       changed = true;
-                               }
-                       }
-               }
-
-               return changed;
-       }
-
-       /**
-        * Removes all the items from this model.
-        */
-       public void clearItems() {
-               items.clear();
-       }
-
-       /**
-        * Filter the current elements.
-        * <p>
-        * This method will clear all the elements then look into all the items:
-        * those that pass the given filter will be copied as elements.
-        * 
-        * @param filter
-        *            the filter to select which elements to keep; an item that pass
-        *            the filter will be copied as an element (can be NULL, in that
-        *            case all items will be copied as elements)
-        */
-       @SuppressWarnings("unchecked") // JList<?> not compatible Java 1.6
-       public void filter(Predicate<T> filter) {
-               ListSnapshot snapshot = null;
-
-               if (keepSelection)
-                       snapshot = new ListSnapshot(list);
-
-               clear();
-               for (T item : items) {
-                       if (filter == null || filter.test(item)) {
-                               addElement(item);
-                       }
-               }
-
-               if (keepSelection)
-                       snapshot.apply();
-
-               list.repaint();
-       }
-
-       /**
-        * Return the currently selected elements.
-        * 
-        * @return the selected elements
-        */
-       public List<T> getSelectedElements() {
-               List<T> selected = new ArrayList<T>();
-               for (int index : list.getSelectedIndices()) {
-                       selected.add(get(index));
-               }
-
-               return selected;
-       }
-
-       /**
-        * Return the selected element if <b>one</b> and <b>only one</b> element is
-        * selected. I.E., if zero, two or more elements are selected, NULL will be
-        * returned.
-        * 
-        * @return the element if it is the only selected element, NULL otherwise
-        */
-       public T getUniqueSelectedElement() {
-               List<T> selected = getSelectedElements();
-               if (selected.size() == 1) {
-                       return selected.get(0);
-               }
-
-               return null;
-       }
-
-       /**
-        * Notify that this element has been changed.
-        * 
-        * @param index
-        *            the index of the element
-        */
-       public void fireElementChanged(int index) {
-               if (index >= 0) {
-                       fireContentsChanged(this, index, index);
-               }
-       }
-
-       /**
-        * Notify that this element has been changed.
-        * 
-        * @param element
-        *            the element
-        */
-       public void fireElementChanged(T element) {
-               int index = indexOf(element);
-               if (index >= 0) {
-                       fireContentsChanged(this, index, index);
-               }
-       }
-
-       @SuppressWarnings("unchecked") // JList<?> not compatible Java 1.6
-       @Override
-       public T get(int index) {
-               return (T) super.get(index);
-       }
-
-       private Window newTooltip(final int index, final MouseEvent me) {
-               final T value = ListModel.this.get(index);
-               final Window newTooltip = tooltipCreator.generateTooltip(value, true);
-               if (newTooltip != null) {
-                       newTooltip.addMouseListener(new MouseAdapter() {
-                               @Override
-                               public void mouseClicked(MouseEvent e) {
-                                       Window promotedTooltip = tooltipCreator
-                                                       .generateTooltip(value, false);
-                                       if (promotedTooltip != null) {
-                                               promotedTooltip.setLocation(me.getXOnScreen(),
-                                                               me.getYOnScreen());
-                                               promotedTooltip.setVisible(true);
-                                       }
-
-                                       newTooltip.setVisible(false);
-                               }
-                       });
-
-                       newTooltip.setLocation(me.getXOnScreen(), me.getYOnScreen());
-                       showTooltip(newTooltip);
-               }
-
-               return newTooltip;
-       }
-
-       private void showTooltip(Window tooltip) {
-               synchronized (tooltipWatcher) {
-                       if (this.tooltip != null) {
-                               this.tooltip.setVisible(false);
-                               this.tooltip.dispose();
-                       }
-
-                       this.tooltip = tooltip;
-
-                       if (tooltip != null) {
-                               tooltip.setVisible(true);
-                       }
-               }
-       }
-
-       /**
-        * Generate a {@link ListCellRenderer} that supports {@link Hoverable}
-        * elements.
-        * 
-        * @param <T>
-        *            the type of elements and items (the same type), which should
-        *            implement {@link Hoverable} (it will not cause issues if not,
-        *            but then, it will be a default renderer)
-        * @param model
-        *            the model to use
-        * 
-        * @return a suitable, {@link Hoverable} compatible renderer
-        */
-       static public <T extends Component> ListCellRenderer6<T> generateRenderer(
-                       final ListModel<T> model) {
-               return new ListCellRenderer6<T>() {
-                       @Override
-                       public Component getListCellRendererComponent(JList6<T> list,
-                                       T item, int index, boolean isSelected,
-                                       boolean cellHasFocus) {
-                               if (item instanceof Hoverable) {
-                                       Hoverable hoverable = (Hoverable) item;
-                                       hoverable.setSelected(isSelected);
-                                       hoverable.setHovered(model.isHovered(index));
-                               }
-
-                               return item;
-                       }
-               };
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ListSnapshot.java b/src/be/nikiroo/utils/ui/ListSnapshot.java
deleted file mode 100644 (file)
index d2e89c8..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.JList;
-
-public class ListSnapshot {
-       private JList list;
-       private List<Object> elements = new ArrayList<Object>();
-
-       public ListSnapshot(JList list) {
-               this.list = list;
-
-               for (int index : list.getSelectedIndices()) {
-                       elements.add(list.getModel().getElementAt(index));
-               }
-       }
-
-       public void apply() {
-               applyTo(list);
-       }
-
-       public void applyTo(JList list) {
-               List<Integer> indices = new ArrayList<Integer>();
-               for (int i = 0; i < list.getModel().getSize(); i++) {
-                       Object newObject = list.getModel().getElementAt(i);
-                       for (Object oldObject : elements) {
-                               if (isSameElement(oldObject, newObject)) {
-                                       indices.add(i);
-                                       break;
-                               }
-                       }
-               }
-
-               int a[] = new int[indices.size()];
-               for (int i = 0; i < indices.size(); i++) {
-                       a[i] = indices.get(i);
-               }
-               list.setSelectedIndices(a);
-       }
-
-       // You can override this
-       protected boolean isSameElement(Object oldElement, Object newElement) {
-               if (oldElement == null || newElement == null)
-                       return oldElement == null && newElement == null;
-
-               return oldElement.toString().equals(newElement.toString());
-       }
-
-       @Override
-       public String toString() {
-               StringBuilder builder = new StringBuilder();
-               builder.append("List Snapshot of: ").append(list).append("\n");
-               builder.append("Selected elements:\n");
-               for (Object element : elements) {
-                       builder.append("\t").append(element).append("\n");
-               }
-
-               return builder.toString();
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ListenerItem.java b/src/be/nikiroo/utils/ui/ListenerItem.java
deleted file mode 100644 (file)
index 3fa41c8..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.event.ActionListener;
-
-/**
- * The default {@link ActionListener} add/remove/fire methods.
- * 
- * @author niki
- */
-public interface ListenerItem {
-       /**
-        * Check that this {@link ListenerItem} currently has
-        * {@link ActionListener}s that listen on it.
-        * 
-        * @return TRUE if it has
-        */
-       public boolean hasListeners();
-
-       /**
-        * Check how many events are currently waiting for an
-        * {@link ActionListener}.
-        * 
-        * @return the number of waiting events (can be 0)
-        */
-       public int getWaitingEventCount();
-
-       /**
-        * Adds the specified action listener to receive action events from this
-        * {@link ListenerItem}.
-        *
-        * @param listener
-        *            the action listener to be added
-        */
-       public void addActionListener(ActionListener listener);
-
-       /**
-        * Removes the specified action listener so that it no longer receives
-        * action events from this {@link ListenerItem}.
-        *
-        * @param listener
-        *            the action listener to be removed
-        */
-       public void removeActionListener(ActionListener listener);
-
-       /**
-        * Notify the listeners of an action.
-        * 
-        * @param listenerCommand
-        *            A string that may specify a command (possibly one of several)
-        *            associated with the event
-        */
-       public void fireActionPerformed(String listenerCommand);
-}
diff --git a/src/be/nikiroo/utils/ui/ListenerPanel.java b/src/be/nikiroo/utils/ui/ListenerPanel.java
deleted file mode 100644 (file)
index ada0796..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.LinkedList;
-import java.util.Queue;
-
-import javax.swing.JPanel;
-
-/**
- * A {@link JPanel} with the default {@link ActionListener} add/remove/fire
- * methods.
- * <p>
- * Note that it will queue all events until at least one listener comes (or
- * comes back!); this first (or at least currently unique) listener will drain
- * the queue.
- * 
- * @author niki
- */
-public class ListenerPanel extends JPanel implements ListenerItem {
-       private static final long serialVersionUID = 1L;
-
-       /** Waiting queue until at least one listener is here to get the events. */
-       private final Queue<ActionEvent> waitingQueue;
-
-       /**
-        * Create a new {@link ListenerPanel}.
-        */
-       public ListenerPanel() {
-               waitingQueue = new LinkedList<ActionEvent>();
-       }
-
-       @Override
-       public synchronized boolean hasListeners() {
-               return listenerList.getListenerList().length > 1;
-       }
-
-       @Override
-       public synchronized int getWaitingEventCount() {
-               return waitingQueue.size();
-       }
-
-       @Override
-       public synchronized void addActionListener(ActionListener listener) {
-               if (!hasListeners()) {
-                       while (!waitingQueue.isEmpty()) {
-                               listener.actionPerformed(waitingQueue.remove());
-                       }
-               }
-
-               listenerList.add(ActionListener.class, listener);
-       }
-
-       @Override
-       public synchronized void removeActionListener(ActionListener listener) {
-               listenerList.remove(ActionListener.class, listener);
-       }
-
-       @Override
-       public synchronized void fireActionPerformed(String listenerCommand) {
-               ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
-                               listenerCommand);
-
-               ActionListener[] listeners = getListeners(ActionListener.class);
-               if (listeners.length > 0) {
-                       for (ActionListener action : listeners) {
-                               action.actionPerformed(e);
-                       }
-               } else {
-                       waitingQueue.add(e);
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/NavBar.java b/src/be/nikiroo/utils/ui/NavBar.java
deleted file mode 100644 (file)
index 607b2cf..0000000
+++ /dev/null
@@ -1,414 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.Dimension;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.Icon;
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JTextField;
-
-/**
- * A Swing-based navigation bar, that displays first/previous/next/last page
- * buttons.
- * 
- * @author niki
- */
-public class NavBar extends ListenerPanel {
-       private static final long serialVersionUID = 1L;
-
-       /** The event that is fired on page change. */
-       public static final String PAGE_CHANGED = "page changed";
-
-       private JTextField page;
-       private JLabel pageLabel;
-       private JLabel maxPage;
-       private JLabel label;
-
-       private int index = 0;
-       private int min = 0;
-       private int max = 0;
-       private String extraLabel = null;
-
-       private boolean vertical;
-
-       private JButton first;
-       private JButton previous;
-       private JButton next;
-       private JButton last;
-
-       /**
-        * Create a new navigation bar.
-        * <p>
-        * The minimum must be lower or equal to the maximum, but a max of "-1"
-        * means "infinite".
-        * <p>
-        * A {@link NavBar#PAGE_CHANGED} event will be fired on startup.
-        * 
-        * @param min
-        *            the minimum page number (cannot be negative)
-        * @param max
-        *            the maximum page number (cannot be lower than min, except if
-        *            -1 (infinite))
-        * 
-        * @throws IndexOutOfBoundsException
-        *             if min &gt; max and max is not "-1"
-        */
-       public NavBar(int min, int max) {
-               if (min > max && max != -1) {
-                       throw new IndexOutOfBoundsException(
-                                       String.format("min (%d) > max (%d)", min, max));
-               }
-
-               // Page navigation
-               first = new JButton();
-               first.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               first();
-                       }
-               });
-
-               previous = new JButton();
-               previous.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               previous();
-                       }
-               });
-
-               final int defaultHeight = new JButton("dummy")
-                               .getPreferredSize().height;
-               final int width4 = new JButton("1234").getPreferredSize().width;
-               page = new JTextField(Integer.toString(min));
-               page.setPreferredSize(new Dimension(width4, defaultHeight));
-               page.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               try {
-                                       int pageNb = Integer.parseInt(page.getText());
-                                       if (pageNb < NavBar.this.min || pageNb > NavBar.this.max) {
-                                               throw new NumberFormatException("invalid");
-                                       }
-
-                                       if (setIndex(pageNb))
-                                               fireActionPerformed(PAGE_CHANGED);
-                               } catch (NumberFormatException nfe) {
-                                       page.setText(Integer.toString(index));
-                               }
-                       }
-               });
-
-               pageLabel = new JLabel(Integer.toString(min));
-               pageLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
-
-               maxPage = new JLabel("of " + max);
-               maxPage.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
-
-               next = new JButton();
-               next.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               next();
-                       }
-               });
-
-               last = new JButton();
-               last.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               last();
-                       }
-               });
-
-               label = new JLabel("");
-
-               // Set the << < > >> "icons"
-               setIcons(null, null, null, null);
-
-               this.min = min;
-               this.max = max;
-               this.index = min;
-
-               updateEnabled();
-               updateLabel();
-               setOrientation(vertical);
-
-               fireActionPerformed(PAGE_CHANGED);
-       }
-
-       /**
-        * The current index, must be between {@link NavBar#min} and
-        * {@link NavBar#max}, both inclusive.
-        * 
-        * @return the index
-        */
-       public int getIndex() {
-               return index;
-       }
-
-       /**
-        * The current index, should be between {@link NavBar#min} and
-        * {@link NavBar#max}, both inclusive.
-        * 
-        * @param index
-        *            the new index
-        * 
-        * @return TRUE if the index changed, FALSE if not (either it was already at
-        *         that value, or it is outside of the bounds set by
-        *         {@link NavBar#min} and {@link NavBar#max})
-        */
-       public synchronized boolean setIndex(int index) {
-               if (index != this.index) {
-                       if (index < min || (index > max && max != -1)) {
-                               return false;
-                       }
-
-                       this.index = index;
-                       updateLabel();
-                       updateEnabled();
-
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
-        * The minimun page number. Cannot be negative.
-        * 
-        * @return the min
-        */
-       public int getMin() {
-               return min;
-       }
-
-       /**
-        * The minimum page number. Cannot be negative.
-        * <p>
-        * May update the index if needed (if the index is &lt; the new min).
-        * <p>
-        * Will also (always) update the label and enable/disable the required
-        * buttons.
-        * 
-        * @param min
-        *            the new min
-        */
-       public synchronized void setMin(int min) {
-               this.min = min;
-               if (index < min) {
-                       index = min;
-               }
-
-               updateEnabled();
-               updateLabel();
-       }
-
-       /**
-        * The maximum page number. Cannot be lower than min, except if -1
-        * (infinite).
-        * 
-        * @return the max
-        */
-       public int getMax() {
-               return max;
-       }
-
-       /**
-        * The maximum page number. Cannot be lower than min, except if -1
-        * (infinite).
-        * <p>
-        * May update the index if needed (if the index is &gt; the new max).
-        * <p>
-        * Will also (always) update the label and enable/disable the required
-        * buttons.
-        * 
-        * @param max
-        *            the new max
-        */
-       public synchronized void setMax(int max) {
-               this.max = max;
-               if (index > max && max != -1) {
-                       index = max;
-               }
-
-               maxPage.setText("of " + max);
-               updateEnabled();
-               updateLabel();
-       }
-
-       /**
-        * The current extra label to display.
-        * 
-        * @return the current label
-        */
-       public String getExtraLabel() {
-               return extraLabel;
-       }
-
-       /**
-        * The current extra label to display.
-        * 
-        * @param currentLabel
-        *            the new current label
-        */
-       public void setExtraLabel(String currentLabel) {
-               this.extraLabel = currentLabel;
-               updateLabel();
-       }
-
-       /**
-        * Change the page to the next one.
-        * 
-        * @return TRUE if it changed
-        */
-       public synchronized boolean next() {
-               if (setIndex(index + 1)) {
-                       fireActionPerformed(PAGE_CHANGED);
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
-        * Change the page to the previous one.
-        * 
-        * @return TRUE if it changed
-        */
-       public synchronized boolean previous() {
-               if (setIndex(index - 1)) {
-                       fireActionPerformed(PAGE_CHANGED);
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
-        * Change the page to the first one.
-        * 
-        * @return TRUE if it changed
-        */
-       public synchronized boolean first() {
-               if (setIndex(min)) {
-                       fireActionPerformed(PAGE_CHANGED);
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
-        * Change the page to the last one.
-        * 
-        * @return TRUE if it changed
-        */
-       public synchronized boolean last() {
-               if (setIndex(max)) {
-                       fireActionPerformed(PAGE_CHANGED);
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
-        * Set icons for the buttons instead of square brackets.
-        * <p>
-        * Any NULL value will make the button use square brackets again.
-        * 
-        * @param first
-        *            the icon of the button "go to first page"
-        * @param previous
-        *            the icon of the button "go to previous page"
-        * @param next
-        *            the icon of the button "go to next page"
-        * @param last
-        *            the icon of the button "go to last page"
-        */
-       public void setIcons(Icon first, Icon previous, Icon next, Icon last) {
-               this.first.setIcon(first);
-               this.first.setText(first == null ? "<<" : "");
-               this.previous.setIcon(previous);
-               this.previous.setText(previous == null ? "<" : "");
-               this.next.setIcon(next);
-               this.next.setText(next == null ? ">" : "");
-               this.last.setIcon(last);
-               this.last.setText(last == null ? ">>" : "");
-       }
-
-       /**
-        * The general orientation of the component.
-        * 
-        * @return TRUE for vertical orientation, FALSE for horisontal orientation
-        */
-       public boolean getOrientation() {
-               return vertical;
-       }
-
-       /**
-        * Update the general orientation of the component.
-        * 
-        * @param vertical
-        *            TRUE for vertical orientation, FALSE for horisontal
-        *            orientation
-        * 
-        * @return TRUE if it changed something
-        */
-       public boolean setOrientation(boolean vertical) {
-               if (getWidth() == 0 || this.vertical != vertical) {
-                       this.vertical = vertical;
-
-                       BoxLayout layout = new BoxLayout(this,
-                                       vertical ? BoxLayout.Y_AXIS : BoxLayout.X_AXIS);
-                       this.removeAll();
-                       setLayout(layout);
-
-                       this.add(first);
-                       this.add(previous);
-                       if (vertical) {
-                               this.add(pageLabel);
-                       } else {
-                               this.add(page);
-                       }
-                       this.add(maxPage);
-                       this.add(next);
-                       this.add(last);
-
-                       if (!vertical) {
-                               this.add(label);
-                       }
-
-                       this.revalidate();
-                       this.repaint();
-
-                       return true;
-               }
-
-               return false;
-       }
-
-       /**
-        * Update the label displayed in the UI.
-        */
-       private void updateLabel() {
-               label.setText(getExtraLabel());
-               pageLabel.setText(Integer.toString(index));
-               page.setText(Integer.toString(index));
-       }
-
-       /**
-        * Update the navigation buttons "enabled" state according to the current
-        * index value.
-        */
-       private synchronized void updateEnabled() {
-               first.setEnabled(index > min);
-               previous.setEnabled(index > min);
-               next.setEnabled(index < max || max == -1);
-               last.setEnabled(index < max || max == -1);
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/ProgressBar.java b/src/be/nikiroo/utils/ui/ProgressBar.java
deleted file mode 100644 (file)
index 11e1e6c..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.GridLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
-import javax.swing.SwingUtilities;
-
-import be.nikiroo.utils.Progress;
-
-/**
- * A graphical control to show the progress of a {@link Progress}.
- * <p>
- * This control is <b>NOT</b> thread-safe.
- * 
- * @author niki
- */
-public class ProgressBar extends JPanel {
-       private static final long serialVersionUID = 1L;
-
-       private Map<Progress, JProgressBar> bars;
-       private List<ActionListener> actionListeners;
-       private List<ActionListener> updateListeners;
-       private Progress pg;
-       private Object lock = new Object();
-
-       public ProgressBar() {
-               bars = new HashMap<Progress, JProgressBar>();
-               actionListeners = new ArrayList<ActionListener>();
-               updateListeners = new ArrayList<ActionListener>();
-       }
-       
-       public ProgressBar(Progress pg) {
-               this();
-               setProgress(pg);
-       }
-
-       public void setProgress(final Progress pg) {
-               this.pg = pg;
-
-               SwingUtilities.invokeLater(new Runnable() {
-                       @Override
-                       public void run() {
-                               if (pg != null) {
-                                       final JProgressBar bar = new JProgressBar();
-                                       bar.setStringPainted(true);
-
-                                       bars.clear();
-                                       bars.put(pg, bar);
-
-                                       bar.setMinimum(pg.getMin());
-                                       bar.setMaximum(pg.getMax());
-                                       bar.setValue(pg.getProgress());
-                                       bar.setString(pg.getName());
-
-                                       pg.addProgressListener(new Progress.ProgressListener() {
-                                               @Override
-                                               public void progress(Progress progress, String name) {
-                                                       final Progress.ProgressListener l = this;
-                                                       SwingUtilities.invokeLater(new Runnable() {
-                                                               @Override
-                                                               public void run() {
-                                                                       Map<Progress, JProgressBar> newBars = new HashMap<Progress, JProgressBar>();
-                                                                       newBars.put(pg, bar);
-
-                                                                       bar.setMinimum(pg.getMin());
-                                                                       bar.setMaximum(pg.getMax());
-                                                                       bar.setValue(pg.getProgress());
-                                                                       bar.setString(pg.getName());
-
-                                                                       synchronized (lock) {
-                                                                       for (Progress pgChild : getChildrenAsOrderedList(pg)) {
-                                                                               JProgressBar barChild = bars
-                                                                                               .get(pgChild);
-                                                                               if (barChild == null) {
-                                                                                       barChild = new JProgressBar();
-                                                                                       barChild.setStringPainted(true);
-                                                                               }
-
-                                                                               newBars.put(pgChild, barChild);
-
-                                                                               barChild.setMinimum(pgChild.getMin());
-                                                                               barChild.setMaximum(pgChild.getMax());
-                                                                               barChild.setValue(pgChild.getProgress());
-                                                                               barChild.setString(pgChild.getName());
-                                                                       }
-                                                                       
-                                                                       if (ProgressBar.this.pg == null) {
-                                                                               bars.clear();
-                                                                       } else {
-                                                                               bars = newBars;
-                                                                       }
-                                                                       }
-                                                                       
-                                                                       if (ProgressBar.this.pg != null) {
-                                                                               if (pg.isDone()) {
-                                                                                       pg.removeProgressListener(l);
-                                                                                       for (ActionListener listener : actionListeners) {
-                                                                                               listener.actionPerformed(new ActionEvent(
-                                                                                                               ProgressBar.this, 0,
-                                                                                                               "done"));
-                                                                                       }
-                                                                               }
-
-                                                                               update();
-                                                                       }
-                                                               }
-                                                       });
-                                               }
-                                       });
-                               }
-
-                               update();
-                       }
-               });
-       }
-
-       public void addActionListener(ActionListener l) {
-               actionListeners.add(l);
-       }
-
-       public void clearActionListeners() {
-               actionListeners.clear();
-       }
-
-       public void addUpdateListener(ActionListener l) {
-               updateListeners.add(l);
-       }
-
-       public void clearUpdateListeners() {
-               updateListeners.clear();
-       }
-
-       public int getProgress() {
-               if (pg == null) {
-                       return 0;
-               }
-
-               return pg.getProgress();
-       }
-
-       // only named ones
-       private List<Progress> getChildrenAsOrderedList(Progress pg) {
-               List<Progress> children = new ArrayList<Progress>();
-               
-               synchronized (lock) {
-                       for (Progress child : pg.getChildren()) {
-                       if (child.getName() != null && !child.getName().isEmpty()) {
-                               children.add(child);
-                       }
-                       children.addAll(getChildrenAsOrderedList(child));
-               }
-               }
-               
-               return children;
-       }
-
-       private void update() {
-               synchronized (lock) {
-                       invalidate();
-                       removeAll();
-
-                       if (pg != null) {
-                               setLayout(new GridLayout(bars.size(), 1));
-                               add(bars.get(pg), 0);
-                               for (Progress child : getChildrenAsOrderedList(pg)) {
-                                       JProgressBar jbar = bars.get(child);
-                                       if (jbar != null) {
-                                               add(jbar);
-                                       }
-                               }
-                       }
-
-                       validate();
-                       repaint();
-               }
-
-               for (ActionListener listener : updateListeners) {
-                       listener.actionPerformed(new ActionEvent(this, 0, "update"));
-               }
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/SearchBar.java b/src/be/nikiroo/utils/ui/SearchBar.java
deleted file mode 100644 (file)
index ffbc78b..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
-import javax.swing.JTextField;
-import javax.swing.SwingUtilities;
-
-import be.nikiroo.utils.IOUtils;
-
-/**
- * A generic search/filter bar.
- * 
- * @author niki
- */
-public class SearchBar extends ListenerPanel {
-       static private final long serialVersionUID = 1L;
-       static private ImageIcon searchIcon;
-       static private ImageIcon clearIcon;
-
-       private JButton search;
-       private JTextField text;
-       private JButton clear;
-
-       private boolean realTime;
-
-       /**
-        * Create a new {@link SearchBar}.
-        */
-       public SearchBar() {
-               setLayout(new BorderLayout());
-
-               // TODO: make an option to change the default setting here:
-               // (can already be manually toggled by the user)
-               realTime = true;
-
-               if (searchIcon == null)
-                       searchIcon = getIcon("search-16x16.png");
-               if (clearIcon == null)
-                       clearIcon = getIcon("clear-16x16.png");
-
-               search = new JButton(searchIcon);
-               if (searchIcon == null) {
-                       search.setText("[s]");
-               }
-               UIUtils.setButtonPressed(search, realTime);
-               search.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               realTime = !realTime;
-                               UIUtils.setButtonPressed(search, realTime);
-                               text.requestFocus();
-
-                               if (realTime) {
-                                       fireActionPerformed(getText());
-                               }
-                       }
-               });
-
-               text = new JTextField();
-               text.addKeyListener(new KeyAdapter() {
-                       @Override
-                       public void keyTyped(final KeyEvent e) {
-                               super.keyTyped(e);
-                               SwingUtilities.invokeLater(new Runnable() {
-                                       @Override
-                                       public void run() {
-                                               boolean empty = (text.getText().isEmpty());
-                                               clear.setVisible(!empty);
-
-                                               if (realTime) {
-                                                       fireActionPerformed(getText());
-                                               }
-                                       }
-                               });
-                       }
-               });
-               text.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               if (!realTime) {
-                                       fireActionPerformed(getText());
-                               }
-                       }
-               });
-
-               clear = new JButton(clearIcon);
-               if (clearIcon == null) {
-                       clear.setText("(c)");
-               }
-               clear.setBackground(text.getBackground());
-               clear.setVisible(false);
-               clear.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               text.setText("");
-                               clear.setVisible(false);
-                               text.requestFocus();
-
-                               fireActionPerformed(getText());
-                       }
-               });
-
-               add(search, BorderLayout.WEST);
-               add(text, BorderLayout.CENTER);
-               add(clear, BorderLayout.EAST);
-       }
-
-       /**
-        * Return the current text displayed by this {@link SearchBar}, or an empty
-        * {@link String} if none.
-        * 
-        * @return the text, cannot be NULL
-        */
-       public String getText() {
-               // Should usually not be NULL, but not impossible
-               String text = this.text.getText();
-               return text == null ? "" : text;
-       }
-
-       @Override
-       public void setEnabled(boolean enabled) {
-               search.setEnabled(enabled);
-               clear.setEnabled(enabled);
-               text.setEnabled(enabled);
-               super.setEnabled(enabled);
-       }
-
-       private ImageIcon getIcon(String name) {
-               InputStream in = IOUtils.openResource(SearchBar.class, name);
-               if (in != null) {
-                       try {
-                               return new ImageIcon(IOUtils.toByteArray(in));
-                       } catch (IOException e) {
-                               e.printStackTrace();
-                       }
-               }
-
-               return null;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/TreeCellSpanner.java b/src/be/nikiroo/utils/ui/TreeCellSpanner.java
deleted file mode 100644 (file)
index 703bfa1..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- *    This program is free software: you can redistribute it and/or modify
- *    it under the terms of the GNU Lesser General Public License as published
- *    by the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
- *
- *    You should have received a copy of the GNU Lesser General Public License
- *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-// Can be found at: https://code.google.com/archive/p/aephyr/source/default/source
-// package aephyr.swing;
-package be.nikiroo.utils.ui;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import javax.swing.*;
-import javax.swing.tree.*;
-
-import java.util.*;
-
-public class TreeCellSpanner extends Container implements TreeCellRenderer, ComponentListener {
-       
-       public TreeCellSpanner(JTree tree, TreeCellRenderer renderer) {
-               if (tree == null || renderer == null)
-                       throw new NullPointerException();
-               this.tree = tree;
-               this.renderer = renderer;
-               treeParent = tree.getParent();
-               if (treeParent != null && treeParent instanceof JViewport) {
-                       treeParent.addComponentListener(this);
-               } else {
-                       treeParent = null;
-                       tree.addComponentListener(this);
-               }
-       }
-       
-       protected final JTree tree;
-       
-       private TreeCellRenderer renderer;
-       
-       private Component rendererComponent;
-       
-       private Container treeParent;
-       
-       private Map<TreePath,Integer> offsets = new HashMap<TreePath,Integer>();
-       
-       private TreePath path;
-       
-       public TreeCellRenderer getRenderer() {
-               return renderer;
-       }
-       
-       @Override
-       public Component getTreeCellRendererComponent(JTree tree, Object value,
-                       boolean selected, boolean expanded, boolean leaf, int row,
-                       boolean hasFocus) {
-               path = tree.getPathForRow(row);
-               if (path != null && path.getLastPathComponent() != value)
-                       path = null;
-               rendererComponent = renderer.getTreeCellRendererComponent(
-                               tree, value, selected, expanded, leaf, row, hasFocus);
-               if (getComponentCount() < 1 || getComponent(0) != rendererComponent) {
-                       removeAll();
-                       add(rendererComponent);
-               }
-               return this;
-       }
-       
-       @Override
-       public void doLayout() {
-               int x = getX();
-               if (x < 0)
-                       return;
-               if (path != null) {
-                       Integer offset = offsets.get(path);
-                       if (offset == null || offset.intValue() != x) {
-                               offsets.put(path, x);
-                               fireTreePathChanged(path);
-                       }
-               }
-               rendererComponent.setBounds(getX(), getY(), getWidth(), getHeight());
-       }
-       
-       @Override
-       public void paint(Graphics g) {
-               if (rendererComponent != null)
-                       rendererComponent.paint(g);
-       }
-       
-       @Override
-       public Dimension getPreferredSize() {
-               Dimension s = rendererComponent.getPreferredSize();
-               // check if path count is greater than 1 to exclude the root
-               if (path != null && path.getPathCount() > 1) {
-                       Integer offset = offsets.get(path);
-                       if (offset != null) {
-                               int width;
-                               if (tree.getParent() == treeParent) {
-                                       width = treeParent.getWidth();
-                               } else {
-                                       if (treeParent != null) {
-                                               treeParent.removeComponentListener(this);
-                                               tree.addComponentListener(this);
-                                               treeParent = null;
-                                       }
-                                       if (tree.getParent() instanceof JViewport) {
-                                               treeParent = tree.getParent();
-                                               tree.removeComponentListener(this);
-                                               treeParent.addComponentListener(this);
-                                               width = treeParent.getWidth();
-                                       } else {
-                                               width = tree.getWidth();
-                                       }
-                               }
-                               s.width = width - offset;
-                       }
-               }
-               return s;
-       }
-
-       
-       protected void fireTreePathChanged(TreePath path) {
-               if (path.getPathCount() > 1) {
-                       // this cannot be used for the root node or else
-                       // the entire tree will keep being revalidated ad infinitum
-                       TreeModel model = tree.getModel();
-                       Object node = path.getLastPathComponent();
-                       if (node instanceof TreeNode && (model instanceof DefaultTreeModel
-                                       || (model instanceof TreeModelTransformer<?> &&
-                                       (model=((TreeModelTransformer<?>)model).getModel()) instanceof DefaultTreeModel))) {
-                               ((DefaultTreeModel)model).nodeChanged((TreeNode)node);
-                       } else {
-                               model.valueForPathChanged(path, node.toString());
-                       }
-               } else {
-                       // root!
-                       
-               }
-       }
-
-       
-       private int lastWidth;
-
-       @Override
-       public void componentHidden(ComponentEvent e) {}
-
-       @Override
-       public void componentMoved(ComponentEvent e) {}
-
-       @Override
-       public void componentResized(ComponentEvent e) {
-               if (e.getComponent().getWidth() != lastWidth) {
-                       lastWidth = e.getComponent().getWidth();
-                       for (int row=tree.getRowCount(); --row>=0;) {
-                               fireTreePathChanged(tree.getPathForRow(row));
-                       }
-               }
-       }
-
-       @Override
-       public void componentShown(ComponentEvent e) {}
-       
-}
diff --git a/src/be/nikiroo/utils/ui/TreeModelTransformer.java b/src/be/nikiroo/utils/ui/TreeModelTransformer.java
deleted file mode 100644 (file)
index 9568f17..0000000
+++ /dev/null
@@ -1,1217 +0,0 @@
-/*
- *    This program is free software: you can redistribute it and/or modify
- *    it under the terms of the GNU Lesser General Public License as published
- *    by the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
- *
- *    You should have received a copy of the GNU Lesser General Public License
- *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-// Can be found at: https://code.google.com/archive/p/aephyr/source/default/source
-// package aephyr.swing;
-package be.nikiroo.utils.ui;
-
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.swing.JTree;
-import javax.swing.SortOrder;
-import javax.swing.event.EventListenerList;
-import javax.swing.event.TreeExpansionEvent;
-import javax.swing.event.TreeExpansionListener;
-import javax.swing.event.TreeModelEvent;
-import javax.swing.event.TreeModelListener;
-import javax.swing.event.TreeWillExpandListener;
-import javax.swing.tree.ExpandVetoException;
-import javax.swing.tree.TreeModel;
-import javax.swing.tree.TreePath;
-
-
-public class TreeModelTransformer<N> implements TreeModel {
-
-       public TreeModelTransformer(JTree tree, TreeModel model) {
-               if (tree == null)
-                       throw new IllegalArgumentException();
-               if (model == null)
-                       throw new IllegalArgumentException();
-               this.tree = tree;
-               this.model = model;
-               handler = createHandler();
-               addListeners();
-       }
-       
-       private JTree tree;
-       
-       private TreeModel model;
-
-       private Handler handler;
-       
-       private Filter<N> filter;
-       
-       private TreePath filterStartPath;
-       
-       private int filterDepthLimit;
-       
-       private SortOrder sortOrder = SortOrder.UNSORTED;
-       
-       private Map<Object,Converter> converters;
-       
-       protected EventListenerList listenerList = new EventListenerList();
-
-       protected Handler createHandler() {
-               return new Handler();
-       }
-       
-       protected void addListeners() {
-               tree.addTreeExpansionListener(handler);
-               model.addTreeModelListener(handler);
-       }
-
-       protected void removeListeners() {
-               tree.removeTreeExpansionListener(handler);
-               model.removeTreeModelListener(handler);
-       }
-       
-       public void dispose() {
-               removeListeners();
-       }
-       
-       public TreeModel getModel() {
-               return model;
-       }
-
-       private Converter getConverter(Object node) {
-               return converters == null ? null : converters.get(node);
-       }
-
-       int convertRowIndexToView(Object parent, int index) {
-               Converter converter = getConverter(parent);
-               if (converter != null)
-                       return converter.convertRowIndexToView(index);
-               return index;
-       }
-
-       int convertRowIndexToModel(Object parent, int index) {
-               Converter converter = getConverter(parent);
-               if (converter != null)
-                       return converter.convertRowIndexToModel(index);
-               return index;
-       }
-
-       @Override
-       public Object getChild(Object parent, int index) {
-               return model.getChild(parent, convertRowIndexToModel(parent, index));
-       }
-
-       @Override
-       public int getChildCount(Object parent) {
-               Converter converter = getConverter(parent);
-               if (converter != null)
-                       return converter.getChildCount();
-               return model.getChildCount(parent);
-       }
-
-       @Override
-       public int getIndexOfChild(Object parent, Object child) {
-               int index = model.getIndexOfChild(parent, child);
-               if (index < 0)
-                       return -1;
-               return convertRowIndexToView(parent, index);
-       }
-
-       @Override
-       public Object getRoot() {
-               return model.getRoot();
-       }
-
-       @Override
-       public boolean isLeaf(Object node) {
-               return model.isLeaf(node);
-       }
-
-       @Override
-       public void valueForPathChanged(TreePath path, Object newValue) {
-               model.valueForPathChanged(path, newValue);
-       }
-
-       @Override
-       public void addTreeModelListener(TreeModelListener l) {
-               listenerList.add(TreeModelListener.class, l);
-       }
-       
-       @Override
-       public void removeTreeModelListener(TreeModelListener l) {
-               listenerList.remove(TreeModelListener.class, l);
-       }
-       
-       /**
-        * Set the comparator that compares nodes in sorting.
-        * @param comparator
-        * @see #getComparator()
-        */
-       public void setComparator(Comparator<N> comparator) {
-               handler.setComparator(comparator);
-       }
-       
-       /**
-        * @return comparator that compares nodes
-        * @see #setComparator(Comparator)
-        */
-       public Comparator<N> getComparator() {
-               return handler.getComparator();
-       }
-       
-       public void setSortOrder(SortOrder newOrder) {
-               SortOrder oldOrder = sortOrder;
-               if (oldOrder == newOrder)
-                       return;
-               sortOrder = newOrder;
-               ArrayList<TreePath> paths = null;
-               switch (newOrder) {
-               case ASCENDING:
-                       if (oldOrder == SortOrder.DESCENDING) {
-                               flip();
-                       } else {
-                               paths = sort();
-                       }
-                       break;
-               case DESCENDING:
-                       if (oldOrder == SortOrder.ASCENDING) {
-                               flip();
-                       } else {
-                               paths = sort();
-                       }
-                       break;
-               case UNSORTED:
-                       unsort();
-                       break;
-               }
-               fireTreeStructureChangedAndExpand(new TreePath(getRoot()), paths, true);
-       }
-       
-       public SortOrder getSortOrder() {
-               return sortOrder;
-       }
-       
-       public void toggleSortOrder() {
-               setSortOrder(sortOrder == SortOrder.ASCENDING ?
-                               SortOrder.DESCENDING : SortOrder.ASCENDING);
-       }
-       
-       
-       /**
-        * Flip all sorted paths.
-        */
-       private void flip() {
-               for (Converter c : converters.values()) {
-                       flip(c.viewToModel);
-               }
-       }
-
-       /**
-        * Flip array.
-        * @param array
-        */
-       private static void flip(int[] array) {
-               for (int left=0, right=array.length-1;
-                               left<right; left++, right--) {
-                       int tmp = array[left];
-                       array[left] = array[right];
-                       array[right] = tmp;
-               }
-       }
-       
-       private void unsort() {
-               if (filter == null) {
-                       converters = null;
-               } else {
-                       Iterator<Converter> cons = converters.values().iterator();
-                       while (cons.hasNext()) {
-                               Converter converter = cons.next();
-                               if (!converter.isFiltered()) {
-                                       cons.remove();
-                               } else {
-                                       Arrays.sort(converter.viewToModel);
-                               }
-                       }
-               }
-       }
-       
-       /**
-        * Sort root and expanded descendants.
-        * @return list of paths that were sorted
-        */
-       private ArrayList<TreePath> sort() {
-               if (converters == null)
-                       converters = createConvertersMap(); //new IdentityHashMap<Object,Converter>();
-               return sortHierarchy(new TreePath(model.getRoot()));
-       }
-
-       /**
-        * Sort path and expanded descendants.
-        * @param path
-        * @return list of paths that were sorted
-        */
-       private ArrayList<TreePath> sortHierarchy(TreePath path) {
-               ValueIndexPair<N>[] pairs = createValueIndexPairArray(20);
-               ArrayList<TreePath> list = new ArrayList<TreePath>();
-               pairs = sort(path.getLastPathComponent(), pairs);
-               list.add(path);
-               Enumeration<TreePath> paths = tree.getExpandedDescendants(path);
-               if (paths != null)
-                       while (paths.hasMoreElements()) {
-                               path = paths.nextElement();
-                               pairs = sort(path.getLastPathComponent(), pairs);
-                               list.add(path);
-                       }
-               return list;
-       }
-       
-       private ValueIndexPair<N>[] sort(Object node, ValueIndexPair<N>[] pairs) {
-               Converter converter = getConverter(node);
-               TreeModel mdl = model;
-               int[] vtm;
-               if (converter != null) {
-                       vtm = converter.viewToModel;
-                       if (pairs.length < vtm.length)
-                               pairs = createValueIndexPairArray(vtm.length);
-                       for (int i=vtm.length; --i>=0;) {
-                               int idx = vtm[i];
-                               pairs[i].index = idx;
-                               pairs[i].value = (N)mdl.getChild(node, idx);
-                       }
-               } else {
-                       int count = mdl.getChildCount(node);
-                       if (count <= 0)
-                               return pairs;
-                       if (pairs.length < count)
-                               pairs = createValueIndexPairArray(count);
-                       for (int i=count; --i>=0;) {
-                               pairs[i].index = i;
-                               pairs[i].value = (N)mdl.getChild(node, i);
-                       }
-                       vtm = new int[count];
-               }
-               Arrays.sort(pairs, 0, vtm.length, handler);
-               for (int i=vtm.length; --i>=0;)
-                       vtm[i] = pairs[i].index;
-               if (converter == null) {
-                       converters.put(node, new Converter(vtm, false));
-               }
-               if (sortOrder == SortOrder.DESCENDING)
-                       flip(vtm);
-               return pairs;
-       }
-       
-       private ValueIndexPair<N>[] createValueIndexPairArray(int len) {
-               ValueIndexPair<N>[] pairs = new ValueIndexPair[len];
-               for (int i=len; --i>=0;)
-                       pairs[i] = new ValueIndexPair<N>();
-               return pairs;
-       }
-       
-       public void setFilter(Filter<N> filter) {
-               setFilter(filter, null);
-       }
-       
-       public void setFilter(Filter<N> filter, TreePath startingPath) {
-               setFilter(filter, null, -1);
-       }
-       
-       public void setFilter(Filter<N> filter, TreePath startingPath, int depthLimit) {
-               if (filter == null && startingPath != null)
-                       throw new IllegalArgumentException();
-               if (startingPath != null && startingPath.getPathCount() == 1)
-                       startingPath = null;
-               Filter<N> oldFilter = this.filter;
-               TreePath oldStartPath = filterStartPath;
-               this.filter = filter;
-               filterStartPath = startingPath;
-               filterDepthLimit = depthLimit;
-               applyFilter(oldFilter, oldStartPath, null, true);
-       }
-       
-       public Filter<N> getFilter() {
-               return filter;
-       }
-       
-       public TreePath getFilterStartPath() {
-               return filterStartPath;
-       }
-       
-       private void applyFilter(Filter<N> oldFilter, TreePath oldStartPath, Collection<TreePath> expanded, boolean sort) {
-               TreePath startingPath = filterStartPath;
-               ArrayList<TreePath> expand = null;
-               if (filter == null) {
-                       converters = null;
-               } else {
-                       if (converters == null || startingPath == null) {
-                               converters = createConvertersMap();
-                       } else if (oldFilter != null) {
-                               // unfilter the oldStartPath if oldStartPath isn't descendant of startingPath
-                               if (oldStartPath == null) {
-                                       converters = createConvertersMap();
-                                       fireTreeStructureChangedAndExpand(new TreePath(getRoot()), null, true);
-                               } else if (!startingPath.isDescendant(oldStartPath)) {
-                                       Object node = oldStartPath.getLastPathComponent();
-                                       handler.removeConverter(getConverter(node), node);
-                                       fireTreeStructureChangedAndExpand(oldStartPath, null, true);
-                               }
-                       }
-                       expand = new ArrayList<TreePath>();
-                       TreePath path = startingPath != null ? startingPath : new TreePath(getRoot());
-                       if (!applyFilter(filter, path, expand, filterDepthLimit)) {
-                               converters.put(path.getLastPathComponent(), new Converter(Converter.EMPTY, true));
-                       }
-               }
-               if (startingPath == null)
-                       startingPath = new TreePath(getRoot());
-               fireTreeStructureChanged(startingPath);
-               if (expanded != null)
-                       expand.retainAll(expanded);
-               expandPaths(expand);
-               if (sort && sortOrder != SortOrder.UNSORTED) {
-                       if (filter == null)
-                               converters = createConvertersMap();
-                       if (startingPath.getPathCount() > 1 && oldFilter != null) {
-                               // upgrade startingPath or sort oldStartPath
-                               if (oldStartPath == null) {
-                                       startingPath = new TreePath(getRoot());
-                               } else if (oldStartPath.isDescendant(startingPath)) {
-                                       startingPath = oldStartPath;
-                               } else if (!startingPath.isDescendant(oldStartPath)) {
-                                       expand = sortHierarchy(oldStartPath);
-                                       fireTreeStructureChanged(oldStartPath);
-                                       expandPaths(expand);
-                               }
-                       }
-                       expand = sortHierarchy(startingPath);
-                       fireTreeStructureChanged(startingPath);
-                       expandPaths(expand);
-               }
-
-       }
-       
-       private boolean applyFilter(Filter<N> filter, TreePath path, ArrayList<TreePath> expand) {
-               int depthLimit = filterDepthLimit;
-               if (depthLimit >= 0) {
-                       depthLimit -= filterStartPath.getPathCount() - path.getPathCount();
-                       if (depthLimit <= 0)
-                               return false;
-               }
-               return applyFilter(filter, path, expand, depthLimit);
-       }
-       
-       private boolean applyFilter(Filter<N> filter, TreePath path, ArrayList<TreePath> expand, int depthLimit) {
-               Object node = path.getLastPathComponent();
-               int count = model.getChildCount(node);
-               int[] viewToModel = null;
-               int viewIndex = 0;
-               boolean needsExpand = false;
-               boolean isExpanded = false;
-               if (depthLimit > 0)
-                       depthLimit--;
-               for (int i=0; i<count; i++) {
-                       Object child = model.getChild(node, i);
-                       boolean leaf = model.isLeaf(child);
-                       if (filter.acceptNode(path, (N)child, leaf)) {
-                               if (viewToModel == null)
-                                       viewToModel = new int[count-i];
-                               viewToModel[viewIndex++] = i;
-                               needsExpand = true;
-                       } else if (depthLimit != 0 && !leaf) {
-                               if (applyFilter(filter, path.pathByAddingChild(child), expand, depthLimit)) {
-                                       if (viewToModel == null)
-                                               viewToModel = new int[count-i];
-                                       viewToModel[viewIndex++] = i;
-                                       isExpanded = true;
-                               }
-                       }
-               }
-               if (needsExpand && expand != null && !isExpanded && path.getPathCount() > 1) {
-                       expand.add(path);
-               }
-               if (viewToModel != null) {
-                       if (viewIndex < viewToModel.length)
-                               viewToModel = Arrays.copyOf(viewToModel, viewIndex);
-                       // a node must have a converter to signify that tree modifications
-                       // need to query the filter, so have to put in converter
-                       // even if viewIndex == viewToModel.length
-                       converters.put(node, new Converter(viewToModel, true));
-                       return true;
-               }
-               return false;
-       }
-
-       
-       private void expandPaths(ArrayList<TreePath> paths) {
-               if (paths == null || paths.isEmpty())
-                       return;
-               JTree tre = tree;
-               for (TreePath path : paths)
-                       tre.expandPath(path);
-       }
-       
-       
-       private void fireTreeStructureChangedAndExpand(TreePath path, ArrayList<TreePath> list, boolean retainSelection) {
-               Enumeration<TreePath> paths = list != null ?
-                               Collections.enumeration(list) : tree.getExpandedDescendants(path);
-               TreePath[] sel = retainSelection ? tree.getSelectionPaths() : null;
-               fireTreeStructureChanged(path);
-               if (paths != null)
-                       while (paths.hasMoreElements())
-                               tree.expandPath(paths.nextElement());
-               if (sel != null)
-                       tree.setSelectionPaths(sel);
-       }
-
-       
-       
-       protected void fireTreeStructureChanged(TreePath path) {
-               Object[] listeners = listenerList.getListenerList();
-               TreeModelEvent e = null;
-               for (int i = listeners.length-2; i>=0; i-=2) {
-                       if (listeners[i]==TreeModelListener.class) {
-                               if (e == null)
-                                       e = new TreeModelEvent(this, path, null, null);
-                               ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
-                       }
-               }
-       }
-       
-       protected void fireTreeNodesChanged(TreePath path, int[] childIndices, Object[] childNodes) {
-               Object[] listeners = listenerList.getListenerList();
-               TreeModelEvent e = null;
-               for (int i = listeners.length-2; i>=0; i-=2) {
-                       if (listeners[i]==TreeModelListener.class) {
-                               if (e == null)
-                                       e = new TreeModelEvent(this, path, childIndices, childNodes);
-                               ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
-                       }
-               }
-       }
-       
-       protected void fireTreeNodesInserted(TreePath path, int[] childIndices, Object[] childNodes) {
-               Object[] listeners = listenerList.getListenerList();
-               TreeModelEvent e = null;
-               for (int i = listeners.length-2; i>=0; i-=2) {
-                       if (listeners[i]==TreeModelListener.class) {
-                               if (e == null)
-                                       e = new TreeModelEvent(this, path, childIndices, childNodes);
-                               ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
-                       }
-               }
-       }
-
-       protected void fireTreeNodesRemoved(TreePath path, int[] childIndices, Object[] childNodes) {
-               Object[] listeners = listenerList.getListenerList();
-               TreeModelEvent e = null;
-               for (int i = listeners.length-2; i>=0; i-=2) {
-                       if (listeners[i]==TreeModelListener.class) {
-                               if (e == null)
-                                       e = new TreeModelEvent(this, path, childIndices, childNodes);
-                               ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
-                       }
-               }
-       }
-       
-       
-       protected class Handler implements Comparator<ValueIndexPair<N>>,
-                       TreeModelListener, TreeExpansionListener {
-               
-               private Comparator<N> comparator;
-               
-               private Collator collator = Collator.getInstance();
-               
-               void setComparator(Comparator<N> cmp) {
-                       comparator = cmp;
-                       collator = cmp == null ? Collator.getInstance() : null;
-               }
-               
-               Comparator<N> getComparator() {
-                       return comparator;
-               }
-               
-               // TODO, maybe switch to TreeWillExpandListener?
-               // TreeExpansionListener was used in case an expanded node
-               // had children that would also be expanded, but it is impossible
-               // for hidden nodes' expansion state to survive a SortOrder change
-               // since they are all erased when the tree structure change event
-               // is fired after changing the SortOrder.
-               
-               @Override
-               public void treeCollapsed(TreeExpansionEvent e) {}
-
-               @Override
-               public void treeExpanded(TreeExpansionEvent e) {
-                       if (sortOrder != SortOrder.UNSORTED) {
-                               TreePath path = e.getPath();
-                               Converter converter = getConverter(path.getLastPathComponent());
-                               if (converter == null) {
-                                       ArrayList<TreePath> paths = sortHierarchy(path);
-                                       fireTreeStructureChangedAndExpand(path, paths, false);
-                               }
-                       }
-               }
-               
-               private boolean isFiltered(Object node) {
-                       Converter c = getConverter(node);
-                       return c == null ? false : c.isFiltered();
-               }
-               
-               private boolean acceptable(TreePath path, Object[] childNodes, int index, ArrayList<TreePath> expand) {
-                       return acceptable(path, childNodes, index) ||
-                                       applyFilter(filter, path.pathByAddingChild(childNodes[index]), expand);
-               }
-               
-               @Override
-               public void treeNodesChanged(TreeModelEvent e) {
-                       treeNodesChanged(e.getTreePath(), e.getChildIndices(), e.getChildren());
-               }
-               
-               protected void treeNodesChanged(TreePath path, int[] childIndices, Object[] childNodes) {
-                       if (childIndices == null) {
-                               // path should be root path
-                               // reapply filter
-                               if (filter != null)
-                                       applyFilter(null, null, null, true);
-                               return;
-                       }
-                       Converter converter = getConverter(path.getLastPathComponent());
-                       ArrayList<TreePath> expand = null;
-                       if (converter != null) {
-                               expand = new ArrayList<TreePath>();
-                               int childIndex = 0;
-                               for (int i=0; i<childIndices.length; i++) {
-                                       int idx = converter.convertRowIndexToView(childIndices[i]);
-                                       if (idx >= 0) {
-                                               // see if the filter dislikes the nodes new state
-                                               if (converter.isFiltered() &&
-                                                               !isFiltered(childNodes[i]) &&
-                                                               !acceptable(path, childNodes, i)) {
-                                                       // maybe it likes a child nodes state
-                                                       if (!applyFilter(filter, path.pathByAddingChild(childNodes[i]), expand))
-                                                               remove(path, childNodes[i]);
-                                                       continue;
-                                               }
-                                               childNodes[childIndex] = childNodes[i];
-                                               childIndices[childIndex++] = idx;
-                                       } else if (acceptable(path, childNodes, i, expand)) {
-                                               int viewIndex = insert(path.getLastPathComponent(),
-                                                               childNodes[i], childIndices[i], converter);
-                                               fireTreeNodesInserted(path, indices(viewIndex), nodes(childNodes[i]));
-                                       }
-                               }
-                               if (childIndex == 0) {
-                                       maybeFireStructureChange(path, expand);
-                                       return;
-                               }
-                               if (sortOrder != SortOrder.UNSORTED && converter.getChildCount() > 1) {
-                                       sort(path.getLastPathComponent(), createValueIndexPairArray(converter.getChildCount()));
-                                       fireTreeStructureChangedAndExpand(path, null, true);
-                                       expandPaths(expand);
-                                       return;
-                               }
-                               if (childIndex != childIndices.length) {
-                                       childIndices = Arrays.copyOf(childIndices, childIndex);
-                                       childNodes = Arrays.copyOf(childNodes, childIndex);
-                               }
-                       } else if (filter != null && isFilteredOut(path)) {
-                               // see if the filter likes the nodes new states
-                               expand = new ArrayList<TreePath>();
-                               int[] vtm = null;
-                               int idx = 0;
-                               for (int i=0; i<childIndices.length; i++) {
-                                       if (acceptable(path, childNodes, i, expand)) {
-                                               if (vtm == null)
-                                                       vtm = new int[childIndices.length-i];
-                                               vtm[idx++] = childIndices[i];
-                                       }
-                               }
-                               // filter in path if appropriate
-                               if (vtm != null)
-                                       filterIn(vtm, idx, path, expand);
-                               return;
-                       }
-                       // must fire tree nodes changed even if a
-                       // structure change will be fired because the
-                       // expanded paths need to be updated first
-                       fireTreeNodesChanged(path, childIndices, childNodes);
-                       maybeFireStructureChange(path, expand);
-               }
-               
-               /**
-                * Helper method for treeNodesChanged...
-                * @param path
-                * @param expand
-                */
-               private void maybeFireStructureChange(TreePath path, ArrayList<TreePath> expand) {
-                       if (expand != null && !expand.isEmpty()) {
-                               Enumeration<TreePath> expanded = tree.getExpandedDescendants(path);
-                               fireTreeStructureChanged(path);
-                               if (expanded != null)
-                                       while (expanded.hasMoreElements())
-                                               tree.expandPath(expanded.nextElement());
-                               expandPaths(expand);
-                       }
-               }
-               
-               @Override
-               public void treeNodesInserted(TreeModelEvent e) {
-                       treeNodesInserted(e.getTreePath(), e.getChildIndices(), e.getChildren());
-               }
-
-               protected void treeNodesInserted(TreePath path, int[] childIndices, Object[] childNodes) {
-                       Object parent = path.getLastPathComponent();
-                       Converter converter = getConverter(parent);
-                       ArrayList<TreePath> expand = null;
-                       if (converter != null) {
-//                             if (childIndices.length > 3 && !converter.isFiltered()
-//                                             && childIndices.length > converter.getChildCount()/10) {
-//                                     TreePath expand = sortHierarchy(path);
-//                                     fireTreeStructureChangedAndExpand(expand);
-//                                     return;
-//                             }
-                               int childIndex = 0;
-                               for (int i=0; i<childIndices.length; i++) {
-                                       if (converter.isFiltered()) {
-                                               // path hasn't met the filter criteria, so childNodes must be filtered
-                                               if (expand == null)
-                                                       expand = new ArrayList<TreePath>();
-                                               if (!applyFilter(filter, path.pathByAddingChild(childNodes[i]), expand))
-                                                       continue;
-                                       }
-                                       // shift the appropriate cached modelIndices
-                                       int[] vtm = converter.viewToModel;
-                                       int modelIndex = childIndices[i];
-                                       for (int j=vtm.length; --j>=0;) {
-                                               if (vtm[j] >= modelIndex)
-                                                       vtm[j] += 1;
-                                       }
-                                       // insert modelIndex to converter
-                                       int viewIndex = insert(parent, childNodes[i], modelIndex, converter);
-                                       childNodes[childIndex] = childNodes[i];
-                                       childIndices[childIndex++] = viewIndex;
-                               }
-                               if (childIndex == 0)
-                                       return;
-                               if (childIndex != childIndices.length) {
-                                       childIndices = Arrays.copyOf(childIndices, childIndex);
-                                       childNodes = Arrays.copyOf(childNodes, childIndex);
-                               }
-                               if (childIndex > 1 && sortOrder != SortOrder.UNSORTED) {
-                                       sort(childIndices, childNodes);
-                               }
-                       } else if (filter != null && isFilteredOut(path)) {
-                               // apply filter to inserted nodes
-                               int[] vtm = null;
-                               int idx = 0;
-                               expand = new ArrayList<TreePath>();
-                               for (int i=0; i<childIndices.length; i++) {
-                                       if (acceptable(path, childNodes, i, expand)) {
-                                               if (vtm == null)
-                                                       vtm = new int[childIndices.length-i];
-                                               vtm[idx++] = childIndices[i];
-                                       }
-                               }
-                               // filter in path if appropriate
-                               if (vtm != null)
-                                       filterIn(vtm, idx, path, expand);
-                               return;
-                       }
-                       fireTreeNodesInserted(path, childIndices, childNodes);
-                       expandPaths(expand);
-               }
-               
-               @Override
-               public void treeNodesRemoved(TreeModelEvent e) {
-                       treeNodesRemoved(e.getTreePath(), e.getChildIndices(), e.getChildren());
-               }
-               
-
-               private boolean isFilterStartPath(TreePath path) {
-                       if (filterStartPath == null)
-                               return path.getPathCount() == 1;
-                       return filterStartPath.equals(path);
-               }
-               
-               protected void treeNodesRemoved(TreePath path, int[] childIndices, Object[] childNodes) {
-                       Object parent = path.getLastPathComponent();
-                       Converter converter = getConverter(parent);
-                       if (converter != null) {
-                               int len = 0;
-                               for (int i=0; i<childNodes.length; i++) {
-                                       removeConverter(childNodes[i]);
-                                       int viewIndex = converter.convertRowIndexToView(childIndices[i]);
-                                       if (viewIndex >= 0) {
-                                               childNodes[len] = childNodes[i];
-                                               childIndices[len++] = viewIndex;
-                                       }
-                               }
-                               if (len == 0)
-                                       return;
-                               if (converter.isFiltered() && converter.getChildCount() == len) {
-                                       ArrayList<TreePath> expand = new ArrayList<TreePath>();
-                                       if (applyFilter(filter, path, expand)) {
-                                               expand.retainAll(getExpandedPaths(path));
-                                               if (sortOrder != SortOrder.UNSORTED)
-                                                       sortHierarchy(path);
-                                               fireTreeStructureChangedAndExpand(path, expand, true);
-                                       } else if (isFilterStartPath(path)) {
-                                               converters.put(parent, new Converter(Converter.EMPTY, true));
-                                               fireTreeStructureChanged(path);
-                                       } else {
-                                               remove(path.getParentPath(), parent);
-                                       }
-                                       return;
-                               }
-                               if (len != childIndices.length) {
-                                       childIndices = Arrays.copyOf(childIndices, len);
-                                       childNodes = Arrays.copyOf(childNodes, len);
-                               }
-                               if (len > 1 && sortOrder != SortOrder.UNSORTED) {
-                                       sort(childIndices, childNodes);
-                               }
-                               if (childIndices.length == 1) {
-                                       converter.remove(converter.convertRowIndexToModel(childIndices[0]));
-                               } else {
-                                       converter.remove(childIndices);
-                               }
-                       } else if (filter != null && isFilteredOut(path)) {
-                               return;
-                       }
-                       fireTreeNodesRemoved(path, childIndices, childNodes);
-               }
-               
-               private Collection<TreePath> getExpandedPaths(TreePath path) {
-                       Enumeration<TreePath> en = tree.getExpandedDescendants(path);
-                       if (en == null)
-                               return Collections.emptySet();
-                       HashSet<TreePath> expanded = new HashSet<TreePath>();
-                       while (en.hasMoreElements())
-                               expanded.add(en.nextElement());
-                       return expanded;
-               }
-
-               @Override
-               public void treeStructureChanged(TreeModelEvent e) {
-                       if (converters != null) {
-                               // not enough information to properly clean up
-                               // reapply filter/sort
-                               converters = createConvertersMap();
-                               TreePath[] sel = tree.getSelectionPaths();
-                               if (filter != null) {
-                                       applyFilter(null, null, getExpandedPaths(new TreePath(getRoot())), false);
-                               }
-                               if (sortOrder != SortOrder.UNSORTED) {
-                                       TreePath path = new TreePath(getRoot());
-                                       ArrayList<TreePath> expand = sortHierarchy(path);
-                                       fireTreeStructureChangedAndExpand(path, expand, false);
-                               }
-                               if (sel != null) {
-                                       tree.clearSelection();
-                                       TreePath changedPath = e.getTreePath();
-                                       for (TreePath path : sel) {
-                                               if (!changedPath.isDescendant(path))
-                                                       tree.addSelectionPath(path);
-                                       }
-                               }
-                       } else {
-                               fireTreeStructureChanged(e.getTreePath());
-                       }
-               }
-               
-
-               @Override
-               public final int compare(ValueIndexPair<N> a, ValueIndexPair<N> b) {
-                       return compareNodes(a.value, b.value);
-               }
-
-               
-               protected int compareNodes(N a, N b) {
-                       if (comparator != null)
-                               return comparator.compare(a, b);
-                       return collator.compare(a.toString(), b.toString());
-               }
-
-               private void removeConverter(Object node) {
-                       Converter c = getConverter(node);
-                       if (c != null)
-                               removeConverter(c, node);
-               }
-               
-               private void removeConverter(Converter converter, Object node) {
-                       for (int i=converter.getChildCount(); --i>=0;) {
-                               int index = converter.convertRowIndexToModel(i);
-                               Object child = model.getChild(node, index);
-                               Converter c = getConverter(child);
-                               if (c != null)
-                                       removeConverter(c, child);
-                       }
-                       converters.remove(node);
-               }
-               
-               private boolean isFilteredOut(TreePath path) {
-                       if (filterStartPath != null && !filterStartPath.isDescendant(path))
-                               return false;
-                       TreePath parent = path.getParentPath();
-                       // root should always have a converter if filter is non-null,
-                       // so if parent is ever null, there is a bug somewhere else
-                       Converter c = getConverter(parent.getLastPathComponent());
-                       if (c != null) {
-                               return getIndexOfChild(
-                                               parent.getLastPathComponent(),
-                                               path.getLastPathComponent()) < 0;
-                       }
-                       return isFilteredOut(parent);
-               }
-               
-               private void filterIn(int[] vtm, int vtmLength, TreePath path, ArrayList<TreePath> expand) {
-                       Object node = path.getLastPathComponent();
-                       if (vtmLength != vtm.length)
-                               vtm = Arrays.copyOf(vtm, vtmLength);
-                       Converter converter = new Converter(vtm, true);
-                       converters.put(node, converter);
-                       insert(path.getParentPath(), node);
-                       tree.expandPath(path);
-                       expandPaths(expand);
-               }
-
-               private boolean acceptable(TreePath path, Object[] nodes, int index) {
-                       Object node = nodes[index];
-                       return filter.acceptNode(path, (N)node, model.isLeaf(node));
-               }
-               
-               private int ascInsertionIndex(int[] vtm, Object parent, N node, int idx) {
-                       for (int i=vtm.length; --i>=0;) {
-                               int cmp = compareNodes(node, (N)model.getChild(parent, vtm[i]));
-                               if (cmp > 0 || (cmp == 0 && idx > vtm[i])) {
-                                       return i+1;
-                               }
-                       }
-                       return 0;
-               }
-               
-               
-               private int dscInsertionIndex(int[] vtm, Object parent, N node, int idx) {
-                       for (int i=vtm.length; --i>=0;) {
-                               int cmp = compareNodes(node, (N)model.getChild(parent, vtm[i]));
-                               if (cmp < 0) {
-                                       return i+1;
-                               } else if (cmp == 0 && idx < vtm[i]) {
-                                       return i;
-                               }
-                       }
-                       return 0;
-               }
-
-               
-               /**
-                * Inserts the specified path and node and any parent paths as necessary.
-                * <p>
-                * Fires appropriate event.
-                * @param path
-                * @param node
-                */
-               private void insert(TreePath path, Object node) {
-                       Object parent = path.getLastPathComponent();
-                       Converter converter = converters.get(parent);
-                       int modelIndex = model.getIndexOfChild(parent, node);
-                       if (converter == null) {
-                               converter = new Converter(indices(modelIndex), true);
-                               converters.put(parent, converter);
-                               insert(path.getParentPath(), parent);
-                       } else {
-                               int viewIndex = insert(parent, node, modelIndex, converter);
-                               fireTreeNodesInserted(path, indices(viewIndex), nodes(node));
-                       }
-               }
-               
-               /**
-                * Inserts node into parent in correct sort order.
-                * <p>
-                * Responsibility of caller to fire appropriate event with the returned viewIndex.
-                * @param path
-                * @param node
-                * @param modelIndex
-                * @param converter
-                * @return viewIndex
-                */
-               private int insert(Object parent, Object node, int modelIndex, Converter converter) {
-                       int[] vtm = converter.viewToModel;
-                       int viewIndex;
-                       switch (sortOrder) {
-                       case ASCENDING:
-                               viewIndex = ascInsertionIndex(vtm, parent, (N)node, modelIndex);
-                               break;
-                       case DESCENDING:
-                               viewIndex = dscInsertionIndex(vtm, parent, (N)node, modelIndex);
-                               break;
-                       default: case UNSORTED:
-                               viewIndex = unsortedInsertionIndex(vtm, modelIndex);
-                               break;
-                       }
-                       int[] a = new int[vtm.length+1];
-                       System.arraycopy(vtm, 0, a, 0, viewIndex);
-                       System.arraycopy(vtm, viewIndex, a, viewIndex+1, vtm.length-viewIndex);
-                       a[viewIndex] = modelIndex;
-                       converter.viewToModel = a;
-                       return viewIndex;
-               }
-               
-               private void remove(TreePath path, Object node) {
-                       Object parent = path.getLastPathComponent();
-                       if (path.getPathCount() == 1 || (filterStartPath != null && filterStartPath.equals(path))) {
-                               removeConverter(node);
-                               converters.put(parent, new Converter(Converter.EMPTY, true));
-                               fireTreeNodesRemoved(path, indices(0), nodes(node));
-                               return;
-                       }
-                       Converter converter = converters.get(parent);
-                       int modelIndex = model.getIndexOfChild(parent, node);
-                       int viewIndex = converter.remove(modelIndex);
-                       switch (viewIndex) {
-                       default:
-                               removeConverter(node);
-                               fireTreeNodesRemoved(path, indices(viewIndex), nodes(node));
-                               break;
-                       case Converter.ONLY_INDEX:
-//                             if (path.getParentPath() == null) {
-//                                     // reached filter root
-//                                     removeConverter(node);
-//                                     converters.put(parent, new Converter(Converter.EMPTY, true));
-//                                     fireTreeNodesRemoved(path, indices(0), nodes(node));
-//                                     return;
-//                             }
-                               remove(path.getParentPath(), parent);
-                               break;
-                       case Converter.INDEX_NOT_FOUND:
-                               removeConverter(node);
-                       }
-               }
-               
-               
-               
-       }
-       
-       
-
-       private static int unsortedInsertionIndex(int[] vtm, int idx) {
-               for (int i=vtm.length; --i>=0;)
-                       if (vtm[i] < idx)
-                               return i+1;
-               return 0;
-       }
-       
-       private static void sort(int[] childIndices, Object[] childNodes) {
-               int len = childIndices.length;
-               ValueIndexPair[] pairs = new ValueIndexPair[len];
-               for (int i=len; --i>=0;)
-                       pairs[i] = new ValueIndexPair<Object>(childIndices[i], childNodes[i]);
-               Arrays.sort(pairs);
-               for (int i=len; --i>=0;) {
-                       childIndices[i] = pairs[i].index;
-                       childNodes[i] = pairs[i].value;
-               }
-       }
-       
-       private static int[] indices(int...indices) {
-               return indices;
-       }
-       
-       private static Object[] nodes(Object...nodes) {
-               return nodes;
-       }
-       
-
-
-       
-       /**
-        * This class has a dual purpose, both related to comparing/sorting.
-        * <p>
-        * The Handler class sorts an array of ValueIndexPair based on the value.
-        * Used for sorting the view.
-        * <p>
-        * ValueIndexPair sorts itself based on the index.
-        * Used for sorting childIndices for fire* methods.
-        */
-       private static class ValueIndexPair<N> implements Comparable<ValueIndexPair<N>> {
-               ValueIndexPair() {}
-               
-               ValueIndexPair(int idx, N val) {
-                       index = idx;
-                       value = val;
-               }
-               
-               N value;
-               
-               int index;
-               
-               public int compareTo(ValueIndexPair<N> o) {
-                       return index - o.index;
-               }
-       }
-       
-       private static class Converter {
-               
-               static final int[] EMPTY = new int[0];
-               
-               static final int ONLY_INDEX = -2;
-               
-               static final int INDEX_NOT_FOUND = -1;
-               
-               Converter(int[] vtm, boolean filtered) {
-                       viewToModel = vtm;
-                       isFiltered = filtered;
-               }
-               
-               private int[] viewToModel;
-               
-               private boolean isFiltered;
-               
-//             public boolean equals(Converter conv) {
-//                     if (conv == null)
-//                             return false;
-//                     if (isFiltered != conv.isFiltered)
-//                             return false;
-//                     return Arrays.equals(viewToModel, conv.viewToModel);
-//             }
-               
-               boolean isFiltered() {
-                       return isFiltered;
-               }
-               
-               void remove(int viewIndices[]) {
-                       int len = viewToModel.length - viewIndices.length;
-                       if (len == 0) {
-                               viewToModel = EMPTY;
-                       } else {
-                               int[] oldVTM = viewToModel;
-                               int[] newVTM = new int[len];
-                               for (int oldIndex=0, newIndex=0, removeIndex=0;
-                                               newIndex<newVTM.length;
-                                               newIndex++, oldIndex++) {
-                                       while (removeIndex < viewIndices.length && oldIndex == viewIndices[removeIndex]) {
-                                               int idx = oldVTM[oldIndex];
-                                               removeIndex++;
-                                               oldIndex++;
-                                               for (int i=newIndex; --i>=0;)
-                                                       if (newVTM[i] > idx)
-                                                               newVTM[i]--;
-                                               for (int i=oldIndex; i<oldVTM.length; i++)
-                                                       if (oldVTM[i] > idx)
-                                                               oldVTM[i]--;
-                                       }
-                                       newVTM[newIndex] = oldVTM[oldIndex];
-                               }
-                               viewToModel = newVTM;
-                       }
-               }
-               
-               /**
-                * @param modelIndex
-                * @return viewIndex that was removed<br>
-                *              or <code>ONLY_INDEX</code> if the modelIndex is the only one in the view<br>
-                *              or <code>INDEX_NOT_FOUND</code> if the modelIndex is not in the view
-                */
-               int remove(int modelIndex) {
-                       int[] vtm = viewToModel;
-                       for (int i=vtm.length; --i>=0;) {
-                               if (vtm[i] > modelIndex) {
-                                       vtm[i] -= 1;
-                               } else if (vtm[i] == modelIndex) {
-                                       if (vtm.length == 1) {
-                                               viewToModel = EMPTY;
-                                               return ONLY_INDEX;
-                                       }
-                                       int viewIndex = i;
-                                       while (--i>=0) {
-                                               if (vtm[i] > modelIndex)
-                                                       vtm[i] -= 1;
-                                       }
-                                       int[] a = new int[vtm.length-1];
-                                       if (viewIndex > 0)
-                                               System.arraycopy(vtm, 0, a, 0, viewIndex);
-                                       int len = a.length-viewIndex;
-                                       if (len > 0)
-                                               System.arraycopy(vtm, viewIndex+1, a, viewIndex, len);
-                                       viewToModel = a;
-                                       return viewIndex;
-                               }
-                       }
-                       return INDEX_NOT_FOUND;
-               }
-               
-               
-               int getChildCount() {
-                       return viewToModel.length;
-               }
-               
-               /**
-                * @param modelIndex
-                * @return viewIndex corresponding to modelIndex<br>
-                *              or <code>INDEX_NOT_FOUND</code> if the modelIndex is not in the view
-                */
-               int convertRowIndexToView(int modelIndex) {
-                       int[] vtm = viewToModel;
-                       for (int i=vtm.length; --i>=0;) {
-                               if (vtm[i] == modelIndex)
-                                       return i;
-                       }
-                       return INDEX_NOT_FOUND;
-               }
-               
-               int convertRowIndexToModel(int viewIndex) {
-                       return viewToModel[viewIndex];
-               }
-       }
-
-       public interface Filter<N> {
-               boolean acceptNode(TreePath parent, N node, boolean leaf);
-       }
-
-       public static class RegexFilter<N> implements Filter<N> {
-               
-               public RegexFilter(Pattern pattern, boolean leaf) {
-                       matcher = pattern.matcher("");
-                       leafOnly = leaf;
-               }
-               
-               private Matcher matcher;
-               
-               private boolean leafOnly;
-               
-               public boolean acceptNode(TreePath parent, N node, boolean leaf) {
-                       if (leafOnly && !leaf)
-                               return false;
-                       matcher.reset(getStringValue(node));
-                       return matcher.find();
-               }
-               
-               protected String getStringValue(N node) {
-                       return node.toString();
-               }
-       }
-       
-
-       private static Map<Object,Converter> createConvertersMap() {
-               return new HashMap<Object,Converter>();
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/TreeSnapshot.java b/src/be/nikiroo/utils/ui/TreeSnapshot.java
deleted file mode 100644 (file)
index ef9a6fb..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-import javax.swing.JTree;
-import javax.swing.tree.TreeModel;
-import javax.swing.tree.TreeNode;
-import javax.swing.tree.TreePath;
-
-public class TreeSnapshot {
-       private interface NodeAction {
-               public void run(TreeNode node);
-       }
-
-       private JTree tree;
-       private TreePath[] selectionPaths;
-       private List<TreePath> expanded;
-
-       public TreeSnapshot(JTree tree) {
-               this.tree = tree;
-
-               selectionPaths = tree.getSelectionPaths();
-               if (selectionPaths == null) {
-                       selectionPaths = new TreePath[0];
-               }
-
-               expanded = new ArrayList<TreePath>();
-               forEach(tree, new NodeAction() {
-                       @Override
-                       public void run(TreeNode node) {
-                               TreePath path = nodeToPath(node);
-                               if (path != null) {
-                                       if (TreeSnapshot.this.tree.isExpanded(path)) {
-                                               expanded.add(path);
-                                       }
-                               }
-                       }
-               });
-       }
-
-       public void apply() {
-               applyTo(tree);
-       }
-
-       public void applyTo(JTree tree) {
-               final List<TreePath> newExpanded = new ArrayList<TreePath>();
-               final List<TreePath> newSlectionPaths = new ArrayList<TreePath>();
-
-               forEach(tree, new NodeAction() {
-                       @Override
-                       public void run(TreeNode newNode) {
-                               TreePath newPath = nodeToPath(newNode);
-                               if (newPath != null) {
-                                       for (TreePath path : selectionPaths) {
-                                               if (isSamePath(path, newPath)) {
-                                                       newSlectionPaths.add(newPath);
-                                                       if (expanded.contains(path)) {
-                                                               newExpanded.add(newPath);
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               });
-
-               for (TreePath newPath : newExpanded) {
-                       tree.expandPath(newPath);
-               }
-
-               tree.setSelectionPaths(newSlectionPaths.toArray(new TreePath[0]));
-       }
-
-       // You can override this
-       protected boolean isSamePath(TreePath oldPath, TreePath newPath) {
-               return newPath.toString().equals(oldPath.toString());
-       }
-
-       private void forEach(JTree tree, NodeAction action) {
-               forEach(tree.getModel(), tree.getModel().getRoot(), action);
-       }
-
-       private void forEach(TreeModel model, Object parent, NodeAction action) {
-               if (!(parent instanceof TreeNode))
-                       return;
-
-               TreeNode node = (TreeNode) parent;
-
-               action.run(node);
-               int count = model.getChildCount(node);
-               for (int i = 0; i < count; i++) {
-                       Object child = model.getChild(node, i);
-                       forEach(model, child, action);
-               }
-       }
-
-       private static TreePath nodeToPath(TreeNode node) {
-               List<Object> nodes = new LinkedList<Object>();
-               if (node != null) {
-                       nodes.add(node);
-                       node = node.getParent();
-                       while (node != null) {
-                               nodes.add(0, node);
-                               node = node.getParent();
-                       }
-               }
-
-               return nodes.isEmpty() ? null : new TreePath(nodes.toArray());
-       }
-
-       @Override
-       public String toString() {
-               StringBuilder builder = new StringBuilder();
-               builder.append("Tree Snapshot of: ").append(tree).append("\n");
-               builder.append("Selected paths:\n");
-               for (TreePath path : selectionPaths) {
-                       builder.append("\t").append(path).append("\n");
-               }
-               builder.append("Expanded paths:\n");
-               for (TreePath epath : expanded) {
-                       builder.append("\t").append(epath).append("\n");
-               }
-
-               return builder.toString();
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/UIUtils.java b/src/be/nikiroo/utils/ui/UIUtils.java
deleted file mode 100644 (file)
index ce7bcc1..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Desktop;
-import java.awt.GradientPaint;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.RadialGradientPaint;
-import java.awt.RenderingHints;
-import java.io.IOException;
-import java.net.URISyntaxException;
-
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JEditorPane;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JScrollPane;
-import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
-import javax.swing.event.HyperlinkEvent;
-import javax.swing.event.HyperlinkListener;
-
-import be.nikiroo.utils.Version;
-import be.nikiroo.utils.VersionCheck;
-
-/**
- * Some Java Swing utilities.
- * 
- * @author niki
- */
-public class UIUtils {
-       static private Color buttonNormal;
-       static private Color buttonPressed;
-       
-       /**
-        * Set a fake "native Look &amp; Feel" for the application if possible
-        * (check for the one currently in use, then try GTK).
-        * <p>
-        * <b>Must</b> be called prior to any GUI work.
-        * 
-        * @return TRUE if it succeeded
-        */
-       static public boolean setLookAndFeel() {
-               // native look & feel
-               String noLF = "javax.swing.plaf.metal.MetalLookAndFeel";
-               String lf = UIManager.getSystemLookAndFeelClassName();
-               if (lf.equals(noLF))
-                       lf = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
-               
-               return setLookAndFeel(lf);
-       }
-
-       /**
-        * Switch to the given Look &amp; Feel for the application if possible
-        * (check for the one currently in use, then try GTK).
-        * <p>
-        * <b>Must</b> be called prior to any GUI work.
-        * 
-        * @param laf
-        *            the Look &amp; Feel to use
-        * 
-        * @return TRUE if it succeeded
-        */
-       static public boolean setLookAndFeel(String laf) {
-               try {
-                       UIManager.setLookAndFeel(laf);
-                       return true;
-               } catch (InstantiationException e) {
-               } catch (ClassNotFoundException e) {
-               } catch (UnsupportedLookAndFeelException e) {
-               } catch (IllegalAccessException e) {
-               }
-
-               return false;
-       }
-
-       /**
-        * Draw a 3D-looking ellipse at the given location, if the given
-        * {@link Graphics} object is compatible (with {@link Graphics2D}); draw a
-        * simple ellipse if not.
-        * 
-        * @param g
-        *            the {@link Graphics} to draw on
-        * @param color
-        *            the base colour
-        * @param x
-        *            the X coordinate
-        * @param y
-        *            the Y coordinate
-        * @param width
-        *            the width radius
-        * @param height
-        *            the height radius
-        */
-       static public void drawEllipse3D(Graphics g, Color color, int x, int y,
-                       int width, int height) {
-               drawEllipse3D(g, color, x, y, width, height, true);
-       }
-
-       /**
-        * Draw a 3D-looking ellipse at the given location, if the given
-        * {@link Graphics} object is compatible (with {@link Graphics2D}); draw a
-        * simple ellipse if not.
-        * 
-        * @param g
-        *            the {@link Graphics} to draw on
-        * @param color
-        *            the base colour
-        * @param x
-        *            the X coordinate of the upper left corner
-        * @param y
-        *            the Y coordinate of the upper left corner
-        * @param width
-        *            the width radius
-        * @param height
-        *            the height radius
-        * @param fill
-        *            fill the content of the ellipse
-        */
-       static public void drawEllipse3D(Graphics g, Color color, int x, int y,
-                       int width, int height, boolean fill) {
-               if (g instanceof Graphics2D) {
-                       Graphics2D g2 = (Graphics2D) g;
-                       g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-                                       RenderingHints.VALUE_ANTIALIAS_ON);
-
-                       // Retains the previous state
-                       Paint oldPaint = g2.getPaint();
-
-                       // Base shape
-                       g2.setColor(color);
-                       if (fill) {
-                               g2.fillOval(x, y, width, height);
-                       } else {
-                               g2.drawOval(x, y, width, height);
-                       }
-
-                       // Compute dark/bright colours
-                       Paint p = null;
-                       Color dark = color.darker().darker();
-                       Color bright = color.brighter().brighter();
-                       Color darkEnd = new Color(dark.getRed(), dark.getGreen(),
-                                       dark.getBlue(), 0);
-                       Color darkPartial = new Color(dark.getRed(), dark.getGreen(),
-                                       dark.getBlue(), 64);
-                       Color brightEnd = new Color(bright.getRed(), bright.getGreen(),
-                                       bright.getBlue(), 0);
-
-                       // Adds shadows at the bottom left
-                       p = new GradientPaint(0, height, dark, width, 0, darkEnd);
-                       g2.setPaint(p);
-                       if (fill) {
-                               g2.fillOval(x, y, width, height);
-                       } else {
-                               g2.drawOval(x, y, width, height);
-                       }
-                       // Adds highlights at the top right
-                       p = new GradientPaint(width, 0, bright, 0, height, brightEnd);
-                       g2.setPaint(p);
-                       if (fill) {
-                               g2.fillOval(x, y, width, height);
-                       } else {
-                               g2.drawOval(x, y, width, height);
-                       }
-
-                       // Darken the edges
-                       p = new RadialGradientPaint(x + width / 2f, y + height / 2f,
-                                       Math.min(width / 2f, height / 2f), new float[] { 0f, 1f },
-                                       new Color[] { darkEnd, darkPartial },
-                                       RadialGradientPaint.CycleMethod.NO_CYCLE);
-                       g2.setPaint(p);
-                       if (fill) {
-                               g2.fillOval(x, y, width, height);
-                       } else {
-                               g2.drawOval(x, y, width, height);
-                       }
-
-                       // Adds inner highlight at the top right
-                       p = new RadialGradientPaint(x + 3f * width / 4f, y + height / 4f,
-                                       Math.min(width / 4f, height / 4f),
-                                       new float[] { 0.0f, 0.8f },
-                                       new Color[] { bright, brightEnd },
-                                       RadialGradientPaint.CycleMethod.NO_CYCLE);
-                       g2.setPaint(p);
-                       if (fill) {
-                               g2.fillOval(x * 2, y, width, height);
-                       } else {
-                               g2.drawOval(x * 2, y, width, height);
-                       }
-
-                       // Reset original paint
-                       g2.setPaint(oldPaint);
-               } else {
-                       g.setColor(color);
-                       if (fill) {
-                               g.fillOval(x, y, width, height);
-                       } else {
-                               g.drawOval(x, y, width, height);
-                       }
-               }
-       }
-
-       /**
-        * Add a {@link JScrollPane} around the given panel and use a sensible (for
-        * me) increment for the mouse wheel.
-        * 
-        * @param pane
-        *            the panel to wrap in a {@link JScrollPane}
-        * @param allowHorizontal
-        *            allow horizontal scrolling (not always desired)
-        * 
-        * @return the {@link JScrollPane}
-        */
-       static public JScrollPane scroll(JComponent pane, boolean allowHorizontal) {
-               return scroll(pane, allowHorizontal, true);
-       }
-
-       /**
-        * Add a {@link JScrollPane} around the given panel and use a sensible (for
-        * me) increment for the mouse wheel.
-        * 
-        * @param pane
-        *            the panel to wrap in a {@link JScrollPane}
-        * @param allowHorizontal
-        *            allow horizontal scrolling (not always desired)
-        * @param allowVertical
-        *            allow vertical scrolling (usually yes, but sometimes you only
-        *            want horizontal)
-        * 
-        * @return the {@link JScrollPane}
-        */
-       static public JScrollPane scroll(JComponent pane, boolean allowHorizontal,
-                       boolean allowVertical) {
-               JScrollPane scroll = new JScrollPane(pane);
-
-               scroll.getVerticalScrollBar().setUnitIncrement(16);
-               scroll.getHorizontalScrollBar().setUnitIncrement(16);
-
-               if (!allowHorizontal) {
-                       scroll.setHorizontalScrollBarPolicy(
-                                       JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
-               }
-               if (!allowVertical) {
-                       scroll.setVerticalScrollBarPolicy(
-                                       JScrollPane.VERTICAL_SCROLLBAR_NEVER);
-               }
-
-               return scroll;
-       }
-
-       /**
-        * Show a confirmation message to the user to show him the changes since
-        * last version.
-        * <p>
-        * HTML 3.2 supported, links included (the user browser will be launched if
-        * possible).
-        * <p>
-        * If this is already the latest version, a message will still be displayed.
-        * 
-        * @param parentComponent
-        *            determines the {@link java.awt.Frame} in which the dialog is
-        *            displayed; if <code>null</code>, or if the
-        *            <code>parentComponent</code> has no {@link java.awt.Frame}, a
-        *            default {@link java.awt.Frame} is used
-        * @param updates
-        *            the new version
-        * @param introText
-        *            an introduction text before the list of changes
-        * @param title
-        *            the title of the dialog
-        * 
-        * @return TRUE if the user clicked on OK, false if the dialog was dismissed
-        */
-       static public boolean showUpdatedDialog(Component parentComponent,
-                       VersionCheck updates, String introText, String title) {
-               
-               StringBuilder builder = new StringBuilder();
-               final JEditorPane updateMessage = new JEditorPane("text/html", "");
-               if (introText != null && !introText.isEmpty()) {
-                       builder.append(introText);
-                       builder.append("<br>");
-                       builder.append("<br>");
-               }
-               for (Version v : updates.getNewer()) {
-                       builder.append("\t<b>" //
-                                       + "Version " + v.toString() //
-                                       + "</b>");
-                       builder.append("<br>");
-                       builder.append("<ul>");
-                       for (String item : updates.getChanges().get(v)) {
-                               builder.append("<li>" + item + "</li>");
-                       }
-                       builder.append("</ul>");
-               }
-
-               // html content
-               updateMessage.setText("<html><body>" //
-                               + builder//
-                               + "</body></html>");
-
-               // handle link events
-               updateMessage.addHyperlinkListener(new HyperlinkListener() {
-                       @Override
-                       public void hyperlinkUpdate(HyperlinkEvent e) {
-                               if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED))
-                                       try {
-                                               Desktop.getDesktop().browse(e.getURL().toURI());
-                                       } catch (IOException ee) {
-                                               ee.printStackTrace();
-                                       } catch (URISyntaxException ee) {
-                                               ee.printStackTrace();
-                                       }
-                       }
-               });
-               updateMessage.setEditable(false);
-               updateMessage.setBackground(new JLabel().getBackground());
-               updateMessage.addHyperlinkListener(new HyperlinkListener() {
-                       @Override
-                       public void hyperlinkUpdate(HyperlinkEvent evn) {
-                               if (evn.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
-                                       if (Desktop.isDesktopSupported()) {
-                                               try {
-                                                       Desktop.getDesktop().browse(evn.getURL().toURI());
-                                               } catch (IOException e) {
-                                               } catch (URISyntaxException e) {
-                                               }
-                                       }
-                               }
-                       }
-               });
-
-               return JOptionPane.showConfirmDialog(parentComponent, updateMessage,
-                               title, JOptionPane.DEFAULT_OPTION) == JOptionPane.OK_OPTION;
-       }
-       
-       /**
-        * Set the given {@link JButton} as "pressed" (selected, but with more UI
-        * visibility).
-        * <p>
-        * The {@link JButton} will answer {@link JButton#isSelected()} if it is
-        * pressed.
-        * 
-        * @param button
-        *            the button to select/press
-        * @param pressed
-        *            the new "pressed" state
-        */
-       static public void setButtonPressed(JButton button, boolean pressed) {
-               if (buttonNormal == null) {
-                       JButton defButton = new JButton(" ");
-                       buttonNormal = defButton.getBackground();
-                       if (buttonNormal.getBlue() >= 128) {
-                               buttonPressed = new Color( //
-                                               Math.max(buttonNormal.getRed() - 100, 0), //
-                                               Math.max(buttonNormal.getGreen() - 100, 0), //
-                                               Math.max(buttonNormal.getBlue() - 100, 0));
-                       } else {
-                               buttonPressed = new Color( //
-                                               Math.min(buttonNormal.getRed() + 100, 255), //
-                                               Math.min(buttonNormal.getGreen() + 100, 255), //
-                                               Math.min(buttonNormal.getBlue() + 100, 255));
-                       }
-               }
-
-               button.setSelected(pressed);
-               button.setBackground(pressed ? buttonPressed : buttonNormal);
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/WaitingDialog.java b/src/be/nikiroo/utils/ui/WaitingDialog.java
deleted file mode 100644 (file)
index 0fd4574..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.Window;
-
-import javax.swing.JDialog;
-import javax.swing.JLabel;
-import javax.swing.SwingWorker;
-import javax.swing.border.EmptyBorder;
-
-import be.nikiroo.utils.Progress;
-import be.nikiroo.utils.Progress.ProgressListener;
-
-/**
- * A small waiting dialog that will show only if more than X milliseconds passed
- * before we dismiss it.
- * 
- * @author niki
- */
-public class WaitingDialog extends JDialog {
-       private static final long serialVersionUID = 1L;
-
-       private boolean waitScreen;
-       private Object waitLock = new Object();
-
-       private Progress pg;
-       private ProgressListener pgl;
-
-       /**
-        * Create a new {@link WaitingDialog}.
-        * 
-        * @param parent
-        *            the parent/owner of this {@link WaitingDialog}
-        * @param delayMs
-        *            the delay after which to show the dialog if it is still not
-        *            dismiss (see {@link WaitingDialog#dismiss()})
-        */
-       public WaitingDialog(Window parent, long delayMs) {
-               this(parent, delayMs, null, null);
-       }
-
-       /**
-        * Create a new {@link WaitingDialog}.
-        * 
-        * @param parent
-        *            the parent/owner of this {@link WaitingDialog}
-        * @param delayMs
-        *            the delay after which to show the dialog if it is still not
-        *            dismiss (see {@link WaitingDialog#dismiss()})
-        * @param pg
-        *            the {@link Progress} to listen on -- when it is
-        *            {@link Progress#done()}, this {@link WaitingDialog} will
-        *            automatically be dismissed as if
-        *            {@link WaitingDialog#dismiss()} was called
-        */
-       public WaitingDialog(Window parent, long delayMs, Progress pg) {
-               this(parent, delayMs, pg, null);
-       }
-
-       /**
-        * Create a new {@link WaitingDialog}.
-        * 
-        * @param parent
-        *            the parent/owner of this {@link WaitingDialog}
-        * @param delayMs
-        *            the delay after which to show the dialog if it is still not
-        *            dismiss (see {@link WaitingDialog#dismiss()})
-        * @param waitingText
-        *            a waiting text to display (note: you may want to subclass it
-        *            for nicer UI)
-        */
-       public WaitingDialog(Window parent, long delayMs, String waitingText) {
-               this(parent, delayMs, null, waitingText);
-       }
-
-       /**
-        * Create a new {@link WaitingDialog}.
-        * 
-        * @param parent
-        *            the parent/owner of this {@link WaitingDialog}
-        * @param delayMs
-        *            the delay after which to show the dialog if it is still not
-        *            dismiss (see {@link WaitingDialog#dismiss()})
-        * @param pg
-        *            the {@link Progress} to listen on -- when it is
-        *            {@link Progress#done()}, this {@link WaitingDialog} will
-        *            automatically be dismissed as if
-        *            {@link WaitingDialog#dismiss()} was called
-        * @param waitingText
-        *            a waiting text to display (note: you may want to subclass it
-        *            for nicer UI)
-        */
-       public WaitingDialog(Window parent, long delayMs, Progress pg,
-                       String waitingText) {
-               super(parent);
-
-               this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
-
-               this.pg = pg;
-
-               if (waitingText != null) {
-                       JLabel waitingTextLabel = new JLabel(waitingText);
-                       this.add(waitingTextLabel);
-                       waitingTextLabel.setBorder(new EmptyBorder(10, 10, 10, 10));
-                       this.pack();
-               }
-
-               if (pg != null) {
-                       pgl = new ProgressListener() {
-                               @Override
-                               public void progress(Progress progress, String name) {
-                                       if (WaitingDialog.this.pg.isDone()) {
-                                               // Must be done out of this thread (cannot remove a pgl
-                                               // from a running pgl)
-                                               new SwingWorker<Void, Void>() {
-                                                       @Override
-                                                       protected Void doInBackground() throws Exception {
-                                                               return null;
-                                                       }
-
-                                                       @Override
-                                                       public void done() {
-                                                               dismiss();
-                                                       }
-                                               }.execute();
-                                       }
-                               }
-                       };
-
-                       pg.addProgressListener(pgl);
-
-                       if (pg.isDone()) {
-                               dismiss();
-                               return;
-                       }
-               }
-
-               final long delay = delayMs;
-               new Thread(new Runnable() {
-                       @Override
-                       public void run() {
-                               try {
-                                       Thread.sleep(delay);
-                               } catch (InterruptedException e) {
-                               }
-
-                               synchronized (waitLock) {
-                                       if (!waitScreen) {
-                                               waitScreen = true;
-                                               setVisible(true);
-                                       }
-                               }
-                       }
-               }).start();
-       }
-
-       /**
-        * Notify this {@link WaitingDialog} that the job is done, and dismiss it if
-        * it was already showing on screen (or never show it if it was not).
-        * <p>
-        * Will also dispose the {@link WaitingDialog}.
-        */
-       public void dismiss() {
-               synchronized (waitLock) {
-                       if (waitScreen) {
-                               setVisible(false);
-                       }
-                       waitScreen = true;
-               }
-
-               if (pg != null) {
-                       pg.removeProgressListener(pgl);
-               }
-
-               dispose();
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/WrapLayout.java b/src/be/nikiroo/utils/ui/WrapLayout.java
deleted file mode 100644 (file)
index 7f34d79..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.Insets;
-
-import javax.swing.JScrollPane;
-import javax.swing.SwingUtilities;
-
-/**
- * FlowLayout subclass that fully supports wrapping of components.
- * 
- * @author https://tips4java.wordpress.com/2008/11/06/wrap-layout/
- */
-public class WrapLayout extends FlowLayout {
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * Constructs a new <code>WrapLayout</code> with a left alignment and a
-        * default 5-unit horizontal and vertical gap.
-        */
-       public WrapLayout() {
-               super();
-       }
-
-       /**
-        * Constructs a new <code>FlowLayout</code> with the specified alignment and
-        * a default 5-unit horizontal and vertical gap. The value of the alignment
-        * argument must be one of <code>WrapLayout</code>, <code>WrapLayout</code>,
-        * or <code>WrapLayout</code>.
-        * 
-        * @param align
-        *            the alignment value
-        */
-       public WrapLayout(int align) {
-               super(align);
-       }
-
-       /**
-        * Creates a new flow layout manager with the indicated alignment and the
-        * indicated horizontal and vertical gaps.
-        * <p>
-        * The value of the alignment argument must be one of
-        * <code>WrapLayout</code>, <code>WrapLayout</code>, or
-        * <code>WrapLayout</code>.
-        * 
-        * @param align
-        *            the alignment value
-        * @param hgap
-        *            the horizontal gap between components
-        * @param vgap
-        *            the vertical gap between components
-        */
-       public WrapLayout(int align, int hgap, int vgap) {
-               super(align, hgap, vgap);
-       }
-
-       /**
-        * Returns the preferred dimensions for this layout given the <i>visible</i>
-        * components in the specified target container.
-        * 
-        * @param target
-        *            the component which needs to be laid out
-        * @return the preferred dimensions to lay out the subcomponents of the
-        *         specified container
-        */
-       @Override
-       public Dimension preferredLayoutSize(Container target) {
-               return layoutSize(target, true);
-       }
-
-       /**
-        * Returns the minimum dimensions needed to layout the <i>visible</i>
-        * components contained in the specified target container.
-        * 
-        * @param target
-        *            the component which needs to be laid out
-        * @return the minimum dimensions to lay out the subcomponents of the
-        *         specified container
-        */
-       @Override
-       public Dimension minimumLayoutSize(Container target) {
-               Dimension minimum = layoutSize(target, false);
-               minimum.width -= (getHgap() + 1);
-               return minimum;
-       }
-
-       /**
-        * Returns the minimum or preferred dimension needed to layout the target
-        * container.
-        *
-        * @param target
-        *            target to get layout size for
-        * @param preferred
-        *            should preferred size be calculated
-        * @return the dimension to layout the target container
-        */
-       private Dimension layoutSize(Container target, boolean preferred) {
-               synchronized (target.getTreeLock()) {
-                       // Each row must fit with the width allocated to the containter.
-                       // When the container width = 0, the preferred width of the
-                       // container
-                       // has not yet been calculated so lets ask for the maximum.
-
-                       int targetWidth = target.getSize().width;
-                       Container container = target;
-
-                       while (container.getSize().width == 0
-                                       && container.getParent() != null) {
-                               container = container.getParent();
-                       }
-
-                       targetWidth = container.getSize().width;
-
-                       if (targetWidth == 0)
-                               targetWidth = Integer.MAX_VALUE;
-
-                       int hgap = getHgap();
-                       int vgap = getVgap();
-                       Insets insets = target.getInsets();
-                       int horizontalInsetsAndGap = insets.left + insets.right
-                                       + (hgap * 2);
-                       int maxWidth = targetWidth - horizontalInsetsAndGap;
-
-                       // Fit components into the allowed width
-
-                       Dimension dim = new Dimension(0, 0);
-                       int rowWidth = 0;
-                       int rowHeight = 0;
-
-                       int nmembers = target.getComponentCount();
-
-                       for (int i = 0; i < nmembers; i++) {
-                               Component m = target.getComponent(i);
-
-                               if (m.isVisible()) {
-                                       Dimension d = preferred ? m.getPreferredSize() : m
-                                                       .getMinimumSize();
-
-                                       // Can't add the component to current row. Start a new
-                                       // row.
-
-                                       if (rowWidth + d.width > maxWidth) {
-                                               addRow(dim, rowWidth, rowHeight);
-                                               rowWidth = 0;
-                                               rowHeight = 0;
-                                       }
-
-                                       // Add a horizontal gap for all components after the
-                                       // first
-
-                                       if (rowWidth != 0) {
-                                               rowWidth += hgap;
-                                       }
-
-                                       rowWidth += d.width;
-                                       rowHeight = Math.max(rowHeight, d.height);
-                               }
-                       }
-
-                       addRow(dim, rowWidth, rowHeight);
-
-                       dim.width += horizontalInsetsAndGap;
-                       dim.height += insets.top + insets.bottom + vgap * 2;
-
-                       // When using a scroll pane or the DecoratedLookAndFeel we need
-                       // to
-                       // make sure the preferred size is less than the size of the
-                       // target containter so shrinking the container size works
-                       // correctly. Removing the horizontal gap is an easy way to do
-                       // this.
-
-                       Container scrollPane = SwingUtilities.getAncestorOfClass(
-                                       JScrollPane.class, target);
-
-                       if (scrollPane != null && target.isValid()) {
-                               dim.width -= (hgap + 1);
-                       }
-
-                       return dim;
-               }
-       }
-
-       /*
-        * A new row has been completed. Use the dimensions of this row to update
-        * the preferred size for the container.
-        * 
-        * @param dim update the width and height when appropriate
-        * 
-        * @param rowWidth the width of the row to add
-        * 
-        * @param rowHeight the height of the row to add
-        */
-       private void addRow(Dimension dim, int rowWidth, int rowHeight) {
-               dim.width = Math.max(dim.width, rowWidth);
-
-               if (dim.height > 0) {
-                       dim.height += getVgap();
-               }
-
-               dim.height += rowHeight;
-       }
-}
\ No newline at end of file
diff --git a/src/be/nikiroo/utils/ui/ZoomBox.java b/src/be/nikiroo/utils/ui/ZoomBox.java
deleted file mode 100644 (file)
index a8f9609..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-package be.nikiroo.utils.ui;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.DefaultComboBoxModel;
-import javax.swing.Icon;
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-
-/**
- * A small panel that let you choose a zoom level or an actual zoom value (when
- * there is enough space to allow that).
- * 
- * @author niki
- */
-public class ZoomBox extends ListenerPanel {
-       private static final long serialVersionUID = 1L;
-
-       /** The event that is fired on zoom change. */
-       public static final String ZOOM_CHANGED = "zoom_changed";
-
-       private enum ZoomLevel {
-               FIT_TO_WIDTH(0, true), //
-               FIT_TO_HEIGHT(0, false), //
-               ACTUAL_SIZE(1, null), //
-               HALF_SIZE(0.5, null), //
-               DOUBLE_SIZE(2, null),//
-               ;
-
-               private final double zoom;
-               private final Boolean snapMode;
-
-               private ZoomLevel(double zoom, Boolean snapMode) {
-                       this.zoom = zoom;
-                       this.snapMode = snapMode;
-               }
-
-               public double getZoom() {
-                       return zoom;
-               }
-
-               public Boolean getSnapToWidth() {
-                       return snapMode;
-               }
-
-               /**
-                * Use default values that can be understood by a human.
-                */
-               @Override
-               public String toString() {
-                       switch (this) {
-                       case FIT_TO_WIDTH:
-                               return "Fit to width";
-                       case FIT_TO_HEIGHT:
-                               return "Fit to height";
-                       case ACTUAL_SIZE:
-                               return "Actual size";
-                       case HALF_SIZE:
-                               return "Half size";
-                       case DOUBLE_SIZE:
-                               return "Double size";
-                       }
-                       return super.toString();
-               }
-
-               static ZoomLevel[] values(boolean orderedSelection) {
-                       if (orderedSelection) {
-                               return new ZoomLevel[] { //
-                                               FIT_TO_WIDTH, //
-                                               FIT_TO_HEIGHT, //
-                                               ACTUAL_SIZE, //
-                                               HALF_SIZE, //
-                                               DOUBLE_SIZE,//
-                               };
-                       }
-
-                       return values();
-               }
-       }
-
-       private boolean vertical;
-       private boolean small;
-
-       private JButton zoomIn;
-       private JButton zoomOut;
-       private JButton snapWidth;
-       private JButton snapHeight;
-       private JLabel zoomLabel;
-
-       @SuppressWarnings("rawtypes") // JComboBox<?> is not java 1.6 compatible
-       private JComboBox zoombox;
-
-       private double zoom = 1;
-       private Boolean snapMode = true;
-
-       @SuppressWarnings("rawtypes") // JComboBox<?> not compatible java 1.6
-       private DefaultComboBoxModel zoomBoxModel;
-
-       /**
-        * Create a new {@link ZoomBox}.
-        */
-       @SuppressWarnings({ "unchecked", "rawtypes" }) // JComboBox<?> not
-                                                                                                       // compatible java 1.6
-       public ZoomBox() {
-               zoomIn = new JButton();
-               zoomIn.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               zoomIn(1);
-                       }
-               });
-
-               zoomBoxModel = new DefaultComboBoxModel(ZoomLevel.values(true));
-               zoombox = new JComboBox(zoomBoxModel);
-               zoombox.setEditable(true);
-               zoombox.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               Object selected = zoomBoxModel.getSelectedItem();
-
-                               if (selected == null) {
-                                       return;
-                               }
-
-                               if (selected instanceof ZoomLevel) {
-                                       ZoomLevel selectedZoomLevel = (ZoomLevel) selected;
-                                       setZoomSnapMode(selectedZoomLevel.getZoom(),
-                                                       selectedZoomLevel.getSnapToWidth());
-                               } else {
-                                       String selectedString = selected.toString();
-                                       selectedString = selectedString.trim();
-                                       if (selectedString.endsWith("%")) {
-                                               selectedString = selectedString
-                                                               .substring(0, selectedString.length() - 1)
-                                                               .trim();
-                                       }
-
-                                       try {
-                                               int pc = Integer.parseInt(selectedString);
-                                               if (pc <= 0) {
-                                                       throw new NumberFormatException("invalid");
-                                               }
-
-                                               setZoomSnapMode(pc / 100.0, null);
-                                       } catch (NumberFormatException nfe) {
-                                       }
-                               }
-
-                               fireActionPerformed(ZOOM_CHANGED);
-                       }
-               });
-
-               zoomOut = new JButton();
-               zoomOut.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               zoomOut(1);
-                       }
-               });
-
-               snapWidth = new JButton();
-               snapWidth.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               setSnapMode(true);
-                       }
-               });
-
-               snapHeight = new JButton();
-               snapHeight.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               setSnapMode(false);
-                       }
-               });
-
-               zoomLabel = new JLabel();
-               zoomLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 0));
-
-               setIcons(null, null, null, null);
-               setOrientation(vertical);
-       }
-
-       /**
-        * The zoom level.
-        * <p>
-        * It usually returns 1 (default value), the value you passed yourself or 1
-        * (a snap to width or snap to height was asked by the user).
-        * <p>
-        * Will cause a fire event if needed.
-        * 
-        * @param zoom
-        *            the zoom level
-        */
-       public void setZoom(double zoom) {
-               if (this.zoom != zoom) {
-                       doSetZoom(zoom);
-                       fireActionPerformed(ZOOM_CHANGED);
-               }
-       }
-
-       /**
-        * The snap mode (NULL means no snap mode, TRUE for snap to width, FALSE for
-        * snap to height).
-        * <p>
-        * Will cause a fire event if needed.
-        * 
-        * @param snapToWidth
-        *            the snap mode
-        */
-       public void setSnapMode(Boolean snapToWidth) {
-               if (this.snapMode != snapToWidth) {
-                       doSetSnapMode(snapToWidth);
-                       fireActionPerformed(ZOOM_CHANGED);
-               }
-       }
-
-       /**
-        * Set both {@link ZoomBox#setZoom(double)} and
-        * {@link ZoomBox#setSnapMode(Boolean)} but fire only one change event.
-        * <p>
-        * Will cause a fire event if needed.
-        * 
-        * @param zoom
-        *            the zoom level
-        * @param snapMode
-        *            the snap mode
-        */
-       public void setZoomSnapMode(double zoom, Boolean snapMode) {
-               if (this.zoom != zoom || this.snapMode != snapMode) {
-                       doSetZoom(zoom);
-                       doSetSnapMode(snapMode);
-                       fireActionPerformed(ZOOM_CHANGED);
-               }
-       }
-
-       /**
-        * The zoom level.
-        * <p>
-        * It usually returns 1 (default value), the value you passed yourself or 0
-        * (a snap to width or snap to height was asked by the user).
-        * 
-        * @return the zoom level
-        */
-       public double getZoom() {
-               return zoom;
-       }
-
-       /**
-        * The snap mode (NULL means no snap mode, TRUE for snap to width, FALSE for
-        * snap to height).
-        * 
-        * @return the snap mode
-        */
-       public Boolean getSnapMode() {
-               return snapMode;
-       }
-
-       /**
-        * Zoom in, by a certain amount in "steps".
-        * <p>
-        * Note that zoomIn(-1) is the same as zoomOut(1).
-        * 
-        * @param steps
-        *            the number of zoom steps to make, can be negative
-        */
-       public void zoomIn(int steps) {
-               // TODO: redo zoomIn/zoomOut correctly
-               if (steps < 0) {
-                       zoomOut(-steps);
-                       return;
-               }
-
-               double newZoom = zoom;
-               for (int i = 0; i < steps; i++) {
-                       newZoom = newZoom + (newZoom < 0.1 ? 0.01 : 0.1);
-                       if (newZoom > 0.1) {
-                               newZoom = Math.round(newZoom * 10.0) / 10.0; // snap to 10%
-                       } else {
-                               newZoom = Math.round(newZoom * 100.0) / 100.0; // snap to 1%
-                       }
-               }
-
-               setZoomSnapMode(newZoom, null);
-               fireActionPerformed(ZOOM_CHANGED);
-       }
-
-       /**
-        * Zoom out, by a certain amount in "steps".
-        * <p>
-        * Note that zoomOut(-1) is the same as zoomIn(1).
-        * 
-        * @param steps
-        *            the number of zoom steps to make, can be negative
-        */
-       public void zoomOut(int steps) {
-               if (steps < 0) {
-                       zoomIn(-steps);
-                       return;
-               }
-
-               double newZoom = zoom;
-               for (int i = 0; i < steps; i++) {
-                       newZoom = newZoom - (newZoom > 0.19 ? 0.1 : 0.01);
-                       if (newZoom < 0.01) {
-                               newZoom = 0.01;
-                               break;
-                       }
-
-                       if (newZoom > 0.1) {
-                               newZoom = Math.round(newZoom * 10.0) / 10.0; // snap to 10%
-                       } else {
-                               newZoom = Math.round(newZoom * 100.0) / 100.0; // snap to 1%
-                       }
-               }
-
-               setZoomSnapMode(newZoom, null);
-               fireActionPerformed(ZOOM_CHANGED);
-       }
-
-       /**
-        * Set icons for the buttons instead of square brackets.
-        * <p>
-        * Any NULL value will make the button use square brackets again.
-        * 
-        * @param zoomIn
-        *            the icon of the button "go to first page"
-        * @param zoomOut
-        *            the icon of the button "go to previous page"
-        * @param snapWidth
-        *            the icon of the button "go to next page"
-        * @param snapHeight
-        *            the icon of the button "go to last page"
-        */
-       public void setIcons(Icon zoomIn, Icon zoomOut, Icon snapWidth,
-                       Icon snapHeight) {
-               this.zoomIn.setIcon(zoomIn);
-               this.zoomIn.setText(zoomIn == null ? "+" : "");
-               this.zoomOut.setIcon(zoomOut);
-               this.zoomOut.setText(zoomOut == null ? "-" : "");
-               this.snapWidth.setIcon(snapWidth);
-               this.snapWidth.setText(snapWidth == null ? "W" : "");
-               this.snapHeight.setIcon(snapHeight);
-               this.snapHeight.setText(snapHeight == null ? "H" : "");
-       }
-
-       /**
-        * A smaller {@link ZoomBox} that uses buttons instead of a big combo box
-        * for the zoom modes.
-        * <p>
-        * Always small in vertical orientation.
-        * 
-        * @return TRUE if it is small
-        */
-       public boolean getSmall() {
-               return small;
-       }
-
-       /**
-        * A smaller {@link ZoomBox} that uses buttons instead of a big combo box
-        * for the zoom modes.
-        * <p>
-        * Always small in vertical orientation.
-        * 
-        * @param small
-        *            TRUE to set it small
-        * 
-        * @return TRUE if it changed something
-        */
-       public boolean setSmall(boolean small) {
-               return setUi(small, vertical);
-       }
-
-       /**
-        * The general orientation of the component.
-        * 
-        * @return TRUE for vertical orientation, FALSE for horisontal orientation
-        */
-       public boolean getOrientation() {
-               return vertical;
-       }
-
-       /**
-        * The general orientation of the component.
-        * 
-        * @param vertical
-        *            TRUE for vertical orientation, FALSE for horisontal
-        *            orientation
-        * 
-        * @return TRUE if it changed something
-        */
-       public boolean setOrientation(boolean vertical) {
-               return setUi(small, vertical);
-       }
-
-       /**
-        * Set the zoom level, no fire event.
-        * <p>
-        * It usually returns 1 (default value), the value you passed yourself or 0
-        * (a snap to width or snap to height was asked by the user).
-        * 
-        * @param zoom
-        *            the zoom level
-        */
-       private void doSetZoom(double zoom) {
-               if (zoom > 0) {
-                       String zoomStr = Integer.toString((int) Math.round(zoom * 100))
-                                       + " %";
-                       zoomLabel.setText(zoomStr);
-                       if (snapMode == null) {
-                               zoomBoxModel.setSelectedItem(zoomStr);
-                       }
-               }
-
-               this.zoom = zoom;
-       }
-
-       /**
-        * Set the snap mode, no fire event.
-        * 
-        * @param snapToWidth
-        *            the snap mode
-        */
-       private void doSetSnapMode(Boolean snapToWidth) {
-               if (snapToWidth == null) {
-                       String zoomStr = Integer.toString((int) Math.round(zoom * 100))
-                                       + " %";
-                       if (zoom > 0) {
-                               zoomBoxModel.setSelectedItem(zoomStr);
-                       }
-               } else {
-                       for (ZoomLevel level : ZoomLevel.values()) {
-                               if (level.getSnapToWidth() == snapToWidth) {
-                                       zoomBoxModel.setSelectedItem(level);
-                               }
-                       }
-               }
-
-               this.snapMode = snapToWidth;
-       }
-
-       private boolean setUi(boolean small, boolean vertical) {
-               if (getWidth() == 0 || this.small != small
-                               || this.vertical != vertical) {
-                       this.small = small;
-                       this.vertical = vertical;
-
-                       BoxLayout layout = new BoxLayout(this,
-                                       vertical ? BoxLayout.Y_AXIS : BoxLayout.X_AXIS);
-                       this.removeAll();
-                       setLayout(layout);
-
-                       if (vertical || small) {
-                               this.add(zoomIn);
-                               this.add(snapWidth);
-                               this.add(snapHeight);
-                               this.add(zoomOut);
-                               this.add(zoomLabel);
-                       } else {
-                               this.add(zoomIn);
-                               this.add(zoombox);
-                               this.add(zoomOut);
-                       }
-
-                       this.revalidate();
-                       this.repaint();
-
-                       return true;
-               }
-
-               return false;
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/clear-16x16.png b/src/be/nikiroo/utils/ui/clear-16x16.png
deleted file mode 100644 (file)
index 3ea2622..0000000
Binary files a/src/be/nikiroo/utils/ui/clear-16x16.png and /dev/null differ
diff --git a/src/be/nikiroo/utils/ui/compat/DefaultListModel6.java b/src/be/nikiroo/utils/ui/compat/DefaultListModel6.java
deleted file mode 100644 (file)
index 3f7552f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-package be.nikiroo.utils.ui.compat;
-
-import javax.swing.DefaultListModel;
-import javax.swing.JList;
-
-/**
- * Compatibility layer so I can at least get rid of the warnings of using
- * {@link JList} without a parameter (and still staying Java 1.6 compatible).
- * <p>
- * This class is merely a {@link DefaultListModel} that you can parametrise also
- * in Java 1.6.
- * 
- * @author niki
- *
- * @param <E>
- *            the type to use
- */
-@SuppressWarnings("rawtypes") // not compatible Java 1.6
-public class DefaultListModel6<E> extends DefaultListModel
-               implements ListModel6<E> {
-       private static final long serialVersionUID = 1L;
-}
diff --git a/src/be/nikiroo/utils/ui/compat/JList6.java b/src/be/nikiroo/utils/ui/compat/JList6.java
deleted file mode 100644 (file)
index a504abb..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-package be.nikiroo.utils.ui.compat;
-
-import javax.swing.JList;
-import javax.swing.ListCellRenderer;
-import javax.swing.ListModel;
-
-/**
- * Compatibility layer so I can at least get rid of the warnings of using
- * {@link JList} without a parameter (and still staying Java 1.6 compatible).
- * <p>
- * This class is merely a {@link JList} that you can parametrise also in Java
- * 1.6.
- * 
- * @author niki
- *
- * @param <E>
- *            the type to use
- */
-@SuppressWarnings({ "unchecked", "rawtypes" }) // not compatible Java 1.6
-public class JList6<E> extends JList {
-       private static final long serialVersionUID = 1L;
-
-       @Override
-       @Deprecated
-       /**
-        * @deprecated please use {@link JList6#setCellRenderer(ListCellRenderer6)}
-        *             instead
-        */
-       public void setCellRenderer(ListCellRenderer cellRenderer) {
-               super.setCellRenderer(cellRenderer);
-       }
-
-       /**
-        * Sets the delegate that is used to paint each cell in the list. The job of
-        * a cell renderer is discussed in detail in the <a href="#renderer">class
-        * level documentation</a>.
-        * <p>
-        * If the {@code prototypeCellValue} property is {@code non-null}, setting
-        * the cell renderer also causes the {@code fixedCellWidth} and
-        * {@code fixedCellHeight} properties to be re-calculated. Only one
-        * <code>PropertyChangeEvent</code> is generated however - for the
-        * <code>cellRenderer</code> property.
-        * <p>
-        * The default value of this property is provided by the {@code ListUI}
-        * delegate, i.e. by the look and feel implementation.
-        * <p>
-        * This is a JavaBeans bound property.
-        *
-        * @param cellRenderer
-        *            the <code>ListCellRenderer</code> that paints list cells
-        * @see #getCellRenderer
-        * @beaninfo bound: true attribute: visualUpdate true description: The
-        *           component used to draw the cells.
-        */
-       public void setCellRenderer(ListCellRenderer6<E> cellRenderer) {
-               super.setCellRenderer(cellRenderer);
-       }
-
-       @Override
-       @Deprecated
-       public void setModel(ListModel model) {
-               super.setModel(model);
-       }
-
-       /**
-        * Sets the model that represents the contents or "value" of the list,
-        * notifies property change listeners, and then clears the list's selection.
-        * <p>
-        * This is a JavaBeans bound property.
-        *
-        * @param model
-        *            the <code>ListModel</code> that provides the list of items for
-        *            display
-        * @exception IllegalArgumentException
-        *                if <code>model</code> is <code>null</code>
-        * @see #getModel
-        * @see #clearSelection
-        * @beaninfo bound: true attribute: visualUpdate true description: The
-        *           object that contains the data to be drawn by this JList.
-        */
-       public void setModel(ListModel6<E> model) {
-               super.setModel(model);
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/compat/ListCellRenderer6.java b/src/be/nikiroo/utils/ui/compat/ListCellRenderer6.java
deleted file mode 100644 (file)
index bc76e80..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-package be.nikiroo.utils.ui.compat;
-
-import java.awt.Component;
-
-import javax.swing.JList;
-import javax.swing.ListCellRenderer;
-import javax.swing.ListModel;
-import javax.swing.ListSelectionModel;
-
-/**
- * Compatibility layer so I can at least get rid of the warnings of using
- * {@link JList} without a parameter (and still staying Java 1.6 compatible).
- * <p>
- * This class is merely a {@link ListCellRenderer} that you can parametrise also
- * in Java 1.6.
- * 
- * @author niki
- *
- * @param <E>
- *            the type to use
- */
-@SuppressWarnings({ "unchecked", "rawtypes" }) // not compatible Java 1.6
-public abstract class ListCellRenderer6<E> implements ListCellRenderer {
-       @Override
-       @Deprecated
-       /**
-        * @deprecated please use@deprecated please use
-        *             {@link ListCellRenderer6#getListCellRendererComponent(JList6, Object, int, boolean, boolean)}
-        *             instead
-        *             {@link ListCellRenderer6#getListCellRendererComponent(JList6, Object, int, boolean, boolean)}
-        *             instead
-        */
-       public Component getListCellRendererComponent(JList list, Object value,
-                       int index, boolean isSelected, boolean cellHasFocus) {
-               return getListCellRendererComponent((JList6<E>) list, (E) value, index,
-                               isSelected, cellHasFocus);
-       }
-
-       /**
-        * Return a component that has been configured to display the specified
-        * value. That component's <code>paint</code> method is then called to
-        * "render" the cell. If it is necessary to compute the dimensions of a list
-        * because the list cells do not have a fixed size, this method is called to
-        * generate a component on which <code>getPreferredSize</code> can be
-        * invoked.
-        *
-        * @param list
-        *            The JList we're painting.
-        * @param value
-        *            The value returned by list.getModel().getElementAt(index).
-        * @param index
-        *            The cells index.
-        * @param isSelected
-        *            True if the specified cell was selected.
-        * @param cellHasFocus
-        *            True if the specified cell has the focus.
-        * @return A component whose paint() method will render the specified value.
-        *
-        * @see JList
-        * @see ListSelectionModel
-        * @see ListModel
-        */
-       public abstract Component getListCellRendererComponent(JList6<E> list,
-                       E value, int index, boolean isSelected, boolean cellHasFocus);
-}
diff --git a/src/be/nikiroo/utils/ui/compat/ListModel6.java b/src/be/nikiroo/utils/ui/compat/ListModel6.java
deleted file mode 100644 (file)
index 938da14..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-package be.nikiroo.utils.ui.compat;
-
-import javax.swing.JList;
-
-/**
- * Compatibility layer so I can at least get rid of the warnings of using
- * {@link JList} without a parameter (and still staying Java 1.6 compatible).
- * <p>
- * This class is merely a {@link javax.swing.ListModel} that you can parametrise
- * also in Java 1.6.
- * 
- * @author niki
- *
- * @param <E>
- *            the type to use
- */
-@SuppressWarnings("rawtypes") // not compatible Java 1.6
-public interface ListModel6<E> extends javax.swing.ListModel {
-}
diff --git a/src/be/nikiroo/utils/ui/search-16x16.png b/src/be/nikiroo/utils/ui/search-16x16.png
deleted file mode 100644 (file)
index 0164b31..0000000
Binary files a/src/be/nikiroo/utils/ui/search-16x16.png and /dev/null differ
diff --git a/src/be/nikiroo/utils/ui/test/ProgressBarManualTest.java b/src/be/nikiroo/utils/ui/test/ProgressBarManualTest.java
deleted file mode 100644 (file)
index b416cbc..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package be.nikiroo.utils.ui.test;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
-import javax.swing.JButton;
-import javax.swing.JFrame;
-
-import be.nikiroo.utils.Progress;
-import be.nikiroo.utils.ui.ProgressBar;
-
-public class ProgressBarManualTest extends JFrame {
-       private static final long serialVersionUID = 1L;
-       private int i = 0;
-
-       public ProgressBarManualTest() {
-               final ProgressBar bar = new ProgressBar();
-               final Progress pg = new Progress("name");
-               final Progress pg2 = new Progress("second level", 0, 2);
-               final Progress pg3 = new Progress("third level");
-
-               setLayout(new BorderLayout());
-               this.add(bar, BorderLayout.SOUTH);
-
-               final JButton b = new JButton("Set pg to 10%");
-               b.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               switch (i) {
-                               case 0:
-                                       pg.setProgress(10);
-                                       pg2.setProgress(0);
-                                       b.setText("Set pg to 20%");
-                                       break;
-                               case 1:
-                                       pg.setProgress(20);
-                                       b.setText("Add pg2 (0-2)");
-                                       break;
-                               case 2:
-                                       pg.addProgress(pg2, 80);
-                                       pg2.setProgress(0);
-                                       b.setText("Add pg3 (0-100)");
-                                       break;
-                               case 3:
-                                       pg2.addProgress(pg3, 2);
-                                       pg3.setProgress(0);
-                                       b.setText("Set pg3 to 10%");
-                                       break;
-                               case 4:
-                                       pg3.setProgress(10);
-                                       b.setText("Set pg3 to 20%");
-                                       break;
-                               case 5:
-                                       pg3.setProgress(20);
-                                       b.setText("Set pg3 to 60%");
-                                       break;
-                               case 6:
-                                       pg3.setProgress(60);
-                                       b.setText("Set pg3 to 100%");
-                                       break;
-                               case 7:
-                                       pg3.setProgress(100);
-                                       b.setText("[done]");
-                                       break;
-                               }
-
-                               i++;
-                       }
-               });
-               this.add(b, BorderLayout.CENTER);
-
-               setSize(800, 600);
-               setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-
-               bar.setProgress(pg);
-       }
-
-       public static void main(String[] args) {
-               new ProgressBarManualTest().setVisible(true);
-       }
-}
diff --git a/src/be/nikiroo/utils/ui/test/TestUI.java b/src/be/nikiroo/utils/ui/test/TestUI.java
deleted file mode 100644 (file)
index c260295..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-package be.nikiroo.utils.ui.test;
-
-public class TestUI {
-       // TODO: make a GUI tester
-       public TestUI() {
-               ProgressBarManualTest a = new ProgressBarManualTest();
-       }
-}