From: Niki Roo Date: Sun, 3 Dec 2017 17:26:41 +0000 (+0100) Subject: Version 4.0.1: android compatibility X-Git-Tag: fanfix-swing-0.0.1~21^2~233 X-Git-Url: https://git.nikiroo.be/?a=commitdiff_plain;h=e8aa5bf9227a0d6a6d0bb6a8bc0cc04d0f4d601a;p=fanfix-swing.git Version 4.0.1: android compatibility --- diff --git a/Makefile.base b/Makefile.base index 31437788..aeebcbdb 100644 --- a/Makefile.base +++ b/Makefile.base @@ -2,6 +2,7 @@ # # Version: # - 1.0.0: add a version comment +# - 1.1.0: add help, sjar # Required parameters (the commented out ones are supposed to change per project): @@ -22,31 +23,34 @@ JAR = jar RJAR = java RJAR_FLAGS += -jar -# Usual options: -# make : to build the jar file -# make libs : to update the libraries into src/ -# make build : to update the binaries (not the jar) -# make test : to update the test binaries -# make build jar : to update the binaries and jar file -# make clean : to clean the directory of intermediate files -# make mrpropre : to clean the directory of all outputs -# make run : to run the program from the binaries -# make run-test : to run the test program from the binaries -# make jrun : to run the program from the jar file -# make install : to install the application into $PREFIX - -# Note: build is actually slower than rebuild in most cases except when -# small changes only are detected ; so we use rebuild by default - all: build jar -.PHONY: all clean mrproper mrpropre build run jrun jar resources test-resources install libs love +help: + @echo "Usual options:" + @echo "==============" + @echo " make : to build the jar file" + @echo " make help : to get this help screen" + @echo " make libs : to update the libraries into src/" + @echo " make build : to update the binaries (not the jar)" + @echo " make test : to update the test binaries" + @echo " make build jar : to update the binaries and jar file" + @echo " make sjar : to create the sources jar file" + @echo " make clean : to clean the directory of intermediate files" + @echo " make mrpropre : to clean the directory of all outputs" + @echo " make run : to run the program from the binaries" + @echo " make run-test : to run the test program from the binaries" + @echo " make jrun : to run the program from the jar file" + @echo " make install : to install the application into $PREFIX" + +.PHONY: all clean mrproper mrpropre build run jrun jar sjar resources test-resources install libs love bin: @mkdir -p bin jar: $(NAME).jar +sjar: $(NAME)-sources.jar + build: resources @echo Compiling program... @echo " src/$(MAIN)" @@ -123,15 +127,17 @@ libs: bin done ) @[ ! -d libs ] || touch bin/libs -$(NAME).jar: resources - @[ -e bin/$(MAIN).class ] || echo You need to build the sources - @[ -e bin/$(MAIN).class ] - @echo Making JAR file... +$(NAME)-sources.jar: libs + @echo Making sources JAR file... @echo > bin/manifest @[ "$(SJAR_FLAGS)" = "" ] || echo Creating $(NAME)-sources.jar... @[ "$(SJAR_FLAGS)" = "" ] || $(JAR) cfm $(NAME)-sources.jar bin/manifest $(SJAR_FLAGS) @[ "$(SJAR_FLAGS)" = "" ] || [ ! -e VERSION ] || echo Copying to "$(NAME)-`cat VERSION`-sources.jar"... @[ "$(SJAR_FLAGS)" = "" ] || [ ! -e VERSION ] || cp $(NAME)-sources.jar "$(NAME)-`cat VERSION`-sources.jar" + +$(NAME).jar: resources + @[ -e bin/$(MAIN).class ] || echo You need to build the sources + @[ -e bin/$(MAIN).class ] @echo "Main-Class: `echo "$(MAIN)" | sed 's:/:.:g'`" > bin/manifest @echo >> bin/manifest $(JAR) cfm $(NAME).jar bin/manifest $(JAR_FLAGS) diff --git a/VERSION b/VERSION index fcdb2e10..1454f6ed 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.0 +4.0.1 diff --git a/changelog.md b/changelog.md index b54742cd..0355d593 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # nikiroo-utils +## Version 4.0.1 + +- Android compatibility (see configure.sh --android=yes) + ## Version 4.0.0 - Deplace all dependencies on java.awt into its own package (ui) diff --git a/src/be/nikiroo/utils/Cache.java b/src/be/nikiroo/utils/Cache.java index 111fc769..2b32d78d 100644 --- a/src/be/nikiroo/utils/Cache.java +++ b/src/be/nikiroo/utils/Cache.java @@ -21,6 +21,12 @@ public class Cache { private long tooOldStable; private TraceHandler tracer = new TraceHandler(); + /** + * Only for inheritance. + */ + protected Cache() { + } + /** * Create a new {@link Cache} object. * @@ -77,6 +83,24 @@ public class Cache { 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. * @@ -92,9 +116,26 @@ public class Cache { * */ public boolean check(URL url, boolean allowTooOld, boolean stable) { - File file = getCached(url); - if (file.exists() && file.isFile()) { - if (allowTooOld || !isOld(file, 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)) { return true; } } @@ -214,15 +255,13 @@ public class Cache { * @param uniqueID * a unique ID used to locate the cached resource * - * @return the resulting {@link File} - * * @throws IOException * in case of I/O error */ - public File save(InputStream in, String uniqueID) throws IOException { + public void save(InputStream in, String uniqueID) throws IOException { File cached = getCached(uniqueID); cached.getParentFile().mkdirs(); - return save(in, cached); + save(in, cached); } /** @@ -233,14 +272,12 @@ public class Cache { * @param url * the {@link URL} used to locate the cached resource * - * @return the actual cache file - * * @throws IOException * in case of I/O error */ - public File save(InputStream in, URL url) throws IOException { + public void save(InputStream in, URL url) throws IOException { File cached = getCached(url); - return save(in, cached); + save(in, cached); } /** @@ -251,14 +288,11 @@ public class Cache { * @param cached * the cached {@link File} to save to * - * @return the actual cache file - * * @throws IOException * in case of I/O error */ - private File save(InputStream in, File cached) throws IOException { + private void save(InputStream in, File cached) throws IOException { IOUtils.write(in, cached); - return cached; } /** diff --git a/src/be/nikiroo/utils/CacheMemory.java b/src/be/nikiroo/utils/CacheMemory.java new file mode 100644 index 00000000..8f9e7253 --- /dev/null +++ b/src/be/nikiroo/utils/CacheMemory.java @@ -0,0 +1,105 @@ +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 void save(InputStream in, String uniqueID) throws IOException { + data.put(getKey(uniqueID), IOUtils.toByteArray(in)); + } + + @Override + public void save(InputStream in, URL url) throws IOException { + data.put(getKey(url), IOUtils.toByteArray(in)); + } + + /** + * 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/ImageUtils.java b/src/be/nikiroo/utils/ImageUtils.java index 37d73191..cacff8d5 100644 --- a/src/be/nikiroo/utils/ImageUtils.java +++ b/src/be/nikiroo/utils/ImageUtils.java @@ -196,7 +196,8 @@ public abstract class ImageUtils { * @return the {@link ImageUtils} */ private static ImageUtils newObject() { - for (String clazz : new String[] { "be.nikiroo.utils.ui.ImageUtilsAwt" }) { + for (String clazz : new String[] { "be.nikiroo.utils.ui.ImageUtilsAwt", + "be.nikiroo.utils.android.ImageUtilsAndroid" }) { try { return (ImageUtils) SerialUtils.createObject(clazz); } catch (Exception e) { diff --git a/src/be/nikiroo/utils/StringUtils.java b/src/be/nikiroo/utils/StringUtils.java index a6117a04..1e9d7ed7 100644 --- a/src/be/nikiroo/utils/StringUtils.java +++ b/src/be/nikiroo/utils/StringUtils.java @@ -36,8 +36,7 @@ public class StringUtils { End } - static private Pattern marks = Pattern - .compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+"); + static private Pattern marks = getMarks(); /** * Fix the size of the given {@link String} either with space-padding or by @@ -146,7 +145,9 @@ public class StringUtils { if (removeAllAccents) { input = Normalizer.normalize(input, Form.NFKD); - input = marks.matcher(input).replaceAll(""); + if (marks != null) { + input = marks.matcher(input).replaceAll(""); + } } input = Normalizer.normalize(input, Form.NFKC); @@ -195,15 +196,15 @@ public class StringUtils { * 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 + * "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) { + static public long toTime(String displayTime) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - try { - return sdf.parse(displayTime).getTime(); - } catch (ParseException e) { - return -1; - } + return sdf.parse(displayTime).getTime(); } /** @@ -340,4 +341,19 @@ public class StringUtils { scan.close(); } } + + /** + * 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; + } + } } diff --git a/src/be/nikiroo/utils/android/ImageUtilsAndroid.java b/src/be/nikiroo/utils/android/ImageUtilsAndroid.java new file mode 100644 index 00000000..483c44ff --- /dev/null +++ b/src/be/nikiroo/utils/android/ImageUtilsAndroid.java @@ -0,0 +1,70 @@ +package be.nikiroo.utils.android; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import be.nikiroo.utils.Image; +import be.nikiroo.utils.ImageUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * This class offer some utilities based around images and uses the Android framework. + * + * @author niki + */ +public class ImageUtilsAndroid extends ImageUtils { + @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 change: 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 { + Bitmap image = BitmapFactory.decodeByteArray(img.getData(), 0, + img.getData().length); + if (image == null) { + throw new IOException("Failed to convert input to image"); + } + + return image; + } +} diff --git a/src/be/nikiroo/utils/android/test/TestAndroid.java b/src/be/nikiroo/utils/android/test/TestAndroid.java new file mode 100644 index 00000000..2ded4e16 --- /dev/null +++ b/src/be/nikiroo/utils/android/test/TestAndroid.java @@ -0,0 +1,7 @@ +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/resources/Bundle.java b/src/be/nikiroo/utils/resources/Bundle.java index 3c448efa..92b02eb4 100644 --- a/src/be/nikiroo/utils/resources/Bundle.java +++ b/src/be/nikiroo/utils/resources/Bundle.java @@ -258,9 +258,9 @@ public class Bundle> { * @return the associated value */ public char getCharacter(E id, char def) { - String s = getString(id).trim(); - if (s.length() > 0) { - return s.charAt(0); + String s = getString(id); + if (s != null && s.length() > 0) { + return s.trim().charAt(0); } return def; @@ -446,6 +446,40 @@ public class Bundle> { 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} @@ -530,12 +564,13 @@ public class Bundle> { Meta.Format format = meta.format(); String[] list = meta.list(); boolean nullable = meta.nullable(); + String def = meta.def(); String info = meta.info(); boolean array = meta.array(); // Default, empty values -> NULL - if (desc.length() + list.length + info.length() == 0 && !group - && nullable && format == Meta.Format.STRING) { + if (desc.length() + list.length + info.length() + def.length() == 0 + && !group && nullable && format == Meta.Format.STRING) { return null; } @@ -667,9 +702,11 @@ public class Bundle> { 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) { @@ -684,37 +721,50 @@ public class Bundle> { } if (!found) { - String bname = type.getPackage().getName() + "." + name.name(); + // 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) { - // We have no bundle for this Bundle - System.err.println("No bundle found for: " + bname); - resetMap(null); + 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 empty if - * bundle is NULL. + * Reset the backing map to the content of the given bundle, or with default + * valiues if bundle is NULL. * * @param bundle * the bundle to copy */ protected void resetMap(ResourceBundle bundle) { this.map.clear(); - - if (bundle != null) { - for (E field : type.getEnumConstants()) { - try { - String value = bundle.getString(field.name()); - this.map.put(field.name(), - value == null ? null : value.trim()); - } catch (MissingResourceException e) { + 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 = meta.def(); + } + + this.map.put(id.name(), value == null ? null : value.trim()); } + } catch (MissingResourceException e) { } } } diff --git a/src/be/nikiroo/utils/resources/Meta.java b/src/be/nikiroo/utils/resources/Meta.java index d377dcc5..d50664da 100644 --- a/src/be/nikiroo/utils/resources/Meta.java +++ b/src/be/nikiroo/utils/resources/Meta.java @@ -63,7 +63,7 @@ public @interface Meta { * use the value in the program, and LANGUAGE_CODE_FR, LANGUAGE_CODE_EN * inside for which the value must be set. * - * @return the group + * @return TRUE if it is a group */ boolean group() default false; @@ -89,6 +89,13 @@ public @interface Meta { */ 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. * diff --git a/src/be/nikiroo/utils/resources/TransBundle_ResourceList.java b/src/be/nikiroo/utils/resources/TransBundle_ResourceList.java index e1c87403..9983b8bc 100644 --- a/src/be/nikiroo/utils/resources/TransBundle_ResourceList.java +++ b/src/be/nikiroo/utils/resources/TransBundle_ResourceList.java @@ -10,6 +10,7 @@ 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; @@ -84,24 +85,41 @@ class TransBundle_ResourceList { private static Collection getResourcesFromDirectory( final File directory, final Pattern pattern) { - final ArrayList retval = new ArrayList(); + 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(); - for (final File file : fileList) { - if (file.isDirectory()) { - retval.addAll(getResourcesFromDirectory(file, pattern)); - } else { - try { - final String fileName = file.getCanonicalPath(); - final boolean accept = pattern.matcher(fileName).matches(); - if (accept) { - retval.add(fileName); + 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) { } - } catch (final IOException e) { - throw new Error(e); } } } - - return retval; } } diff --git a/src/be/nikiroo/utils/ui/ImageUtilsAwt.java b/src/be/nikiroo/utils/ui/ImageUtilsAwt.java index e1fcac7b..26d14aab 100644 --- a/src/be/nikiroo/utils/ui/ImageUtilsAwt.java +++ b/src/be/nikiroo/utils/ui/ImageUtilsAwt.java @@ -23,7 +23,7 @@ public class ImageUtilsAwt extends ImageUtils { public void saveAsImage(Image img, File target, String format) throws IOException { try { - BufferedImage image = ImageUtilsAwt.fromImage(img); + BufferedImage image = fromImage(img); boolean ok = false; try { @@ -34,7 +34,7 @@ public class ImageUtilsAwt extends ImageUtils { } // Some formats are not reliable - // Second change: PNG + // Second chance: PNG if (!ok && !format.equals("png")) { ok = ImageIO.write(image, "png", target); }