From: Niki Roo Date: Fri, 20 Sep 2019 12:02:11 +0000 (+0200) Subject: delete src before subtree X-Git-Url: http://git.nikiroo.be/?p=nikiroo-utils.git;a=commitdiff_plain;h=ad207feb2815e429ae32484bc6930990099f8ea4 delete src before subtree --- diff --git a/src/be/nikiroo/utils/Cache.java b/src/be/nikiroo/utils/Cache.java deleted file mode 100644 index 6233082..0000000 --- a/src/be/nikiroo/utils/Cache.java +++ /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. - *

- * 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. - *

- * 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. - *

- * 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 index 232b632..0000000 --- a/src/be/nikiroo/utils/CacheMemory.java +++ /dev/null @@ -1,109 +0,0 @@ -package be.nikiroo.utils; - -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 data = new HashMap(); - - /** - * Create a new {@link CacheMemory}. - */ - public CacheMemory() { - } - - @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(uniqueID, allowTooOld, stable); - } - - return null; - } - - @Override - public InputStream load(URL url, boolean allowTooOld, boolean stable) { - if (check(url, allowTooOld, stable)) { - return load(url, allowTooOld, stable); - } - - 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 "_/" + uniqueID; - } - - /** - * Return a key mapping to the given urm. - * - * @param url - * thr url - * - * @return the key - */ - private String getKey(URL url) { - return url.toString(); - } -} diff --git a/src/be/nikiroo/utils/CryptUtils.java b/src/be/nikiroo/utils/CryptUtils.java deleted file mode 100644 index 638f82f..0000000 --- a/src/be/nikiroo/utils/CryptUtils.java +++ /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. - *

- * It is multi-thread compatible, but beware: - *

- *

- * 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}. - *

- * Some part of the key will be used to generate a 128 bits key and - * initialize the {@link CryptUtils}; even NULL will generate something. - *

- * This is most probably not secure. Do not use if you actually care - * about security. - * - * @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. - *

- * The key must 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 must 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 was 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 NOT 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 index 0487933..0000000 --- a/src/be/nikiroo/utils/Downloader.java +++ /dev/null @@ -1,478 +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). - *

- * 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 (!) - */ - 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 (!) - * @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. - *

- * If TRUE, it will only check the cache if any. - *

- * Default is FALSE. - * - * @return TRUE if offline - */ - public boolean isOffline() { - return offline; - } - - /** - * This {@link Downloader} is forbidden to try and connect to the network. - *

- * If TRUE, it will only check the cache if any. - *

- * 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. - *

- * 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 cookiesValues, Map postParams, - Map 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 cookiesValues, Map postParams, - Map 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 cookiesValues, Map postParams, - Map 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 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 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 cookiesValues) - throws IOException { - URLConnection conn = url.openConnection(); - - String cookies = generateCookies(cookiesValues); - if (cookies != null && !cookies.isEmpty()) { - conn.setRequestProperty("Cookie", cookies); - } - - 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 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 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/IOUtils.java b/src/be/nikiroo/utils/IOUtils.java deleted file mode 100644 index e3837e1..0000000 --- a/src/be/nikiroo/utils/IOUtils.java +++ /dev/null @@ -1,476 +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 .zip 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. - *

- * 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 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. - *

- * 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. - *

- * 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 deltree(File target, List errorAcc) { - if (errorAcc == null) { - errorAcc = new ArrayList(); - } - - 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 given /-separated resource (from the binary root). - * - * @param name - * the resource name - * - * @return the opened resource if found, NLL if not - */ - public static InputStream openResource(String name) { - ClassLoader loader = IOUtils.class.getClassLoader(); - if (loader == null) { - loader = ClassLoader.getSystemClassLoader(); - } - - return loader.getResourceAsStream(name); - } - - /** - * Return a resetable {@link InputStream} from this stream, and reset it. - * - * @param in - * the input stream - * @return the resetable stream, which may 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 index 4518577..0000000 --- a/src/be/nikiroo/utils/Image.java +++ /dev/null @@ -1,281 +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. - * - *

- * 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}. - *

- * This {@link InputStream} will (always) be a new one, and you are - * responsible for it. - *

- * Note: take care that the {@link InputStream} must not live past - * the {@link Image} life time! - * - * @return the stream - * - * @throws IOException - * in case of I/O error - */ - public InputStream newInputStream() throws IOException { - return new MarkableFileInputStream(data); - } - - /** - * Read 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. - *

- * Note that even if you don't, the program will still try 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. - *

- * Caution: the directory will be owned by the system, all its files - * now belong to us (and will most probably be deleted). - *

- * 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 owned 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 index fb86929..0000000 --- a/src/be/nikiroo/utils/ImageUtils.java +++ /dev/null @@ -1,220 +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; - - /** - * Return the EXIF transformation flag of this image if any. - * - *

- * Note: this code has been found on internet; thank you anonymous coder. - *

- * - * @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/MarkableFileInputStream.java b/src/be/nikiroo/utils/MarkableFileInputStream.java deleted file mode 100644 index 3f28064..0000000 --- a/src/be/nikiroo/utils/MarkableFileInputStream.java +++ /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/Progress.java b/src/be/nikiroo/utils/Progress.java deleted file mode 100644 index dea6be3..0000000 --- a/src/be/nikiroo/utils/Progress.java +++ /dev/null @@ -1,433 +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. - * - * @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 Progress parent = null; - private Object lock = new Object(); - private String name; - private Map children; - private List 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(); - this.listeners = new ArrayList(); - setMinMax(min, max); - setProgress(min); - } - - /** - * The name of this {@link Progress} step. - * - * @return the name - */ - 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 < 0 or if min > 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 < 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 < 0 or if min > 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. - * - * @param pg - * the {@link Progress} to report as the progression emitter - * @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}. - * - * @return the children (Who will think of the children??) - */ - public List getChildren() { - synchronized (lock) { - return new ArrayList(children.keySet()); - } - } - - /** - * 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 this - * @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. - *

- * 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 entry : children.entrySet()) { - total += (entry.getValue() / (max - min)) - * entry.getKey().getRelativeProgress(); - } - - setRelativeProgress(pg, name, total); - } - } - }; - - synchronized (lock) { - progress.parent = this; - this.children.put(progress, weight); - progress.addProgressListener(progressListener); - } - } -} diff --git a/src/be/nikiroo/utils/Proxy.java b/src/be/nikiroo/utils/Proxy.java deleted file mode 100644 index 750b3ee..0000000 --- a/src/be/nikiroo/utils/Proxy.java +++ /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: - *

    - *
  • ((user(:pass)@)proxy:port)
  • - *
  • System proxy is noted :
  • - *
- * Some examples: - *
    - *
  • → do not use any proxy
  • - *
  • : → use the system proxy
  • - *
  • user@prox.com → use the proxy "prox.com" with default port - * and user "user"
  • - *
  • prox.com:8080 → use the proxy "prox.com" on port 8080
  • - *
  • user:pass@prox.com:8080 → use "prox.com" on port 8080 - * authenticated as "user" with password "pass"
  • - *
  • user:pass@: → use the system proxy authenticated as user - * "user" with password "pass"
  • - *
- * - * @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 index ed20291..0000000 --- a/src/be/nikiroo/utils/StringJustifier.java +++ /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 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 right(final String str, final int n) { - List result = new LinkedList(); - - /* - * Same as left(), but preceed each line with spaces to make it n chars - * long. - */ - List 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 center(final String str, final int n) { - List result = new LinkedList(); - - /* - * Same as left(), but preceed/succeed each line with spaces to make it - * n chars long. - */ - List 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 full(final String str, final int n) { - List result = new LinkedList(); - - /* - * 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 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 left(final String data, final int width, - boolean minTwoWords) { - List lines = new LinkedList(); - - 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 index b3c1071..0000000 --- a/src/be/nikiroo/utils/StringUtils.java +++ /dev/null @@ -1,1162 +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 size - */ - 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 size 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 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 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 justifyText(List 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 justifyText(List text, int width, - Alignment align) { - List result = new ArrayList(); - - // Content <-> Bullet spacing (null = no spacing) - List> lines = new ArrayList>(); - 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( - previous.toString(), previousItemBulletSpacing)); - previous.setLength(0); - previousItemBulletSpacing = itemBulletSpacing; - } else { - previous.append(' '); - } - } - - previous.append(current); - - } - - if (previous != null) { - lines.add(new AbstractMap.SimpleEntry(previous - .toString(), previousItemBulletSpacing)); - } - - for (Entry 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). - *

- * 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). - *

- * 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 - */ - 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. - *

- *

- * Examples: - *

    - *
  • 8 765 becomes "8 k"
  • - *
  • 998 765 becomes "998 k"
  • - *
  • 12 987 364 becomes "12 M"
  • - *
  • 5 534 333 221 becomes "5 G"
  • - *
- * - * @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. - *

- * Examples (assuming decimalPositions = 1): - *

    - *
  • 8 765 becomes "8.7 k"
  • - *
  • 998 765 becomes "998.7 k"
  • - *
  • 12 987 364 becomes "12.9 M"
  • - *
  • 5 534 333 221 becomes "5.5 G"
  • - *
- * - * @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. - *

- * Of course, the conversion to and from display form is lossy (example: - * 6870 to "6.5k" to 6500). - * - * @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. - *

- * Of course, the conversion to and from display form is lossy (example: - * 6870 to "6.5k" to 6500). - * - * @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 related: - // - - /** - * Check if this line ends as a complete line (ends with a "." or similar). - *

- * 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. - *

- * 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. - *

- * 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 index b54f0bc..0000000 --- a/src/be/nikiroo/utils/TempFiles.java +++ /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. - *

- * The whole repository will be deleted on close (if you fail to call it, - * the program will try to call it on JVM termination). - * - * @param name - * the instance name (will be part 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. - *

- * The whole repository will be deleted on close (if you fail to call it, - * the program will try to call it on JVM termination). - *

- * Be careful, this instance will own 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 part 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 part 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. - *

- * Note that creating 2 temporary directories with the same name will result - * in two different 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 index 0a09712..0000000 --- a/src/be/nikiroo/utils/TraceHandler.java +++ /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. - *

- * By default, will only be effective if {@link TraceHandler#traceLevel} is - * not 0. - *

- * 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. - *

- * 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 index 269edb6..0000000 --- a/src/be/nikiroo/utils/Version.java +++ /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 { - 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 MAJOR.MINOR.PATCH(-TAG(TAG_VERSION)). - * - * @param version - * the version (MAJOR.MINOR.PATCH, - * MAJOR.MINOR.PATCH-TAG or - * MAJOR.MINOR.PATCH-TAGVERSIONTAG) - */ - 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. - *

- * 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. - *

- * 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. - *

- * 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}. - *

- * Note that a tagged version is considered newer than a non-tagged version, - * but two tagged versions with different tags are not comparable. - *

- * 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}. - *

- * Note that a tagged version is considered newer than a non-tagged version, - * but two tagged versions with different tags are not comparable. - *

- * 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). - *

- * 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/android/ImageUtilsAndroid.java b/src/be/nikiroo/utils/android/ImageUtilsAndroid.java deleted file mode 100644 index c2e269c..0000000 --- a/src/be/nikiroo/utils/android/ImageUtilsAndroid.java +++ /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 index 2ded4e1..0000000 --- a/src/be/nikiroo/utils/android/test/TestAndroid.java +++ /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 index 1b7ab85..0000000 --- a/src/be/nikiroo/utils/main/bridge.java +++ /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 which is different (it will then use an empty encryption - * key)). - */ - KEY, - /** - * The encryption key for the output data (optional, but can also be - * empty which is different (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 index 9cc6f0c..0000000 --- a/src/be/nikiroo/utils/main/img2aa.java +++ /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) - *

- * See "--help". - * - * @param args - */ - public static void main(String[] args) { - Dimension size = null; - Mode mode = null; - boolean invert = false; - List inputs = new ArrayList(); - 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 index 2a83389..0000000 --- a/src/be/nikiroo/utils/main/justify.java +++ /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) - *

- *

    - *
  • mode: left, right, center or full justification (defaults to left)
  • - *
  • max width: the maximum width of a line, or "" for "no maximum" - * (defaults to "no maximum")
  • - *
- * - * @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 lines = new ArrayList(); - 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 index 0c57bf9..0000000 --- a/src/be/nikiroo/utils/resources/Bundle.java +++ /dev/null @@ -1,1286 +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.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. - *

- * It also sports a writable change map, and you can save back the - * {@link Bundle} to file with {@link Bundle#updateFile(String)}. - * - * @param - * the enum to use to get values out of this class - * - * @author niki - */ - -public class Bundle> { - /** The type of E. */ - protected Class 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 descriptionBundle; - - /** R/O map */ - private Map map; - /** R/W map */ - private Map 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 type, Enum name, - TransBundle descriptionBundle) { - this.type = type; - this.keyType = name; - this.descriptionBundle = descriptionBundle; - - this.map = new HashMap(); - this.changeMap = new HashMap(); - 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 not set AND - * there is no default value - * - * @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}. - *

- * 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 NULL 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}. - *

- * 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, or NULL if not found (not present in the - * resource file) - */ - public String getString(E id, String def, int item) { - String rep = getString(id.name(), null); - if (rep == null) { - try { - Meta meta = type.getDeclaredField(id.name()).getAnnotation( - Meta.class); - rep = meta.def(); - } catch (NoSuchFieldException e) { - } catch (SecurityException e) { - } - } - - if (rep == null || rep.isEmpty()) { - return def; - } - - if (item >= 0) { - List 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 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). - *

- * Will only accept suffixes that form an existing id. - *

- * 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). - *

- * Will only accept suffixes that form an existing id. - *

- * 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). - *

- * Will only accept suffixes that form an existing id. - *

- * 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, or NULL if not found (not present in the - * resource file) - */ - 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). - *

- * 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). - *

- * 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}. - *

- * 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}. - *

- * 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}. - *

- * 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}. - *

- * 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. - *

- * 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. - *

- * 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}. - *

- * 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}. - *

- * 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}. - *

- * 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. - *

- * The returned value is an ARGB value. - *

- * 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. - *

- * The returned value is an ARGB value. - *

- * 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. - *

- * The returned value is an ARGB value. - *

- * 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. - *

- * 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. - *

- * 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 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. - *

- * 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 getList(E id, List def) { - List 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. - *

- * 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 getList(E id, List 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 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 value, int item) { - setString(id, BundleHelper.fromList(value), item); - } - - /** - * Create/update the .properties file. - *

- * 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). - *

- * Will update the files in {@link Bundles#getDirectory()}; it MUST - * be set. - * - * @throws IOException - * in case of IO errors - */ - public void updateFile() throws IOException { - updateFile(Bundles.getDirectory()); - } - - /** - * Create/update the .properties file. - *

- * 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, MUST NOT 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. - *

- * 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). - *

- * Will delete the files in {@link Bundles#getDirectory()}; it MUST - * be set. - * - * @return TRUE if the file was deleted - */ - public boolean deleteFile() { - return deleteFile(Bundles.getDirectory()); - } - - /** - * Delete the .properties file. - *

- * 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, MUST NOT 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 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); - } - - /** - * Get the value for the given key if it exists in the internal map, or - * def if not. - * - * @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 def 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 .properties file. - * - * @return the name - */ - protected String getBundleDisplayName() { - return keyType.toString(); - } - - /** - * Write the header found in the configuration .properties 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. - *

- * 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. - *

- * 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) { - // Look into Bundles.getDirectory() for .properties files - try { - File file = getPropertyFile(dir, name.name(), locale); - if (file != null) { - Reader reader = new InputStreamReader(new FileInputStream( - file), "UTF-8"); - resetMap(new PropertyResourceBundle(reader)); - 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(); - for (Field field : type.getDeclaredFields()) { - try { - Meta meta = field.getAnnotation(Meta.class); - if (meta != null) { - E id = Enum.valueOf(type, field.getName()); - - String value; - if (bundle != null) { - value = bundle.getString(id.name()); - } else { - value = null; - } - - 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(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) 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 .properties) - * @param locale - * the {@link Locale} - * - * @return the closest match or NULL if none - */ - private File getPropertyFile(String dir, String name, Locale locale) { - List locales = new ArrayList(); - 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 index c6b26c7..0000000 --- a/src/be/nikiroo/utils/resources/BundleHelper.java +++ /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. - *

- * 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. - *

- * 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. - *

- * Note: null, "strange text", ""... will all be converted to NULL - * (remember: any {@link String} whose length is not 1 is not 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. - *

- * 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. - *

- * 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 list = parseList(raw, -1); - if (list == null) { - return -1; - } - - return list.size(); - } - - /** - * Return a {@link String} representation of the given list of values. - *

- * 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 parseList(String str, int item) { - if (str == null) { - return null; - } - - if (item >= 0) { - str = getItem(str, item); - } - - List list = new ArrayList(); - 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. - *

- * NULL will be assimilated to an empty {@link String} if later non-null - * values exist, or just ignored if not. - *

- * Example: - *

    - *
  • 1,NULL, 3 will become 1, - * "", 3
  • - *
  • 1,NULL, NULL will become 1
  • - *
  • NULL, NULL, NULL will become an empty list - *
  • - *
- * - * @param list - * the input value - * - * @return the raw {@link String} value that correspond to it - */ - static public String fromList(List list) { - if (list == null) { - list = new ArrayList(); - } - - 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. - *

- * NULL will be assimilated to an empty {@link String} if later non-null - * values exist, or just ignored if not. - *

- * Example: - *

    - *
  • 1,NULL, 3 will become 1, - * "", 3
  • - *
  • 1,NULL, NULL will become 1
  • - *
  • NULL, NULL, NULL will become an empty list - *
  • - *
- * - * @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 list, String value, int item) { - if (list == null) { - list = new ArrayList(); - } - - 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. - *

- * NULL will be assimilated to an empty {@link String} if later non-null - * values exist, or just ignored if not. - *

- * Example: - *

    - *
  • 1,NULL, 3 will become 1, - * "", 3
  • - *
  • 1,NULL, NULL will become 1
  • - *
  • NULL, NULL, NULL will become an empty list - *
  • - *
- * - * @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...). - *

- * 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). - *

- * 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 item if found, - * NULL if not - */ - static private String getItem(String value, int item) { - if (item >= 0) { - value = null; - List 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 index ad7b99d..0000000 --- a/src/be/nikiroo/utils/resources/Bundles.java +++ /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 .properties - * 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 .properties - * 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 .properties - * 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 index b53da9d..0000000 --- a/src/be/nikiroo/utils/resources/FixedResourceBundleControl.java +++ /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 index 8ed74dc..0000000 --- a/src/be/nikiroo/utils/resources/Meta.java +++ /dev/null @@ -1,122 +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). - *

- * 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...). - *

- * 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 is only used as a group, not as an option. - *

- * 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. - *

- * The list items are separated by a comma, each surrounded by - * double-quotes, with backslashes and double-quotes escaped by a backslash. - *

- * Example: "un", "deux" - * - * @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 index f7598f1..0000000 --- a/src/be/nikiroo/utils/resources/MetaInfo.java +++ /dev/null @@ -1,747 +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 - * the type of {@link Bundle} to edit - */ -public class MetaInfo> implements Iterable> { - private final Bundle bundle; - private final E id; - - private Meta meta; - private List> children = new ArrayList>(); - - private String value; - private List reloadedListeners = new ArrayList(); - private List saveListeners = new ArrayList(); - - private String name; - private String description; - - private boolean dirty; - - /** - * Create a new {@link MetaInfo} from a value (without children). - *

- * For instance, you can call - * new MetaInfo(Config.class, configBundle, Config.MY_VALUE). - * - * @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 type, Bundle 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.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). - *

- * 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). - *

- * Never NULL. - * - * - * @return the name, never NULL - */ - public String getName() { - return name; - } - - /** - * 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...). - *

- * For group, the first line ('\\n'-separated) will be used as a title while - * the rest will be the description. - *

- * If a default value is known, it will be specified here, too. - *

- * 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}. - *

- * 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. - *

- * 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 getKnownLanguages() { - if (bundle instanceof TransBundle) { - return ((TransBundle) bundle).getKnownLanguages(); - } - - return new ArrayList(); - } - - /** - * This item is a comma-separated list of values instead of a single value. - *

- * The list items are separated by a comma, each surrounded by - * double-quotes, with backslashes and double-quotes escaped by a backslash. - *

- * Example: "un", "deux" - * - * @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. - *

- * 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 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 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. - *

- * 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. - *

- * 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. - *

- * 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 getList(int item, boolean useDefaultIfEmpty) { - return BundleHelper.parseList(getString(item, useDefaultIfEmpty), -1); - } - - /** - * A {@link String} representation of the default list of values. - *

- * 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 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. - *

- * 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. - *

- * 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 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(reloadedListeners)) { - try { - listener.run(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - /** - * Add a listener that will be called after a reload operation. - *

- * 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}. - *

- * Note that listeners will be called before the dirty check and - * before 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(saveListeners)) { - try { - listener.run(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (!onlyIfDirty || isDirty()) { - bundle.setString(id, value); - } - } - - /** - * Add a listener that will be called before a save operation. - *

- * 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). - *

- * Sub-items are declared when a {@link Meta} has an ID that starts with the - * ID of a {@link Meta#group()} {@link MetaInfo}. - *

- * For instance: - *

    - *
  • {@link Meta} MY_PREFIX is a {@link Meta#group()}
  • - *
  • {@link Meta} MY_PREFIX_DESCRIPTION is another {@link Meta}
  • - *
  • MY_PREFIX_DESCRIPTION will be a child of MY_PREFIX
  • - *
- * - * @return the sub-items if any - */ - public List> getChildren() { - return children; - } - - @Override - public Iterator> iterator() { - return children.iterator(); - } - - /** - * Create a list of {@link MetaInfo}, one for each of the item in the given - * {@link Bundle}. - * - * @param - * 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 > List> getItems(Class type, - Bundle bundle) { - List> list = new ArrayList>(); - List> shadow = new ArrayList>(); - for (E id : type.getEnumConstants()) { - MetaInfo info = new MetaInfo(type, bundle, id); - list.add(info); - shadow.add(info); - } - - for (int i = 0; i < list.size(); i++) { - MetaInfo info = list.get(i); - - MetaInfo 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: - *
    - *
  • the parent is a {@link Meta#group()}
  • - *
  • the parent Id is a substring of the Id of the given {@link MetaInfo}
  • - *
  • there is no other parent sharing a substring for this - * {@link MetaInfo} with a longer Id
  • - *
- * - * @param - * 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 > MetaInfo findParent(MetaInfo info, - List> candidates) { - String id = info.id.toString(); - MetaInfo group = null; - for (MetaInfo 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 > 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 index 28fa280..0000000 --- a/src/be/nikiroo/utils/resources/TransBundle.java +++ /dev/null @@ -1,398 +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. - *

- * Two special cases are handled for the used enum: - *

    - *
  • NULL will always will return an empty {@link String}
  • - *
  • DUMMY will return "[DUMMY]" (maybe with a suffix and/or "NOUTF")
  • - *
- * - * @param - * the enum to use to get values out of this class - * - * @author niki - */ -public class TransBundle> extends Bundle { - 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 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 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 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); - } 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 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. - *

- * Note: this method is NOT 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); - 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 getKnownLanguages(Enum name) { - List resources = new LinkedList(); - - 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 index 9983b8b..0000000 --- a/src/be/nikiroo/utils/resources/TransBundle_ResourceList.java +++ /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 getResources(final Pattern pattern) { - final ArrayList retval = new ArrayList(); - 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 getResources(final String element, - final Pattern pattern) { - final ArrayList retval = new ArrayList(); - 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 getResourcesFromJarFile(final File file, - final Pattern pattern) { - final ArrayList retval = new ArrayList(); - 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 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 getResourcesFromDirectory( - final File directory, final Pattern pattern) { - List acc = new ArrayList(); - List dirs = new ArrayList(); - getResourcesFromDirectory(acc, dirs, directory, pattern); - - List rep = new ArrayList(); - for (String value : acc) { - if (pattern.matcher(value).matches()) { - rep.add(value); - } - } - - return rep; - } - - private static void getResourcesFromDirectory(List acc, - List 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 index bda940b..0000000 --- a/src/be/nikiroo/utils/resources/package-info.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * This package encloses the classes needed to use - * {@link be.nikiroo.utils.resources.Bundle}s - *

- * Those are basically a .properties resource linked to an enumeration - * listing all the fields you can use. The classes can also be used to update - * the linked .properties files (or export them, which is useful when - * you work from a JAR file). - *

- * 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 index e58ccf2..0000000 --- a/src/be/nikiroo/utils/serial/CustomSerializer.java +++ /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: - *

    - *
  • custom^TYPE^ENCODED_VALUE
  • - *
- *

- * In this scheme, the values are: - *

    - *
  • custom: a fixed keyword
  • - *
  • ^: a fixed separator character (the - * ENCODED_VALUE can still use it inside its content, though
  • - *
  • TYPE: the object type of this value
  • - *
  • ENCODED_VALUE: the custom encoded value
  • - *
- *

- * 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 ENCODED_VALUE from this - * value. - *

- * The value 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 ENCODED_VALUE. - *

- * The value in the {@link InputStream} in will always be of the - * supported type. - * - * @param in - * the {@link InputStream} containing the - * ENCODED_VALUE - * - * @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. - *

- * It must 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 - * ENCODED_VALUE part. - *

- * 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. - *

- * We do not expect the full content here but only: - *

    - *
  • ENCODED_VALUE - *
  • - *
- * That is, we do not expect the "custom^TYPE^" - * 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 index 2470bde..0000000 --- a/src/be/nikiroo/utils/serial/Exporter.java +++ /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}. - *

- * This class does not support inner classes (it does support nested classes, - * though). - * - * @author niki - */ -public class Exporter { - private Map 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(); - } - - /** - * Serialise the given object and add it to the list. - *

- * Important: 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 index 81814df..0000000 --- a/src/be/nikiroo/utils/serial/Importer.java +++ /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. - *

- * 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 map; - - private String currentFieldName; - - /** - * Create a new {@link Importer}. - */ - public Importer() { - map = new HashMap(); - map.put("NULL", null); - } - - private Importer(Map 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 MUST 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 index ad3b5d4..0000000 --- a/src/be/nikiroo/utils/serial/SerialUtils.java +++ /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. - *

- * Note that we do not support inner classes (but we do support nested classes) - * and all objects require an empty constructor to be deserialised. - *

- * It is possible to add support to custom types (both the encoder and the - * decoder will require the custom classes) -- see {@link CustomSerializer}. - *

- * Default supported types are: - *

    - *
  • NULL (as a null value)
  • - *
  • String
  • - *
  • Boolean
  • - *
  • Byte
  • - *
  • Character
  • - *
  • Short
  • - *
  • Long
  • - *
  • Float
  • - *
  • Double
  • - *
  • Integer
  • - *
  • Enum (any enum whose name and value is known by the caller)
  • - *
  • java.awt.image.BufferedImage (as a {@link CustomSerializer})
  • - *
  • An array of the above (as a {@link CustomSerializer})
  • - *
  • URL
  • - *
- * - * @author niki - */ -public class SerialUtils { - private static Map customTypes; - - static { - customTypes = new HashMap(); - - // 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 list = new ArrayList(); - 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 args = new ArrayList(); - List> classes = new ArrayList>(); - 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}. - *

- * Important: 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 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. - *

- * 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 compound objects are not supported here. - *

- * 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. - *

- * A supported object in this context means an object we can directly - * encode, like an Integer or a String (see - * {@link SerialUtils#decode(String)}. - *

- * Custom objects and arrays are also considered supported here, but - * compound objects are not. - *

- * 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. - *

- * A supported object in this context means an object we can directly - * encode, like an Integer or a String. - *

- * For custom objects and arrays, you should use - * {@link SerialUtils#decode(InputStream)} or directly {@link Importer}. - *

- * 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) 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 index 6a19368..0000000 --- a/src/be/nikiroo/utils/serial/server/ConnectAction.java +++ /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. - *

- * 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. - *

- * Thus, it is only called on the server. - *

- * 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 server) - */ - 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. - *

- * Also used internally for the client (only do something if there is - * contentToSend). - *

- * 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. - *

- * Also used internally for the client (only do something if there is - * contentToSend). - *

- * 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. - *

- * Also used internally for the client (only do something if there is - * contentToSend). - *

- * Will only flush the data if there is contentToSend. - *

- * Note that the behaviour is slightly different for String and Object - * reading regarding exceptions: - *

    - *
  • NULL means that the counter part has no more data to send
  • - *
  • All the exceptions except {@link IOException} are there for Object - * conversion
  • - *
- * - * @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 index cb6bef3..0000000 --- a/src/be/nikiroo/utils/serial/server/ConnectActionClient.java +++ /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. - *

- * It represents a single action: a client is expected to only execute one - * action. - * - * @author niki - */ -abstract class ConnectActionClient { - /** - * The underlying {@link ConnectAction}. - *

- * 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. - *

- * 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 index 9385645..0000000 --- a/src/be/nikiroo/utils/serial/server/ConnectActionClientObject.java +++ /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. - *

- * 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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 index 3005cee..0000000 --- a/src/be/nikiroo/utils/serial/server/ConnectActionClientString.java +++ /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. - *

- * 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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 index 350d3fe..0000000 --- a/src/be/nikiroo/utils/serial/server/ConnectActionServer.java +++ /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. - *

- * 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}. - *

- * 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). - *

- * 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). - *

- * 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. - *

- * 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. - *

- * 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 index 07d9867..0000000 --- a/src/be/nikiroo/utils/serial/server/ConnectActionServerObject.java +++ /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. - *

- * 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 index 8d113c1..0000000 --- a/src/be/nikiroo/utils/serial/server/ConnectActionServerString.java +++ /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. - *

- * 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 index 0470159..0000000 --- a/src/be/nikiroo/utils/serial/server/Server.java +++ /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. - *

- * 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. - *

- * 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. - *

- * 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). - *

- * Can only be called once. - *

- * 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). - *

- * Can only be called once. - *

- * 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. - *

- * 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. - *

- * 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 index 0b734c6..0000000 --- a/src/be/nikiroo/utils/serial/server/ServerBridge.java +++ /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. - *

- * It can, of course, inspect the data that goes through it (by default, it - * prints traces of the data). - *

- * 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}. - *

- * The trace levels are handled as follow: - *

    - *
  • 1: it will only print basic IN/OUT messages with length
  • - *
  • 2: it will try to interpret it as an object (SLOW) and print the - * object class if possible
  • - *
  • 3: it will try to print the {@link Object#toString()} value, or the - * data if it is not an object
  • - *
  • 4: it will also print the unzipped serialised value if it is an - * object
  • - *
- * - * @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 index a6a5dd1..0000000 --- a/src/be/nikiroo/utils/serial/server/ServerObject.java +++ /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. - *

- * 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. - *

- * 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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 index 3c982fd..0000000 --- a/src/be/nikiroo/utils/serial/server/ServerString.java +++ /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. - *

- * 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. - *

- * 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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. - *

- * Please use the version with key encryption (this deprecated - * version uses an empty key when ssl is TRUE and no - * key (NULL) when ssl 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 index d54794b..0000000 --- a/src/be/nikiroo/utils/streams/Base64.java +++ /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 2045 and 3548. - */ -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. - * - *

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. - * - *

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. - * - *

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 - * RFC 2045). - */ - 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 index a3afaef..0000000 --- a/src/be/nikiroo/utils/streams/Base64InputStream.java +++ /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 index ab4e457..0000000 --- a/src/be/nikiroo/utils/streams/Base64OutputStream.java +++ /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 index 683fa55..0000000 --- a/src/be/nikiroo/utils/streams/BufferedInputStream.java +++ /dev/null @@ -1,522 +0,0 @@ -package be.nikiroo.utils.streams; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; - -import be.nikiroo.utils.StringUtils; - -/** - * A simple {@link InputStream} that is buffered with a bytes array. - *

- * 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). - *

- * A second buffer of twice the size can sometimes be created as needed for - * the {@link BufferedInputStream#startsWith(byte[])} search operation. - */ - 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; - - // special use, prefetched next buffer - private byte[] buffer2; - private int pos2; - private int len2; - private byte[] originalBuffer; - - 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.originalBuffer = this.buffer; - this.start = 0; - this.stop = 0; - } - - /** - * 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.originalBuffer = this.buffer; - this.start = offset; - this.stop = length; - } - - /** - * The internal buffer size (can be useful to know for search methods). - * - * @return the size of the internal buffer, in bytes. - */ - public int getInternalBufferSize() { - return originalBuffer.length; - } - - /** - * 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. - *

- * You are now responsible for it — you must close it. - *

- * 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. - *

- * Note: the search term size must 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. - *

- * Note: the search term size must 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 (stop - start) == search.length; - } - - return false; - } - - /** - * Check if the current content (what will be read next) starts with the - * given search term. - *

- * Note: the search term size must 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. - *

- * An empty string will always return true (unless the stream is closed, - * which would throw an {@link IOException}). - *

- * Note: the search term size must 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 { - if (search.length > originalBuffer.length) { - throw new IOException( - "This stream does not support searching for more than " - + buffer.length + " bytes"); - } - - checkClose(); - - if (available() < search.length) { - preRead(); - } - - if (available() >= search.length) { - // Easy path - return StreamUtils.startsWith(search, buffer, start, stop); - } else if (in != null && !eof) { - // Harder path - if (buffer2 == null && buffer.length == originalBuffer.length) { - buffer2 = Arrays.copyOf(buffer, buffer.length * 2); - - pos2 = buffer.length; - len2 = read(in, buffer2, pos2, buffer.length); - if (len2 > 0) { - bytesRead += len2; - } - - // Note: here, len/len2 = INDEX of last good byte - len2 += pos2; - } - - return StreamUtils.startsWith(search, buffer2, pos2, len2); - } - - return false; - } - - /** - * 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 { - checkClose(); - - preRead(); - if (eof) { - return -1; - } - - return buffer[start++]; - } - - @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; - } - - 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 (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; - } - - return Math.max(0, stop - start); - } - - /** - * Closes this stream and releases any system resources associated with the - * stream. - *

- * Including the under-laying {@link InputStream}. - *

- * Note: 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. - *

- * Including the under-laying {@link InputStream} if - * incudingSubStream is true. - *

- * You can call this method multiple times, it will not cause an - * {@link IOException} for subsequent calls. - *

- * Note: 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(); - } - } - } - } - - /** - * 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; - if (buffer2 != null) { - buffer = buffer2; - start = pos2; - stop = len2; - - buffer2 = null; - pos2 = 0; - len2 = 0; - } else { - buffer = originalBuffer; - - stop = read(in, buffer, 0, buffer.length); - if (stop > 0) { - bytesRead += stop; - } - } - - hasRead = true; - } - - if (start >= stop) { - eof = true; - } - - return hasRead; - } - - /** - * Read the under-laying stream into the local buffer. - * - * @param in - * the under-laying {@link InputStream} - * @param buffer - * the buffer we use in this {@link BufferedInputStream} - * @param off - * the offset - * @param len - * the length in bytes - * - * @return the number of bytes read - * - * @throws IOException - * in case of I/O error - */ - protected int read(InputStream in, byte[] buffer, int off, int len) - throws IOException { - return in.read(buffer, off, len); - } - - /** - * We have more data available in the buffer or we can, maybe, fetch - * more. - * - * @return TRUE if it is the case, FALSE if not - */ - protected boolean hasMoreData() { - if (closed) { - return false; - } - - return (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 index 1442534..0000000 --- a/src/be/nikiroo/utils/streams/BufferedOutputStream.java +++ /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. - *

- * 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 > the internal buffer - * size). - *

- * 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. - *

- * You are now responsible for it — you must close it. - *

- * 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. - *

- * If {@link BufferedOutputStream#bypassFlush} is false, all writes to the - * under-laying stream are done in this method. - *

- * 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. - *

- * Including the under-laying {@link InputStream}. - *

- * Note: 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. - *

- * Including the under-laying {@link InputStream} if - * incudingSubStream is true. - *

- * You can call this method multiple times, it will not cause an - * {@link IOException} for subsequent calls. - *

- * Note: 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 index 7622b24..0000000 --- a/src/be/nikiroo/utils/streams/MarkableFileInputStream.java +++ /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 index dcab472..0000000 --- a/src/be/nikiroo/utils/streams/NextableInputStream.java +++ /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). - *

- * 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. - *

- * It can only be called when the "current" stream is spent (i.e., you must - * first process the stream until it is spent). - *

- * {@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. - *

- * 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. - *

- * That is, the next stream, if any, will be the last one and will not be - * subject to the {@link NextableInputStreamStep}. - *

- * 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). - *

- * 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. - *

- * 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()}. - *

- * 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. - *

- * Do not 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 index fda998d..0000000 --- a/src/be/nikiroo/utils/streams/NextableInputStreamStep.java +++ /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. - *

- * 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. - *

- * If we do, return the index at which to stop; if not, return -1. - *

- * This method will not 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 - *

- * 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 index 1cc5139..0000000 --- a/src/be/nikiroo/utils/streams/ReplaceInputStream.java +++ /dev/null @@ -1,162 +0,0 @@ -package be.nikiroo.utils.streams; - -import java.io.IOException; -import java.io.InputStream; - -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 > 2048 bytes — in that case the - * buffer will be twice the largest size of the 'FROM' bytes arrays). - *

- * 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 maxFromSize; - private int maxToSize; - - private byte[] source; - private int spos; - private int slen; - - /** - * Create a {@link ReplaceInputStream} that will replace from with - * to. - * - * @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 from with - * to. - * - * @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 froms - * with tos. - *

- * Note that they will be replaced in order, and that for each from - * a to 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 froms - * with tos. - *

- * Note that they will be replaced in order, and that for each from - * a to 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); - } - - 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 - source = new byte[Math.max(2 * maxFromSize, MIN_BUFFER_SIZE)]; - spos = 0; - slen = 0; - } - - @Override - protected int read(InputStream in, byte[] buffer, int off, int len) - throws IOException { - if (len < maxToSize || source.length < maxToSize * 2) { - throw new IOException( - "An underlaying buffer is too small for these replace values"); - } - - // We need at least one byte of data to process - if (available() < Math.max(maxFromSize, 1) && !eof) { - spos = 0; - slen = in.read(source); - } - - // Note: very simple, not efficient implementation; sorry. - int count = 0; - while (spos < slen && count < len - maxToSize) { - boolean replaced = false; - for (int i = 0; i < froms.length; i++) { - if (froms[i] != null && froms[i].length > 0 - && StreamUtils.startsWith(froms[i], source, spos, slen)) { - if (tos[i] != null && tos[i].length > 0) { - System.arraycopy(tos[i], 0, buffer, off + spos, - tos[i].length); - count += tos[i].length; - } - - spos += froms[i].length; - replaced = true; - break; - } - } - - if (!replaced) { - buffer[off + count++] = source[spos++]; - } - } - - return count; - } -} diff --git a/src/be/nikiroo/utils/streams/ReplaceOutputStream.java b/src/be/nikiroo/utils/streams/ReplaceOutputStream.java deleted file mode 100644 index c6679cc..0000000 --- a/src/be/nikiroo/utils/streams/ReplaceOutputStream.java +++ /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 from with - * to. - * - * @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 from with - * to. - * - * @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 froms - * with tos. - *

- * Note that they will be replaced in order, and that for each from - * a to 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 froms - * with tos. - *

- * Note that they will be replaced in order, and that for each from - * a to 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. - *

- * If {@link BufferedOutputStream#bypassFlush} is false, all writes to the - * under-laying stream are done in this method. - *

- * 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). - *

- * But be careful! 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 index dc75090..0000000 --- a/src/be/nikiroo/utils/streams/StreamUtils.java +++ /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). - *

- * Note: the parameter stop is the index of the last - * position, not the length. - *

- * Note: the search term size must 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 not a - * length, but an index) - * - * @return TRUE if the search content is present at the given location and - * does not exceed the len 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 index fe7b9af..0000000 --- a/src/be/nikiroo/utils/test/TestCase.java +++ /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> skipCompare) throws AssertException { - assertEquals(errorMessage, expected, actual, skipCompare, null); - } - - private void assertEquals(String errorMessage, File expected, File actual, - Map> 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 expectedFiles = Arrays.asList(expected.list()); - Collections.sort(expectedFiles); - List 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 expectedLines = Arrays.asList(IOUtils - .readSmallFile(expected).split("\n")); - List 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> 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 index 895b565..0000000 --- a/src/be/nikiroo/utils/test/TestLauncher.java +++ /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 series; - private List 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(); - tests = new ArrayList(); - 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/test_code/BufferedInputStreamTest.java b/src/be/nikiroo/utils/test_code/BufferedInputStreamTest.java deleted file mode 100644 index c715585..0000000 --- a/src/be/nikiroo/utils/test_code/BufferedInputStreamTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import be.nikiroo.utils.IOUtils; -import be.nikiroo.utils.streams.BufferedInputStream; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class BufferedInputStreamTest extends TestLauncher { - public BufferedInputStreamTest(String[] args) { - super("BufferedInputStream test", args); - - addTest(new TestCase("Simple InputStream reading") { - @Override - public void test() throws Exception { - byte[] expected = new byte[] { 42, 12, 0, 127 }; - BufferedInputStream in = new BufferedInputStream( - new ByteArrayInputStream(expected)); - checkArrays(this, "FIRST", in, expected); - } - }); - - addTest(new TestCase("Simple byte array reading") { - @Override - public void test() throws Exception { - byte[] expected = new byte[] { 42, 12, 0, 127 }; - BufferedInputStream in = new BufferedInputStream(expected); - checkArrays(this, "FIRST", in, expected); - } - }); - - addTest(new TestCase("Byte array is(byte[])") { - @Override - public void test() throws Exception { - byte[] expected = new byte[] { 42, 12, 0, 127 }; - BufferedInputStream in = new BufferedInputStream(expected); - assertEquals( - "The array should be considered identical to its source", - true, in.is(expected)); - assertEquals( - "The array should be considered different to that one", - false, in.is(new byte[] { 42, 12, 0, 121 })); - in.close(); - } - }); - - addTest(new TestCase("InputStream is(byte[])") { - @Override - public void test() throws Exception { - byte[] expected = new byte[] { 42, 12, 0, 127 }; - BufferedInputStream in = new BufferedInputStream( - new ByteArrayInputStream(expected)); - assertEquals( - "The array should be considered identical to its source", - true, in.is(expected)); - assertEquals( - "The array should be considered different to that one", - false, in.is(new byte[] { 42, 12, 0, 121 })); - in.close(); - } - }); - - addTest(new TestCase("Byte array is(String)") { - @Override - public void test() throws Exception { - String expected = "Testy"; - BufferedInputStream in = new BufferedInputStream( - expected.getBytes("UTF-8")); - assertEquals( - "The array should be considered identical to its source", - true, in.is(expected)); - assertEquals( - "The array should be considered different to that one", - false, in.is("Autre")); - assertEquals( - "The array should be considered different to that one", - false, in.is("Test")); - in.close(); - } - }); - - addTest(new TestCase("InputStream is(String)") { - @Override - public void test() throws Exception { - String expected = "Testy"; - BufferedInputStream in = new BufferedInputStream( - new ByteArrayInputStream(expected.getBytes("UTF-8"))); - assertEquals( - "The array should be considered identical to its source", - true, in.is(expected)); - assertEquals( - "The array should be considered different to that one", - false, in.is("Autre")); - assertEquals( - "The array should be considered different to that one", - false, in.is("Testy.")); - in.close(); - } - }); - } - - static void checkArrays(TestCase test, String prefix, InputStream in, - byte[] expected) throws Exception { - byte[] actual = IOUtils.toByteArray(in); - test.assertEquals("The " + prefix - + " resulting array has not the correct number of items", - expected.length, actual.length); - for (int i = 0; i < actual.length; i++) { - test.assertEquals(prefix + ": item " + i - + " (0-based) is not the same", expected[i], actual[i]); - } - } -} diff --git a/src/be/nikiroo/utils/test_code/BufferedOutputStreamTest.java b/src/be/nikiroo/utils/test_code/BufferedOutputStreamTest.java deleted file mode 100644 index 5646e61..0000000 --- a/src/be/nikiroo/utils/test_code/BufferedOutputStreamTest.java +++ /dev/null @@ -1,136 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.List; - -import be.nikiroo.utils.streams.BufferedOutputStream; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class BufferedOutputStreamTest extends TestLauncher { - public BufferedOutputStreamTest(String[] args) { - super("BufferedOutputStream test", args); - - addTest(new TestCase("Single write") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - BufferedOutputStream out = new BufferedOutputStream(bout); - - byte[] data = new byte[] { 42, 12, 0, 127 }; - - out.write(data); - out.close(); - - checkArrays(this, "FIRST", bout, data); - } - }); - - addTest(new TestCase("Single write of 5000 bytes") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - BufferedOutputStream out = new BufferedOutputStream(bout); - - byte[] data = new byte[5000]; - for (int i = 0; i < data.length; i++) { - data[i] = (byte) (i % 255); - } - - out.write(data); - out.close(); - - checkArrays(this, "FIRST", bout, data); - } - }); - - addTest(new TestCase("Multiple writes") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - BufferedOutputStream out = new BufferedOutputStream(bout); - - byte[] data1 = new byte[] { 42, 12, 0, 127 }; - byte[] data2 = new byte[] { 15, 55 }; - byte[] data3 = new byte[] {}; - - byte[] dataAll = new byte[] { 42, 12, 0, 127, 15, 55 }; - - out.write(data1); - out.write(data2); - out.write(data3); - out.close(); - - checkArrays(this, "FIRST", bout, dataAll); - } - }); - - addTest(new TestCase("Multiple writes for a 5000 bytes total") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - BufferedOutputStream out = new BufferedOutputStream(bout); - - byte[] data = new byte[] { 42, 12, 0, 127, 51, 2, 32, 66, 7, 87 }; - - List bytes = new ArrayList(); - - // write 400 * 10 + 1000 bytes = 5000 - for (int i = 0; i < 400; i++) { - for (int j = 0; j < data.length; j++) { - bytes.add(data[j]); - } - out.write(data); - } - - for (int i = 0; i < 1000; i++) { - for (int j = 0; j < data.length; j++) { - bytes.add(data[j]); - } - out.write(data); - } - - out.close(); - - byte[] abytes = new byte[bytes.size()]; - for (int i = 0; i < bytes.size(); i++) { - abytes[i] = bytes.get(i); - } - - checkArrays(this, "FIRST", bout, abytes); - } - }); - } - - static void checkArrays(TestCase test, String prefix, - ByteArrayOutputStream bout, byte[] expected) throws Exception { - byte[] actual = bout.toByteArray(); - - if (false) { - System.out.print("\nExpected data: [ "); - for (int i = 0; i < expected.length; i++) { - if (i > 0) - System.out.print(", "); - System.out.print(expected[i]); - } - System.out.println(" ]"); - - System.out.print("Actual data : [ "); - for (int i = 0; i < actual.length; i++) { - if (i > 0) - System.out.print(", "); - System.out.print(actual[i]); - } - System.out.println(" ]"); - } - - test.assertEquals("The " + prefix - + " resulting array has not the correct number of items", - expected.length, actual.length); - for (int i = 0; i < actual.length; i++) { - test.assertEquals(prefix + ": item " + i - + " (0-based) is not the same", expected[i], actual[i]); - } - } -} diff --git a/src/be/nikiroo/utils/test_code/BundleTest.java b/src/be/nikiroo/utils/test_code/BundleTest.java deleted file mode 100644 index 2e25eb0..0000000 --- a/src/be/nikiroo/utils/test_code/BundleTest.java +++ /dev/null @@ -1,249 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import be.nikiroo.utils.IOUtils; -import be.nikiroo.utils.resources.Bundle; -import be.nikiroo.utils.resources.Bundles; -import be.nikiroo.utils.resources.Meta; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class BundleTest extends TestLauncher { - private File tmp; - private B b = new B(); - - public BundleTest(String[] args) { - this("Bundle test", args); - } - - protected BundleTest(String name, String[] args) { - super(name, args); - - for (TestCase test : getSimpleTests()) { - addTest(test); - } - - addSeries(new TestLauncher("After saving/reloading the resources", args) { - { - for (TestCase test : getSimpleTests()) { - addTest(test); - } - } - - @Override - protected void start() throws Exception { - tmp = File.createTempFile("nikiroo-utils", ".test"); - tmp.delete(); - tmp.mkdir(); - b.updateFile(tmp.getAbsolutePath()); - Bundles.setDirectory(tmp.getAbsolutePath()); - b.reload(false); - } - - @Override - protected void stop() { - IOUtils.deltree(tmp); - } - }); - - addSeries(new TestLauncher("Read/Write support", args) { - { - addTest(new TestCase("Reload") { - @Override - public void test() throws Exception { - String def = b.getString(E.ONE); - String val = "Something"; - - b.setString(E.ONE, val); - b.updateFile(); - b.reload(true); - - assertEquals("We should have reset the bundle", def, - b.getString(E.ONE)); - - b.reload(false); - - assertEquals("We should have reloaded the same files", - val, b.getString(E.ONE)); - - // reset values for next tests - b.reload(true); - b.updateFile(); - } - }); - - addTest(new TestCase("Set/Get") { - @Override - public void test() throws Exception { - String val = "Newp"; - b.setString(E.ONE, val); - String setGet = b.getString(E.ONE); - - assertEquals(val, setGet); - - // reset values for next tests - b.restoreSnapshot(null); - } - }); - - addTest(new TestCase("Snapshots") { - @Override - public void test() throws Exception { - String val = "Newp"; - String def = b.getString(E.ONE); - - b.setString(E.ONE, val); - Object snap = b.takeSnapshot(); - - b.restoreSnapshot(null); - assertEquals( - "restoreChanges(null) should clear the changes", - def, b.getString(E.ONE)); - b.restoreSnapshot(snap); - assertEquals( - "restoreChanges(snapshot) should restore the changes", - val, b.getString(E.ONE)); - - // reset values for next tests - b.restoreSnapshot(null); - } - }); - - addTest(new TestCase("updateFile with changes") { - @Override - public void test() throws Exception { - String val = "Go to disk! (UTF-8 test: 日本語)"; - - String def = b.getString(E.ONE); - b.setString(E.ONE, val); - b.updateFile(tmp.getAbsolutePath()); - b.reload(false); - - assertEquals(val, b.getString(E.ONE)); - - // reset values for next tests - b.setString(E.ONE, def); - b.updateFile(tmp.getAbsolutePath()); - b.reload(false); - } - }); - } - - @Override - protected void start() throws Exception { - tmp = File.createTempFile("nikiroo-utils", ".test"); - tmp.delete(); - tmp.mkdir(); - b.updateFile(tmp.getAbsolutePath()); - Bundles.setDirectory(tmp.getAbsolutePath()); - b.reload(false); - } - - @Override - protected void stop() { - IOUtils.deltree(tmp); - } - }); - } - - private List getSimpleTests() { - String pre = ""; - - List list = new ArrayList(); - - list.add(new TestCase(pre + "getString simple") { - @Override - public void test() throws Exception { - assertEquals("un", b.getString(E.ONE)); - } - }); - - list.add(new TestCase(pre + "getStringX with null suffix") { - @Override - public void test() throws Exception { - assertEquals("un", b.getStringX(E.ONE, null)); - } - }); - - list.add(new TestCase(pre + "getStringX with empty suffix") { - @Override - public void test() throws Exception { - assertEquals(null, b.getStringX(E.ONE, "")); - } - }); - - list.add(new TestCase(pre + "getStringX with existing suffix") { - @Override - public void test() throws Exception { - assertEquals("un + suffix", b.getStringX(E.ONE, "suffix")); - } - }); - - list.add(new TestCase(pre + "getStringX with not existing suffix") { - @Override - public void test() throws Exception { - assertEquals(null, b.getStringX(E.ONE, "fake")); - } - }); - - list.add(new TestCase(pre + "getString with UTF-8 content") { - @Override - public void test() throws Exception { - assertEquals("日本語 Nihongo", b.getString(E.JAPANESE)); - } - }); - - return list; - } - - /** - * {@link Bundle}. - * - * @author niki - */ - private class B extends Bundle { - protected B() { - super(E.class, N.bundle_test, null); - } - - @Override - // ...and make it public - public Object takeSnapshot() { - return super.takeSnapshot(); - } - - @Override - // ...and make it public - public void restoreSnapshot(Object snap) { - super.restoreSnapshot(snap); - } - } - - /** - * Key enum for the {@link Bundle}. - * - * @author niki - */ - private enum E { - @Meta - ONE, // - @Meta - ONE_SUFFIX, // - @Meta - TWO, // - @Meta - JAPANESE - } - - /** - * Name enum for the {@link Bundle}. - * - * @author niki - */ - private enum N { - bundle_test - } -} diff --git a/src/be/nikiroo/utils/test_code/CryptUtilsTest.java b/src/be/nikiroo/utils/test_code/CryptUtilsTest.java deleted file mode 100644 index 0c53461..0000000 --- a/src/be/nikiroo/utils/test_code/CryptUtilsTest.java +++ /dev/null @@ -1,155 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; - -import be.nikiroo.utils.CryptUtils; -import be.nikiroo.utils.IOUtils; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class CryptUtilsTest extends TestLauncher { - private String key; - private CryptUtils crypt; - - public CryptUtilsTest(String[] args) { - super("CryptUtils test", args); - - String longKey = "some long string with more than 128 bits (=32 bytes) of data"; - - addSeries(new CryptUtilsTest(args, "Manual input wuth NULL key", null, - 1)); - addSeries(new CryptUtilsTest(args, "Streams with NULL key", null, true)); - - addSeries(new CryptUtilsTest(args, "Manual input with emptykey", "", 1)); - addSeries(new CryptUtilsTest(args, "Streams with empty key", "", true)); - - addSeries(new CryptUtilsTest(args, "Manual input with long key", - longKey, 1)); - addSeries(new CryptUtilsTest(args, "Streams with long key", longKey, - true)); - } - - @Override - protected void addTest(final TestCase test) { - super.addTest(new TestCase(test.getName()) { - @Override - public void test() throws Exception { - test.test(); - } - - @Override - public void setUp() throws Exception { - crypt = new CryptUtils(key); - test.setUp(); - } - - @Override - public void tearDown() throws Exception { - test.tearDown(); - crypt = null; - } - }); - } - - private CryptUtilsTest(String[] args, String title, String key, - @SuppressWarnings("unused") int dummy) { - super(title, args); - this.key = key; - - final String longData = "Le premier jour, Le Grand Barbu dans le cloud fit la lumière, et il vit que c'était bien. Ou quelque chose comme ça. Je préfère la Science-Fiction en général, je trouve ça plus sain :/"; - - addTest(new TestCase("Short") { - @Override - public void test() throws Exception { - String orig = "data"; - byte[] encrypted = crypt.encrypt(orig); - String decrypted = crypt.decrypts(encrypted); - - assertEquals(orig, decrypted); - } - }); - - addTest(new TestCase("Short, base64") { - @Override - public void test() throws Exception { - String orig = "data"; - String encrypted = crypt.encrypt64(orig); - String decrypted = crypt.decrypt64s(encrypted); - - assertEquals(orig, decrypted); - } - }); - - addTest(new TestCase("Empty") { - @Override - public void test() throws Exception { - String orig = ""; - byte[] encrypted = crypt.encrypt(orig); - String decrypted = crypt.decrypts(encrypted); - - assertEquals(orig, decrypted); - } - }); - - addTest(new TestCase("Empty, base64") { - @Override - public void test() throws Exception { - String orig = ""; - String encrypted = crypt.encrypt64(orig); - String decrypted = crypt.decrypt64s(encrypted); - - assertEquals(orig, decrypted); - } - }); - - addTest(new TestCase("Long") { - @Override - public void test() throws Exception { - String orig = longData; - byte[] encrypted = crypt.encrypt(orig); - String decrypted = crypt.decrypts(encrypted); - - assertEquals(orig, decrypted); - } - }); - - addTest(new TestCase("Long, base64") { - @Override - public void test() throws Exception { - String orig = longData; - String encrypted = crypt.encrypt64(orig); - String decrypted = crypt.decrypt64s(encrypted); - - assertEquals(orig, decrypted); - } - }); - } - - private CryptUtilsTest(String[] args, String title, String key, - @SuppressWarnings("unused") boolean dummy) { - super(title, args); - this.key = key; - - addTest(new TestCase("Simple test") { - @Override - public void test() throws Exception { - InputStream in = new ByteArrayInputStream(new byte[] { 42, 127, - 12 }); - crypt.encrypt(in); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.write(in, out); - byte[] result = out.toByteArray(); - - assertEquals( - "We wrote 3 bytes, we expected 3 bytes back but got: " - + result.length, result.length, result.length); - - assertEquals(42, result[0]); - assertEquals(127, result[1]); - assertEquals(12, result[2]); - } - }); - } -} diff --git a/src/be/nikiroo/utils/test_code/IOUtilsTest.java b/src/be/nikiroo/utils/test_code/IOUtilsTest.java deleted file mode 100644 index 9f22896..0000000 --- a/src/be/nikiroo/utils/test_code/IOUtilsTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.InputStream; - -import be.nikiroo.utils.IOUtils; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class IOUtilsTest extends TestLauncher { - public IOUtilsTest(String[] args) { - super("IOUtils test", args); - - addTest(new TestCase("openResource") { - @Override - public void test() throws Exception { - InputStream in = IOUtils.openResource("VERSION"); - assertNotNull( - "The VERSION file is supposed to be present in the binaries", - in); - in.close(); - } - }); - } -} diff --git a/src/be/nikiroo/utils/test_code/NextableInputStreamTest.java b/src/be/nikiroo/utils/test_code/NextableInputStreamTest.java deleted file mode 100644 index 463a123..0000000 --- a/src/be/nikiroo/utils/test_code/NextableInputStreamTest.java +++ /dev/null @@ -1,345 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import be.nikiroo.utils.IOUtils; -import be.nikiroo.utils.streams.NextableInputStream; -import be.nikiroo.utils.streams.NextableInputStreamStep; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -public class NextableInputStreamTest extends TestLauncher { - public NextableInputStreamTest(String[] args) { - super("NextableInputStream test", args); - - addTest(new TestCase("Simple byte array reading") { - @Override - public void test() throws Exception { - byte[] expected = new byte[] { 42, 12, 0, 127 }; - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(expected), null); - checkNext(this, "READ", in, expected); - } - }); - - addTest(new TestCase("Stop at 12") { - @Override - public void test() throws Exception { - byte[] expected = new byte[] { 42, 12, 0, 127 }; - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(expected), - new NextableInputStreamStep(12)); - - checkNext(this, "FIRST", in, new byte[] { 42 }); - } - }); - - addTest(new TestCase("Stop at 12, resume, stop again, resume") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127, 12, 51, 11, 12 }; - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(data), - new NextableInputStreamStep(12)); - - checkNext(this, "FIRST", in, new byte[] { 42 }); - checkNext(this, "SECOND", in, new byte[] { 0, 127 }); - checkNext(this, "THIRD", in, new byte[] { 51, 11 }); - } - }); - - addTest(new TestCase("Encapsulation") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 4, 127, 12, 5 }; - NextableInputStream in4 = new NextableInputStream( - new ByteArrayInputStream(data), - new NextableInputStreamStep(4)); - NextableInputStream subIn12 = new NextableInputStream(in4, - new NextableInputStreamStep(12)); - - in4.next(); - checkNext(this, "SUB FIRST", subIn12, new byte[] { 42 }); - checkNext(this, "SUB SECOND", subIn12, new byte[] { 0 }); - - assertEquals("The subIn still has some data", false, - subIn12.next()); - - checkNext(this, "MAIN LAST", in4, new byte[] { 127, 12, 5 }); - } - }); - - addTest(new TestCase("UTF-8 text lines test") { - @Override - public void test() throws Exception { - String ln1 = "Ligne première"; - String ln2 = "Ligne la deuxième du nom"; - byte[] data = (ln1 + "\n" + ln2).getBytes("UTF-8"); - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(data), - new NextableInputStreamStep('\n')); - - checkNext(this, "FIRST", in, ln1.getBytes("UTF-8")); - checkNext(this, "SECOND", in, ln2.getBytes("UTF-8")); - } - }); - - addTest(new TestCase("nextAll()") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127, 12, 51, 11, 12 }; - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(data), - new NextableInputStreamStep(12)); - - checkNext(this, "FIRST", in, new byte[] { 42 }); - checkNextAll(this, "REST", in, new byte[] { 0, 127, 12, 51, 11, - 12 }); - assertEquals("The stream still has some data", false, in.next()); - } - }); - - addTest(new TestCase("getBytesRead()") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127, 12, 51, 11, 12 }; - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(data), - new NextableInputStreamStep(12)); - - in.nextAll(); - IOUtils.toByteArray(in); - - assertEquals("The number of bytes read is not correct", - data.length, in.getBytesRead()); - } - }); - - addTest(new TestCase("bytes array input") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127, 12, 51, 11, 12 }; - NextableInputStream in = new NextableInputStream(data, - new NextableInputStreamStep(12)); - - checkNext(this, "FIRST", in, new byte[] { 42 }); - checkNext(this, "SECOND", in, new byte[] { 0, 127 }); - checkNext(this, "THIRD", in, new byte[] { 51, 11 }); - } - }); - - addTest(new TestCase("Skip data") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127, 12, 51, 11, 12 }; - NextableInputStream in = new NextableInputStream(data, null); - in.next(); - - byte[] rest = new byte[] { 12, 51, 11, 12 }; - - in.skip(4); - assertEquals("STARTS_WITH OK_1", true, in.startsWith(rest)); - assertEquals("STARTS_WITH KO_1", false, - in.startsWith(new byte[] { 0 })); - assertEquals("STARTS_WITH KO_2", false, in.startsWith(data)); - assertEquals("STARTS_WITH KO_3", false, - in.startsWith(new byte[] { 1, 2, 3 })); - assertEquals("STARTS_WITH OK_2", true, in.startsWith(rest)); - assertEquals("READ REST", IOUtils.readSmallStream(in), - new String(rest)); - in.close(); - } - }); - - addTest(new TestCase("Starts with") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127, 12, 51, 11, 12 }; - NextableInputStream in = new NextableInputStream(data, null); - in.next(); - - // yes - assertEquals("It actually starts with that", true, - in.startsWith(new byte[] { 42 })); - assertEquals("It actually starts with that", true, - in.startsWith(new byte[] { 42, 12 })); - assertEquals("It actually is the same array", true, - in.startsWith(data)); - - // no - assertEquals("It actually does not start with that", false, - in.startsWith(new byte[] { 12 })); - assertEquals( - "It actually does not start with that", - false, - in.startsWith(new byte[] { 42, 12, 0, 127, 12, 51, 11, - 11 })); - - // too big - try { - in.startsWith(new byte[] { 42, 12, 0, 127, 12, 51, 11, 12, - 0 }); - fail("Searching a prefix bigger than the array should throw an IOException"); - } catch (IOException e) { - } - - in.close(); - } - }); - - addTest(new TestCase("Starts with strings") { - @Override - public void test() throws Exception { - String text = "Fanfan et Toto vont à la mer"; - byte[] data = text.getBytes("UTF-8"); - NextableInputStream in = new NextableInputStream(data, null); - in.next(); - - // yes - assertEquals("It actually starts with that", true, - in.startsWith("F")); - assertEquals("It actually starts with that", true, - in.startsWith("Fanfan et")); - assertEquals("It actually is the same text", true, - in.startsWith(text)); - - // no - assertEquals("It actually does not start with that", false, - in.startsWith("Toto")); - assertEquals("It actually does not start with that", false, - in.startsWith("Fanfan et Toto vont à la mee")); - - // too big - try { - in.startsWith("Fanfan et Toto vont à la mer."); - fail("Searching a prefix bigger than the array should throw an IOException"); - } catch (IOException e) { - } - - in.close(); - } - }); - - addTest(new TestCase("Starts With strings + steps") { - @Override - public void test() throws Exception { - String data = "{\nREF: fanfan\n}"; - NextableInputStream in = new NextableInputStream( - data.getBytes("UTF-8"), new NextableInputStreamStep( - '\n')); - in.next(); - - assertEquals("STARTS_WITH OK", true, in.startsWith("{")); - in.skip(1); - assertEquals("STARTS_WITH WHEN SPENT", false, - in.startsWith("{")); - - checkNext(this, "PARTIAL CONTENT", in, - "REF: fanfan".getBytes("UTF-8")); - } - }); - - addTest(new TestCase("InputStream is(String)") { - @Override - public void test() throws Exception { - String data = "{\nREF: fanfan\n}"; - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(data.getBytes("UTF-8")), - new NextableInputStreamStep('\n')); - - in.next(); - assertEquals("Item 1 OK", true, in.is("{")); - assertEquals("Item 1 KO_1", false, in.is("|")); - assertEquals("Item 1 KO_2", false, in.is("{}")); - in.skip(1); - in.next(); - assertEquals("Item 2 OK", true, in.is("REF: fanfan")); - assertEquals("Item 2 KO", false, in.is("REF: fanfan.")); - IOUtils.readSmallStream(in); - in.next(); - assertEquals("Item 3 OK", true, in.is("}")); - - in.close(); - } - }); - - addTest(new TestCase("Bytes NextAll test") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127, 12, 51, 11, 12 }; - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(data), - new NextableInputStreamStep(12)); - - checkNext(this, "FIRST", in, new byte[] { 42 }); - checkNextAll(this, "SECOND", in, new byte[] { 0, 127, 12, 51, - 11, 12 }); - } - }); - - addTest(new TestCase("String NextAll test") { - @Override - public void test() throws Exception { - String d1 = "^java.lang.String"; - String d2 = "\"http://example.com/query.html\""; - String data = d1 + ":" + d2; - NextableInputStream in = new NextableInputStream( - new ByteArrayInputStream(data.getBytes("UTF-8")), - new NextableInputStreamStep(':')); - - checkNext(this, "FIRST", in, d1.getBytes("UTF-8")); - checkNextAll(this, "SECOND", in, d2.getBytes("UTF-8")); - } - }); - - addTest(new TestCase("NextAll in Next test") { - @Override - public void test() throws Exception { - String line1 = "première ligne"; - String d1 = "^java.lang.String"; - String d2 = "\"http://example.com/query.html\""; - String line3 = "end of lines"; - String data = line1 + "\n" + d1 + ":" + d2 + "\n" + line3; - - NextableInputStream inL = new NextableInputStream( - new ByteArrayInputStream(data.getBytes("UTF-8")), - new NextableInputStreamStep('\n')); - - checkNext(this, "Line 1", inL, line1.getBytes("UTF-8")); - inL.next(); - - NextableInputStream in = new NextableInputStream(inL, - new NextableInputStreamStep(':')); - - checkNext(this, "Line 2 FIRST", in, d1.getBytes("UTF-8")); - checkNextAll(this, "Line 2 SECOND", in, d2.getBytes("UTF-8")); - } - }); - } - - static void checkNext(TestCase test, String prefix, NextableInputStream in, - byte[] expected) throws Exception { - test.assertEquals("Cannot get " + prefix + " entry", true, in.next()); - checkArrays(test, prefix, in, expected); - } - - static void checkNextAll(TestCase test, String prefix, - NextableInputStream in, byte[] expected) throws Exception { - test.assertEquals("Cannot get " + prefix + " entries", true, - in.nextAll()); - checkArrays(test, prefix, in, expected); - } - - static void checkArrays(TestCase test, String prefix, - NextableInputStream in, byte[] expected) throws Exception { - byte[] actual = IOUtils.toByteArray(in); - test.assertEquals("The " + prefix - + " resulting array has not the correct number of items", - expected.length, actual.length); - for (int i = 0; i < actual.length; i++) { - test.assertEquals("Item " + i + " (0-based) is not the same", - expected[i], actual[i]); - } - } -} diff --git a/src/be/nikiroo/utils/test_code/ProgressTest.java b/src/be/nikiroo/utils/test_code/ProgressTest.java deleted file mode 100644 index 22e36cb..0000000 --- a/src/be/nikiroo/utils/test_code/ProgressTest.java +++ /dev/null @@ -1,319 +0,0 @@ -package be.nikiroo.utils.test_code; - -import be.nikiroo.utils.Progress; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class ProgressTest extends TestLauncher { - public ProgressTest(String[] args) { - super("Progress reporting", args); - - addSeries(new TestLauncher("Simple progress", args) { - { - addTest(new TestCase("Relative values and direct values") { - @Override - public void test() throws Exception { - Progress p = new Progress(); - assertEquals(0, p.getProgress()); - assertEquals(0, p.getRelativeProgress()); - p.setProgress(33); - assertEquals(33, p.getProgress()); - assertEquals(0.33, p.getRelativeProgress()); - p.setMax(3); - p.setProgress(1); - assertEquals(1, p.getProgress()); - assertEquals( - generateAssertMessage("0.33..", - p.getRelativeProgress()), true, - p.getRelativeProgress() >= 0.332); - assertEquals( - generateAssertMessage("0.33..", - p.getRelativeProgress()), true, - p.getRelativeProgress() <= 0.334); - } - }); - - addTest(new TestCase("Listeners at first level") { - int pg; - - @Override - public void test() throws Exception { - Progress p = new Progress(); - p.addProgressListener(new Progress.ProgressListener() { - @Override - public void progress(Progress progress, String name) { - pg = progress.getProgress(); - } - }); - - p.setProgress(42); - assertEquals(42, pg); - p.setProgress(0); - assertEquals(0, pg); - } - }); - } - }); - - addSeries(new TestLauncher("Progress with children", args) { - { - addTest(new TestCase("One child") { - @Override - public void test() throws Exception { - Progress p = new Progress(); - Progress child = new Progress(); - - p.addProgress(child, 100); - - child.setProgress(42); - assertEquals(42, p.getProgress()); - } - }); - - addTest(new TestCase("Multiple children") { - @Override - public void test() throws Exception { - Progress p = new Progress(); - Progress child1 = new Progress(); - Progress child2 = new Progress(); - Progress child3 = new Progress(); - - p.addProgress(child1, 20); - p.addProgress(child2, 60); - p.addProgress(child3, 20); - - child1.setProgress(50); - assertEquals(10, p.getProgress()); - child2.setProgress(100); - assertEquals(70, p.getProgress()); - child3.setProgress(100); - assertEquals(90, p.getProgress()); - child1.setProgress(100); - assertEquals(100, p.getProgress()); - } - }); - - addTest(new TestCase("Listeners with children") { - int pg; - - @Override - public void test() throws Exception { - final Progress p = new Progress(); - Progress child1 = new Progress(); - Progress child2 = new Progress(); - p.addProgress(child1, 50); - p.addProgress(child2, 50); - - p.addProgressListener(new Progress.ProgressListener() { - @Override - public void progress(Progress progress, String name) { - pg = p.getProgress(); - } - }); - - child1.setProgress(50); - assertEquals(25, pg); - child2.setProgress(100); - assertEquals(75, pg); - child1.setProgress(100); - assertEquals(100, pg); - } - }); - - addTest(new TestCase("Listeners with children, not 1-100") { - int pg; - - @Override - public void test() throws Exception { - final Progress p = new Progress(); - p.setMax(1000); - - Progress child1 = new Progress(); - child1.setMax(2); - - Progress child2 = new Progress(); - p.addProgress(child1, 500); - p.addProgress(child2, 500); - - p.addProgressListener(new Progress.ProgressListener() { - @Override - public void progress(Progress progress, String name) { - pg = p.getProgress(); - } - }); - - child1.setProgress(1); - assertEquals(250, pg); - child2.setProgress(100); - assertEquals(750, pg); - child1.setProgress(2); - assertEquals(1000, pg); - } - }); - - addTest(new TestCase( - "Listeners with children, not 1-100, local progress") { - int pg; - - @Override - public void test() throws Exception { - final Progress p = new Progress(); - p.setMax(1000); - - Progress child1 = new Progress(); - child1.setMax(2); - - Progress child2 = new Progress(); - p.addProgress(child1, 400); - p.addProgress(child2, 400); - // 200 = local progress - - p.addProgressListener(new Progress.ProgressListener() { - @Override - public void progress(Progress progress, String name) { - pg = p.getProgress(); - } - }); - - child1.setProgress(1); - assertEquals(200, pg); - child2.setProgress(100); - assertEquals(600, pg); - p.setProgress(100); - assertEquals(700, pg); - child1.setProgress(2); - assertEquals(900, pg); - p.setProgress(200); - assertEquals(1000, pg); - } - }); - - addTest(new TestCase("Listeners with 5+ children, 4+ depth") { - int pg; - - @Override - public void test() throws Exception { - final Progress p = new Progress(); - Progress child1 = new Progress(); - Progress child2 = new Progress(); - p.addProgress(child1, 50); - p.addProgress(child2, 50); - Progress child11 = new Progress(); - child1.addProgress(child11, 100); - Progress child111 = new Progress(); - child11.addProgress(child111, 100); - Progress child1111 = new Progress(); - child111.addProgress(child1111, 20); - Progress child1112 = new Progress(); - child111.addProgress(child1112, 20); - Progress child1113 = new Progress(); - child111.addProgress(child1113, 20); - Progress child1114 = new Progress(); - child111.addProgress(child1114, 20); - Progress child1115 = new Progress(); - child111.addProgress(child1115, 20); - - p.addProgressListener(new Progress.ProgressListener() { - @Override - public void progress(Progress progress, String name) { - pg = p.getProgress(); - } - }); - - child1111.setProgress(100); - child1112.setProgress(50); - child1113.setProgress(25); - child1114.setProgress(25); - child1115.setProgress(50); - assertEquals(25, pg); - child2.setProgress(100); - assertEquals(75, pg); - child1111.setProgress(100); - child1112.setProgress(100); - child1113.setProgress(100); - child1114.setProgress(100); - child1115.setProgress(100); - assertEquals(100, pg); - } - }); - - addTest(new TestCase("Listeners with children, multi-thread") { - int pg; - boolean decrease; - Object lock1 = new Object(); - Object lock2 = new Object(); - int currentStep1; - int currentStep2; - - @Override - public void test() throws Exception { - final Progress p = new Progress(0, 200); - - final Progress child1 = new Progress(); - final Progress child2 = new Progress(); - p.addProgress(child1, 100); - p.addProgress(child2, 100); - - p.addProgressListener(new Progress.ProgressListener() { - @Override - public void progress(Progress progress, String name) { - int now = p.getProgress(); - if (now < pg) { - decrease = true; - } - pg = now; - } - }); - - // Run 200 concurrent threads, 2 at a time allowed to - // make progress (each on a different child) - for (int i = 0; i <= 100; i++) { - final int step = i; - new Thread(new Runnable() { - @Override - public void run() { - synchronized (lock1) { - if (step > currentStep1) { - currentStep1 = step; - child1.setProgress(step); - } - } - } - }).start(); - - new Thread(new Runnable() { - @Override - public void run() { - synchronized (lock2) { - if (step > currentStep2) { - currentStep2 = step; - child2.setProgress(step); - } - } - } - }).start(); - } - - int i; - int timeout = 20; // in 1/10th of seconds - for (i = 0; i < timeout - && (currentStep1 + currentStep2) < 200; i++) { - Thread.sleep(100); - } - - assertEquals("The test froze at step " + currentStep1 - + " + " + currentStep2, true, i < timeout); - assertEquals( - "There should not have any decresing steps", - decrease, false); - assertEquals("The progress should have reached 200", - 200, p.getProgress()); - assertEquals( - "The progress should have reached completion", - true, p.isDone()); - } - }); - } - }); - } -} diff --git a/src/be/nikiroo/utils/test_code/ReplaceInputStreamTest.java b/src/be/nikiroo/utils/test_code/ReplaceInputStreamTest.java deleted file mode 100644 index e6e2112..0000000 --- a/src/be/nikiroo/utils/test_code/ReplaceInputStreamTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import be.nikiroo.utils.IOUtils; -import be.nikiroo.utils.streams.ReplaceInputStream; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class ReplaceInputStreamTest extends TestLauncher { - public ReplaceInputStreamTest(String[] args) { - super("ReplaceInputStream test", args); - - addTest(new TestCase("Empty replace") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127 }; - ReplaceInputStream in = new ReplaceInputStream( - new ByteArrayInputStream(data), new byte[0], - new byte[0]); - - checkArrays(this, "FIRST", in, data); - } - }); - - addTest(new TestCase("Simple replace") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127 }; - ReplaceInputStream in = new ReplaceInputStream( - new ByteArrayInputStream(data), new byte[] { 0 }, - new byte[] { 10 }); - - checkArrays(this, "FIRST", in, new byte[] { 42, 12, 10, 127 }); - } - }); - - addTest(new TestCase("3/4 replace") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127 }; - ReplaceInputStream in = new ReplaceInputStream( - new ByteArrayInputStream(data), - new byte[] { 12, 0, 127 }, new byte[] { 10, 10, 10 }); - - checkArrays(this, "FIRST", in, new byte[] { 42, 10, 10, 10 }); - } - }); - - addTest(new TestCase("Lnger replace") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127 }; - ReplaceInputStream in = new ReplaceInputStream( - new ByteArrayInputStream(data), new byte[] { 0 }, - new byte[] { 10, 10, 10 }); - - checkArrays(this, "FIRST", in, new byte[] { 42, 12, 10, 10, 10, - 127 }); - } - }); - - addTest(new TestCase("Shorter replace") { - @Override - public void test() throws Exception { - byte[] data = new byte[] { 42, 12, 0, 127 }; - ReplaceInputStream in = new ReplaceInputStream( - new ByteArrayInputStream(data), - new byte[] { 42, 12, 0 }, new byte[] { 10 }); - - checkArrays(this, "FIRST", in, new byte[] { 10, 127 }); - } - }); - - addTest(new TestCase("String replace") { - @Override - public void test() throws Exception { - byte[] data = "I like red".getBytes("UTF-8"); - ReplaceInputStream in = new ReplaceInputStream( - new ByteArrayInputStream(data), - "red".getBytes("UTF-8"), "blue".getBytes("UTF-8")); - - checkArrays(this, "FIRST", in, "I like blue".getBytes("UTF-8")); - - data = "I like blue".getBytes("UTF-8"); - in = new ReplaceInputStream(new ByteArrayInputStream(data), - "blue".getBytes("UTF-8"), "red".getBytes("UTF-8")); - - checkArrays(this, "FIRST", in, "I like red".getBytes("UTF-8")); - } - }); - } - - static void checkArrays(TestCase test, String prefix, InputStream in, - byte[] expected) throws Exception { - byte[] actual = IOUtils.toByteArray(in); - test.assertEquals("The " + prefix - + " resulting array has not the correct number of items", - expected.length, actual.length); - for (int i = 0; i < actual.length; i++) { - test.assertEquals("Item " + i + " (0-based) is not the same", - expected[i], actual[i]); - } - } -} diff --git a/src/be/nikiroo/utils/test_code/ReplaceOutputStreamTest.java b/src/be/nikiroo/utils/test_code/ReplaceOutputStreamTest.java deleted file mode 100644 index 1db3397..0000000 --- a/src/be/nikiroo/utils/test_code/ReplaceOutputStreamTest.java +++ /dev/null @@ -1,168 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.ByteArrayOutputStream; - -import be.nikiroo.utils.streams.ReplaceOutputStream; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class ReplaceOutputStreamTest extends TestLauncher { - public ReplaceOutputStreamTest(String[] args) { - super("ReplaceOutputStream test", args); - - addTest(new TestCase("Single write, empty bytes replaces") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ReplaceOutputStream out = new ReplaceOutputStream(bout, - new byte[0], new byte[0]); - - byte[] data = new byte[] { 42, 12, 0, 127 }; - - out.write(data); - out.close(); - - checkArrays(this, "FIRST", bout, data); - } - }); - - addTest(new TestCase("Multiple writes, empty Strings replaces") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ReplaceOutputStream out = new ReplaceOutputStream(bout, "", ""); - - byte[] data1 = new byte[] { 42, 12, 0, 127 }; - byte[] data2 = new byte[] { 15, 55 }; - byte[] data3 = new byte[] {}; - - byte[] dataAll = new byte[] { 42, 12, 0, 127, 15, 55 }; - - out.write(data1); - out.write(data2); - out.write(data3); - out.close(); - - checkArrays(this, "FIRST", bout, dataAll); - } - }); - - addTest(new TestCase("Single write, bytes replaces") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ReplaceOutputStream out = new ReplaceOutputStream(bout, - new byte[] { 12 }, new byte[] { 55 }); - - byte[] data = new byte[] { 42, 12, 0, 127 }; - - out.write(data); - out.close(); - - checkArrays(this, "FIRST", bout, new byte[] { 42, 55, 0, 127 }); - } - }); - - addTest(new TestCase("Multiple writes, Strings replaces") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ReplaceOutputStream out = new ReplaceOutputStream(bout, "(-)", - "(.)"); - - byte[] data1 = "un mot ".getBytes("UTF-8"); - byte[] data2 = "(-) of twee ".getBytes("UTF-8"); - byte[] data3 = "(-) makes the difference".getBytes("UTF-8"); - - out.write(data1); - out.write(data2); - out.write(data3); - out.close(); - - checkArrays(this, "FIRST", bout, - "un mot (.) of twee (.) makes the difference" - .getBytes("UTF-8")); - } - }); - - addTest(new TestCase("Single write, longer bytes replaces") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ReplaceOutputStream out = new ReplaceOutputStream(bout, - new byte[] { 12 }, new byte[] { 55, 55, 66 }); - - byte[] data = new byte[] { 42, 12, 0, 127 }; - - out.write(data); - out.close(); - - checkArrays(this, "FIRST", bout, new byte[] { 42, 55, 55, 66, - 0, 127 }); - } - }); - - addTest(new TestCase("Single write, shorter bytes replaces") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ReplaceOutputStream out = new ReplaceOutputStream(bout, - new byte[] { 12, 0 }, new byte[] { 55 }); - - byte[] data = new byte[] { 42, 12, 0, 127 }; - - out.write(data); - out.close(); - - checkArrays(this, "FIRST", bout, new byte[] { 42, 55, 127 }); - } - }); - - addTest(new TestCase("Single write, remove bytes replaces") { - @Override - public void test() throws Exception { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ReplaceOutputStream out = new ReplaceOutputStream(bout, - new byte[] { 12 }, new byte[] {}); - - byte[] data = new byte[] { 42, 12, 0, 127 }; - - out.write(data); - out.close(); - - checkArrays(this, "FIRST", bout, new byte[] { 42, 0, 127 }); - } - }); - } - - static void checkArrays(TestCase test, String prefix, - ByteArrayOutputStream bout, byte[] expected) throws Exception { - byte[] actual = bout.toByteArray(); - - if (false) { - System.out.print("\nExpected data: [ "); - for (int i = 0; i < expected.length; i++) { - if (i > 0) - System.out.print(", "); - System.out.print(expected[i]); - } - System.out.println(" ]"); - - System.out.print("Actual data : [ "); - for (int i = 0; i < actual.length; i++) { - if (i > 0) - System.out.print(", "); - System.out.print(actual[i]); - } - System.out.println(" ]"); - } - - test.assertEquals("The " + prefix - + " resulting array has not the correct number of items", - expected.length, actual.length); - for (int i = 0; i < actual.length; i++) { - test.assertEquals(prefix + ": item " + i - + " (0-based) is not the same", expected[i], actual[i]); - } - } -} diff --git a/src/be/nikiroo/utils/test_code/SerialServerTest.java b/src/be/nikiroo/utils/test_code/SerialServerTest.java deleted file mode 100644 index c10a158..0000000 --- a/src/be/nikiroo/utils/test_code/SerialServerTest.java +++ /dev/null @@ -1,637 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.net.URL; - -import be.nikiroo.utils.Version; -import be.nikiroo.utils.serial.server.ConnectActionClientObject; -import be.nikiroo.utils.serial.server.ConnectActionClientString; -import be.nikiroo.utils.serial.server.ConnectActionServerObject; -import be.nikiroo.utils.serial.server.ConnectActionServerString; -import be.nikiroo.utils.serial.server.ServerBridge; -import be.nikiroo.utils.serial.server.ServerObject; -import be.nikiroo.utils.serial.server.ServerString; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class SerialServerTest extends TestLauncher { - public SerialServerTest(String[] args) { - super("SerialServer test", args); - - for (String key : new String[] { null, - "some super secret encryption key" }) { - for (boolean bridge : new Boolean[] { false, true }) { - final String skey = (key != null ? "(encrypted)" - : "(plain text)"); - final String sbridge = (bridge ? " with bridge" : ""); - - addSeries(new SerialServerTest(args, key, skey, bridge, - sbridge, "ServerString")); - - addSeries(new SerialServerTest(args, key, skey, bridge, - sbridge, new Object() { - @Override - public String toString() { - return "ServerObject"; - } - })); - } - } - } - - private SerialServerTest(final String[] args, final String key, - final String skey, final boolean bridge, final String sbridge, - final String title) { - - super(title + " " + skey + sbridge, args); - - addTest(new TestCase("Simple connection " + skey) { - @Override - public void test() throws Exception { - final String[] rec = new String[1]; - - ServerString server = new ServerString(this.getName(), 0, key) { - @Override - protected String onRequest( - ConnectActionServerString action, Version version, - String data, long id) throws Exception { - return null; - } - - @Override - protected void onError(Exception e) { - } - }; - - int port = server.getPort(); - assertEquals("A port should have been assigned", true, port > 0); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - - port = br.getPort(); - assertEquals( - "A port should have been assigned to the bridge", - true, port > 0); - - br.start(); - } - - try { - try { - new ConnectActionClientString(null, port, key) { - @Override - public void action(Version version) - throws Exception { - rec[0] = "ok"; - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - assertNotNull("The client action was not run", rec[0]); - assertEquals("ok", rec[0]); - } - }); - - addTest(new TestCase("Simple exchange " + skey) { - final String[] sent = new String[1]; - final String[] recd = new String[1]; - final Exception[] err = new Exception[1]; - - @Override - public void test() throws Exception { - ServerString server = new ServerString(this.getName(), 0, key) { - @Override - protected String onRequest( - ConnectActionServerString action, Version version, - String data, long id) throws Exception { - sent[0] = data; - return "pong"; - } - - @Override - protected void onError(Exception e) { - err[0] = e; - } - }; - - int port = server.getPort(); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - port = br.getPort(); - br.start(); - } - - try { - try { - new ConnectActionClientString(null, port, key) { - @Override - public void action(Version version) - throws Exception { - recd[0] = send("ping"); - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - if (err[0] != null) { - fail("An exception was thrown: " + err[0].getMessage(), - err[0]); - } - - assertEquals("ping", sent[0]); - assertEquals("pong", recd[0]); - } - }); - - addTest(new TestCase("Multiple exchanges " + skey) { - final String[] sent = new String[3]; - final String[] recd = new String[3]; - final Exception[] err = new Exception[1]; - - @Override - public void test() throws Exception { - ServerString server = new ServerString(this.getName(), 0, key) { - @Override - protected String onRequest( - ConnectActionServerString action, Version version, - String data, long id) throws Exception { - sent[0] = data; - action.send("pong"); - sent[1] = action.rec(); - return "pong2"; - } - - @Override - protected void onError(Exception e) { - err[0] = e; - } - }; - - int port = server.getPort(); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - port = br.getPort(); - br.start(); - } - - try { - try { - new ConnectActionClientString(null, port, key) { - @Override - public void action(Version version) - throws Exception { - recd[0] = send("ping"); - recd[1] = send("ping2"); - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - if (err[0] != null) { - fail("An exception was thrown: " + err[0].getMessage(), - err[0]); - } - - assertEquals("ping", sent[0]); - assertEquals("pong", recd[0]); - assertEquals("ping2", sent[1]); - assertEquals("pong2", recd[1]); - } - }); - - addTest(new TestCase("Multiple call from client " + skey) { - final String[] sent = new String[3]; - final String[] recd = new String[3]; - final Exception[] err = new Exception[1]; - - @Override - public void test() throws Exception { - ServerString server = new ServerString(this.getName(), 0, key) { - @Override - protected String onRequest( - ConnectActionServerString action, Version version, - String data, long id) throws Exception { - sent[Integer.parseInt(data)] = data; - return "" + (Integer.parseInt(data) * 2); - } - - @Override - protected void onError(Exception e) { - err[0] = e; - } - }; - - int port = server.getPort(); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - port = br.getPort(); - br.start(); - } - - try { - try { - new ConnectActionClientString(null, port, key) { - @Override - public void action(Version version) - throws Exception { - for (int i = 0; i < 3; i++) { - recd[i] = send("" + i); - } - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - if (err[0] != null) { - fail("An exception was thrown: " + err[0].getMessage(), - err[0]); - } - - assertEquals("0", sent[0]); - assertEquals("0", recd[0]); - assertEquals("1", sent[1]); - assertEquals("2", recd[1]); - assertEquals("2", sent[2]); - assertEquals("4", recd[2]); - } - }); - } - - private SerialServerTest(final String[] args, final String key, - final String skey, final boolean bridge, final String sbridge, - final Object title) { - - super(title + " " + skey + sbridge, args); - - addTest(new TestCase("Simple connection " + skey) { - @Override - public void test() throws Exception { - final Object[] rec = new Object[1]; - - ServerObject server = new ServerObject(this.getName(), 0, key) { - @Override - protected Object onRequest( - ConnectActionServerObject action, Version version, - Object data, long id) throws Exception { - return null; - } - - @Override - protected void onError(Exception e) { - } - }; - - int port = server.getPort(); - assertEquals("A port should have been assigned", true, port > 0); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - port = br.getPort(); - br.start(); - } - - try { - try { - new ConnectActionClientObject(null, port, key) { - @Override - public void action(Version version) - throws Exception { - rec[0] = true; - } - - @Override - protected void onError(Exception e) { - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - assertNotNull("The client action was not run", rec[0]); - assertEquals(true, (boolean) ((Boolean) rec[0])); - } - }); - - addTest(new TestCase("Simple exchange " + skey) { - final Object[] sent = new Object[1]; - final Object[] recd = new Object[1]; - final Exception[] err = new Exception[1]; - - @Override - public void test() throws Exception { - ServerObject server = new ServerObject(this.getName(), 0, key) { - @Override - protected Object onRequest( - ConnectActionServerObject action, Version version, - Object data, long id) throws Exception { - sent[0] = data; - return "pong"; - } - - @Override - protected void onError(Exception e) { - err[0] = e; - } - }; - - int port = server.getPort(); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - port = br.getPort(); - br.start(); - } - - try { - try { - new ConnectActionClientObject(null, port, key) { - @Override - public void action(Version version) - throws Exception { - recd[0] = send("ping"); - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - if (err[0] != null) { - fail("An exception was thrown: " + err[0].getMessage(), - err[0]); - } - - assertEquals("ping", sent[0]); - assertEquals("pong", recd[0]); - } - }); - - addTest(new TestCase("Multiple exchanges " + skey) { - final Object[] sent = new Object[3]; - final Object[] recd = new Object[3]; - final Exception[] err = new Exception[1]; - - @Override - public void test() throws Exception { - ServerObject server = new ServerObject(this.getName(), 0, key) { - @Override - protected Object onRequest( - ConnectActionServerObject action, Version version, - Object data, long id) throws Exception { - sent[0] = data; - action.send("pong"); - sent[1] = action.rec(); - return "pong2"; - } - - @Override - protected void onError(Exception e) { - err[0] = e; - } - }; - - int port = server.getPort(); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - port = br.getPort(); - br.start(); - } - - try { - try { - new ConnectActionClientObject(null, port, key) { - @Override - public void action(Version version) - throws Exception { - recd[0] = send("ping"); - recd[1] = send("ping2"); - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - if (err[0] != null) { - fail("An exception was thrown: " + err[0].getMessage(), - err[0]); - } - - assertEquals("ping", sent[0]); - assertEquals("pong", recd[0]); - assertEquals("ping2", sent[1]); - assertEquals("pong2", recd[1]); - } - }); - - addTest(new TestCase("Object array of URLs " + skey) { - final Object[] sent = new Object[1]; - final Object[] recd = new Object[1]; - final Exception[] err = new Exception[1]; - - @Override - public void test() throws Exception { - ServerObject server = new ServerObject(this.getName(), 0, key) { - @Override - protected Object onRequest( - ConnectActionServerObject action, Version version, - Object data, long id) throws Exception { - sent[0] = data; - return new Object[] { "ACK" }; - } - - @Override - protected void onError(Exception e) { - err[0] = e; - } - }; - - int port = server.getPort(); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - port = br.getPort(); - br.start(); - } - - try { - try { - new ConnectActionClientObject(null, port, key) { - @Override - public void action(Version version) - throws Exception { - recd[0] = send(new Object[] { - "key", - new URL( - "https://example.com/from_client"), - "https://example.com/from_client" }); - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - if (err[0] != null) { - fail("An exception was thrown: " + err[0].getMessage(), - err[0]); - } - - Object[] sento = (Object[]) (sent[0]); - Object[] recdo = (Object[]) (recd[0]); - - assertEquals("key", sento[0]); - assertEquals("https://example.com/from_client", - ((URL) sento[1]).toString()); - assertEquals("https://example.com/from_client", sento[2]); - assertEquals("ACK", recdo[0]); - } - }); - - addTest(new TestCase("Multiple call from client " + skey) { - final Object[] sent = new Object[3]; - final Object[] recd = new Object[3]; - final Exception[] err = new Exception[1]; - - @Override - public void test() throws Exception { - ServerObject server = new ServerObject(this.getName(), 0, key) { - @Override - protected Object onRequest( - ConnectActionServerObject action, Version version, - Object data, long id) throws Exception { - sent[(Integer) data] = data; - return ((Integer) data) * 2; - } - - @Override - protected void onError(Exception e) { - err[0] = e; - } - }; - - int port = server.getPort(); - - server.start(); - - ServerBridge br = null; - if (bridge) { - br = new ServerBridge(0, key, "", port, key); - br.setTraceHandler(null); - port = br.getPort(); - br.start(); - } - - try { - try { - new ConnectActionClientObject(null, port, key) { - @Override - public void action(Version version) - throws Exception { - for (int i = 0; i < 3; i++) { - recd[i] = send(i); - } - } - }.connect(); - } finally { - server.stop(); - } - } finally { - if (br != null) { - br.stop(); - } - } - - if (err[0] != null) { - fail("An exception was thrown: " + err[0].getMessage(), - err[0]); - } - - assertEquals(0, sent[0]); - assertEquals(0, recd[0]); - assertEquals(1, sent[1]); - assertEquals(2, recd[1]); - assertEquals(2, sent[2]); - assertEquals(4, recd[2]); - } - }); - } -} diff --git a/src/be/nikiroo/utils/test_code/SerialTest.java b/src/be/nikiroo/utils/test_code/SerialTest.java deleted file mode 100644 index bf08f5c..0000000 --- a/src/be/nikiroo/utils/test_code/SerialTest.java +++ /dev/null @@ -1,281 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.NotSerializableException; -import java.net.URL; -import java.util.Arrays; - -import be.nikiroo.utils.serial.Exporter; -import be.nikiroo.utils.serial.Importer; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class SerialTest extends TestLauncher { - /** - * Required for Import/Export of objects. - */ - public SerialTest() { - this(null); - } - - private void encodeRecodeTest(TestCase test, Object data) throws Exception { - byte[] encoded = toBytes(data, true); - Object redata = fromBytes(toBytes(data, false)); - byte[] reencoded = toBytes(redata, true); - - // We suppose text mode - if (encoded.length < 256 && reencoded.length < 256) { - test.assertEquals("Different data after encode/decode/encode", - new String(encoded, "UTF-8"), - new String(reencoded, "UTF-8")); - } else { - test.assertEquals("Different data after encode/decode/encode", - true, Arrays.equals(encoded, reencoded)); - } - } - - // try to remove pointer addresses - private byte[] toBytes(Object data, boolean clearRefs) - throws NotSerializableException, IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Exporter(out).append(data); - out.flush(); - - if (clearRefs) { - String tmp = new String(out.toByteArray(), "UTF-8"); - tmp = tmp.replaceAll("@[0-9]*", "@REF"); - return tmp.getBytes("UTF-8"); - } - - return out.toByteArray(); - } - - private Object fromBytes(byte[] data) throws NoSuchFieldException, - NoSuchMethodException, ClassNotFoundException, - NullPointerException, IOException { - - InputStream in = new ByteArrayInputStream(data); - try { - return new Importer().read(in).getValue(); - } finally { - in.close(); - } - } - - public SerialTest(String[] args) { - super("Serial test", args); - - addTest(new TestCase("Simple class Import/Export") { - @Override - public void test() throws Exception { - Data data = new Data(42); - encodeRecodeTest(this, data); - } - }); - - addTest(new TestCase() { - @SuppressWarnings("unused") - private TestCase me = setName("Anonymous inner class"); - - @Override - public void test() throws Exception { - Data data = new Data() { - @SuppressWarnings("unused") - int value = 42; - }; - encodeRecodeTest(this, data); - } - }); - addTest(new TestCase() { - @SuppressWarnings("unused") - private TestCase me = setName("Array of anonymous inner classes"); - - @Override - public void test() throws Exception { - Data[] data = new Data[] { new Data() { - @SuppressWarnings("unused") - int value = 42; - } }; - - byte[] encoded = toBytes(data, false); - Object redata = fromBytes(encoded); - - // Comparing the 2 arrays won't be useful, because the @REFs - // will be ZIP-encoded; so we parse and re-encode each object - - byte[] encoded1 = toBytes(data[0], true); - byte[] reencoded1 = toBytes(((Object[]) redata)[0], true); - - assertEquals("Different data after encode/decode/encode", true, - Arrays.equals(encoded1, reencoded1)); - } - }); - addTest(new TestCase("URL Import/Export") { - @Override - public void test() throws Exception { - URL data = new URL("https://fanfan.be/"); - encodeRecodeTest(this, data); - } - }); - addTest(new TestCase("URL-String Import/Export") { - @Override - public void test() throws Exception { - String data = new URL("https://fanfan.be/").toString(); - encodeRecodeTest(this, data); - } - }); - addTest(new TestCase("URL/URL-String arrays Import/Export") { - @Override - public void test() throws Exception { - final String url = "https://fanfan.be/"; - Object[] data = new Object[] { new URL(url), url }; - - byte[] encoded = toBytes(data, false); - Object redata = fromBytes(encoded); - - // Comparing the 2 arrays won't be useful, because the @REFs - // will be ZIP-encoded; so we parse and re-encode each object - - byte[] encoded1 = toBytes(data[0], true); - byte[] reencoded1 = toBytes(((Object[]) redata)[0], true); - byte[] encoded2 = toBytes(data[1], true); - byte[] reencoded2 = toBytes(((Object[]) redata)[1], true); - - assertEquals("Different data 1 after encode/decode/encode", - true, Arrays.equals(encoded1, reencoded1)); - assertEquals("Different data 2 after encode/decode/encode", - true, Arrays.equals(encoded2, reencoded2)); - } - }); - addTest(new TestCase("Import/Export with nested objects") { - @Override - public void test() throws Exception { - Data data = new DataObject(new Data(21)); - encodeRecodeTest(this, data); - } - }); - addTest(new TestCase("Import/Export String in object") { - @Override - public void test() throws Exception { - Data data = new DataString("fanfan"); - encodeRecodeTest(this, data); - data = new DataString("http://example.com/query.html"); - encodeRecodeTest(this, data); - data = new DataString("Test|Ché|http://|\"\\\"Pouch\\"); - encodeRecodeTest(this, data); - data = new DataString("Test|Ché\\n|\nhttp://|\"\\\"Pouch\\"); - encodeRecodeTest(this, data); - } - }); - addTest(new TestCase("Import/Export with nested objects forming a loop") { - @Override - public void test() throws Exception { - DataLoop data = new DataLoop("looping"); - data.next = new DataLoop("level 2"); - data.next.next = data; - encodeRecodeTest(this, data); - } - }); - addTest(new TestCase("Array in Object Import/Export") { - @Override - public void test() throws Exception { - Object data = new DataArray();// new String[] { "un", "deux" }; - encodeRecodeTest(this, data); - } - }); - addTest(new TestCase("Array Import/Export") { - @Override - public void test() throws Exception { - Object data = new String[] { "un", "deux" }; - encodeRecodeTest(this, data); - } - }); - addTest(new TestCase("Enum Import/Export") { - @Override - public void test() throws Exception { - Object data = EnumToSend.FANFAN; - encodeRecodeTest(this, data); - } - }); - } - - class DataArray { - public String[] data = new String[] { "un", "deux" }; - } - - class Data { - private int value; - - private Data() { - } - - public Data(int value) { - this.value = value; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof Data) { - Data other = (Data) obj; - return other.value == this.value; - } - - return false; - } - - @Override - public int hashCode() { - return new Integer(value).hashCode(); - } - } - - @SuppressWarnings("unused") - class DataObject extends Data { - private Data data; - - @SuppressWarnings("synthetic-access") - private DataObject() { - } - - @SuppressWarnings("synthetic-access") - public DataObject(Data data) { - this.data = data; - } - } - - @SuppressWarnings("unused") - class DataString extends Data { - private String data; - - @SuppressWarnings("synthetic-access") - private DataString() { - } - - @SuppressWarnings("synthetic-access") - public DataString(String data) { - this.data = data; - } - } - - @SuppressWarnings("unused") - class DataLoop extends Data { - public DataLoop next; - private String value; - - @SuppressWarnings("synthetic-access") - private DataLoop() { - } - - @SuppressWarnings("synthetic-access") - public DataLoop(String value) { - this.value = value; - } - } - - enum EnumToSend { - FANFAN, TULIPE, - } -} diff --git a/src/be/nikiroo/utils/test_code/StringUtilsTest.java b/src/be/nikiroo/utils/test_code/StringUtilsTest.java deleted file mode 100644 index a441195..0000000 --- a/src/be/nikiroo/utils/test_code/StringUtilsTest.java +++ /dev/null @@ -1,304 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import be.nikiroo.utils.StringUtils; -import be.nikiroo.utils.StringUtils.Alignment; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class StringUtilsTest extends TestLauncher { - public StringUtilsTest(String[] args) { - super("StringUtils test", args); - - addTest(new TestCase("Time serialisation") { - @Override - public void test() throws Exception { - for (long fullTime : new Long[] { 0l, 123456l, 123456000l, - new Date().getTime() }) { - // precise to the second, no more - long time = (fullTime / 1000) * 1000; - - String displayTime = StringUtils.fromTime(time); - assertNotNull("The stringified time for " + time - + " should not be null", displayTime); - assertEquals("The stringified time for " + time - + " should not be empty", false, displayTime.trim() - .isEmpty()); - - assertEquals("The time " + time - + " should be loop-convertable", time, - StringUtils.toTime(displayTime)); - - assertEquals("The time " + displayTime - + " should be loop-convertable", displayTime, - StringUtils.fromTime(StringUtils - .toTime(displayTime))); - } - } - }); - - addTest(new TestCase("MD5") { - @Override - public void test() throws Exception { - String mess = "The String we got is not what 'md5sum' said it should heve been"; - assertEquals(mess, "34ded48fcff4221d644be9a37e1cb1d9", - StringUtils.getMd5Hash("fanfan la tulipe")); - assertEquals(mess, "7691b0cb74ed0f94b4d8cd858abe1165", - StringUtils.getMd5Hash("je te do-o-o-o-o-o-nne")); - } - }); - - addTest(new TestCase("Padding") { - @Override - public void test() throws Exception { - for (String data : new String[] { "fanfan", "la tulipe", - "1234567890", "12345678901234567890", "1", "" }) { - String result = StringUtils.padString(data, -1); - assertEquals("A size of -1 is expected to produce a noop", - true, data.equals(result)); - for (int size : new Integer[] { 0, 1, 5, 10, 40 }) { - result = StringUtils.padString(data, size); - assertEquals( - "Padding a String at a certain size should give a String of the given size", - size, result.length()); - assertEquals( - "Padding a String should not change the content", - true, data.trim().startsWith(result.trim())); - - result = StringUtils.padString(data, size, false, null); - assertEquals( - "Padding a String without cutting should not shorten the String", - true, data.length() <= result.length()); - assertEquals( - "Padding a String without cutting should keep the whole content", - true, data.trim().equals(result.trim())); - - result = StringUtils.padString(data, size, false, - Alignment.RIGHT); - if (size > data.length()) { - assertEquals( - "Padding a String to the end should work as expected", - true, result.endsWith(data)); - } - - result = StringUtils.padString(data, size, false, - Alignment.JUSTIFY); - if (size > data.length()) { - String unspacedData = data.trim(); - String unspacedResult = result.trim(); - for (int i = 0; i < size; i++) { - unspacedData = unspacedData.replace(" ", " "); - unspacedResult = unspacedResult.replace(" ", - " "); - } - - assertEquals( - "Justified text trimmed with all spaces collapsed " - + "sould be identical to original text " - + "trimmed with all spaces collapsed", - unspacedData, unspacedResult); - } - - result = StringUtils.padString(data, size, false, - Alignment.CENTER); - if (size > data.length()) { - int before = 0; - for (int i = 0; i < result.length() - && result.charAt(i) == ' '; i++) { - before++; - } - - int after = 0; - for (int i = result.length() - 1; i >= 0 - && result.charAt(i) == ' '; i--) { - after++; - } - - if (result.trim().isEmpty()) { - after = before / 2; - if (before > (2 * after)) { - before = after + 1; - } else { - before = after; - } - } - - assertEquals( - "Padding a String on center should work as expected", - result.length(), before + data.length() - + after); - assertEquals( - "Padding a String on center should not uncenter the content", - true, Math.abs(before - after) <= 1); - } - } - } - } - }); - - addTest(new TestCase("Justifying") { - @Override - public void test() throws Exception { - Map>>> source = new HashMap>>>(); - addValue(source, Alignment.LEFT, "testy", -1, "testy"); - addValue(source, Alignment.RIGHT, "testy", -1, "testy"); - addValue(source, Alignment.CENTER, "testy", -1, "testy"); - addValue(source, Alignment.JUSTIFY, "testy", -1, "testy"); - addValue(source, Alignment.LEFT, "testy", 5, "testy"); - addValue(source, Alignment.LEFT, "testy", 3, "te-", "sty"); - addValue(source, Alignment.LEFT, - "Un petit texte qui se mettra sur plusieurs lignes", - 10, "Un petit", "texte qui", "se mettra", "sur", - "plusieurs", "lignes"); - addValue(source, Alignment.LEFT, - "Un petit texte qui se mettra sur plusieurs lignes", 7, - "Un", "petit", "texte", "qui se", "mettra", "sur", - "plusie-", "urs", "lignes"); - addValue(source, Alignment.RIGHT, - "Un petit texte qui se mettra sur plusieurs lignes", 7, - " Un", " petit", " texte", " qui se", " mettra", - " sur", "plusie-", " urs", " lignes"); - addValue(source, Alignment.CENTER, - "Un petit texte qui se mettra sur plusieurs lignes", 7, - " Un ", " petit ", " texte ", "qui se ", "mettra ", - " sur ", "plusie-", " urs ", "lignes "); - addValue(source, Alignment.JUSTIFY, - "Un petit texte qui se mettra sur plusieurs lignes", 7, - "Un pet-", "it tex-", "te qui", "se met-", "tra sur", - "plusie-", "urs li-", "gnes"); - addValue(source, Alignment.JUSTIFY, - "Un petit texte qui se mettra sur plusieurs lignes", - 14, "Un petit", "texte qui se", - "mettra sur", "plusieurs lig-", "nes"); - addValue(source, Alignment.JUSTIFY, "le dash-test", 9, - "le dash-", "test"); - - for (String data : source.keySet()) { - for (int size : source.get(data).keySet()) { - Alignment align = source.get(data).get(size).getKey(); - List values = source.get(data).get(size) - .getValue(); - - List result = StringUtils.justifyText(data, - size, align); - - // System.out.println("[" + data + " (" + size + ")" + - // "] -> ["); - // for (int i = 0; i < result.size(); i++) { - // String resultLine = result.get(i); - // System.out.println(i + ": " + resultLine); - // } - // System.out.println("]"); - - assertEquals(values, result); - } - } - } - }); - - addTest(new TestCase("unhtml") { - @Override - public void test() throws Exception { - Map data = new HashMap(); - data.put("aa", "aa"); - data.put("test with spaces ", "test with spaces "); - data.put("link", "link"); - data.put("Digimon", "Digimon"); - data.put("", ""); - data.put(" ", " "); - - for (Entry entry : data.entrySet()) { - String result = StringUtils.unhtml(entry.getKey()); - assertEquals("Result is not what we expected", - entry.getValue(), result); - } - } - }); - - addTest(new TestCase("zip64") { - @Override - public void test() throws Exception { - String orig = "test"; - String zipped = StringUtils.zip64(orig); - String unzipped = StringUtils.unzip64s(zipped); - assertEquals(orig, unzipped); - } - }); - - addTest(new TestCase("format/toNumber simple") { - @Override - public void test() throws Exception { - assertEquals(263l, StringUtils.toNumber("263")); - assertEquals(21200l, StringUtils.toNumber("21200")); - assertEquals(0l, StringUtils.toNumber("0")); - assertEquals("263", StringUtils.formatNumber(263l)); - assertEquals("21 k", StringUtils.formatNumber(21000l)); - assertEquals("0", StringUtils.formatNumber(0l)); - } - }); - - addTest(new TestCase("format/toNumber not 000") { - @Override - public void test() throws Exception { - assertEquals(263200l, StringUtils.toNumber("263.2 k")); - assertEquals(42000l, StringUtils.toNumber("42.0 k")); - assertEquals(12000000l, StringUtils.toNumber("12 M")); - assertEquals(2000000000l, StringUtils.toNumber("2 G")); - assertEquals("263 k", StringUtils.formatNumber(263012l)); - assertEquals("42 k", StringUtils.formatNumber(42012l)); - assertEquals("12 M", StringUtils.formatNumber(12012121l)); - assertEquals("7 G", StringUtils.formatNumber(7364635928l)); - } - }); - - addTest(new TestCase("format/toNumber decimals") { - @Override - public void test() throws Exception { - assertEquals(263200l, StringUtils.toNumber("263.2 k")); - assertEquals(1200l, StringUtils.toNumber("1.2 k")); - assertEquals(42700000l, StringUtils.toNumber("42.7 M")); - assertEquals(1220l, StringUtils.toNumber("1.22 k")); - assertEquals(1432l, StringUtils.toNumber("1.432 k")); - assertEquals(6938l, StringUtils.toNumber("6.938 k")); - assertEquals("1.3 k", StringUtils.formatNumber(1300l, 1)); - assertEquals("263.2020 k", StringUtils.formatNumber(263202l, 4)); - assertEquals("1.26 k", StringUtils.formatNumber(1267l, 2)); - assertEquals("42.7 M", StringUtils.formatNumber(42712121l, 1)); - assertEquals("5.09 G", StringUtils.formatNumber(5094837485l, 2)); - } - }); - } - - static private void addValue( - Map>>> source, - final Alignment align, String input, int size, - final String... result) { - if (!source.containsKey(input)) { - source.put(input, - new HashMap>>()); - } - - source.get(input).put(size, new Entry>() { - @Override - public Alignment getKey() { - return align; - } - - @Override - public List getValue() { - return Arrays.asList(result); - } - - @Override - public List setValue(List value) { - return null; - } - }); - } -} diff --git a/src/be/nikiroo/utils/test_code/TempFilesTest.java b/src/be/nikiroo/utils/test_code/TempFilesTest.java deleted file mode 100644 index dad4cac..0000000 --- a/src/be/nikiroo/utils/test_code/TempFilesTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package be.nikiroo.utils.test_code; - -import java.io.File; -import java.io.IOException; - -import be.nikiroo.utils.TempFiles; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class TempFilesTest extends TestLauncher { - public TempFilesTest(String[] args) { - super("TempFiles", args); - - addTest(new TestCase("Name is correct") { - @Override - public void test() throws Exception { - RootTempFiles files = new RootTempFiles("testy"); - try { - assertEquals("The root was not created", true, files - .getRoot().exists()); - assertEquals( - "The root is not prefixed with the expected name", - true, files.getRoot().getName().startsWith("testy")); - - } finally { - files.close(); - } - } - }); - - addTest(new TestCase("Clean after itself no use") { - @Override - public void test() throws Exception { - RootTempFiles files = new RootTempFiles("testy2"); - try { - assertEquals("The root was not created", true, files - .getRoot().exists()); - } finally { - files.close(); - assertEquals("The root was not deleted", false, files - .getRoot().exists()); - } - } - }); - - addTest(new TestCase("Clean after itself after usage") { - @Override - public void test() throws Exception { - RootTempFiles files = new RootTempFiles("testy3"); - try { - assertEquals("The root was not created", true, files - .getRoot().exists()); - files.createTempFile("test"); - } finally { - files.close(); - assertEquals("The root was not deleted", false, files - .getRoot().exists()); - assertEquals("The main root in /tmp was not deleted", - false, files.getRoot().getParentFile().exists()); - } - } - }); - - addTest(new TestCase("Temporary directories") { - @Override - public void test() throws Exception { - RootTempFiles files = new RootTempFiles("testy4"); - File file = null; - try { - File dir = files.createTempDir("test"); - file = new File(dir, "fanfan"); - file.createNewFile(); - assertEquals( - "Cannot create a file in a temporary directory", - true, file.exists()); - } finally { - files.close(); - if (file != null) { - assertEquals( - "A file created in a temporary directory should be deleted on close", - false, file.exists()); - } - assertEquals("The root was not deleted", false, files - .getRoot().exists()); - } - } - }); - } - - private class RootTempFiles extends TempFiles { - private File root = null; - - public RootTempFiles(String name) throws IOException { - super(name); - } - - public File getRoot() { - if (root != null) - return root; - return super.root; - } - - @Override - public synchronized void close() throws IOException { - root = super.root; - super.close(); - } - } -} diff --git a/src/be/nikiroo/utils/test_code/Test.java b/src/be/nikiroo/utils/test_code/Test.java deleted file mode 100644 index 8d99cba..0000000 --- a/src/be/nikiroo/utils/test_code/Test.java +++ /dev/null @@ -1,68 +0,0 @@ -package be.nikiroo.utils.test_code; - -import be.nikiroo.utils.Cache; -import be.nikiroo.utils.CacheMemory; -import be.nikiroo.utils.Downloader; -import be.nikiroo.utils.Proxy; -import be.nikiroo.utils.main.bridge; -import be.nikiroo.utils.main.img2aa; -import be.nikiroo.utils.main.justify; -import be.nikiroo.utils.test.TestLauncher; -import be.nikiroo.utils.ui.UIUtils; - -/** - * Tests for nikiroo-utils. - * - * @author niki - */ -public class Test extends TestLauncher { - /** - * Start the tests. - * - * @param args - * the arguments (which are passed as-is to the other test - * classes) - */ - public Test(String[] args) { - super("Nikiroo-utils", args); - - // setDetails(true); - - addSeries(new ProgressTest(args)); - addSeries(new BundleTest(args)); - addSeries(new IOUtilsTest(args)); - addSeries(new VersionTest(args)); - addSeries(new SerialTest(args)); - addSeries(new SerialServerTest(args)); - addSeries(new StringUtilsTest(args)); - addSeries(new TempFilesTest(args)); - addSeries(new CryptUtilsTest(args)); - addSeries(new BufferedInputStreamTest(args)); - addSeries(new NextableInputStreamTest(args)); - addSeries(new ReplaceInputStreamTest(args)); - addSeries(new BufferedOutputStreamTest(args)); - addSeries(new ReplaceOutputStreamTest(args)); - - // TODO: test cache and downloader - Cache cache = null; - CacheMemory memcache = null; - Downloader downloader = null; - - // To include the sources: - img2aa siu; - justify ssu; - bridge aa; - Proxy proxy; - UIUtils uiUtils; - } - - /** - * Main entry point of the program. - * - * @param args - * the arguments passed to the {@link TestLauncher}s. - */ - static public void main(String[] args) { - System.exit(new Test(args).launch()); - } -} diff --git a/src/be/nikiroo/utils/test_code/VersionTest.java b/src/be/nikiroo/utils/test_code/VersionTest.java deleted file mode 100644 index 2d84476..0000000 --- a/src/be/nikiroo/utils/test_code/VersionTest.java +++ /dev/null @@ -1,140 +0,0 @@ -package be.nikiroo.utils.test_code; - -import be.nikiroo.utils.Version; -import be.nikiroo.utils.test.TestCase; -import be.nikiroo.utils.test.TestLauncher; - -class VersionTest extends TestLauncher { - public VersionTest(String[] args) { - super("Version test", args); - - addTest(new TestCase("String <-> int") { - @Override - public void test() throws Exception { - assertEquals("Cannot parse version 1.2.3 from int to String", - "1.2.3", new Version(1, 2, 3).toString()); - assertEquals( - "Cannot parse major version \"1.2.3\" from String to int", - 1, new Version("1.2.3").getMajor()); - assertEquals( - "Cannot parse minor version \"1.2.3\" from String to int", - 2, new Version("1.2.3").getMinor()); - assertEquals( - "Cannot parse patch version \"1.2.3\" from String to int", - 3, new Version("1.2.3").getPatch()); - } - }); - - addTest(new TestCase("Bad input") { - @Override - public void test() throws Exception { - assertEquals( - "Bad input should return an empty version", - true, - new Version( - "Doors 98 SE Special Deluxe Edition Pro++ Not-Home") - .isEmpty()); - - assertEquals( - "Bad input should return [unknown]", - "[unknown]", - new Version( - "Doors 98 SE Special Deluxe Edition Pro++ Not-Home") - .toString()); - } - }); - - addTest(new TestCase("Read current version") { - @Override - public void test() throws Exception { - assertNotNull("The version should not be NULL (in any case!)", - Version.getCurrentVersion()); - assertEquals("The current version should not be empty", false, - Version.getCurrentVersion().isEmpty()); - } - }); - - addTest(new TestCase("Tag version") { - @Override - public void test() throws Exception { - Version version = new Version("1.0.0-debian0"); - assertEquals("debian", version.getTag()); - assertEquals(0, version.getTagVersion()); - version = new Version("1.0.0-debian.0"); - assertEquals("debian.", version.getTag()); - assertEquals(0, version.getTagVersion()); - version = new Version("1.0.0-debian-0"); - assertEquals("debian-", version.getTag()); - assertEquals(0, version.getTagVersion()); - version = new Version("1.0.0-debian-12"); - assertEquals("debian-", version.getTag()); - assertEquals(12, version.getTagVersion()); - - // tag with no tag version - version = new Version("1.0.0-dev"); - assertEquals(1, version.getMajor()); - assertEquals(0, version.getMinor()); - assertEquals(0, version.getPatch()); - assertEquals("dev", version.getTag()); - assertEquals(-1, version.getTagVersion()); - } - }); - - addTest(new TestCase("Comparing versions") { - @Override - public void test() throws Exception { - assertEquals(true, - new Version(1, 1, 1).isNewerThan(new Version(1, 1, 0))); - assertEquals(true, - new Version(2, 0, 0).isNewerThan(new Version(1, 1, 1))); - assertEquals(true, - new Version(10, 7, 8).isNewerThan(new Version(9, 9, 9))); - assertEquals(true, - new Version(0, 0, 0).isOlderThan(new Version(0, 0, 1))); - assertEquals(1, - new Version(1, 1, 1).compareTo(new Version(0, 1, 1))); - assertEquals(-1, - new Version(0, 0, 1).compareTo(new Version(0, 1, 1))); - assertEquals(0, - new Version(0, 0, 1).compareTo(new Version(0, 0, 1))); - assertEquals(true, - new Version(0, 0, 1).equals(new Version(0, 0, 1))); - assertEquals(false, - new Version(0, 2, 1).equals(new Version(0, 0, 1))); - - assertEquals(true, - new Version(1, 0, 1, "my.tag.", 2).equals(new Version( - 1, 0, 1, "my.tag.", 2))); - assertEquals(false, - new Version(1, 0, 1, "my.tag.", 2).equals(new Version( - 1, 0, 0, "my.tag.", 2))); - assertEquals(false, - new Version(1, 0, 1, "my.tag.", 2).equals(new Version( - 1, 0, 1, "not-my.tag.", 2))); - } - }); - - addTest(new TestCase("toString") { - @Override - public void test() throws Exception { - // Check leading 0s: - Version version = new Version("01.002.4"); - assertEquals("Leading 0s not working", "1.2.4", - version.toString()); - - // Check spacing - version = new Version("1 . 2.4 "); - assertEquals("Additional spaces not working", "1.2.4", - version.toString()); - - String[] tests = new String[] { "1.0.0", "1.2.3", "1.0.0-dev", - "1.1.2-niki0" }; - for (String test : tests) { - version = new Version(test); - assertEquals("toString and back conversion failed", test, - version.toString()); - } - } - }); - } -} diff --git a/src/be/nikiroo/utils/test_code/bundle_test.properties b/src/be/nikiroo/utils/test_code/bundle_test.properties deleted file mode 100644 index 5222c59..0000000 --- a/src/be/nikiroo/utils/test_code/bundle_test.properties +++ /dev/null @@ -1,3 +0,0 @@ -ONE = un -ONE_SUFFIX = un + suffix -JAPANESE = 日本語 Nihongo \ No newline at end of file diff --git a/src/be/nikiroo/utils/ui/ConfigEditor.java b/src/be/nikiroo/utils/ui/ConfigEditor.java deleted file mode 100644 index c687c98..0000000 --- a/src/be/nikiroo/utils/ui/ConfigEditor.java +++ /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}. - *

- * 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 - * the type of {@link Bundle} to edit - */ -public class ConfigEditor> extends JPanel { - private static final long serialVersionUID = 1L; - private List> 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 type, final Bundle 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>(); - List> groupedItems = MetaInfo.getItems(type, bundle); - for (MetaInfo 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 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 item : items) { - item.reload(); - } - bundle.reload(false); - bundle.restoreSnapshot(snap); - } - })); - - buttons.add(createButton("Save", new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - for (MetaInfo 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 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 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 index 1f69886..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItem.java +++ /dev/null @@ -1,764 +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.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -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}. - *

- * 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 - * the type of {@link Bundle} to edit - */ -public abstract class ConfigItem> 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 original value before current changes. */ - private Object orig; - private List origs = new ArrayList(); - private List dirtyBits; - - /** The fields (one for non-array, a list for arrays). */ - private JComponent field; - private List fields = new ArrayList(); - - /** The fields to panel map to get the actual item added to 'main'. */ - private Map itemFields = new HashMap(); - - /** The main panel with all the fields in it. */ - private JPanel main; - - /** The {@link MetaInfo} linked to the field. */ - protected MetaInfo info; - - /** - * 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 (info.isArray()) { - this.setLayout(new BorderLayout()); - add(label(nhgap), BorderLayout.WEST); - - main = new JPanel(); - - main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS)); - int size = info.getListSize(false); - for (int i = 0; i < size; i++) { - addItem(i); - } - main.revalidate(); - main.repaint(); - - final JButton add = new JButton(); - setImage(add, img64add, "+"); - - add.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - addItem(fields.size()); - 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 = createField(-1); - add(field, BorderLayout.CENTER); - } - } - - private void addItem(final int item) { - JPanel minusPanel = new JPanel(new BorderLayout()); - itemFields.put(item, minusPanel); - - JComponent field = createField(item); - - final JButton remove = new JButton(); - setImage(remove, img64remove, "-"); - - remove.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - removeItem(item); - } - }); - - minusPanel.add(field, BorderLayout.CENTER); - minusPanel.add(remove, BorderLayout.EAST); - - main.add(minusPanel); - main.revalidate(); - main.repaint(); - } - - private void 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); - } - - main.remove(itemFields.remove(last)); - main.revalidate(); - main.repaint(); - } - - /** - * 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 info, boolean autoDirtyHandling) { - this.info = info; - if (!autoDirtyHandling) { - dirtyBits = new ArrayList(); - } - } - - /** - * Create an empty graphical component to be used later by - * {@link ConfigItem#createField(int)}. - *

- * 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 new field for the given graphical component at the given index - * (note that the component is usually created by - * {@link ConfigItem#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, JComponent 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 ConfigItem#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) { - 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. - *

- * Has no effect if the class is using automatic dirty handling (see - * {@link ConfigItem#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) { - if (dirtyBits != null) { - dirtyBits.add(item); - } - } - - /** - * Check if the value changed since the last load/save into the linked - * {@link MetaInfo}. - *

- * 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) { - // 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}. - */ - private void reload() { - if (info.isArray()) { - while (!itemFields.isEmpty()) { - main.remove(itemFields.remove(itemFields.size() - 1)); - } - main.revalidate(); - main.repaint(); - for (int item = 0; item < info.getListSize(false); item++) { - reload(item); - } - } else { - reload(-1); - } - } - - /** - * 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); - } - - 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. - *

- * This method does not 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 - */ - protected JComponent createField(final int item) { - JComponent 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(); - } - }); - - int height = Math - .max(getMinimumHeight(), field.getMinimumSize().height); - field.setPreferredSize(new Dimension(200, height)); - - return field; - } - - /** - * 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(info.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 = (info.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, - info.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 - * 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 > ConfigItem createItem( - MetaInfo info, int nhgap) { - - ConfigItem configItem; - switch (info.getFormat()) { - case BOOLEAN: - configItem = new ConfigItemBoolean(info); - break; - case COLOR: - configItem = new ConfigItemColor(info); - break; - case FILE: - configItem = new ConfigItemBrowse(info, false); - break; - case DIRECTORY: - configItem = new ConfigItemBrowse(info, true); - break; - case COMBO_LIST: - configItem = new ConfigItemCombobox(info, true); - break; - case FIXED_LIST: - configItem = new ConfigItemCombobox(info, false); - break; - case INT: - configItem = new ConfigItemInteger(info); - break; - case PASSWORD: - configItem = new ConfigItemPassword(info); - break; - case LOCALE: - configItem = new ConfigItemLocale(info); - break; - case STRING: - default: - configItem = new ConfigItemString(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/ConfigItemBoolean.java b/src/be/nikiroo/utils/ui/ConfigItemBoolean.java deleted file mode 100644 index 255ec13..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItemBoolean.java +++ /dev/null @@ -1,66 +0,0 @@ -package be.nikiroo.utils.ui; - -import javax.swing.JCheckBox; -import javax.swing.JComponent; - -import be.nikiroo.utils.resources.MetaInfo; - -class ConfigItemBoolean> extends ConfigItem { - private static final long serialVersionUID = 1L; - - /** - * Create a new {@link ConfigItemBoolean} for the given {@link MetaInfo}. - * - * @param info - * the {@link MetaInfo} - */ - public ConfigItemBoolean(MetaInfo 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 info.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) { - info.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 \"" - + info.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 index 6c8af99..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItemBrowse.java +++ /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> extends ConfigItem { - private static final long serialVersionUID = 1L; - - private boolean dir; - private Map fields = new HashMap(); - - /** - * 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 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 = info.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) { - info.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 index 500efff..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItemColor.java +++ /dev/null @@ -1,168 +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> extends ConfigItem { - private static final long serialVersionUID = 1L; - - private Map fields = new HashMap(); - private Map panels = new HashMap(); - - /** - * Create a new {@link ConfigItemColor} for the given {@link MetaInfo}. - * - * @param info - * the {@link MetaInfo} - */ - public ConfigItemColor(MetaInfo 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 info.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) { - info.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 = info.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, - info.getName(), initialColor); - if (newColor != null) { - info.setColor(newColor.getRGB(), item); - field.setText(info.getString(item, false)); - colorWheel.setIcon(getIcon(17, info.getColor(item, true))); - } - } - }); - - field.addKeyListener(new KeyAdapter() { - @Override - public void keyTyped(KeyEvent e) { - info.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 index b77e0a8..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItemCombobox.java +++ /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> extends ConfigItem { - 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 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 info.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) { - info.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 index 9b838a5..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItemInteger.java +++ /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> extends ConfigItem { - private static final long serialVersionUID = 1L; - - /** - * Create a new {@link ConfigItemInteger} for the given {@link MetaInfo}. - * - * @param info - * the {@link MetaInfo} - */ - public ConfigItemInteger(MetaInfo 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 info.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) { - info.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 index eef8da0..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItemLocale.java +++ /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> extends ConfigItemCombobox { - private static final long serialVersionUID = 1L; - - /** - * Create a new {@link ConfigItemLocale} for the given {@link MetaInfo}. - * - * @param info - * the {@link MetaInfo} - */ - public ConfigItemLocale(MetaInfo 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 index 348b78f..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItemPassword.java +++ /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> extends ConfigItem { - 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 fields = new HashMap(); - - /** - * Create a new {@link ConfigItemPassword} for the given {@link MetaInfo}. - * - * @param info - * the {@link MetaInfo} - */ - public ConfigItemPassword(MetaInfo 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 info.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) { - info.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 index 99a8cc3..0000000 --- a/src/be/nikiroo/utils/ui/ConfigItemString.java +++ /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> extends ConfigItem { - private static final long serialVersionUID = 1L; - - /** - * Create a new {@link ConfigItemString} for the given {@link MetaInfo}. - * - * @param info - * the {@link MetaInfo} - */ - public ConfigItemString(MetaInfo 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 info.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) { - info.setString((String) value, item); - } - - @Override - protected JComponent createEmptyField(int item) { - return new JTextField(); - } -} diff --git a/src/be/nikiroo/utils/ui/ImageTextAwt.java b/src/be/nikiroo/utils/ui/ImageTextAwt.java deleted file mode 100644 index 4c0c824..0000000 --- a/src/be/nikiroo/utils/ui/ImageTextAwt.java +++ /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 - *

    - *
  • space (blank)
  • - *
  • low shade (░)
  • - *
  • medium shade (▒)
  • - *
  • high shade (▓)
  • - *
  • full block (█)
  • - *
- */ - 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; MUST 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. - * - *

- * 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). - *

- * 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 index 4cf12c0..0000000 --- a/src/be/nikiroo/utils/ui/ImageUtilsAwt.java +++ /dev/null @@ -1,180 +0,0 @@ -package be.nikiroo.utils.ui; - -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.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 { - @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 { - 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 { - 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); - } - - // 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 (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; - } -} diff --git a/src/be/nikiroo/utils/ui/ProgressBar.java b/src/be/nikiroo/utils/ui/ProgressBar.java deleted file mode 100644 index 219cde9..0000000 --- a/src/be/nikiroo/utils/ui/ProgressBar.java +++ /dev/null @@ -1,183 +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}. - *

- * This control is NOT thread-safe. - * - * @author niki - */ -public class ProgressBar extends JPanel { - private static final long serialVersionUID = 1L; - - private Map bars; - private List actionListeners; - private List updateListeners; - private Progress pg; - private Object lock = new Object(); - - public ProgressBar() { - bars = new HashMap(); - actionListeners = new ArrayList(); - updateListeners = new ArrayList(); - } - - 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 newBars = new HashMap(); - 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 getChildrenAsOrderedList(Progress pg) { - List children = new ArrayList(); - - 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/UIUtils.java b/src/be/nikiroo/utils/ui/UIUtils.java deleted file mode 100644 index 24cbf64..0000000 --- a/src/be/nikiroo/utils/ui/UIUtils.java +++ /dev/null @@ -1,118 +0,0 @@ -package be.nikiroo.utils.ui; - -import java.awt.Color; -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 javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; - -/** - * Some Java Swing utilities. - * - * @author niki - */ -public class UIUtils { - /** - * Set a fake "native look & feel" for the application if possible - * (check for the one currently in use, then try GTK). - *

- * Must be called prior to any GUI work. - */ - static public void setLookAndFeel() { - // native look & feel - try { - String noLF = "javax.swing.plaf.metal.MetalLookAndFeel"; - String lf = UIManager.getSystemLookAndFeelClassName(); - if (lf.equals(noLF)) - lf = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"; - UIManager.setLookAndFeel(lf); - } catch (InstantiationException e) { - } catch (ClassNotFoundException e) { - } catch (UnsupportedLookAndFeelException e) { - } catch (IllegalAccessException e) { - } - } - - /** - * 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) { - 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); - g2.fillOval(x, y, width, height); - - // Compute dark/bright colours - Paint p = null; - Color dark = color.darker(); - Color bright = color.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); - g2.fillOval(x, y, width, height); - - // Adds highlights at the top right - p = new GradientPaint(width, 0, bright, 0, height, brightEnd); - g2.setPaint(p); - g2.fillOval(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); - g2.fillOval(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); - g2.fillOval(x * 2, y, width, height); - - // Reset original paint - g2.setPaint(oldPaint); - } else { - g.setColor(color); - g.fillOval(x, y, width, height); - } - } -} diff --git a/src/be/nikiroo/utils/ui/WrapLayout.java b/src/be/nikiroo/utils/ui/WrapLayout.java deleted file mode 100644 index 7f34d79..0000000 --- a/src/be/nikiroo/utils/ui/WrapLayout.java +++ /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 WrapLayout with a left alignment and a - * default 5-unit horizontal and vertical gap. - */ - public WrapLayout() { - super(); - } - - /** - * Constructs a new FlowLayout with the specified alignment and - * a default 5-unit horizontal and vertical gap. The value of the alignment - * argument must be one of WrapLayout, WrapLayout, - * or WrapLayout. - * - * @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. - *

- * The value of the alignment argument must be one of - * WrapLayout, WrapLayout, or - * WrapLayout. - * - * @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 visible - * 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 visible - * 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/test/ProgressBarManualTest.java b/src/be/nikiroo/utils/ui/test/ProgressBarManualTest.java deleted file mode 100644 index b416cbc..0000000 --- a/src/be/nikiroo/utils/ui/test/ProgressBarManualTest.java +++ /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 index c260295..0000000 --- a/src/be/nikiroo/utils/ui/test/TestUI.java +++ /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(); - } -}