diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/Jodd.java b/fine-jodd/src/main/java/com/fr/third/jodd/Jodd.java index f6855e059..5cdb45be8 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/Jodd.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/Jodd.java @@ -25,208 +25,19 @@ package com.fr.third.jodd; -import com.fr.third.jodd.exception.UncheckedException; -import com.fr.third.jodd.exception.UncheckedException; - -import java.lang.reflect.Field; - /** - * Jodd! This is the module manager for Jodd modules. On the very first access, - * all modules get loaded and information about available modules is set. - * In some environments such OSGI, however, classloader lookup does not work. - * Then you need to manually initialize all Jodd components that are in use. - *

- * Each module class contains some static global configuration. - * Each module class has initialize itself in static block, so first access - * to the config will also initialize the module. First module initialization - * will trigger initialization of all modules (as defined in static block - * of this class). Each module has to have static method init() - * that register the module here. This method should be used when modules - * can not be found by classloader. - *

- * Important: static block and init methods must be declared last - * in the module class! Also, if module class contains some default instance - * (as part of the module's configuration), this instance must not - * use any other configuration in the constructor! Otherwise, that value - * could not be changed. + * Jodd! */ public class Jodd { - private static int ndx = 0; - - public static final int CORE = ndx++; - public static final int BEAN = ndx++; - public static final int DB = ndx++; - public static final int DECORA = ndx++; - public static final int HTTP = ndx++; - public static final int HTML_STAPLER = ndx++; - public static final int INTROSPECTOR = ndx++; - public static final int JSON = ndx++; - public static final int JTX = ndx++; - public static final int LAGARTO = ndx++; - public static final int MADVOC = ndx++; - public static final int MAIL = ndx++; - public static final int PETITE = ndx++; - public static final int PROPS = ndx++; - public static final int PROXETTA = ndx++; - public static final int SERVLET = ndx++; - public static final int UPLOAD = ndx++; - public static final int VTOR = ndx++; - - private static final Object[] MODULES = new Object[ndx]; - private static final String[] NAMES = new String[ndx]; - - static { - initAllModules(); - } - - /** - * Manual initialization of a module. - */ - public static void init(Class joddModuleClass) { - String name = joddModuleClass.getName(); - - int moduleId = -1; - - for (int i = 0; i < NAMES.length; i++) { - String moduleName = NAMES[i]; - if (name.equals(moduleName)) { - moduleId = i; - break; - } - } - - if (moduleId == -1) { - throw new IllegalArgumentException("Invalid module: " + joddModuleClass); - } - - Object module = MODULES[moduleId]; - - if (module != null) { - if (module.getClass() == joddModuleClass) { - // already registered - return; - } - } - - MODULES[moduleId] = joddModuleClass; - - updateModuleInstance(moduleId); - } - - /** - * Loads all modules on the classpath by using classloader - * of this class. - */ - public static void initAllModules() { - final Field[] fields = Jodd.class.getFields(); - - final ClassLoader classLoader = Jodd.class.getClassLoader(); - - for (Field field : fields) { - int index; - - try { - index = ((Integer) field.get(null)).intValue(); - } catch (IllegalAccessException iaex) { - throw new UncheckedException(iaex); - } - - String moduleName = field.getName(); - - String packageName = moduleName.toLowerCase(); - - while (true) { - int ndx = packageName.indexOf('_'); - - if (ndx == -1) { - break; - } - - packageName = packageName.substring(0, ndx) + - packageName.substring(ndx + 1); - } - - moduleName = moduleName.substring(0, 1).toUpperCase() + - moduleName.substring(1, moduleName.length()).toLowerCase(); - - while (true) { - int ndx = moduleName.indexOf('_'); - - if (ndx == -1) { - break; - } - - moduleName = moduleName.substring(0, ndx) + - moduleName.substring(ndx + 1, ndx + 2).toUpperCase() + - moduleName.substring(ndx + 2); - } - - - String moduleClass = "com.fr.third.jodd." + packageName + ".Jodd" + moduleName; - - NAMES[index] = moduleClass; - - try { - MODULES[index] = classLoader.loadClass(moduleClass); - } catch (ClassNotFoundException cnfex) { - // ignore - } - } - - for (int i = 0; i < MODULES.length; i++) { - updateModuleInstance(i); - } - } - - /** - * Updates modules instances by creating new modules. - * When new module is created, {@link JoddModule#start()} - * will be called only once. - */ - private static void updateModuleInstance(int moduleId) { - Object module = MODULES[moduleId]; - - if (module == null) { - return; - } - - if (module instanceof Class) { - Class type = (Class) module; - try { - - module = type.newInstance(); - MODULES[moduleId] = module; - - if (module instanceof JoddModule) { - ((JoddModule) module).start(); - } - } catch (Exception ex) { - MODULES[moduleId] = null; - throw new UncheckedException(ex); - } - } - } - - // ---------------------------------------------------------------- checkers - - /** - * Returns true if module is loaded. - */ - public static boolean isModuleLoaded(int moduleNdx) { - return MODULES[moduleNdx] != null; - } - /** - * Returns module instance if module is loaded. It may return: - *

+ * Ascii art of Jodds name. Every serious framework needs one:) */ - public static Object getModule(int moduleNdx) { - return MODULES[moduleNdx]; - } + public static String JODD = "\n" + + " __ __ __\n" + + " / /___ ____/ /___/ /\n" + + " __ / / __ \\/ __ / __ / \n" + + " / /_/ / /_/ / /_/ / /_/ / \n" + + " \\____/\\____/\\__,_/\\__,_/ \n"; } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanCopy.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanCopy.java index dffc16418..6aa1f1cfc 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanCopy.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanCopy.java @@ -25,8 +25,6 @@ package com.fr.third.jodd.bean; -import com.fr.third.jodd.util.StringPool; - import java.util.Map; import static com.fr.third.jodd.util.StringPool.LEFT_SQ_BRACKET; @@ -51,12 +49,12 @@ public class BeanCopy extends BeanVisitorImplBase { * Creates new BeanCopy process between the source and the destination. * Both source and destination can be a POJO object or a Map. */ - public BeanCopy(Object source, Object destination) { + public BeanCopy(final Object source, final Object destination) { this.source = source; this.destination = destination; } - private BeanCopy(Object source) { + private BeanCopy(final Object source) { this.source = source; } @@ -64,21 +62,21 @@ public class BeanCopy extends BeanVisitorImplBase { * Simple static factory for BeanCopy. * @see #BeanCopy(Object, Object) */ - public static BeanCopy beans(Object source, Object destination) { + public static BeanCopy beans(final Object source, final Object destination) { return new BeanCopy(source, destination); } /** - * Creates BeanCopy with given POJO bean as a source. + * Creates BeanCopy with given POJO bean as a source. */ - public static BeanCopy fromBean(Object source) { + public static BeanCopy fromBean(final Object source) { return new BeanCopy(source); } /** - * Creates BeanCopy with given Map as a source. + * Creates BeanCopy with given Map as a source. */ - public static BeanCopy fromMap(Map source) { + public static BeanCopy fromMap(final Map source) { BeanCopy beanCopy = new BeanCopy(source); beanCopy.isSourceMap = true; @@ -89,7 +87,7 @@ public class BeanCopy extends BeanVisitorImplBase { /** * Defines source, detects a map. */ - public static BeanCopy from(Object source) { + public static BeanCopy from(final Object source) { BeanCopy beanCopy = new BeanCopy(source); beanCopy.isSourceMap = source instanceof Map; @@ -102,7 +100,7 @@ public class BeanCopy extends BeanVisitorImplBase { /** * Defines destination bean. */ - public BeanCopy toBean(Object destination) { + public BeanCopy toBean(final Object destination) { this.destination = destination; return this; } @@ -110,7 +108,7 @@ public class BeanCopy extends BeanVisitorImplBase { /** * Defines destination map. */ - public BeanCopy toMap(Map destination) { + public BeanCopy toMap(final Map destination) { this.destination = destination; isTargetMap = true; @@ -121,7 +119,7 @@ public class BeanCopy extends BeanVisitorImplBase { /** * Defines destination, detects a map. */ - public BeanCopy to(Object destination) { + public BeanCopy to(final Object destination) { this.destination = destination; this.isTargetMap = destination instanceof Map; @@ -135,7 +133,8 @@ public class BeanCopy extends BeanVisitorImplBase { * Defines if all properties should be copied (when set to true) * or only public (when set to false, default). */ - public BeanCopy declared(boolean declared) { + @Override + public BeanCopy declared(final boolean declared) { this.declared = declared; this.declaredTarget = declared; return this; @@ -144,13 +143,13 @@ public class BeanCopy extends BeanVisitorImplBase { /** * Fine-tuning of the declared behaviour. */ - public BeanCopy declared(boolean declaredSource, boolean declaredTarget) { + public BeanCopy declared(final boolean declaredSource, final boolean declaredTarget) { this.declared = declaredSource; this.declaredTarget = declaredTarget; return this; } - public BeanCopy forced(boolean forced) { + public BeanCopy forced(final boolean forced) { this.forced = forced; return this; } @@ -164,9 +163,9 @@ public class BeanCopy extends BeanVisitorImplBase { */ public void copy() { beanUtil = new BeanUtilBean() - .declared(declared) - .forced(forced) - .silent(true); + .declared(declared) + .forced(forced) + .silent(true); visit(); } @@ -176,9 +175,9 @@ public class BeanCopy extends BeanVisitorImplBase { * destination does not have some of the sources properties. */ @Override - protected boolean visitProperty(String name, Object value) { + protected boolean visitProperty(String name, final Object value) { if (isTargetMap) { - name = StringPool.LEFT_SQ_BRACKET + name + StringPool.RIGHT_SQ_BRACKET; + name = LEFT_SQ_BRACKET + name + RIGHT_SQ_BRACKET; } beanUtil.setProperty(destination, name, value); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanException.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanException.java index eb4a8c738..b556ad43c 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanException.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanException.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.bean; -import com.fr.third.jodd.exception.UncheckedException; import com.fr.third.jodd.exception.UncheckedException; /** @@ -33,23 +32,23 @@ import com.fr.third.jodd.exception.UncheckedException; */ public class BeanException extends UncheckedException { - public BeanException(Throwable t) { + public BeanException(final Throwable t) { super(t); } - public BeanException(String message) { + public BeanException(final String message) { super(message); } - public BeanException(String message, BeanProperty bp) { + public BeanException(final String message, final BeanProperty bp) { super(message + ". Invalid property: " + bp); } - public BeanException(String message, Throwable t) { + public BeanException(final String message, final Throwable t) { super(message, t); } - public BeanException(String message, BeanProperty bp, Throwable t) { + public BeanException(final String message, final BeanProperty bp, final Throwable t) { super(message + ". Invalid property: " + bp, t); } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanProperty.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanProperty.java index c38410463..0042d3262 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanProperty.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanProperty.java @@ -26,15 +26,12 @@ package com.fr.third.jodd.bean; import com.fr.third.jodd.introspector.ClassDescriptor; +import com.fr.third.jodd.introspector.ClassIntrospector; import com.fr.third.jodd.introspector.Getter; -import com.fr.third.jodd.introspector.Introspector; import com.fr.third.jodd.introspector.PropertyDescriptor; import com.fr.third.jodd.introspector.Setter; -import com.fr.third.jodd.introspector.ClassDescriptor; -import com.fr.third.jodd.introspector.Introspector; -import com.fr.third.jodd.introspector.PropertyDescriptor; -import com.fr.third.jodd.introspector.Getter; -import com.fr.third.jodd.introspector.Setter; + +import java.util.function.Supplier; /** * Represents a bean named property. Contains two information: @@ -46,10 +43,10 @@ import com.fr.third.jodd.introspector.Setter; */ class BeanProperty { - BeanProperty(BeanUtilBean beanUtilBean, Object bean, String propertyName) { - this.introspector = beanUtilBean.getIntrospector(); + BeanProperty(final BeanUtilBean beanUtilBean, final Object bean, final String propertyName) { + this.introspector = beanUtilBean.introspector; setName(propertyName); - setBean(bean); + updateBean(bean); this.last = true; this.first = true; this.fullName = bean.getClass().getSimpleName() + '#' + propertyName; @@ -58,7 +55,7 @@ class BeanProperty { // ---------------------------------------------------------------- bean and descriptor final String fullName; // initial name - final Introspector introspector; + final ClassIntrospector introspector; Object bean; private ClassDescriptor cd; String name; // property name @@ -69,7 +66,7 @@ class BeanProperty { /** * Sets current property name. */ - public void setName(String name) { + public void setName(final String name) { this.name = name; this.updateProperty = true; } @@ -77,13 +74,25 @@ class BeanProperty { /** * Sets new bean instance. */ - public void setBean(Object bean) { + private void setBean(final Object bean) { this.bean = bean; this.cd = (bean == null ? null : introspector.lookup(bean.getClass())); this.first = false; this.updateProperty = true; } + /** + * Updates the bean. Detects special case of suppliers. + */ + public void updateBean(final Object bean) { + this.setBean(bean); + + if (this.cd != null && this.cd.isSupplier()) { + final Object newBean = ((Supplier)this.bean).get(); + setBean(newBean); + } + } + // ---------------------------------------------------------------- simple properties // indicates that property descriptor has to be updated @@ -110,7 +119,7 @@ class BeanProperty { /** * Returns getter. */ - public Getter getGetter(boolean declared) { + public Getter getGetter(final boolean declared) { loadPropertyDescriptor(); return propertyDescriptor != null ? propertyDescriptor.getGetter(declared) : null; } @@ -118,7 +127,7 @@ class BeanProperty { /** * Returns setter. */ - public Setter getSetter(boolean declared) { + public Setter getSetter(final boolean declared) { loadPropertyDescriptor(); return propertyDescriptor != null ? propertyDescriptor.getSetter(declared) : null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanTemplateParser.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanTemplateParser.java index bce1693b5..c8e576562 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanTemplateParser.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanTemplateParser.java @@ -25,8 +25,8 @@ package com.fr.third.jodd.bean; -import com.fr.third.jodd.util.StringTemplateParser; -import com.fr.third.jodd.util.StringTemplateParser; +import com.fr.third.jodd.template.ContextTemplateParser; +import com.fr.third.jodd.template.StringTemplateParser; /** * Bean template is a string template with JSP-alike @@ -41,26 +41,20 @@ import com.fr.third.jodd.util.StringTemplateParser; public class BeanTemplateParser extends StringTemplateParser { /** - * Replaces named macros with context values. - * All declared properties are considered during value lookup. + * Creates bean-backed MacroResolver. */ - public String parse(String template, Object context) { - return parse(template, createBeanMacroResolver(context)); + public ContextTemplateParser of(final Object context) { + return template -> parseWithBean(template, context); } - /** - * Creates bean-backed MacroResolver. - */ - public static MacroResolver createBeanMacroResolver(final Object context) { - return new MacroResolver() { - public String resolve(String macroName) { - Object value = BeanUtil.declaredSilent.getProperty(context, macroName); + public String parseWithBean(final String template, final Object context) { + return super.parse(template, macroName -> { + Object value = BeanUtil.declaredSilent.getProperty(context, macroName); - if (value == null) { - return null; - } - return value.toString(); + if (value == null) { + return null; } - }; + return value.toString(); + }); } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtil.java index e576fece1..661166713 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtil.java @@ -25,8 +25,6 @@ package com.fr.third.jodd.bean; -import com.fr.third.jodd.introspector.Introspector; - /** * Supreme utility for reading and writing bean properties. However, this one is the fastest available. * Although it provides various methods, the whole thing can be easily extended to match most needs. @@ -74,13 +72,6 @@ public interface BeanUtil { BeanUtil forcedSilent = new BeanUtilBean().forced(true).silent(true); - // ---------------------------------------------------------------- INTROSPECTOR - - /** - * Returns introspector instance used by this implementation. - */ - Introspector getIntrospector(); - // ---------------------------------------------------------------- SET /** diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilBean.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilBean.java index 04cfe28f3..817800239 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilBean.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilBean.java @@ -25,11 +25,9 @@ package com.fr.third.jodd.bean; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.introspector.Getter; import com.fr.third.jodd.introspector.Setter; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; import com.fr.third.jodd.util.StringUtil; import java.lang.reflect.Array; @@ -45,7 +43,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { /** * Sets the declared flag. */ - public BeanUtilBean declared(boolean declared) { + public BeanUtilBean declared(final boolean declared) { this.isDeclared = declared; return this; } @@ -53,7 +51,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { /** * Sets the forced flag. */ - public BeanUtilBean forced(boolean forced) { + public BeanUtilBean forced(final boolean forced) { this.isForced = forced; return this; } @@ -61,7 +59,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { /** * Sets the silent flag. */ - public BeanUtilBean silent(boolean silent) { + public BeanUtilBean silent(final boolean silent) { this.isSilent = silent; return this; } @@ -72,31 +70,31 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { * Resolves nested property name to the very last indexed property. * If forced, null or non-existing properties will be created. */ - protected void resolveNestedProperties(BeanProperty bp) { + protected void resolveNestedProperties(final BeanProperty bp) { String name = bp.name; int dotNdx; while ((dotNdx = indexOfDot(name)) != -1) { bp.last = false; bp.setName(name.substring(0, dotNdx)); - bp.setBean(getIndexProperty(bp)); + bp.updateBean(getIndexProperty(bp)); name = name.substring(dotNdx + 1); } bp.last = true; bp.setName(name); } - protected boolean resolveExistingNestedProperties(BeanProperty bp) { + protected boolean resolveExistingNestedProperties(final BeanProperty bp) { String name = bp.name; int dotNdx; while ((dotNdx = indexOfDot(name)) != -1) { bp.last = false; bp.setName(name.substring(0, dotNdx)); - String temp = bp.name; + final String temp = bp.name; if (!hasIndexProperty(bp)) { return false; } bp.setName(temp); - bp.setBean(getIndexProperty(bp)); + bp.updateBean(getIndexProperty(bp)); name = name.substring(dotNdx + 1); } bp.last = true; @@ -108,17 +106,17 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { // ---------------------------------------------------------------- simple property @Override - public boolean hasSimpleProperty(Object bean, String property) { + public boolean hasSimpleProperty(final Object bean, final String property) { return hasSimpleProperty(new BeanProperty(this, bean, property)); } - protected boolean hasSimpleProperty(BeanProperty bp) { + protected boolean hasSimpleProperty(final BeanProperty bp) { if (bp.bean == null) { return false; } // try: getter - Getter getter = bp.getGetter(isDeclared); + final Getter getter = bp.getGetter(isDeclared); if (getter != null) { return true; } @@ -135,11 +133,11 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { } @Override - public T getSimpleProperty(Object bean, String property) { + public T getSimpleProperty(final Object bean, final String property) { return (T) getSimpleProperty(new BeanProperty(this, bean, property)); } - protected Object getSimpleProperty(BeanProperty bp) { + protected Object getSimpleProperty(final BeanProperty bp) { if (bp.name.length() == 0) { if (bp.indexString != null) { @@ -196,7 +194,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { } @Override - public void setSimpleProperty(Object bean, String property, Object value) { + public void setSimpleProperty(final Object bean, final String property, final Object value) { setSimpleProperty(new BeanProperty(this, bean, property), value); } @@ -204,7 +202,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { * Sets a value of simple property. */ @SuppressWarnings({"unchecked"}) - protected void setSimpleProperty(BeanProperty bp, Object value) { + protected void setSimpleProperty(final BeanProperty bp, final Object value) { Setter setter = bp.getSetter(isDeclared); // try: setter @@ -226,7 +224,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { // ---------------------------------------------------------------- indexed property - protected boolean hasIndexProperty(BeanProperty bp) { + protected boolean hasIndexProperty(final BeanProperty bp) { if (bp.bean == null) { return false; @@ -263,7 +261,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { } @Override - public T getIndexProperty(Object bean, String property, int index) { + public T getIndexProperty(final Object bean, final String property, final int index) { BeanProperty bp = new BeanProperty(this, bean, property); bp.indexString = bp.index = String.valueOf(index); @@ -279,7 +277,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { * Get non-nested property value: either simple or indexed property. * If forced, missing bean will be created if possible. */ - protected Object getIndexProperty(BeanProperty bp) { + protected Object getIndexProperty(final BeanProperty bp) { bp.indexString = extractIndex(bp); Object value = _getIndexProperty(bp); @@ -289,7 +287,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { return value; } - private Object _getIndexProperty(BeanProperty bp) { + private Object _getIndexProperty(final BeanProperty bp) { Object resultBean = getSimpleProperty(bp); Getter getter = bp.getGetter(isDeclared); @@ -331,7 +329,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { listComponentType = Map.class; } try { - value = ReflectUtil.newInstance(listComponentType); + value = ClassUtil.newInstance(listComponentType); } catch (Exception ex) { if (isSilent) { return null; @@ -360,7 +358,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { mapComponentType = Map.class; } try { - value = ReflectUtil.newInstance(mapComponentType); + value = ClassUtil.newInstance(mapComponentType); } catch (Exception ex) { if (isSilent) { return null; @@ -383,7 +381,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { } @Override - public void setIndexProperty(Object bean, String property, int index, Object value) { + public void setIndexProperty(final Object bean, final String property, final int index, final Object value) { BeanProperty bp = new BeanProperty(this, bean, property); bp.indexString = bp.index = String.valueOf(index); @@ -396,7 +394,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { /** * Sets indexed or regular properties (no nested!). */ - protected void setIndexProperty(BeanProperty bp, Object value) { + protected void setIndexProperty(final BeanProperty bp, final Object value) { bp.indexString = extractIndex(bp); _setIndexProperty(bp, value); @@ -405,7 +403,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { } @SuppressWarnings({"unchecked"}) - private void _setIndexProperty(BeanProperty bp, Object value) { + private void _setIndexProperty(final BeanProperty bp, Object value) { if (bp.indexString == null) { setSimpleProperty(bp, value); return; @@ -469,7 +467,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { // ---------------------------------------------------------------- SET @Override - public void setProperty(Object bean, String name, Object value) { + public void setProperty(final Object bean, final String name, final Object value) { BeanProperty beanProperty = new BeanProperty(this, bean, name); if (!isSilent) { @@ -491,7 +489,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { * Returns value of bean's property. */ @Override - public T getProperty(Object bean, String name) { + public T getProperty(final Object bean, final String name) { BeanProperty beanProperty = new BeanProperty(this, bean, name); if (!isSilent) { resolveNestedProperties(beanProperty); @@ -511,7 +509,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { // ---------------------------------------------------------------- HAS @Override - public boolean hasProperty(Object bean, String name) { + public boolean hasProperty(final Object bean, final String name) { BeanProperty beanProperty = new BeanProperty(this, bean, name); if (!resolveExistingNestedProperties(beanProperty)) { return false; @@ -520,7 +518,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { } @Override - public boolean hasRootProperty(Object bean, String name) { + public boolean hasRootProperty(final Object bean, String name) { int dotNdx = indexOfDot(name); if (dotNdx != -1) { name = name.substring(0, dotNdx); @@ -533,7 +531,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { // ---------------------------------------------------------------- type @Override - public Class getPropertyType(Object bean, String name) { + public Class getPropertyType(final Object bean, final String name) { BeanProperty beanProperty = new BeanProperty(this, bean, name); if (!resolveExistingNestedProperties(beanProperty)) { return null; @@ -550,7 +548,7 @@ public class BeanUtilBean extends BeanUtilUtil implements BeanUtil { * Extract the first name of this reference. */ @Override - public String extractThisReference(String propertyName) { + public String extractThisReference(final String propertyName) { int ndx = StringUtil.indexOfChars(propertyName, INDEX_CHARS); if (ndx == -1) { return propertyName; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilUtil.java index b69597bd5..41276b111 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilUtil.java @@ -25,15 +25,12 @@ package com.fr.third.jodd.bean; -import com.fr.third.jodd.typeconverter.TypeConverterManager; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.introspector.ClassIntrospector; import com.fr.third.jodd.introspector.Getter; -import com.fr.third.jodd.introspector.Introspector; +import com.fr.third.jodd.introspector.MapperFunction; import com.fr.third.jodd.introspector.Setter; import com.fr.third.jodd.typeconverter.TypeConverterManager; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; import java.lang.reflect.Array; import java.util.Collection; @@ -52,66 +49,59 @@ abstract class BeanUtilUtil implements BeanUtil { // ---------------------------------------------------------------- introspector - protected Introspector introspector = JoddBean.introspector; - protected TypeConverterManagerBean typeConverterManager = TypeConverterManager.getDefaultTypeConverterManager(); + protected ClassIntrospector introspector = ClassIntrospector.get(); + protected TypeConverterManager typeConverterManager = TypeConverterManager.get(); /** - * Sets {@link Introspector introspector} implementation. + * Sets {@link ClassIntrospector introspector} implementation. */ - public void setIntrospector(Introspector introspector) { + public void setIntrospector(final ClassIntrospector introspector) { this.introspector = introspector; } /** - * Returns {@link Introspector introspector} implementation. - */ - @Override - public Introspector getIntrospector() { - return introspector; - } - - /** - * Sets {@link TypeConverterManagerBean type converter manager} implementation. + * Sets {@link TypeConverterManager type converter manager} implementation. */ - public void setTypeConverterManager(TypeConverterManagerBean typeConverterManager) { + public void setTypeConverterManager(final TypeConverterManager typeConverterManager) { this.typeConverterManager = typeConverterManager; } - /** - * Returns {@link TypeConverterManagerBean type converter manager} implementation. - */ - public TypeConverterManagerBean getTypeConverterManager() { - return typeConverterManager; - } - /** * Converts object to destination type. Invoked before the * value is set into destination. Throws TypeConversionException * if conversion fails. */ @SuppressWarnings("unchecked") - protected Object convertType(Object value, Class type) { + protected Object convertType(final Object value, final Class type) { return typeConverterManager.convertType(value, type); } /** - * Convert to collection. + * Converter to collection. */ - protected Object convertToCollection(Object value, Class destinationType, Class componentType) { + @SuppressWarnings("unchecked") + protected Object convertToCollection(final Object value, final Class destinationType, final Class componentType) { return typeConverterManager.convertToCollection(value, destinationType, componentType); } - + // ---------------------------------------------------------------- accessors /** * Invokes setter, but first converts type to match the setter type. */ - protected Object invokeSetter(Setter setter, BeanProperty bp, Object value) { + protected Object invokeSetter(final Setter setter, final BeanProperty bp, Object value) { try { - Class type = setter.getSetterRawType(); - if (ReflectUtil.isTypeOf(type, Collection.class)) { + final MapperFunction setterMapperFunction = setter.getMapperFunction(); + + if (setterMapperFunction != null) { + value = setterMapperFunction.apply(value); + } + + final Class type = setter.getSetterRawType(); + + if (ClassUtil.isTypeOf(type, Collection.class)) { Class componentType = setter.getSetterRawComponentType(); value = convertToCollection(value, type, componentType); @@ -136,7 +126,7 @@ abstract class BeanUtilUtil implements BeanUtil { * Returns the element of an array forced. If value is null, it will be instantiated. * If not the last part of indexed bean property, array will be expanded to the index if necessary. */ - protected Object arrayForcedGet(BeanProperty bp, Object array, int index) { + protected Object arrayForcedGet(final BeanProperty bp, Object array, final int index) { Class componentType = array.getClass().getComponentType(); if (!bp.last) { array = ensureArraySize(bp, array, componentType, index); @@ -144,7 +134,8 @@ abstract class BeanUtilUtil implements BeanUtil { Object value = Array.get(array, index); if (value == null) { try { - value = ReflectUtil.newInstance(componentType); + //noinspection unchecked + value = ClassUtil.newInstance(componentType); } catch (Exception ex) { if (isSilent) { return null; @@ -158,9 +149,9 @@ abstract class BeanUtilUtil implements BeanUtil { /** * Sets the array element forced. If index is greater then arrays length, array will be expanded to the index. - * If speed is critical, it is better to allocate an array with proper size before using this method. + * If speed is critical, it is better to allocate an array with proper size before using this method. */ - protected void arrayForcedSet(BeanProperty bp, Object array, int index, Object value) { + protected void arrayForcedSet(final BeanProperty bp, Object array, final int index, Object value) { Class componentType = array.getClass().getComponentType(); array = ensureArraySize(bp, array, componentType, index); value = convertType(value, componentType); @@ -169,7 +160,7 @@ abstract class BeanUtilUtil implements BeanUtil { @SuppressWarnings({"SuspiciousSystemArraycopy"}) - protected Object ensureArraySize(BeanProperty bp, Object array, Class componentType, int index) { + protected Object ensureArraySize(final BeanProperty bp, Object array, final Class componentType, final int index) { int len = Array.getLength(array); if (index >= len) { Object newArray = Array.newInstance(componentType, index + 1); @@ -189,7 +180,7 @@ abstract class BeanUtilUtil implements BeanUtil { } @SuppressWarnings({"unchecked"}) - protected void ensureListSize(List list, int size) { + protected void ensureListSize(final List list, final int size) { int len = list.size(); while (size >= len) { list.add(null); @@ -203,7 +194,7 @@ abstract class BeanUtilUtil implements BeanUtil { * Finds the very first next dot. Ignores dots between index brackets. * Returns -1 when dot is not found. */ - protected int indexOfDot(String name) { + protected int indexOfDot(final String name) { int ndx = 0; int len = name.length(); @@ -235,7 +226,7 @@ abstract class BeanUtilUtil implements BeanUtil { * If index is found, it is stripped from bean property name. * If no index is found, it returns null. */ - protected String extractIndex(BeanProperty bp) { + protected String extractIndex(final BeanProperty bp) { bp.index = null; String name = bp.name; int lastNdx = name.length() - 1; @@ -253,7 +244,7 @@ abstract class BeanUtilUtil implements BeanUtil { return null; } - protected int parseInt(String indexString, BeanProperty bp) { + protected int parseInt(final String indexString, final BeanProperty bp) { try { return Integer.parseInt(indexString); } catch (NumberFormatException nfex) { @@ -268,7 +259,7 @@ abstract class BeanUtilUtil implements BeanUtil { * Creates new instance for current property name through its setter. * It uses default constructor! */ - protected Object createBeanProperty(BeanProperty bp) { + protected Object createBeanProperty(final BeanProperty bp) { Setter setter = bp.getSetter(true); if (setter == null) { return null; @@ -278,7 +269,7 @@ abstract class BeanUtilUtil implements BeanUtil { Object newInstance; try { - newInstance = ReflectUtil.newInstance(type); + newInstance = ClassUtil.newInstance(type); } catch (Exception ex) { if (isSilent) { return null; @@ -297,7 +288,7 @@ abstract class BeanUtilUtil implements BeanUtil { * Extracts generic component type of a property. Returns Object.class * when property does not have component. */ - protected Class extractGenericComponentType(Getter getter) { + protected Class extractGenericComponentType(final Getter getter) { Class componentType = null; if (getter != null) { @@ -313,7 +304,7 @@ abstract class BeanUtilUtil implements BeanUtil { /** * Converts Map index to key type. If conversion fails, original value will be returned. */ - protected Object convertIndexToMapKey(Getter getter, Object index) { + protected Object convertIndexToMapKey(final Getter getter, final Object index) { Class indexType = null; if (getter != null) { @@ -339,7 +330,7 @@ abstract class BeanUtilUtil implements BeanUtil { /** * Extracts type of current property. */ - protected Class extractType(BeanProperty bp) { + protected Class extractType(final BeanProperty bp) { Getter getter = bp.getGetter(isDeclared); if (getter != null) { if (bp.index != null) { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitor.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitor.java index 6cf210579..210c90bb2 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitor.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitor.java @@ -25,20 +25,14 @@ package com.fr.third.jodd.bean; -import com.fr.third.jodd.introspector.ClassDescriptor; -import com.fr.third.jodd.introspector.ClassIntrospector; -import com.fr.third.jodd.introspector.FieldDescriptor; -import com.fr.third.jodd.introspector.MethodDescriptor; -import com.fr.third.jodd.util.InExRuleMatcher; -import com.fr.third.jodd.util.InExRules; -import com.fr.third.jodd.util.StringPool; +import com.fr.third.jodd.inex.InExRuleMatcher; +import com.fr.third.jodd.inex.InExRules; import com.fr.third.jodd.introspector.ClassDescriptor; import com.fr.third.jodd.introspector.ClassIntrospector; import com.fr.third.jodd.introspector.FieldDescriptor; import com.fr.third.jodd.introspector.MethodDescriptor; import com.fr.third.jodd.introspector.PropertyDescriptor; -import com.fr.third.jodd.util.InExRuleMatcher; -import com.fr.third.jodd.util.InExRules; +import com.fr.third.jodd.util.StringUtil; import java.util.ArrayList; import java.util.Map; @@ -62,7 +56,7 @@ public abstract class BeanVisitor implements InExRuleMatcher { /** * Include/exclude rules. */ - protected InExRules rules = new InExRules(this); + protected InExRules rules = new InExRules<>(this); /** * Flag for enabling declared properties, or just public ones. */ @@ -71,6 +65,10 @@ public abstract class BeanVisitor implements InExRuleMatcher { * Defines if null values should be ignored. */ protected boolean ignoreNullValues; + /** + * Defines if empty string should be ignored. + */ + protected boolean ignoreEmptyString; /** * Defines if fields should be included. */ @@ -89,22 +87,22 @@ public abstract class BeanVisitor implements InExRuleMatcher { /** * Returns all bean property names. */ - protected String[] getAllBeanPropertyNames(Class type, boolean declared) { - ClassDescriptor classDescriptor = ClassIntrospector.lookup(type); + protected String[] getAllBeanPropertyNames(final Class type, final boolean declared) { + final ClassDescriptor classDescriptor = ClassIntrospector.get().lookup(type); - PropertyDescriptor[] propertyDescriptors = classDescriptor.getAllPropertyDescriptors(); + final PropertyDescriptor[] propertyDescriptors = classDescriptor.getAllPropertyDescriptors(); - ArrayList names = new ArrayList(propertyDescriptors.length); + final ArrayList names = new ArrayList<>(propertyDescriptors.length); - for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { - MethodDescriptor getter = propertyDescriptor.getReadMethodDescriptor(); + for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) { + final MethodDescriptor getter = propertyDescriptor.getReadMethodDescriptor(); if (getter != null) { if (getter.matchDeclared(declared)) { names.add(propertyDescriptor.getName()); } } else if (includeFields) { - FieldDescriptor field = propertyDescriptor.getFieldDescriptor(); + final FieldDescriptor field = propertyDescriptor.getFieldDescriptor(); if (field != null) { if (field.matchDeclared(declared)) { names.add(field.getName()); @@ -113,22 +111,22 @@ public abstract class BeanVisitor implements InExRuleMatcher { } } - return names.toArray(new String[names.size()]); + return names.toArray(new String[0]); } /** * Returns an array of bean properties. If bean is a Map, * all its keys will be returned. */ - protected String[] resolveProperties(Object bean, boolean declared) { - String[] properties; + protected String[] resolveProperties(final Object bean, final boolean declared) { + final String[] properties; if (bean instanceof Map) { - Set keys = ((Map) bean).keySet(); + final Set keys = ((Map) bean).keySet(); properties = new String[keys.size()]; int ndx = 0; - for (Object key : keys) { + for (final Object key : keys) { properties[ndx] = key.toString(); ndx++; } @@ -143,9 +141,9 @@ public abstract class BeanVisitor implements InExRuleMatcher { * Starts visiting properties. */ public void visit() { - String[] properties = resolveProperties(source, declared); + final String[] properties = resolveProperties(source, declared); - for (String name : properties) { + for (final String name : properties) { if (name == null) { continue; } @@ -154,12 +152,12 @@ public abstract class BeanVisitor implements InExRuleMatcher { continue; } - Object value; + final Object value; String propertyName = name; if (isSourceMap) { - propertyName = StringPool.LEFT_SQ_BRACKET + name + StringPool.RIGHT_SQ_BRACKET; + propertyName = LEFT_SQ_BRACKET + name + RIGHT_SQ_BRACKET; } if (declared) { @@ -172,6 +170,10 @@ public abstract class BeanVisitor implements InExRuleMatcher { continue; } + if (ignoreEmptyString && value instanceof String && StringUtil.isEmpty((String) value)) { + continue; + } + visitProperty(name, value); } } @@ -185,7 +187,8 @@ public abstract class BeanVisitor implements InExRuleMatcher { /** * Compares property name to the rules. */ - public boolean accept(String propertyName, String rule, boolean include) { + @Override + public boolean accept(final String propertyName, final String rule, final boolean include) { return propertyName.equals(rule); } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitorImplBase.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitorImplBase.java index 244ca2787..e29762ee1 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitorImplBase.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitorImplBase.java @@ -31,88 +31,101 @@ package com.fr.third.jodd.bean; */ public abstract class BeanVisitorImplBase extends BeanVisitor { + @SuppressWarnings("unchecked") + protected T _this() { + return (T) this; + } + /** * Excludes all properties, i.e. enables blacklist mode. */ public T excludeAll() { blacklist = false; - return (T) this; + return _this(); } /** * Defines excluded property names. */ - public T exclude(String... excludes) { + public T exclude(final String... excludes) { for (String ex : excludes) { rules.exclude(ex); } - return (T) this; + return _this(); } /** * Exclude a property. */ - public T exclude(String exclude) { + public T exclude(final String exclude) { rules.exclude(exclude); - return (T) this; + return _this(); } /** * Defines included property names. */ - public T include(String... includes) { + public T include(final String... includes) { for (String in : includes) { rules.include(in); } - return (T) this; + return _this(); } /** * Include a property. */ - public T include(String include) { + public T include(final String include) { rules.include(include); - return (T) this; + return _this(); } /** * Defines included property names as public properties * of given template class. Sets to black list mode. */ - public T includeAs(Class template) { + public T includeAs(final Class template) { blacklist = false; String[] properties = getAllBeanPropertyNames(template, false); include(properties); - return (T) this; + return _this(); } /** * Defines if null values should be ignored. */ - public T ignoreNulls(boolean ignoreNulls) { + public T ignoreNulls(final boolean ignoreNulls) { this.ignoreNullValues = ignoreNulls; - return (T) this; + return _this(); + } + + /** + * Defines if empty string should be ignored. + */ + public T ignoreEmptyString(final boolean ignoreEmptyString) { + this.ignoreEmptyString = ignoreEmptyString; + return _this(); } /** * Defines if all properties should be copied (when set to true) * or only public (when set to false, default). */ - public T declared(boolean declared) { + public T declared(final boolean declared) { this.declared = declared; - return (T) this; + return _this(); } /** * Defines if fields without getters should be copied too. */ - public T includeFields(boolean includeFields) { + public T includeFields(final boolean includeFields) { this.includeFields = includeFields; - return (T) this; + return _this(); } } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanWalker.java b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanWalker.java index cd9160b20..d70e0df8c 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanWalker.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanWalker.java @@ -41,18 +41,18 @@ public class BeanWalker extends BeanVisitorImplBase { void visitProperty(String name, Object value); } - public BeanWalker(BeanWalkerCallback callback) { + public BeanWalker(final BeanWalkerCallback callback) { this.callback = callback; } /** * Static ctor. */ - public static BeanWalker walk(BeanWalkerCallback callback) { + public static BeanWalker walk(final BeanWalkerCallback callback) { return new BeanWalker(callback); } - public void source(Object source) { + public void source(final Object source) { this.source = source; isSourceMap = (source instanceof Map); @@ -60,13 +60,13 @@ public class BeanWalker extends BeanVisitorImplBase { visit(); } - public void bean(Object bean) { + public void bean(final Object bean) { this.source = bean; visit(); } - public void map(Map map) { + public void map(final Map map) { this.source = map; this.isSourceMap = true; @@ -75,7 +75,7 @@ public class BeanWalker extends BeanVisitorImplBase { } @Override - protected boolean visitProperty(String name, Object value) { + protected boolean visitProperty(final String name, final Object value) { callback.visitProperty(name, value); return true; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastBooleanBuffer.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastBooleanBuffer.java new file mode 100644 index 000000000..4789f6b7a --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastBooleanBuffer.java @@ -0,0 +1,166 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.buffer; + +import java.util.Arrays; + +/** + * Faster {@code boolean} buffer. + */ +public class FastBooleanBuffer { + + private boolean[] buffer; + private int offset; + + /** + * Creates a new {@code boolean} buffer. The buffer capacity is + * initially 64 booleans, though its size increases if necessary. + */ + public FastBooleanBuffer() { + this.buffer = new boolean[64]; + } + + /** + * Creates a new {@code boolean} buffer, with a buffer capacity of + * the specified size. + * + * @param size the initial size. + * @throws IllegalArgumentException if size is negative. + */ + public FastBooleanBuffer(final int size) { + this.buffer = new boolean[size]; + } + + /** + * Grows the buffer. + */ + private void grow(final int minCapacity) { + final int oldCapacity = buffer.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) { + // special case, min capacity is larger then a grow + newCapacity = minCapacity + 512; + } + buffer = Arrays.copyOf(buffer, newCapacity); + } + + /** + * Appends single {@code boolean} to buffer. + */ + public void append(final boolean element) { + if (offset - buffer.length >= 0) { + grow(offset); + } + + buffer[offset++] = element; + } + + /** + * Appends {@code boolean} array to buffer. + */ + public FastBooleanBuffer append(final boolean[] array, final int off, final int len) { + if (offset + len - buffer.length > 0) { + grow(offset + len); + } + + System.arraycopy(array, off, buffer, offset, len); + offset += len; + return this; + } + + /** + * Appends {@code boolean} array to buffer. + */ + public FastBooleanBuffer append(final boolean[] array) { + return append(array, 0, array.length); + } + + /** + * Appends another fast buffer to this one. + */ + public FastBooleanBuffer append(final FastBooleanBuffer buff) { + if (buff.offset == 0) { + return this; + } + append(buff.buffer, 0, buff.offset); + return this; + } + + /** + * Returns buffer size. + */ + public int size() { + return offset; + } + + /** + * Tests if this buffer has no elements. + */ + public boolean isEmpty() { + return offset == 0; + } + + /** + * Resets the buffer content. + */ + public void clear() { + offset = 0; + } + + /** + * Creates {@code boolean} array from buffered content. + */ + public boolean[] toArray() { + return Arrays.copyOf(buffer, offset); + } + + /** + * Creates {@code boolean} subarray from buffered content. + */ + public boolean[] toArray(final int start, final int len) { + final boolean[] array = new boolean[len]; + + if (len == 0) { + return array; + } + + System.arraycopy(buffer, start, array, 0, len); + + return array; + } + + /** + * Returns {@code boolean} element at given index. + */ + public boolean get(final int index) { + if (index >= offset) { + throw new IndexOutOfBoundsException(); + } + return buffer[index]; + } + + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastByteBuffer.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastByteBuffer.java new file mode 100644 index 000000000..52e3e2f2d --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastByteBuffer.java @@ -0,0 +1,166 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.buffer; + +import java.util.Arrays; + +/** + * Faster {@code byte} buffer. Works faster for smaller buffer sizes. + * After eg. length of 2048 the performances are practically the same. + */ +public class FastByteBuffer { + + private byte[] buffer; + private int offset; + + /** + * Creates a new {@code byte} buffer. The buffer capacity is + * initially 64 bytes, though its size increases if necessary. + */ + public FastByteBuffer() { + this.buffer = new byte[64]; + } + + /** + * Creates a new {@code byte} buffer, with a buffer capacity of + * the specified size. + * + * @param size the initial size. + * @throws IllegalArgumentException if size is negative. + */ + public FastByteBuffer(final int size) { + this.buffer = new byte[size]; + } + + /** + * Grows the buffer. + */ + private void grow(final int minCapacity) { + final int oldCapacity = buffer.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) { + // special case, min capacity is larger then a grow + newCapacity = minCapacity + 512; + } + buffer = Arrays.copyOf(buffer, newCapacity); + } + + /** + * Appends single {@code byte} to buffer. + */ + public void append(final byte element) { + if (offset - buffer.length >= 0) { + grow(offset); + } + + buffer[offset++] = element; + } + + /** + * Appends {@code byte} array to buffer. + */ + public FastByteBuffer append(final byte[] array, final int off, final int len) { + if (offset + len - buffer.length > 0) { + grow(offset + len); + } + + System.arraycopy(array, off, buffer, offset, len); + offset += len; + return this; + } + + /** + * Appends {@code byte} array to buffer. + */ + public FastByteBuffer append(final byte[] array) { + return append(array, 0, array.length); + } + + /** + * Appends another fast buffer to this one. + */ + public FastByteBuffer append(final FastByteBuffer buff) { + if (buff.offset == 0) { + return this; + } + append(buff.buffer, 0, buff.offset); + return this; + } + + /** + * Returns buffer size. + */ + public int size() { + return offset; + } + + /** + * Tests if this buffer has no elements. + */ + public boolean isEmpty() { + return offset == 0; + } + + /** + * Resets the buffer content. + */ + public void clear() { + offset = 0; + } + + /** + * Creates {@code byte} array from buffered content. + */ + public byte[] toArray() { + return Arrays.copyOf(buffer, offset); + } + + /** + * Creates {@code byte} subarray from buffered content. + */ + public byte[] toArray(final int start, final int len) { + final byte[] array = new byte[len]; + + if (len == 0) { + return array; + } + + System.arraycopy(buffer, start, array, 0, len); + + return array; + } + + /** + * Returns {@code byte} element at given index. + */ + public byte get(final int index) { + if (index >= offset) { + throw new IndexOutOfBoundsException(); + } + return buffer[index]; + } + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastCharBuffer.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastCharBuffer.java new file mode 100644 index 000000000..9de22e46a --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastCharBuffer.java @@ -0,0 +1,211 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.buffer; + +import com.fr.third.jodd.util.CharArraySequence; + +import java.util.Arrays; + +/** + * Faster {@code char} buffer. + */ +public class FastCharBuffer implements CharSequence, Appendable { + + private char[] buffer; + private int offset; + + /** + * Creates a new {@code char} buffer. The buffer capacity is + * initially 64 chars, though its size increases if necessary. + */ + public FastCharBuffer() { + this.buffer = new char[64]; + } + + /** + * Creates a new {@code char} buffer, with a buffer capacity of + * the specified size, in chars. + * + * @param size the initial size. + * @throws IllegalArgumentException if size is negative. + */ + public FastCharBuffer(final int size) { + this.buffer = new char[size]; + } + + /** + * Grows the buffer. + */ + private void grow(final int minCapacity) { + final int oldCapacity = buffer.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) { + // special case, min capacity is larger then a grow + newCapacity = minCapacity + 512; + } + buffer = Arrays.copyOf(buffer, newCapacity); + } + + /** + * Appends single {@code char} to buffer. + */ + @Override + public FastCharBuffer append(final char element) { + if (offset - buffer.length >= 0) { + grow(offset); + } + + buffer[offset++] = element; + return this; + } + + /** + * Appends {@code char} array to buffer. + */ + public FastCharBuffer append(final char[] array, final int off, final int len) { + if (offset + len - buffer.length > 0) { + grow(offset + len); + } + + System.arraycopy(array, off, buffer, offset, len); + offset += len; + return this; + } + + /** + * Appends {@code char} array to buffer. + */ + public FastCharBuffer append(final char[] array) { + return append(array, 0, array.length); + } + + /** + * Appends another fast buffer to this one. + */ + public FastCharBuffer append(final FastCharBuffer buff) { + if (buff.offset == 0) { + return this; + } + append(buff.buffer, 0, buff.offset); + return this; + } + + /** + * Returns buffer size. + */ + public int size() { + return offset; + } + + @Override + public int length() { + return offset; + } + + /** + * Tests if this buffer has no elements. + */ + public boolean isEmpty() { + return offset == 0; + } + + /** + * Resets the buffer content. + */ + public void clear() { + offset = 0; + } + + /** + * Creates {@code char} array from buffered content. + */ + public char[] toArray() { + return Arrays.copyOf(buffer, offset); + } + + /** + * Creates {@code char} subarray from buffered content. + */ + public char[] toArray(final int start, final int len) { + final char[] array = new char[len]; + + if (len == 0) { + return array; + } + + System.arraycopy(buffer, start, array, 0, len); + + return array; + } + + /** + * Returns {@code char} element at given index. + */ + public char get(final int index) { + if (index >= offset) { + throw new IndexOutOfBoundsException(); + } + return buffer[index]; + } + + /** + * Appends character sequence to buffer. + */ + @Override + public FastCharBuffer append(final CharSequence csq) { + append(csq, 0, csq.length()); + return this; + } + + /** + * Appends character sequence to buffer. + */ + @Override + public FastCharBuffer append(final CharSequence csq, final int start, final int end) { + for (int i = start; i < end; i++) { + append(csq.charAt(i)); + } + return this; + } + @Override + public char charAt(final int index) { + return get(index); + } + + @Override + public CharSequence subSequence(final int start, final int end) { + return new CharArraySequence(buffer, start, end - start); + } + + /** + * Returns a String of the char buffer. + * Please use {@code StringBuilder} instead, it is faster. + */ + @Override + public String toString() { + return new String(toArray()); + } +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastDoubleBuffer.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastDoubleBuffer.java new file mode 100644 index 000000000..9d7531e27 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastDoubleBuffer.java @@ -0,0 +1,166 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.buffer; + +import java.util.Arrays; + +/** + * Faster {@code double} buffer. + */ +public class FastDoubleBuffer { + + private double[] buffer; + private int offset; + + /** + * Creates a new {@code double} buffer. The buffer capacity is + * initially 64 doubles, though its size increases if necessary. + */ + public FastDoubleBuffer() { + this.buffer = new double[64]; + } + + /** + * Creates a new {@code double} buffer, with a buffer capacity of + * the specified size. + * + * @param size the initial size. + * @throws IllegalArgumentException if size is negative. + */ + public FastDoubleBuffer(final int size) { + this.buffer = new double[size]; + } + + /** + * Grows the buffer. + */ + private void grow(final int minCapacity) { + final int oldCapacity = buffer.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) { + // special case, min capacity is larger then a grow + newCapacity = minCapacity + 512; + } + buffer = Arrays.copyOf(buffer, newCapacity); + } + + /** + * Appends single {@code double} to buffer. + */ + public void append(final double element) { + if (offset - buffer.length >= 0) { + grow(offset); + } + + buffer[offset++] = element; + } + + /** + * Appends {@code double} array to buffer. + */ + public FastDoubleBuffer append(final double[] array, final int off, final int len) { + if (offset + len - buffer.length > 0) { + grow(offset + len); + } + + System.arraycopy(array, off, buffer, offset, len); + offset += len; + return this; + } + + /** + * Appends {@code double} array to buffer. + */ + public FastDoubleBuffer append(final double[] array) { + return append(array, 0, array.length); + } + + /** + * Appends another fast buffer to this one. + */ + public FastDoubleBuffer append(final FastDoubleBuffer buff) { + if (buff.offset == 0) { + return this; + } + append(buff.buffer, 0, buff.offset); + return this; + } + + /** + * Returns buffer size. + */ + public int size() { + return offset; + } + + /** + * Tests if this buffer has no elements. + */ + public boolean isEmpty() { + return offset == 0; + } + + /** + * Resets the buffer content. + */ + public void clear() { + offset = 0; + } + + /** + * Creates {@code double} array from buffered content. + */ + public double[] toArray() { + return Arrays.copyOf(buffer, offset); + } + + /** + * Creates {@code double} subarray from buffered content. + */ + public double[] toArray(final int start, final int len) { + final double[] array = new double[len]; + + if (len == 0) { + return array; + } + + System.arraycopy(buffer, start, array, 0, len); + + return array; + } + + /** + * Returns {@code double} element at given index. + */ + public double get(final int index) { + if (index >= offset) { + throw new IndexOutOfBoundsException(); + } + return buffer[index]; + } + + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastFloatBuffer.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastFloatBuffer.java new file mode 100644 index 000000000..44f8b3a31 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastFloatBuffer.java @@ -0,0 +1,165 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.buffer; + +import java.util.Arrays; + +/** + * Faster {@code float} buffer. + */ +public class FastFloatBuffer { + + private float[] buffer; + private int offset; + + /** + * Creates a new {@code float} buffer. The buffer capacity is + * initially 64 floats, though its size increases if necessary. + */ + public FastFloatBuffer() { + this.buffer = new float[64]; + } + + /** + * Creates a new {@code float} buffer, with a buffer capacity of + * the specified size. + * + * @param size the initial size. + * @throws IllegalArgumentException if size is negative. + */ + public FastFloatBuffer(final int size) { + this.buffer = new float[size]; + } + + /** + * Grows the buffer. + */ + private void grow(final int minCapacity) { + final int oldCapacity = buffer.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) { + // special case, min capacity is larger then a grow + newCapacity = minCapacity + 512; + } + buffer = Arrays.copyOf(buffer, newCapacity); + } + + /** + * Appends single {@code float} to buffer. + */ + public void append(final float element) { + if (offset - buffer.length >= 0) { + grow(offset); + } + + buffer[offset++] = element; + } + + /** + * Appends {@code float} array to buffer. + */ + public FastFloatBuffer append(final float[] array, final int off, final int len) { + if (offset + len - buffer.length > 0) { + grow(offset + len); + } + + System.arraycopy(array, off, buffer, offset, len); + offset += len; + return this; + } + + /** + * Appends {@code float} array to buffer. + */ + public FastFloatBuffer append(final float[] array) { + return append(array, 0, array.length); + } + + /** + * Appends another fast buffer to this one. + */ + public FastFloatBuffer append(final FastFloatBuffer buff) { + if (buff.offset == 0) { + return this; + } + append(buff.buffer, 0, buff.offset); + return this; + } + + /** + * Returns buffer size. + */ + public int size() { + return offset; + } + + /** + * Tests if this buffer has no elements. + */ + public boolean isEmpty() { + return offset == 0; + } + + /** + * Resets the buffer content. + */ + public void clear() { + offset = 0; + } + + /** + * Creates {@code float} array from buffered content. + */ + public float[] toArray() { + return Arrays.copyOf(buffer, offset); + } + + /** + * Creates {@code float} subarray from buffered content. + */ + public float[] toArray(final int start, final int len) { + final float[] array = new float[len]; + + if (len == 0) { + return array; + } + + System.arraycopy(buffer, start, array, 0, len); + + return array; + } + + /** + * Returns {@code float} element at given index. + */ + public float get(final int index) { + if (index >= offset) { + throw new IndexOutOfBoundsException(); + } + return buffer[index]; + } + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastIntBuffer.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastIntBuffer.java new file mode 100644 index 000000000..ce82f1739 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastIntBuffer.java @@ -0,0 +1,166 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.buffer; + +import java.util.Arrays; + +/** + * Faster {@code int} buffer. + */ +public class FastIntBuffer { + + private int[] buffer; + private int offset; + + /** + * Creates a new {@code int} buffer. The buffer capacity is + * initially 64 ints, though its size increases if necessary. + */ + public FastIntBuffer() { + this.buffer = new int[64]; + } + + /** + * Creates a new {@code int} buffer, with a buffer capacity of + * the specified size. + * + * @param size the initial size. + * @throws IllegalArgumentException if size is negative. + */ + public FastIntBuffer(final int size) { + this.buffer = new int[size]; + } + + /** + * Grows the buffer. + */ + private void grow(final int minCapacity) { + final int oldCapacity = buffer.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) { + // special case, min capacity is larger then a grow + newCapacity = minCapacity + 512; + } + buffer = Arrays.copyOf(buffer, newCapacity); + } + + /** + * Appends single {@code int} to buffer. + */ + public void append(final int element) { + if (offset - buffer.length >= 0) { + grow(offset); + } + + buffer[offset++] = element; + } + + /** + * Appends {@code int} array to buffer. + */ + public FastIntBuffer append(final int[] array, final int off, final int len) { + if (offset + len - buffer.length > 0) { + grow(offset + len); + } + + System.arraycopy(array, off, buffer, offset, len); + offset += len; + return this; + } + + /** + * Appends {@code int} array to buffer. + */ + public FastIntBuffer append(final int[] array) { + return append(array, 0, array.length); + } + + /** + * Appends another fast buffer to this one. + */ + public FastIntBuffer append(final FastIntBuffer buff) { + if (buff.offset == 0) { + return this; + } + append(buff.buffer, 0, buff.offset); + return this; + } + + /** + * Returns buffer size. + */ + public int size() { + return offset; + } + + /** + * Tests if this buffer has no elements. + */ + public boolean isEmpty() { + return offset == 0; + } + + /** + * Resets the buffer content. + */ + public void clear() { + offset = 0; + } + + /** + * Creates {@code int} array from buffered content. + */ + public int[] toArray() { + return Arrays.copyOf(buffer, offset); + } + + /** + * Creates {@code int} subarray from buffered content. + */ + public int[] toArray(final int start, final int len) { + final int[] array = new int[len]; + + if (len == 0) { + return array; + } + + System.arraycopy(buffer, start, array, 0, len); + + return array; + } + + /** + * Returns {@code int} element at given index. + */ + public int get(final int index) { + if (index >= offset) { + throw new IndexOutOfBoundsException(); + } + return buffer[index]; + } + + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastLongBuffer.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastLongBuffer.java new file mode 100644 index 000000000..d057d0dd3 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastLongBuffer.java @@ -0,0 +1,166 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.buffer; + +import java.util.Arrays; + +/** + * Faster {@code long} buffer. + */ +public class FastLongBuffer { + + private long[] buffer; + private int offset; + + /** + * Creates a new {@code long} buffer. The buffer capacity is + * initially 64 longs, though its size increases if necessary. + */ + public FastLongBuffer() { + this.buffer = new long[64]; + } + + /** + * Creates a new {@code long} buffer, with a buffer capacity of + * the specified size. + * + * @param size the initial size. + * @throws IllegalArgumentException if size is negative. + */ + public FastLongBuffer(final int size) { + this.buffer = new long[size]; + } + + /** + * Grows the buffer. + */ + private void grow(final int minCapacity) { + final int oldCapacity = buffer.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) { + // special case, min capacity is larger then a grow + newCapacity = minCapacity + 512; + } + buffer = Arrays.copyOf(buffer, newCapacity); + } + + /** + * Appends single {@code long} to buffer. + */ + public void append(final long element) { + if (offset - buffer.length >= 0) { + grow(offset); + } + + buffer[offset++] = element; + } + + /** + * Appends {@code long} array to buffer. + */ + public FastLongBuffer append(final long[] array, final int off, final int len) { + if (offset + len - buffer.length > 0) { + grow(offset + len); + } + + System.arraycopy(array, off, buffer, offset, len); + offset += len; + return this; + } + + /** + * Appends {@code long} array to buffer. + */ + public FastLongBuffer append(final long[] array) { + return append(array, 0, array.length); + } + + /** + * Appends another fast buffer to this one. + */ + public FastLongBuffer append(final FastLongBuffer buff) { + if (buff.offset == 0) { + return this; + } + append(buff.buffer, 0, buff.offset); + return this; + } + + /** + * Returns buffer size. + */ + public int size() { + return offset; + } + + /** + * Tests if this buffer has no elements. + */ + public boolean isEmpty() { + return offset == 0; + } + + /** + * Resets the buffer content. + */ + public void clear() { + offset = 0; + } + + /** + * Creates {@code long} array from buffered content. + */ + public long[] toArray() { + return Arrays.copyOf(buffer, offset); + } + + /** + * Creates {@code long} subarray from buffered content. + */ + public long[] toArray(final int start, final int len) { + final long[] array = new long[len]; + + if (len == 0) { + return array; + } + + System.arraycopy(buffer, start, array, 0, len); + + return array; + } + + /** + * Returns {@code long} element at given index. + */ + public long get(final int index) { + if (index >= offset) { + throw new IndexOutOfBoundsException(); + } + return buffer[index]; + } + + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastShortBuffer.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastShortBuffer.java new file mode 100644 index 000000000..6d741d52b --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastShortBuffer.java @@ -0,0 +1,165 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.buffer; + +import java.util.Arrays; + +/** + * Faster {@code short} buffer. + */ +public class FastShortBuffer { + + private short[] buffer; + private int offset; + + /** + * Creates a new {@code short} buffer. The buffer capacity is + * initially 64 shorts, though its size increases if necessary. + */ + public FastShortBuffer() { + this.buffer = new short[64]; + } + + /** + * Creates a new {@code short} buffer, with a buffer capacity of + * the specified size. + * + * @param size the initial size. + * @throws IllegalArgumentException if size is negative. + */ + public FastShortBuffer(final int size) { + this.buffer = new short[size]; + } + + /** + * Grows the buffer. + */ + private void grow(final int minCapacity) { + final int oldCapacity = buffer.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) { + // special case, min capacity is larger then a grow + newCapacity = minCapacity + 512; + } + buffer = Arrays.copyOf(buffer, newCapacity); + } + + /** + * Appends single {@code short} to buffer. + */ + public void append(final short element) { + if (offset - buffer.length >= 0) { + grow(offset); + } + + buffer[offset++] = element; + } + + /** + * Appends {@code short} array to buffer. + */ + public FastShortBuffer append(final short[] array, final int off, final int len) { + if (offset + len - buffer.length > 0) { + grow(offset + len); + } + + System.arraycopy(array, off, buffer, offset, len); + offset += len; + return this; + } + + /** + * Appends {@code short} array to buffer. + */ + public FastShortBuffer append(final short[] array) { + return append(array, 0, array.length); + } + + /** + * Appends another fast buffer to this one. + */ + public FastShortBuffer append(final FastShortBuffer buff) { + if (buff.offset == 0) { + return this; + } + append(buff.buffer, 0, buff.offset); + return this; + } + + /** + * Returns buffer size. + */ + public int size() { + return offset; + } + + /** + * Tests if this buffer has no elements. + */ + public boolean isEmpty() { + return offset == 0; + } + + /** + * Resets the buffer content. + */ + public void clear() { + offset = 0; + } + + /** + * Creates {@code short} array from buffered content. + */ + public short[] toArray() { + return Arrays.copyOf(buffer, offset); + } + + /** + * Creates {@code short} subarray from buffered content. + */ + public short[] toArray(final int start, final int len) { + final short[] array = new short[len]; + + if (len == 0) { + return array; + } + + System.arraycopy(buffer, start, array, 0, len); + + return array; + } + + /** + * Returns {@code short} element at given index. + */ + public short get(final int index) { + if (index >= offset) { + throw new IndexOutOfBoundsException(); + } + return buffer[index]; + } + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/format/package-info.java b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/package-info.java similarity index 93% rename from fine-jodd/src/main/java/com/fr/third/jodd/format/package-info.java rename to fine-jodd/src/main/java/com/fr/third/jodd/buffer/package-info.java index 91431f890..034811504 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/format/package-info.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/buffer/package-info.java @@ -24,6 +24,6 @@ // POSSIBILITY OF SUCH DAMAGE. /** - * Good, old, C-alike, enhanced and fast printf formatting utility. + * Faster primitive buffers, a simple wrappers over an array. */ -package com.fr.third.jodd.format; \ No newline at end of file +package com.fr.third.jodd.buffer; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/AbstractCacheMap.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/AbstractCacheMap.java index 10340e7b0..65e127365 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/AbstractCacheMap.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/AbstractCacheMap.java @@ -25,10 +25,10 @@ package com.fr.third.jodd.cache; -import java.util.Iterator; +import java.util.HashMap; import java.util.Map; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.Objects; +import java.util.concurrent.locks.StampedLock; /** * Default implementation of timed and size cache map. @@ -43,8 +43,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; */ public abstract class AbstractCacheMap implements Cache { - class CacheObject { - CacheObject(K2 key, V2 object, long ttl) { + static class CacheObject { + CacheObject(final K2 key, final V2 object, final long ttl) { this.key = key; this.cachedObject = object; this.ttl = ttl; @@ -68,14 +68,13 @@ public abstract class AbstractCacheMap implements Cache { accessCount++; return cachedObject; } - } + V2 peekObject() { + return cachedObject; + } + } protected Map> cacheMap; - - private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock(); - private final Lock readLock = cacheLock.readLock(); - private final Lock writeLock = cacheLock.writeLock(); - + private final StampedLock lock = new StampedLock(); // ---------------------------------------------------------------- properties @@ -84,7 +83,8 @@ public abstract class AbstractCacheMap implements Cache { /** * {@inheritDoc} */ - public int getCacheSize() { + @Override + public int limit() { return cacheSize; } @@ -94,7 +94,8 @@ public abstract class AbstractCacheMap implements Cache { * Returns default cache timeout or 0 if it is not set. * Timeout can be set individually for each object. */ - public long getCacheTimeout() { + @Override + public long timeout() { return timeout; } @@ -119,7 +120,8 @@ public abstract class AbstractCacheMap implements Cache { /** * {@inheritDoc} */ - public void put(K key, V object) { + @Override + public void put(final K key, final V object) { put(key, object, timeout); } @@ -127,11 +129,14 @@ public abstract class AbstractCacheMap implements Cache { /** * {@inheritDoc} */ - public void put(K key, V object, long timeout) { - writeLock.lock(); + @Override + public void put(final K key, final V object, final long timeout) { + Objects.requireNonNull(object); + + final long stamp = lock.writeLock(); try { - CacheObject co = new CacheObject(key, object, timeout); + final CacheObject co = createCacheObject(key, object, timeout); if (timeout != 0) { existCustomTimeout = true; } @@ -141,10 +146,13 @@ public abstract class AbstractCacheMap implements Cache { cacheMap.put(key, co); } finally { - writeLock.unlock(); + lock.unlockWrite(stamp); } } + protected CacheObject createCacheObject(K key, V object, long timeout) { + return new CacheObject<>(key, object, timeout); + } // ---------------------------------------------------------------- get @@ -168,18 +176,33 @@ public abstract class AbstractCacheMap implements Cache { /** * {@inheritDoc} */ - public V get(K key) { - readLock.lock(); + @Override + public V get(final K key) { + long stamp = lock.readLock(); try { - CacheObject co = cacheMap.get(key); + final CacheObject co = cacheMap.get(key); if (co == null) { missCount++; return null; } if (co.isExpired()) { - // remove(key); // can't upgrade the lock - cacheMap.remove(key); + final long newStamp = lock.tryConvertToWriteLock(stamp); + + if (newStamp != 0L) { + stamp = newStamp; + // lock is upgraded to write lock + } + else { + // manually upgrade lock to write lock + lock.unlockRead(stamp); + stamp = lock.writeLock(); + } + + final CacheObject removedCo = cacheMap.remove(key); + if (removedCo != null) { + onRemove(removedCo.key, removedCo.cachedObject); + } missCount++; return null; @@ -189,17 +212,10 @@ public abstract class AbstractCacheMap implements Cache { return co.getObject(); } finally { - readLock.unlock(); + lock.unlock(stamp); } } - /** - * {@inheritDoc} - */ - public Iterator iterator() { - return new CacheValuesIterator(this); - } - // ---------------------------------------------------------------- prune /** @@ -210,13 +226,14 @@ public abstract class AbstractCacheMap implements Cache { /** * {@inheritDoc} */ + @Override public final int prune() { - writeLock.lock(); + final long stamp = lock.writeLock(); try { return pruneCache(); } finally { - writeLock.unlock(); + lock.unlockWrite(stamp); } } @@ -225,6 +242,7 @@ public abstract class AbstractCacheMap implements Cache { /** * {@inheritDoc} */ + @Override public boolean isFull() { if (cacheSize == 0) { return false; @@ -232,7 +250,7 @@ public abstract class AbstractCacheMap implements Cache { return cacheMap.size() >= cacheSize; } - protected boolean isReallyFull(K key) { + protected boolean isReallyFull(final K key) { if (cacheSize == 0) { return false; } @@ -247,33 +265,42 @@ public abstract class AbstractCacheMap implements Cache { /** * {@inheritDoc} */ - public void remove(K key) { - writeLock.lock(); + @Override + public V remove(final K key) { + V removedValue = null; + final long stamp = lock.writeLock(); try { - cacheMap.remove(key); + final CacheObject co = cacheMap.remove(key); + + if (co != null) { + onRemove(co.key, co.cachedObject); + removedValue = co.cachedObject; + } } finally { - writeLock.unlock(); + lock.unlockWrite(stamp); } + return removedValue; } /** * {@inheritDoc} */ + @Override public void clear() { - writeLock.lock(); + final long stamp = lock.writeLock(); try { cacheMap.clear(); } finally { - writeLock.unlock(); + lock.unlockWrite(stamp); } } - /** * {@inheritDoc} */ + @Override public int size() { return cacheMap.size(); } @@ -281,7 +308,37 @@ public abstract class AbstractCacheMap implements Cache { /** * {@inheritDoc} */ + @Override public boolean isEmpty() { return size() == 0; } + + /** + * {@inheritDoc} + */ + @Override + public Map snapshot(final boolean peek) { + final long stamp = lock.writeLock(); + try { + final Map map = new HashMap<>(cacheMap.size()); + cacheMap.forEach((key, cacheValue) -> { + if (!cacheValue.isExpired()) { + map.put(key, peek ? cacheValue.peekObject() : cacheValue.getObject()); + } + }); + return map; + } + finally { + lock.unlockWrite(stamp); + } + } + + // ---------------------------------------------------------------- protected + + /** + * Callback called on item removal. The cache is still locked. + */ + protected void onRemove(final K key, final V cachedObject) { + } + } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/Cache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/Cache.java index 482742df5..dae2bbb3d 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/Cache.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/Cache.java @@ -25,7 +25,7 @@ package com.fr.third.jodd.cache; -import java.util.Iterator; +import java.util.Map; /** * Cache interface. @@ -35,12 +35,12 @@ public interface Cache { /** * Returns cache size or 0 if there is no size limit. */ - int getCacheSize(); + int limit(); /** * Returns default timeout or 0 if it is not set. */ - long getCacheTimeout(); + long timeout(); /** * Adds an object to the cache with default timeout. @@ -51,6 +51,7 @@ public interface Cache { /** * Adds an object to the cache with specified timeout after which it becomes expired. * If cache is full, {@link #prune()} is invoked to make room for new object. + * Cached value must be non-null. */ void put(K key, V object, long timeout); @@ -60,11 +61,6 @@ public interface Cache { */ V get(K key); - /** - * Returns iterator over non-expired values. - */ - Iterator iterator(); - /** * Prunes objects from cache and returns the number of removed objects. * Used strategy depends on cache implementation. @@ -78,9 +74,10 @@ public interface Cache { boolean isFull(); /** - * Removes an object from the cache. + * Removes an object from the cache and returns removed value of {@code null} + * if object was not in the cache or was expired. */ - void remove(K key); + V remove(K key); /** * Clears current cache. @@ -96,4 +93,16 @@ public interface Cache { * Returns true if cache is empty. */ boolean isEmpty(); + + /** + * Creates a snapshot from current cache values. Returned values may not + * longer be valid or they might be already expired! Cache is locked during + * the snapshot creation. + * @param peek if set, snapshot will just peek the object and not get them (and modify last access) + */ + Map snapshot(boolean peek); + + default Map snapshot() { + return this.snapshot(false); + } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/FIFOCache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/FIFOCache.java index 61b4d3c0a..0f6d08185 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/FIFOCache.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/FIFOCache.java @@ -25,8 +25,8 @@ package com.fr.third.jodd.cache; -import java.util.LinkedHashMap; import java.util.Iterator; +import java.util.LinkedHashMap; /** * FIFO (first in first out) cache. @@ -42,17 +42,17 @@ import java.util.Iterator; */ public class FIFOCache extends AbstractCacheMap { - public FIFOCache(int cacheSize) { + public FIFOCache(final int cacheSize) { this(cacheSize, 0); } /** * Creates a new LRU cache. */ - public FIFOCache(int cacheSize, long timeout) { + public FIFOCache(final int cacheSize, final long timeout) { this.cacheSize = cacheSize; this.timeout = timeout; - cacheMap = new LinkedHashMap>(cacheSize + 1, 1.0f, false); + cacheMap = new LinkedHashMap<>(cacheSize + 1, 1.0f, false); } @@ -63,13 +63,14 @@ public class FIFOCache extends AbstractCacheMap { */ @Override protected int pruneCache() { - int count = 0; + int count = 0; CacheObject first = null; Iterator> values = cacheMap.values().iterator(); while (values.hasNext()) { CacheObject co = values.next(); if (co.isExpired()) { values.remove(); + onRemove(co.key, co.cachedObject); count++; } if (first == null) { @@ -79,6 +80,7 @@ public class FIFOCache extends AbstractCacheMap { if (isFull()) { if (first != null) { cacheMap.remove(first.key); + onRemove(first.key, first.cachedObject); count++; } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileCache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileCache.java new file mode 100644 index 000000000..6b28c27d5 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileCache.java @@ -0,0 +1,153 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +package com.fr.third.jodd.cache; + +import com.fr.third.jodd.io.FileUtil; + +import java.io.File; +import java.io.IOException; + +/** + * Base in-memory files cache. + */ +public abstract class FileCache { + + protected final Cache cache; + protected final int maxSize; + protected final int maxFileSize; + protected final long timeout; + + protected int usedSize; + + /** + * Creates new File LFU cache. + * @param maxSize total cache size in bytes + * @param maxFileSize max available file size in bytes, may be 0 + * @param timeout timeout, may be 0 + */ + protected FileCache(final int maxSize, final int maxFileSize, final long timeout) { + this.maxSize = maxSize; + this.maxFileSize = maxFileSize; + this.timeout = timeout; + this.cache = createCache(); + } + + /** + * Creates new cache instance for files content. + */ + protected abstract Cache createCache(); + + /** + * Creates CacheObject that updates last access time based on files last modification. + */ + protected AbstractCacheMap.CacheObject createFileCacheObject(File fileKey, byte[] object, long timeout) { + return new AbstractCacheMap.CacheObject(fileKey, object, timeout) { + @Override + boolean isExpired() { + if (fileKey.lastModified() > this.lastAccess) { + this.lastAccess = fileKey.lastModified(); + } + return super.isExpired(); + } + }; + } + + // ---------------------------------------------------------------- get + + /** + * Returns max cache size in bytes. + */ + public int maxSize() { + return maxSize; + } + + /** + * Returns actually used size in bytes. + */ + public int usedSize() { + return usedSize; + } + + /** + * Returns maximum allowed file size that can be added to the cache. + * Files larger than this value will be not added, even if there is + * enough room. + */ + public int maxFileSize() { + return maxFileSize; + } + + /** + * Returns number of cached files. + */ + public int cachedFilesCount() { + return cache.size(); + } + + /** + * Returns timeout. + */ + public long cacheTimeout() { + return cache.timeout(); + } + + /** + * Clears the cache. + */ + public void clear() { + cache.clear(); + usedSize = 0; + } + + // ---------------------------------------------------------------- get + + /** + * Returns cached file bytes. If file is not cached it will be + * read and put in the cache (if all the rules are satisfied). + */ + public byte[] getFileBytes(final File file) throws IOException { + byte[] bytes = cache.get(file); + if (bytes != null) { + return bytes; + } + + // add file + bytes = FileUtil.readBytes(file); + + if ((maxFileSize != 0) && (file.length() > maxFileSize)) { + // don't cache files that size exceed max allowed file size + return bytes; + } + + usedSize += bytes.length; + + // put file into cache + // if used size > total, purge() will be invoked + cache.put(file, bytes); + + return bytes; + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLFUCache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLFUCache.java index 3971fe85b..5857de330 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLFUCache.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLFUCache.java @@ -25,32 +25,23 @@ package com.fr.third.jodd.cache; -import com.fr.third.jodd.io.FileUtil; - import java.io.File; -import java.io.IOException; /** * Files LFU cache stores files content in memory to dramatically * speed up performances for frequently read files. */ -public class FileLFUCache { - - protected final LFUCache cache; - protected final int maxSize; - protected final int maxFileSize; - - protected int usedSize; +public class FileLFUCache extends FileCache { /** * Creates file LFU cache with specified size. Sets * {@link #maxFileSize max available file size} to half of this value. */ - public FileLFUCache(int maxSize) { + public FileLFUCache(final int maxSize) { this(maxSize, maxSize / 2, 0); } - public FileLFUCache(int maxSize, int maxFileSize) { + public FileLFUCache(final int maxSize, final int maxFileSize) { this(maxSize, maxFileSize, 0); } @@ -60,105 +51,32 @@ public class FileLFUCache { * @param maxFileSize max available file size in bytes, may be 0 * @param timeout timeout, may be 0 */ - public FileLFUCache(int maxSize, int maxFileSize, long timeout) { - this.cache = new LFUCache(0, timeout) { + public FileLFUCache(final int maxSize, final int maxFileSize, final long timeout) { + super(maxSize, maxFileSize, timeout); + } + + @Override + protected Cache createCache() { + return new LFUCache(0, timeout) { @Override public boolean isFull() { return usedSize > FileLFUCache.this.maxSize; } @Override - protected boolean isReallyFull(File file) { + protected boolean isReallyFull(final File file) { return isFull(); } @Override - protected void onRemove(File key, byte[] cachedObject) { + protected void onRemove(final File key, final byte[] cachedObject) { usedSize -= cachedObject.length; } + @Override + protected CacheObject createCacheObject(File key, byte[] object, long timeout) { + return createFileCacheObject(key, object, timeout); + } }; - this.maxSize = maxSize; - this.maxFileSize = maxFileSize; - } - - // ---------------------------------------------------------------- get - - /** - * Returns max cache size in bytes. - */ - public int getMaxSize() { - return maxSize; } - - /** - * Returns actually used size in bytes. - */ - public int getUsedSize() { - return usedSize; - } - - /** - * Returns maximum allowed file size that can be added to the cache. - * Files larger than this value will be not added, even if there is - * enough room. - */ - public int getMaxFileSize() { - return maxFileSize; - } - - /** - * Returns number of cached files. - */ - public int getCachedFilesCount() { - return cache.size(); - } - - /** - * Returns timeout. - */ - public long getCacheTimeout() { - return cache.getCacheTimeout(); - } - - /** - * Clears the cache. - */ - public void clear() { - cache.clear(); - usedSize = 0; - } - - // ---------------------------------------------------------------- get - - public byte[] getFileBytes(String fileName) throws IOException { - return getFileBytes(new File(fileName)); - } - - /** - * Returns cached file bytes. - */ - public byte[] getFileBytes(File file) throws IOException { - byte[] bytes = cache.get(file); - if (bytes != null) { - return bytes; - } - - // add file - bytes = FileUtil.readBytes(file); - - if ((maxFileSize != 0) && (file.length() > maxFileSize)) { - // don't cache files that size exceed max allowed file size - return bytes; - } - - usedSize += bytes.length; - - // put file into cache - // if used size > total, purge() will be invoked - cache.put(file, bytes); - - return bytes; - } - } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/CacheValuesIterator.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLRUCache.java similarity index 54% rename from fine-jodd/src/main/java/com/fr/third/jodd/cache/CacheValuesIterator.java rename to fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLRUCache.java index 1d8c6b2b2..464bdb2e4 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/CacheValuesIterator.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLRUCache.java @@ -22,62 +22,60 @@ // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. - package com.fr.third.jodd.cache; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.io.File; /** - * Values iterator for {@link jodd.cache.AbstractCacheMap}. + * Cache of recently used files. */ -public class CacheValuesIterator implements Iterator { - - private final Iterator.CacheObject> iterator; - - private AbstractCacheMap.CacheObject nextValue; - - CacheValuesIterator(AbstractCacheMap abstractCacheMap) { - iterator = abstractCacheMap.cacheMap.values().iterator(); - nextValue(); - } +public class FileLRUCache extends FileCache { /** - * Resolves next value. If next value doesn't exist, next value will be null. + * Creates file LRU cache with specified size. Sets + * {@link #maxFileSize max available file size} to half of this value. */ - private void nextValue() { - while (iterator.hasNext()) { - nextValue = iterator.next(); - if (!nextValue.isExpired()) { - return; - } - } - nextValue = null; + public FileLRUCache(final int maxSize) { + this(maxSize, maxSize / 2, 0); } - /** - * Returns true if there are more elements in the cache. - */ - public boolean hasNext() { - return nextValue != null; + public FileLRUCache(final int maxSize, final int maxFileSize) { + this(maxSize, maxFileSize, 0); } /** - * Returns next non-expired element from the cache. + * Creates new File LRU cache. + * @param maxSize total cache size in bytes + * @param maxFileSize max available file size in bytes, may be 0 + * @param timeout timeout, may be 0 */ - public V next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - V cachedObject = nextValue.cachedObject; - nextValue(); - return cachedObject; + public FileLRUCache(final int maxSize, final int maxFileSize, final long timeout) { + super(maxSize, maxFileSize, timeout); } - /** - * Removes current non-expired element from the cache. - */ - public void remove() { - iterator.remove(); + @Override + protected Cache createCache() { + return new LRUCache(0, timeout) { + @Override + public boolean isFull() { + return usedSize > FileLRUCache.this.maxSize; + } + + @Override + protected boolean isReallyFull(final File file) { + return isFull(); + } + + @Override + protected void onRemove(final File key, final byte[] cachedObject) { + usedSize -= cachedObject.length; + } + + @Override + protected CacheObject createCacheObject(File key, byte[] object, long timeout) { + return createFileCacheObject(key, object, timeout); + } + + }; } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/LFUCache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/LFUCache.java index 434d23e4f..39ee90d72 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/LFUCache.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/LFUCache.java @@ -44,14 +44,14 @@ import java.util.Iterator; */ public class LFUCache extends AbstractCacheMap { - public LFUCache(int maxSize) { + public LFUCache(final int maxSize) { this(maxSize, 0); } - public LFUCache(int maxSize, long timeout) { + public LFUCache(final int maxSize, final long timeout) { this.cacheSize = maxSize; this.timeout = timeout; - cacheMap = new HashMap>(maxSize + 1); + cacheMap = new HashMap<>(maxSize + 1); } // ---------------------------------------------------------------- prune @@ -63,7 +63,7 @@ public class LFUCache extends AbstractCacheMap { */ @Override protected int pruneCache() { - int count = 0; + int count = 0; CacheObject comin = null; // remove expired items and find cached object with minimal access count @@ -76,7 +76,7 @@ public class LFUCache extends AbstractCacheMap { count++; continue; } - + if (comin == null) { comin = co; } else { @@ -101,18 +101,11 @@ public class LFUCache extends AbstractCacheMap { if (co.accessCount <= 0) { values.remove(); onRemove(co.key, co.cachedObject); - count++; + count++; } } } return count; } - /** - * Callback method invoked on cached object removal. - * By default does nothing. - */ - protected void onRemove(K key, V cachedObject) { - } - } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/LRUCache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/LRUCache.java index 2a02d9cd3..87232a0e5 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/LRUCache.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/LRUCache.java @@ -50,19 +50,19 @@ import java.util.Iterator; */ public class LRUCache extends AbstractCacheMap { - public LRUCache(int cacheSize) { + public LRUCache(final int cacheSize) { this(cacheSize, 0); } /** * Creates a new LRU cache. */ - public LRUCache(int cacheSize, long timeout) { + public LRUCache(final int cacheSize, final long timeout) { this.cacheSize = cacheSize; this.timeout = timeout; cacheMap = new LinkedHashMap>(cacheSize + 1, 1.0f, true) { @Override - protected boolean removeEldestEntry(Map.Entry eldest) { + protected boolean removeEldestEntry(final Map.Entry eldest) { return LRUCache.this.removeEldestEntry(size()); } }; @@ -71,7 +71,7 @@ public class LRUCache extends AbstractCacheMap { /** * Removes the eldest entry if current cache size exceed cache size. */ - protected boolean removeEldestEntry(int currentSize) { + protected boolean removeEldestEntry(final int currentSize) { if (cacheSize == 0) { return false; } @@ -88,12 +88,13 @@ public class LRUCache extends AbstractCacheMap { if (!isPruneExpiredActive()) { return 0; } - int count = 0; + int count = 0; Iterator> values = cacheMap.values().iterator(); while (values.hasNext()) { CacheObject co = values.next(); if (co.isExpired()) { values.remove(); + onRemove(co.key, co.cachedObject); count++; } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/NoCache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/NoCache.java index f6227cc35..b618a5366 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/NoCache.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/NoCache.java @@ -25,7 +25,8 @@ package com.fr.third.jodd.cache; -import java.util.Iterator; +import java.util.Collections; +import java.util.Map; /** * Simple no-cache implementations of {@link Cache} for situation when cache @@ -33,52 +34,63 @@ import java.util.Iterator; */ public class NoCache implements Cache { - - public int getCacheSize() { + @Override + public int limit() { return 0; } - public long getCacheTimeout() { + @Override + public long timeout() { return 0; } - public void put(K key, V object) { + @Override + public void put(final K key, final V object) { // ignore } - public void put(K key, V object, long timeout) { + @Override + public void put(final K key, final V object, final long timeout) { // ignore } - public V get(K key) { - return null; - } - - public Iterator iterator() { + @Override + public V get(final K key) { return null; } + @Override public int prune() { return 0; } + @Override public boolean isFull() { return true; } - public void remove(K key) { - // ignore + @Override + public V remove(final K key) { + return null; } + @Override public void clear() { // ignore } + @Override public int size() { return 0; } + @Override public boolean isEmpty() { return true; } + + @Override + public Map snapshot(final boolean peek) { + return Collections.emptyMap(); + } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/TimedCache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/TimedCache.java index 9502f2781..b861314b4 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/cache/TimedCache.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/TimedCache.java @@ -37,10 +37,10 @@ import java.util.TimerTask; */ public class TimedCache extends AbstractCacheMap { - public TimedCache(long timeout) { + public TimedCache(final long timeout) { this.cacheSize = 0; this.timeout = timeout; - cacheMap = new HashMap>(); + cacheMap = new HashMap<>(); } // ---------------------------------------------------------------- prune @@ -50,7 +50,7 @@ public class TimedCache extends AbstractCacheMap { */ @Override protected int pruneCache() { - int count = 0; + int count = 0; Iterator> values = cacheMap.values().iterator(); while (values.hasNext()) { CacheObject co = values.next(); @@ -70,7 +70,7 @@ public class TimedCache extends AbstractCacheMap { /** * Schedules prune. */ - public void schedulePrune(long delay) { + public void schedulePrune(final long delay) { if (pruneTimer != null) { pruneTimer.cancel(); } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cache/TypeCache.java b/fine-jodd/src/main/java/com/fr/third/jodd/cache/TypeCache.java new file mode 100644 index 000000000..bbda93a1c --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cache/TypeCache.java @@ -0,0 +1,205 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.cache; + +import java.util.AbstractMap; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Types cache. Provides several implementations depending on what you need to be addressed. + * There are two things you should take care off: + *
    + *
  • synchronization - especially on storing items. If not synchronized, one instance of an item may be put + * more then once into the map. This is usually fine, as it happens only during the initialization and makes not + * harm if something is created twice
  • + *
  • weak - if your key classes are replaced during the runtime, you should use weak map, in order to automatically + * remove obsolete keys.
  • + *
+ */ +public class TypeCache { + + // ---------------------------------------------------------------- builder + + /** + * Creates a type cache by using a builder. + */ + public static Builder create() { + return new Builder<>(); + } + + /** + * Creates default implementation of the type cache. + */ + @SuppressWarnings("unchecked") + public static TypeCache createDefault() { + return TypeCache.create().get(); + } + + public static class Builder { + private boolean threadsafe; + private boolean weak; + private boolean none; + + /** + * No cache will be used. + * Setting other properties will not have any affect. + */ + public Builder noCache() { + none = true; + return this; + } + + /** + * Cache keys will be weak. + */ + public Builder weak(final boolean weak) { + this.weak = weak; + return this; + } + /** + * Cache will be thread-safe. + */ + public Builder threadsafe(final boolean threadsafe) { + this.threadsafe = threadsafe; + return this; + } + + /** + * Builds a type cache. + */ + public TypeCache get() { + final Map, A> map; + if (none) { + map = new AbstractMap, A>() { + @Override + public A put(final Class key, final A value) { + return null; + } + + @Override + public A get(final Object key) { + return null; + } + + @Override + public Set, A>> entrySet() { + return Collections.emptySet(); + } + }; + } + else if (weak) { + if (threadsafe) { + map = Collections.synchronizedMap(new WeakHashMap<>()); + } else { + map = new WeakHashMap<>(); + } + } else { + if (threadsafe) { + map = new ConcurrentHashMap<>(); + } else { + map = new IdentityHashMap<>(); + } + } + + return new TypeCache<>(map); + } + } + + // ---------------------------------------------------------------- map + + private final Map, T> map; + + private TypeCache(final Map, T> backedMap) { + this.map = backedMap; + } + + /** + * Add values to the map. + */ + public T put(final Class type, final T value) { + return map.put(type, value); + } + + /** + * Returns value from the map or {@code null} if value does not exist. + */ + public T get(final Class key) { + return map.get(key); + } + + /** + * Returns existing value or add default supplied one. + * Use this method instead of {@code get-nullcheck-put} block when + * thread-safety is of importance. + */ + @SuppressWarnings("unchecked") + public T get(final Class key, final Function, ? extends T> mappingFunction) { + return map.computeIfAbsent(key, aClass -> mappingFunction.apply((Class) aClass)); + } + + /** + * Removes element from the type cache. + */ + public T remove(final Class type) { + return map.remove(type); + } + + /** + * Clears complete cache. + */ + public void clear() { + map.clear(); + } + + /** + * Returns cache size. + */ + public int size() { + return map.size(); + } + + /** + * Returns {@code true} if cache is empty. + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Iterates all cached values. + */ + public void forEachValue(final Consumer valueConsumer) { + map.values().forEach(valueConsumer); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/chalk/Chalk.java b/fine-jodd/src/main/java/com/fr/third/jodd/chalk/Chalk.java new file mode 100644 index 000000000..b6f7fad51 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/chalk/Chalk.java @@ -0,0 +1,298 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.chalk; + +import com.fr.third.jodd.util.StringPool; + +/** + * Chalk allows you to color output going to console. + * @see Chalk256 + */ +public class Chalk> { + + /** + * Global flag that disables all the chalks. + * Useful for windows platforms :) + */ + public static boolean enabled = true; + + protected static final String RESET = "0"; + + protected static final String BOLD = "1"; + protected static final String UNBOLD = "22"; // 21 isn't widely supported and 22 does the same thing + protected static final String DIM = "2"; + protected static final String UNDIM = "22"; + protected static final String ITALIC = "3"; + protected static final String UNITALIC = "23"; + protected static final String UNDERLINE = "4"; + protected static final String UNUNDERLINE = "24"; + protected static final String INVERSE = "7"; + protected static final String UNINVERSE = "27"; + protected static final String HIDDEN = "8"; + protected static final String UNHIDDEN = "28"; + protected static final String STRIKETHROUGH = "9"; + protected static final String UNSTRIKETHROUGH = "29"; + + protected static final String COLOR_RESET = "39"; + protected static final String BLACK = "30"; + protected static final String RED = "31"; + protected static final String GREEN = "32"; + protected static final String YELLOW = "33"; + protected static final String BLUE = "34"; + protected static final String MAGENTA = "35"; + protected static final String CYAN = "36"; + protected static final String WHITE = "37"; + protected static final String GRAY = "90"; + + protected static final String BGCOLOR_RESET = "49"; + protected static final String BGBLACK = "40"; + protected static final String BGRED = "41"; + protected static final String BGGREEN = "42"; + protected static final String BGYELLOW = "43"; + protected static final String BGBLUE = "44"; + protected static final String BGMAGENTA = "45"; + protected static final String BGCYAN = "46"; + protected static final String BGWHITE = "47"; + + protected StringBuilder prefix; + protected StringBuilder suffix; + protected String text; + + /** + * Creates new chalk. + */ + public static Chalk chalk() { + return new Chalk(); + } + + @SuppressWarnings("unchecked") + protected T _this() { + return (T) this; + } + + // ---------------------------------------------------------------- style + + public T bold() { + startSequence(BOLD); + endSequence(UNBOLD); + return _this(); + } + + public T italic() { + startSequence(ITALIC); + endSequence(UNITALIC); + return _this(); + } + + public T dim() { + startSequence(DIM); + endSequence(UNDIM); + return _this(); + } + + public T underline() { + startSequence(UNDERLINE); + endSequence(UNUNDERLINE); + return _this(); + } + + public T inverse() { + startSequence(INVERSE); + endSequence(UNINVERSE); + return _this(); + } + + public T hidden() { + startSequence(HIDDEN); + endSequence(UNHIDDEN); + return _this(); + } + public T strikeThrough() { + startSequence(STRIKETHROUGH); + endSequence(UNSTRIKETHROUGH); + return _this(); + } + + // ---------------------------------------------------------------- colors + + public T black() { + startSequence(BLACK); + endSequence(COLOR_RESET); + return _this(); + } + public T red() { + startSequence(RED); + endSequence(COLOR_RESET); + return _this(); + } + public T green() { + startSequence(GREEN); + endSequence(COLOR_RESET); + return _this(); + } + public T yellow() { + startSequence(YELLOW); + endSequence(COLOR_RESET); + return _this(); + } + public T blue() { + startSequence(BLUE); + endSequence(COLOR_RESET); + return _this(); + } + public T magenta() { + startSequence(MAGENTA); + endSequence(COLOR_RESET); + return _this(); + } + public T cyan() { + startSequence(CYAN); + endSequence(COLOR_RESET); + return _this(); + } + public T white() { + startSequence(WHITE); + endSequence(COLOR_RESET); + return _this(); + } + public T gray() { + startSequence(GRAY); + endSequence(COLOR_RESET); + return _this(); + } + public T grey() { + return gray(); + } + + // ---------------------------------------------------------------- bg colors + + public T bgBlack() { + startSequence(BGBLACK); + endSequence(BGCOLOR_RESET); + return _this(); + } + public T bgRed() { + startSequence(BGRED); + endSequence(BGCOLOR_RESET); + return _this(); + } + public T bgGreen() { + startSequence(BGGREEN); + endSequence(BGCOLOR_RESET); + return _this(); + } + public T bgYellow() { + startSequence(BGYELLOW); + endSequence(BGCOLOR_RESET); + return _this(); + } + public T bgBlue() { + startSequence(BGBLUE); + endSequence(BGCOLOR_RESET); + return _this(); + } + public T bgMagenta() { + startSequence(BGMAGENTA); + endSequence(BGCOLOR_RESET); + return _this(); + } + public T bgCyan() { + startSequence(BGCYAN); + endSequence(BGCOLOR_RESET); + return _this(); + } + public T bgWhite() { + startSequence(BGWHITE); + endSequence(BGCOLOR_RESET); + return _this(); + } + + // ---------------------------------------------------------------- internal + + protected void startSequence(final String value) { + if (prefix == null) { + prefix = new StringBuilder(); + prefix.append("\u001B["); + } + else { + prefix.append(StringPool.SEMICOLON); + } + + prefix.append(value); + } + + protected void endSequence(final String value) { + if (suffix == null) { + suffix = new StringBuilder(); + suffix + .append("\u001B[") + .append(value); + } + else { + suffix.insert(2, value + StringPool.SEMICOLON); + } + } + + // ---------------------------------------------------------------- out + + /** + * Returns chalked string. + */ + public String on(final String string) { + if (!enabled) { + return string; + } + + final StringBuilder sb = new StringBuilder(); + + if (prefix != null) { + sb.append(prefix).append("m"); + } + + sb.append(string); + + if (suffix != null) { + sb.append(suffix).append("m"); + } + + return sb.toString(); + } + + /** + * Prints chalked string to system output. + */ + public void print(final String string) { + System.out.print(on(string)); + } + + /** + * Prints chalked string to system output. + */ + public void println(final String string) { + System.out.println(on(string)); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/chalk/Chalk256.java b/fine-jodd/src/main/java/com/fr/third/jodd/chalk/Chalk256.java new file mode 100644 index 000000000..7dfe67dc6 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/chalk/Chalk256.java @@ -0,0 +1,123 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.chalk; + +/** + * Chalk256 allows you to color output going to ansi-256 console. + * @see Chalk + */ +public class Chalk256 extends Chalk { + + private static final String[] FG_CODES = new String[256]; + private static final String[] BG_CODES = new String[256]; + + static { + for (int i = 0; i < FG_CODES.length; i++) { + FG_CODES[i] = "38;5;" + i; + } + for (int i = 0; i < BG_CODES.length; i++) { + BG_CODES[i] = "48;5;" + i; + } + } + + public static Chalk256 chalk() { + return new Chalk256(); + } + + // ---------------------------------------------------------------- fg codes + + public Chalk256 standard(final int index) { + startSequence(FG_CODES[index(index, 0, 8)]); + endSequence(RESET); + return _this(); + } + public Chalk256 bright(final int index) { + startSequence(FG_CODES[index(index, 8, 16)]); + endSequence(RESET); + return _this(); + } + public Chalk256 rgb(final int index) { + startSequence(FG_CODES[index(index, 16, 232)]); + endSequence(RESET); + return _this(); + } + + /** + * Colors with red-green-blue value, in the range 0 to 6. + */ + public Chalk256 rgb(final int r, final int b, final int g) { + startSequence(FG_CODES[index(36*r + 6*g + b,16, 232)]); + endSequence(RESET); + return _this(); + } + public Chalk256 grayscale(final int index) { + startSequence(FG_CODES[index(index, 232, 256)]); + endSequence(RESET); + return _this(); + } + // ---------------------------------------------------------------- bg codes + + public Chalk256 bgStandard(final int index) { + startSequence(BG_CODES[index(index, 0, 8)]); + endSequence(RESET); + return _this(); + } + public Chalk256 bgBright(final int index) { + startSequence(BG_CODES[index(index, 8, 16)]); + endSequence(RESET); + return _this(); + } + public Chalk256 bgRgb(final int index) { + startSequence(BG_CODES[index(index, 16, 232)]); + endSequence(RESET); + return _this(); + } + + /** + * Colors with red-green-blue value, in the range 0 to 6. + */ + public Chalk256 bgRgb(final int r, final int b, final int g) { + startSequence(BG_CODES[index(36*r + 6*g + b,16, 232)]); + endSequence(RESET); + return _this(); + } + public Chalk256 bgGrayscale(final int index) { + startSequence(BG_CODES[index(index, 232, 256)]); + endSequence(RESET); + return _this(); + } + + // ---------------------------------------------------------------- bgcolors + + private int index(int index, final int from, final int to) { + index += from; + if ((index < from) || (index >= to)) { + throw new IllegalArgumentException("Color index not in range: [0, " + (to - from) + "]"); + } + return index; + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/buffer/package-info.java b/fine-jodd/src/main/java/com/fr/third/jodd/chalk/package-info.java similarity index 89% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/buffer/package-info.java rename to fine-jodd/src/main/java/com/fr/third/jodd/chalk/package-info.java index 51255aead..4cb8d94b0 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/buffer/package-info.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/chalk/package-info.java @@ -24,8 +24,6 @@ // POSSIBILITY OF SUCH DAMAGE. /** - * Fast, fast buffers. - * This buffer implementation does not store all data - * in single array, but in array of chunks. + * Little tool for coloring the console output. */ -package com.fr.third.jodd.util.buffer; \ No newline at end of file +package com.fr.third.jodd.chalk; \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/cli/Cli.java b/fine-jodd/src/main/java/com/fr/third/jodd/cli/Cli.java new file mode 100644 index 000000000..ade542bcb --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/cli/Cli.java @@ -0,0 +1,233 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.cli; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class Cli implements Consumer { + + private final List

* More info: Julian Date on Wikipedia + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public class JDateTime implements Comparable, Cloneable, Serializable { public static final String DEFAULT_FORMAT = "YYYY-MM-DD hh:mm:ss.mss"; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JDateTimeDefault.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JDateTimeDefault.java index 4a8429f13..e9646f16c 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JDateTimeDefault.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JDateTimeDefault.java @@ -25,8 +25,6 @@ package com.fr.third.jodd.datetime; -import com.fr.third.jodd.datetime.format.Iso8601JdtFormatter; -import com.fr.third.jodd.datetime.format.JdtFormatter; import com.fr.third.jodd.datetime.format.Iso8601JdtFormatter; import com.fr.third.jodd.datetime.format.JdtFormatter; @@ -35,7 +33,9 @@ import java.util.Locale; /** * Defaults for {@link JDateTime}. + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated @SuppressWarnings({"RedundantFieldInitialization"}) public class JDateTimeDefault { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JStopWatch.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JStopWatch.java index 56c4bf77d..daf047fb9 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JStopWatch.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JStopWatch.java @@ -31,7 +31,10 @@ import java.util.ArrayList; /** * Nice thread-aware stopwatch that supports time spans, cumulative times and laps. * Useful for all kind of profiling, time measurements etc. + * + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public class JStopWatch { /** diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JulianDateStamp.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JulianDateStamp.java index b24c9da0d..e42977955 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JulianDateStamp.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/JulianDateStamp.java @@ -62,7 +62,9 @@ import java.io.Serializable; * @see TimeUtil * @see JDateTime * @see DateTimeStamp + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public class JulianDateStamp implements Serializable, Cloneable { /** diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/Period.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/Period.java index b03c9a950..441a3c612 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/Period.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/Period.java @@ -30,7 +30,10 @@ package com.fr.third.jodd.datetime; * easy to calculate period in days - just by subtracting two julian day numbers. * However, calculating hours, minutes and seconds would require more calculation * and this class provides simple and faster period calculation. + * + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public class Period { protected final long days; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeUtil.java index 2360af7fb..8b13291dc 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeUtil.java @@ -35,7 +35,10 @@ import java.util.Locale; * {@link JDateTime} and it contains few utilities that may be used * elsewhere, although {@link JDateTime} is recommended for all time * manipulation. + * + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public class TimeUtil { public static final int SECONDS_IN_DAY = 60 * 60 * 24; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeZoneUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeZoneUtil.java index 3eedfea39..d8ce150ae 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeZoneUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeZoneUtil.java @@ -29,7 +29,10 @@ import java.util.TimeZone; /** * Misc timezone utilities. + * + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public class TimeZoneUtil { /** diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/AbstractFormatter.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/AbstractFormatter.java index 1c4bbffb5..aec8a7f09 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/AbstractFormatter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/AbstractFormatter.java @@ -25,8 +25,6 @@ package com.fr.third.jodd.datetime.format; -import com.fr.third.jodd.datetime.DateTimeStamp; -import com.fr.third.jodd.datetime.JDateTime; import com.fr.third.jodd.datetime.DateTimeStamp; import com.fr.third.jodd.datetime.JDateTime; import com.fr.third.jodd.util.CharUtil; @@ -49,7 +47,10 @@ import com.fr.third.jodd.util.CharUtil; *

* * It is not necessary to have parsers for all patterns. + * + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public abstract class AbstractFormatter implements JdtFormatter { /** diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/Iso8601JdtFormatter.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/Iso8601JdtFormatter.java index 2720d8c2c..284def1bf 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/Iso8601JdtFormatter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/Iso8601JdtFormatter.java @@ -67,7 +67,10 @@ import java.util.TimeZone; *

* Patterns noted with + sign are used both for conversion and parsing. * All patterns are used for conversion. + * + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public class Iso8601JdtFormatter extends AbstractFormatter { public Iso8601JdtFormatter() { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormat.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormat.java index 73b52ac14..a40ee7439 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormat.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormat.java @@ -27,12 +27,13 @@ package com.fr.third.jodd.datetime.format; import com.fr.third.jodd.datetime.DateTimeStamp; import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.datetime.DateTimeStamp; /** * Immutable format-formatter pair. + * + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public class JdtFormat { protected final String format; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormatter.java b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormatter.java index 06ab86bf0..5d7cff1bc 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormatter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormatter.java @@ -25,8 +25,6 @@ package com.fr.third.jodd.datetime.format; -import com.fr.third.jodd.datetime.DateTimeStamp; -import com.fr.third.jodd.datetime.JDateTime; import com.fr.third.jodd.datetime.DateTimeStamp; import com.fr.third.jodd.datetime.JDateTime; @@ -36,7 +34,9 @@ import java.io.Serializable; * Date time formatter performs conversion both from and to string representation of time. * * @see AbstractFormatter + * @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类 */ +@Deprecated public interface JdtFormatter extends Serializable { /** diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/exception/ExceptionUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/exception/ExceptionUtil.java index 48d00afbc..b04165fa9 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/exception/ExceptionUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/exception/ExceptionUtil.java @@ -28,12 +28,13 @@ package com.fr.third.jodd.exception; import com.fr.third.jodd.io.StreamUtil; import com.fr.third.jodd.util.StringUtil; -import java.io.StringWriter; import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; -import java.sql.SQLException; -import java.lang.reflect.InvocationTargetException; /** * Few exception utilities. @@ -62,9 +63,9 @@ public class ExceptionUtil { /** * Returns stack trace filtered by class names. */ - public static StackTraceElement[] getStackTrace(Throwable t, String[] allow, String[] deny) { + public static StackTraceElement[] getStackTrace(final Throwable t, final String[] allow, final String[] deny) { StackTraceElement[] st = t.getStackTrace(); - ArrayList result = new ArrayList(st.length); + ArrayList result = new ArrayList<>(st.length); elementLoop: for (StackTraceElement element : st) { @@ -72,7 +73,7 @@ public class ExceptionUtil { if (allow != null) { boolean validElemenet = false; for (String filter : allow) { - if (className.indexOf(filter) != -1) { + if (className.contains(filter)) { validElemenet = true; break; } @@ -83,7 +84,7 @@ public class ExceptionUtil { } if (deny != null) { for (String filter : deny) { - if (className.indexOf(filter) != -1) { + if (className.contains(filter)) { continue elementLoop; } } @@ -97,8 +98,8 @@ public class ExceptionUtil { /** * Returns stack trace chain filtered by class names. */ - public static StackTraceElement[][] getStackTraceChain(Throwable t, String[] allow, String[] deny) { - ArrayList result = new ArrayList(); + public static StackTraceElement[][] getStackTraceChain(Throwable t, final String[] allow, final String[] deny) { + ArrayList result = new ArrayList<>(); while (t != null) { StackTraceElement[] stack = getStackTrace(t, allow, deny); result.add(stack); @@ -116,7 +117,7 @@ public class ExceptionUtil { * Returns exception chain starting from top up to root cause. */ public static Throwable[] getExceptionChain(Throwable throwable) { - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList<>(); list.add(throwable); while ((throwable = throwable.getCause()) != null) { list.add(throwable); @@ -132,7 +133,7 @@ public class ExceptionUtil { /** * Prints stack trace into a String. */ - public static String exceptionStackTraceToString(Throwable t) { + public static String exceptionStackTraceToString(final Throwable t) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw, true); @@ -154,15 +155,17 @@ public class ExceptionUtil { t.printStackTrace(pw); t = t.getCause(); } - pw.flush(); - sw.flush(); + + StreamUtil.close(pw); + StreamUtil.close(sw); + return sw.toString(); } /** * Build a message for the given base message and its cause. */ - public static String buildMessage(String message, Throwable cause) { + public static String buildMessage(final String message, Throwable cause) { if (cause != null) { cause = getRootCause(cause); StringBuilder buf = new StringBuilder(); @@ -185,16 +188,24 @@ public class ExceptionUtil { * "root" of the tree, and returns that exception. If no root cause found * returns provided throwable. */ - public static Throwable getRootCause(Throwable throwable) { + public static Throwable getRootCause(final Throwable throwable) { Throwable cause = throwable.getCause(); if (cause == null) { return throwable; } - throwable = cause; - while ((throwable = throwable.getCause()) != null) { - cause = throwable; + + Throwable t = throwable; + + // defend against (malicious?) circularity + for (int i = 0; i < 1000; i++) { + cause = t.getCause(); + if (cause == null) { + return t; + } + t = cause; } - return cause; + + return throwable; } /** @@ -202,7 +213,7 @@ public class ExceptionUtil { * Otherwise, returns null. */ @SuppressWarnings({"unchecked"}) - public static T findCause(Throwable throwable, Class cause) { + public static T findCause(Throwable throwable, final Class cause) { while (throwable != null) { if (throwable.getClass().equals(cause)) { return (T) throwable; @@ -216,11 +227,11 @@ public class ExceptionUtil { // ---------------------------------------------------------------- sql /** - * Rolls up SQL exceptions by taking each proceeding exception - * and making it a child of the previous using the setNextException - * method of SQLException. - */ - public static SQLException rollupSqlExceptions(Collection exceptions) { + * Rolls up SQL exceptions by taking each proceeding exception + * and making it a child of the previous using the setNextException + * method of SQLException. + */ + public static SQLException rollupSqlExceptions(final Collection exceptions) { SQLException parent = null; for (SQLException exception : exceptions) { if (parent != null) { @@ -234,74 +245,57 @@ public class ExceptionUtil { // ---------------------------------------------------------------- misc /** - * Throws target of InvocationTargetException if it is exception. + * Throws checked exceptions in un-checked manner. */ - public static void throwTargetException(InvocationTargetException itex) throws Exception { - throw extractTargetException(itex); - } - public static Exception extractTargetException(InvocationTargetException itex) { - Throwable target = itex.getTargetException(); - return target instanceof Exception ? (Exception) target : itex; + public static void throwRuntimeException(final Throwable throwable) { + throw wrapToRuntimeException(throwable); } - /** - * Throws checked exceptions in un-checked manner. - * Uses deprecated method. - * @see #throwException(Throwable) + * Returns non-null message for a throwable. */ - @SuppressWarnings({"deprecation"}) - public static void throwExceptionAlt(Throwable throwable) { - if (throwable instanceof RuntimeException) { - throw (RuntimeException) throwable; + public static String message(final Throwable throwable) { + String message = throwable.getMessage(); + + if (StringUtil.isBlank(message)) { + message = throwable.toString(); } - throw new UnsupportedOperationException(); + + return message; } /** - * Throws checked exceptions in un-checked manner. - * @see #throwException(Throwable) + * Wraps exception to {@code RuntimeException}. */ - public static void throwException(Throwable throwable) { + public static RuntimeException wrapToRuntimeException(final Throwable throwable) { if (throwable instanceof RuntimeException) { - throw (RuntimeException) throwable; + return (RuntimeException) throwable; } - // can't handle these types - if ((throwable instanceof IllegalAccessException) || (throwable instanceof InstantiationException)) { - throw new IllegalArgumentException(throwable); - } - - try { - synchronized (ThrowableThrower.class) { - ThrowableThrower.throwable = throwable; - ThrowableThrower.class.newInstance(); - } - } catch (InstantiationException iex) { - throw new RuntimeException(iex); - } catch (IllegalAccessException iex) { - throw new RuntimeException(iex); - } finally { - ThrowableThrower.throwable = null; + return new RuntimeException(throwable); + } + public static Exception wrapToException(final Throwable throwable) { + if (throwable instanceof Exception) { + return (Exception) throwable; } + return new RuntimeException(throwable); } /** - * Returns non-null message for a throwable. + * Unwraps invocation and undeclared exceptions to real cause. */ - public static String message(Throwable throwable) { - String message = throwable.getMessage(); - - if (StringUtil.isBlank(message)) { - message = throwable.toString(); + public static Throwable unwrapThrowable(final Throwable wrappedThrowable) { + Throwable unwrapped = wrappedThrowable; + while (true) { + if (unwrapped instanceof InvocationTargetException) { + unwrapped = ((InvocationTargetException) unwrapped).getTargetException(); + } + else if (unwrapped instanceof UndeclaredThrowableException) { + unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable(); + } + else { + return unwrapped; + } } - - return message; } - private static class ThrowableThrower { - private static Throwable throwable; - ThrowableThrower() throws Throwable { - throw throwable; - } - } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/exception/UncheckedException.java b/fine-jodd/src/main/java/com/fr/third/jodd/exception/UncheckedException.java index ada27853c..9e64ef90f 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/exception/UncheckedException.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/exception/UncheckedException.java @@ -25,8 +25,11 @@ package com.fr.third.jodd.exception; -import java.io.PrintWriter; +import java.io.IOException; import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.UncheckedIOException; +import java.util.concurrent.Callable; /** * Unchecked exception and also a wrapper for checked exceptions. @@ -48,13 +51,13 @@ public class UncheckedException extends RuntimeException { // ---------------------------------------------------------------- constructors - public UncheckedException(Throwable t) { + public UncheckedException(final Throwable t) { super(t.getMessage()); cause = t; this.showCauseDetails = true; } - public UncheckedException(Throwable t, boolean showCauseDetails) { + public UncheckedException(final Throwable t, final boolean showCauseDetails) { super(t.getMessage()); cause = t; this.showCauseDetails = showCauseDetails; @@ -66,19 +69,19 @@ public class UncheckedException extends RuntimeException { this.showCauseDetails = false; } - public UncheckedException(String message) { + public UncheckedException(final String message) { super(message); cause = null; this.showCauseDetails = false; } - public UncheckedException(String message, Throwable t) { + public UncheckedException(final String message, final Throwable t) { super(message, t); cause = t; this.showCauseDetails = true; } - public UncheckedException(String message, Throwable t, boolean showCauseDetails) { + public UncheckedException(final String message, final Throwable t, final boolean showCauseDetails) { super(message, t); cause = t; this.showCauseDetails = showCauseDetails; @@ -92,7 +95,7 @@ public class UncheckedException extends RuntimeException { } @Override - public void printStackTrace(PrintStream ps) { + public void printStackTrace(final PrintStream ps) { synchronized (ps) { super.printStackTrace(ps); if ((cause != null) && showCauseDetails) { @@ -104,7 +107,7 @@ public class UncheckedException extends RuntimeException { } @Override - public void printStackTrace(PrintWriter pw) { + public void printStackTrace(final PrintWriter pw) { synchronized (pw) { super.printStackTrace(pw); if ((cause != null) && showCauseDetails) { @@ -131,24 +134,56 @@ public class UncheckedException extends RuntimeException { * Wraps checked exceptions in a UncheckedException. * Unchecked exceptions are not wrapped. */ - public static RuntimeException wrapChecked(Throwable t) { - if (t instanceof RuntimeException) { - return (RuntimeException) t; + public static V callAndWrapException(final Callable callable) { + try { + return callable.call(); + } + catch (IOException ioex) { + throw new UncheckedIOException(ioex); + } + catch (RuntimeException rtex) { + throw rtex; + } + catch (Exception t) { + throw new UncheckedException(t); + } + } + + @FunctionalInterface + public interface CallableVoid { + public void call() throws Exception; + } + + /** + * Wraps checked exceptions in a UncheckedException. + * Unchecked exceptions are not wrapped. + */ + public static void runAndWrapException(final CallableVoid callable) { + try { + callable.call(); + } + catch (IOException ioex) { + throw new UncheckedIOException(ioex); + } + catch (RuntimeException rtex) { + throw rtex; + } + catch (Exception t) { + throw new UncheckedException(t); } - return new UncheckedException(t); } /** * Wraps all exceptions in a UncheckedException */ - public static RuntimeException wrap(Throwable t) { + public static RuntimeException wrap(final Throwable t) { return new UncheckedException(t); } /** * Wraps all exceptions in a UncheckedException */ - public static RuntimeException wrap(Throwable t, String message) { + public static RuntimeException wrap(final Throwable t, final String message) { return new UncheckedException(message, t); } @@ -173,4 +208,5 @@ public class UncheckedException extends RuntimeException { return cause; } + } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/format/Printf.java b/fine-jodd/src/main/java/com/fr/third/jodd/format/Printf.java deleted file mode 100644 index 17b93ba70..000000000 --- a/fine-jodd/src/main/java/com/fr/third/jodd/format/Printf.java +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2003-present, Jodd Team (http://jodd.org) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package com.fr.third.jodd.format; - -/** - * Printf. - * @see PrintfFormat - */ -public class Printf { - - // ---------------------------------------------------------------- primitives - - /** - * @see PrintfFormat#form(byte) - */ - public static String str(String format, byte value) { - return new PrintfFormat(format).form(value); - } - - /** - * @see PrintfFormat#form(char) - */ - public static String str(String format, char value) { - return new PrintfFormat(format).form(value); - } - - /** - * @see PrintfFormat#form(short) - */ - public static String str(String format, short value) { - return new PrintfFormat(format).form(value); - } - - /** - * @see PrintfFormat#form(int) - */ - public static String str(String format, int value) { - return new PrintfFormat(format).form(value); - } - - /** - * @see PrintfFormat#form(long) - */ - public static String str(String format, long value) { - return new PrintfFormat(format).form(value); - } - - /** - * @see PrintfFormat#form(double) - */ - public static String str(String format, float value) { - return new PrintfFormat(format).form(value); - } - - /** - * @see PrintfFormat#form(double) - */ - public static String str(String format, double value) { - return new PrintfFormat(format).form(value); - } - - /** - * @see PrintfFormat#form(boolean) - */ - public static String str(String format, boolean value) { - return new PrintfFormat(format).form(value); - } - - // ---------------------------------------------------------------- objects - - public static String str(String format, String value) { - return new PrintfFormat(format).form(value); - } - - public static String str(String format, Object param) { - return new PrintfFormat(format).form(param); - } - - // ---------------------------------------------------------------- multiple objects - - public static String str(String format, Object... params) { - PrintfFormat pf = new PrintfFormat(); - for (Object param : params) { - pf.reinit(format); - format = pf.form(param); - } - return format; - } - -} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/format/PrintfFormat.java b/fine-jodd/src/main/java/com/fr/third/jodd/format/PrintfFormat.java deleted file mode 100644 index 63d067c16..000000000 --- a/fine-jodd/src/main/java/com/fr/third/jodd/format/PrintfFormat.java +++ /dev/null @@ -1,766 +0,0 @@ -// Copyright (c) 2003-present, Jodd Team (http://jodd.org) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package com.fr.third.jodd.format; - -import com.fr.third.jodd.util.StringPool; - -import java.math.BigInteger; - -/** - * Fast simple and yet useful formatting. - */ -public class PrintfFormat { - - protected int width; - protected int precision; - protected StringBuilder pre; - protected StringBuilder post; - protected boolean leadingZeroes; - protected boolean showPlus; - protected boolean alternate; - protected boolean showSpace; - protected boolean leftAlign; - protected boolean groupDigits; - protected char fmt; // one of cdeEfgGiosxXos... - protected boolean countSignInLen; - private static final BigInteger bgInt = new BigInteger("9223372036854775808"); // 2^63 - - /** - * Formats a number in a printf format, like C. - * - * @param s the format string following printf format string - * The string has a prefix, a format code and a suffix. The prefix and suffix - * become part of the formatted output. The format code directs the - * formatting of the (single) parameter to be formatted. The code has the - * following structure - *

    - *
  • a % (required) - * - *
  • a modifier (optional) - *
    - *
    +
    forces display of + for positive numbers - *
    ~
    do not count leading + or - in length - *
    0
    show leading zeroes - *
    -
    align left in the field - *
    space
    prepend a space in front of positive numbers - *
    #
    use "alternate" format. Add 0 or 0x for octal or hexadecimal numbers; - * add 0b for binary numbers. Don't suppress trailing zeroes in general floating - * point format. - *
    ,
    groups decimal values by thousands (for 'diuxXb' formats) - *
    - * - *
  • an integer denoting field width (optional) - * - *
  • a period (.) followed by an integer denoting precision (optional) - * - *
  • a format descriptor (required) - *
    - *
    f
    floating point number in fixed format, - *
    e, E
    floating point number in exponential notation (scientific format). - * The E format results in an uppercase E for the exponent (1.14130E+003), the e - * format in a lowercase e, - *
    g, G
    floating point number in general format (fixed format for small - * numbers, exponential format for large numbers). Trailing zeroes are suppressed. - * The G format results in an uppercase E for the exponent (if any), the g format - * in a lowercase e,. - *
    d, i
    signed long and integer in decimal, - *
    u
    unsigned long or integer in decimal, - *
    x
    unsigned long or integer in hexadecimal, - *
    o
    unsigned long or integer in octal, - *
    b
    unsigned long or integer in binary, - *
    s
    string (actually, toString() value of an object), - *
    c
    character, - *
    l, L
    boolean in lower or upper case (for booleans and int/longs), - *
    p
    identity hash code of an object (pointer :). - *
    - *
- */ - public PrintfFormat(String s) { - init(s, 0); - } - - /** - * For internal use with {@link #init(String, int)} and {@link #reinit(String)}. - */ - protected PrintfFormat() { - } - - protected PrintfFormat reinit(String s) { - if (pre == null) { - init(s, 0); - } else { - init(s, pre.length()); - } - return this; - } - - protected void init(String s, int i) { - width = 0; - precision = -1; - pre = (i == 0 ? new StringBuilder() : new StringBuilder(s.substring(0, i))); - post = new StringBuilder(); - leadingZeroes = false; - showPlus = false; - alternate = false; - showSpace = false; - leftAlign = false; - countSignInLen = true; - fmt = ' '; - - int length = s.length(); - int parseState; // 0 = prefix, 1 = flags, 2 = width, 3 = precision, 4 = format, 5 = end - - // 0: parse string prefix upto first '%'. - while (true) { - if (i >= length) { - throw new IllegalArgumentException("Format string requires '%'."); - } - char c = s.charAt(i); - if (c != '%') { - pre.append(c); - i++; - continue; - } - if (i >= length - 1) { - throw new IllegalArgumentException("Format string can not end with '%'."); - } - if (s.charAt(i + 1) == '%') { // double '%%' - pre.append('%'); - i += 2; - continue; - } - //parseState = 1; // single $ founded - i++; - break; - } - - // 1: parse flags - flagsloop: - //while (parseState == 1) { - while (true) { - if (i >= length) { - parseState = 5; - break; - } - char c = s.charAt(i); - switch (c) { - case ' ': showSpace = true; break; - case '-': leftAlign = true; break; - case '+': showPlus = true; break; - case '0': leadingZeroes = true; break; - case '#': alternate = true; break; - case '~': countSignInLen = false; break; - case ',': groupDigits = true; break; - default: - parseState = 2; - break flagsloop; - } - i++; - } - - // 2: parse width - while (parseState == 2) { - if (i >= length) { - parseState = 5; - break; - } - char c = s.charAt(i); - if ((c >= '0') && (c <= '9')) { - width = (width * 10) + s.charAt(i) - '0'; - i++; - continue; - } - if (s.charAt(i) == '.') { - parseState = 3; - precision = 0; - i++; - } else { - parseState = 4; - } - break; - } - - // 3: parse precision - while (parseState == 3) { - if (i >= length) { - parseState = 5; - break; - } - char c = s.charAt(i); - if ((c >= '0') && (c <= '9')) { - precision = (precision * 10) + s.charAt(i) - '0'; - i++; - continue; - } - parseState = 4; - break; - } - - // 4: parse format - if (parseState == 4) { - if (i < length) { - fmt = s.charAt(i); - i++; -// } else { -// parseState = 5; - } - } - - // append suffix - if (i < length) { - post.append(s.substring(i, length)); - } - } - - /** - * Formats a double with exp format. - */ - protected String expFormat(double d) { - StringBuilder f = new StringBuilder(); - int e = 0; - double dd = d; - double factor = 1; - - if (d != 0) { - while (dd > 10) { - e++; - factor /= 10; - dd /= 10; - } - while (dd < 1) { - e--; - factor *= 10; - dd *= 10; - } - } - if (((fmt == 'g') || (fmt == 'G')) && (e >= -4) && (e < precision)) { - return fixedFormat(d); - } - - d *= factor; - f.append(fixedFormat(d)); - - if (fmt == 'e' || fmt == 'g') { - f.append('e'); - } else { - f.append('E'); - } - - StringBuilder p = new StringBuilder("000"); - if (e >= 0) { - f.append('+'); - p.append(e); - } else { - f.append('-'); - p.append(-e); - } - - char[] data = new char[3]; - p.getChars(p.length() - 3, p.length(), data, 0); - return f.append(data).toString(); - } - - /** - * Formats a double with fixed format. - */ - protected String fixedFormat(double d) { - boolean removeTrailing = (fmt == 'G' || fmt == 'g') && !alternate; - - // remove trailing zeroes and decimal point - if (d > 0x7FFFFFFFFFFFFFFFL) { - return expFormat(d); - } - if (precision == 0) { - return Long.toString(Math.round(d)); - } - - long whole = (long) d; - double fr = d - whole; // fractional part - - if (fr >= 1 || fr < 0) { - return expFormat(d); - } - - double factor = 1; - StringBuilder leadingZeroesStr = new StringBuilder(); - - for (int i = 1; i <= precision && factor <= 0x7FFFFFFFFFFFFFFFL; i++) { - factor *= 10; - leadingZeroesStr.append('0'); - } - - long l = Math.round(factor * fr); - if (l >= factor) { - l = 0; - whole++; - } - - String z = leadingZeroesStr.toString() + l; - z = '.' + z.substring(z.length() - precision, z.length()); - - if (removeTrailing) { - int t = z.length() - 1; - while (t >= 0 && z.charAt(t) == '0') { - t--; - } - if (t >= 0 && z.charAt(t) == '.') { - t--; - } - z = z.substring(0, t + 1); - } - return whole + z; - } - - /** - * Pads the value with spaces and adds prefix and suffix. - */ - protected String pad(String value) { - String spaces = repeat(' ', width - value.length()); - if (leftAlign) { - return pre + value + spaces + post; - } else { - return pre + spaces + value + post; - } - } - - /** - * Returns new string created by repeating a single character. - */ - protected static String repeat(char c, int n) { - if (n <= 0) { - return (StringPool.EMPTY); - } - char[] buffer = new char[n]; - for (int i = 0; i < n; i++) { - buffer[i] = c; - } - return new String(buffer); - } - - private String getAltPrefixFor(char fmt, String currentPrefix) { - switch(fmt) { - case 'x': - return "0x"; - case 'X': - return "0X"; - case 'b': - return "0b"; - case 'B': - return "0B"; - default: - return currentPrefix; - } - } - - protected String sign(int s, String r) { - String p = StringPool.EMPTY; - - if (s < 0) { - p = StringPool.DASH; - } else if (s > 0) { - if (showPlus) { - p = StringPool.PLUS; - } else if (showSpace) { - p = StringPool.SPACE; - } - } else { - if (alternate) { - if (fmt == 'o' && r.length() > 0 && r.charAt(0) != '0') { - p = "0"; - } else { - p = getAltPrefixFor(fmt, p); - } - } - } - - int w = 0; - - if (leadingZeroes) { - w = width; - } else if ((fmt == 'u' || fmt == 'd' || fmt == 'i' || fmt == 'x' || fmt == 'X' || fmt == 'o') && precision > 0) { - w = precision; - } - - if (countSignInLen) { - return p + repeat('0', w - p.length() - r.length()) + r; - } else { - return p + repeat('0', w - r.length()) + r; - } - } - - /** - * Groups numbers by inserting 'separator' after every group of 'size' digits, - * starting from the right. - */ - protected String groupDigits(String value, int size, char separator) { - if (!groupDigits) { - return value; - } - StringBuilder r = new StringBuilder(value.length() + 10); - int ndx = 0; - int len = value.length() - 1; - int mod = len % size; - while (ndx < len) { - r.append(value.charAt(ndx)); - if (mod == 0) { - r.append(separator); - mod = size; - } - mod--; - ndx++; - } - r.append(value.charAt(ndx)); - return r.toString(); - } - - - - // ---------------------------------------------------------------- public form methods - - /** - * Formats a character into a string (like sprintf in C). - */ - public String form(char value) { - switch(fmt) { - case 'c': - return alternate ? "\\u" + Integer.toHexString((int) value & 0xFFFF) : pad(String.valueOf(value)); - case 'C': - return alternate ? "\\u" + Integer.toHexString((int) value & 0xFFFF).toUpperCase() : pad(String.valueOf(value)); - case 'd': - case 'i': - case 'u': - case 'o': - case 'x': - case 'X': - case 'b': - case 'l': - case 'L': - return form((short) value); - default: - throw newIllegalArgumentException("cCdiuoxXblL"); - } - } - - /** - * Formats a boolean into a string (like sprintf in C). - */ - public String form(boolean value) { - if (fmt == 'l') { - return pad(value ? "true" : "false"); - } - if (fmt == 'L') { - return pad(value ? "TRUE" : "FALSE"); - } - throw newIllegalArgumentException("lL"); - - } - - /** - * Formats a double into a string (like sprintf in C). - */ - public String form(double x) { - String r; - - if (precision < 0) { - precision = 6; - } - - int s = 1; - if (x < 0) { - x = -x; - s = -1; - } - if (fmt == 'f') { - r = fixedFormat(x); - } else if (fmt == 'e' || fmt == 'E' || fmt == 'g' || fmt == 'G') { - r = expFormat(x); - } else { - throw newIllegalArgumentException("feEgG"); - } - return pad(sign(s, r)); - } - - /** - * Formats a long integer into a string (like sprintf in C). - */ - public String form(long x) { - String r; - int s = 0; - - switch (fmt) { - case 'c': - return form((char) x); - case 'd': - if (x < 0) { - r = Long.toString(x).substring(1); - s = -1; - } else { - r = Long.toString(x); - s = 1; - } - r = groupDigits(r, 3, ','); - break; - case 'i': - int xx = (int) x; - if (xx < 0) { - r = Integer.toString(xx).substring(1); - s = -1; - } else { - r = Integer.toString(xx); - s = 1; - } - r = groupDigits(r, 3, ','); - break; - case 'u': - if (x < 0) { - long xl = x & 0x7FFFFFFFFFFFFFFFL; - r = Long.toString(xl); - BigInteger bi = new BigInteger(r); - r = bi.add(bgInt).toString(); - } else { - r = Long.toString(x); - } - r = groupDigits(r, 3, ','); - s = 1; - break; - case 'o': - r = Long.toOctalString(x); - break; - case 'x': - r = Long.toHexString(x); - r = groupDigits(r, 4, ' '); - break; - case 'X': - r = Long.toHexString(x).toUpperCase(); - r = groupDigits(r, 4, ' '); - break; - case 'b': - case 'B': - r = Long.toBinaryString(x); - r = groupDigits(r, 8, ' '); - break; - case 'l': - r = (x == 0 ? "false" : "true"); - break; - case 'L': - r = (x == 0 ? "FALSE" : "TRUE"); - break; - default: - throw new IllegalArgumentException("cdiuoxXbBlL"); - } - - return pad(sign(s, r)); - } - - /** - * Formats an integer into a string (like sprintf in C). - */ - public String form(int x) { - String r; - int s = 0; - - switch (fmt) { - case 'c': - return form((char) x); - case 'd': - case 'i': - if (x < 0) { - r = Integer.toString(x).substring(1); - s = -1; - } else { - r = Integer.toString(x); - s = 1; - } - r = groupDigits(r, 3, ','); - break; - case 'u': - long xl = x & 0x00000000FFFFFFFFL; - r = Long.toString(xl); - r = groupDigits(r, 3, ','); - s = 1; - break; - case 'o': - r = Integer.toOctalString(x); - break; - case 'x': - r = Integer.toHexString(x); - r = groupDigits(r, 4, ' '); - break; - case 'X': - r = Integer.toHexString(x).toUpperCase(); - r = groupDigits(r, 4, ' '); - break; - case 'b': - case 'B': - r = Integer.toBinaryString(x); - r = groupDigits(r, 8, ' '); - break; - case 'l': - r = (x == 0 ? "false" : "true"); - break; - case 'L': - r = (x == 0 ? "FALSE" : "TRUE"); - break; - default: - throw newIllegalArgumentException("cdiuoxXbBlL"); - } - return pad(sign(s, r)); - } - - /** - * Formats a byte into a string (like sprintf in C). - */ - public String form(byte b) { - return formInt(b, 0xFF); - } - - /** - * Formats a short into a string (like sprintf in C). - */ - public String form(short s) { - return formInt(s, 0xFFFF); - } - - /** - * Formatter for both byte and short values. - */ - private String formInt(int value, int unsignedMask) { - String r; - int s = 0; - - switch (fmt) { - case 'c': - return form((char) value); - case 'd': - case 'i': - if (value < 0) { - r = Integer.toString(value).substring(1); - s = -1; - } else { - r = Integer.toString(value); - s = 1; - } - r = groupDigits(r, 3, ','); - break; - case 'u': - int xl = value & unsignedMask; - r = Integer.toString(xl); - r = groupDigits(r, 3, ','); - s = 1; - break; - case 'o': - r = Integer.toOctalString(value & unsignedMask); - break; - case 'x': - r = Integer.toHexString(value & unsignedMask); - r = groupDigits(r, 4, ' '); - break; - case 'X': - r = Integer.toHexString(value & unsignedMask).toUpperCase(); - r = groupDigits(r, 4, ' '); - break; - case 'b': - case 'B': - r = Integer.toBinaryString(value & unsignedMask); - r = groupDigits(r, 8, ' '); - break; - case 'l': - r = (value == 0 ? "false" : "true"); - break; - case 'L': - r = (value == 0 ? "FALSE" : "TRUE"); - break; - default: - throw newIllegalArgumentException("cdiuoxXblL"); - } - return pad(sign(s, r)); - } - - /** - * Formats a object into a string depending on format (like sprintf in C). - * If object is a numeric type and format is not one object formats (like 's' or 'p') - * it will be converted to primitive and formatted as such. - */ - public String form(Object object) { - if (object == null) { - return StringPool.NULL; - } - - switch (fmt) { - case 's' : - String s = object.toString(); - if (precision >= 0 && precision < s.length()) { - s = s.substring(0, precision); - } - return pad(s); - case 'p' : - return Integer.toString(System.identityHashCode(object)); - } - - // check for numeric type - if (object instanceof Number) { - Number number = (Number) object; - if (object instanceof Integer) { - return form(number.intValue()); - } - if (object instanceof Long) { - return form(number.longValue()); - } - if (object instanceof Double) { - return form(number.doubleValue()); - } - if (object instanceof Float) { - return form(number.floatValue()); - } - if (object instanceof Byte) { - return form(number.byteValue()); - } - if (object instanceof Short) { - return form(number.shortValue()); - } - else { - return form(number.intValue()); - } - } - else if (object instanceof Character) { - return form(((Character) object).charValue()); - } - else if (object instanceof Boolean) { - return form(((Boolean)object).booleanValue()); - } - - // throw exception about invalid 'object'-formats - throw newIllegalArgumentException("sp"); - } - - /** - * Creates IllegalArgumentException with message. - */ - protected IllegalArgumentException newIllegalArgumentException(String allowedFormats) { - return new IllegalArgumentException("Invalid format: '" + fmt + "' is not one of '" + allowedFormats + "'"); - } - -} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/InExRuleMatcher.java b/fine-jodd/src/main/java/com/fr/third/jodd/inex/InExRuleMatcher.java similarity index 68% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/InExRuleMatcher.java rename to fine-jodd/src/main/java/com/fr/third/jodd/inex/InExRuleMatcher.java index 2b6372f2c..398d17cb4 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/InExRuleMatcher.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/inex/InExRuleMatcher.java @@ -23,32 +23,29 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.util; +package com.fr.third.jodd.inex; + +import com.fr.third.jodd.util.Wildcard; /** * Rule matcher. */ +@FunctionalInterface public interface InExRuleMatcher { /** - * {@link jodd.util.Wildcard#match(String, String) Wilcard} rule matcher. + * {@link com.fr.third.jodd.util.Wildcard#match(CharSequence, CharSequence) Wilcard} rule matcher. */ - public static final InExRuleMatcher WILDCARD_RULE_MATCHER = new InExRuleMatcher() { - public boolean accept(String value, String rule, boolean include) { - return Wildcard.match(value, rule); - } - }; + InExRuleMatcher WILDCARD_RULE_MATCHER = + (value, rule, include) -> Wildcard.match(value, rule); /** - * {@link jodd.util.Wildcard#matchPath(String, String) Wilcard path} rule matcher. + * {@link com.fr.third.jodd.util.Wildcard#matchPath(String, String) Wilcard path} rule matcher. */ - public static final InExRuleMatcher WILDCARD_PATH_RULE_MATCHER = new InExRuleMatcher() { - public boolean accept(String value, String rule, boolean include) { - return Wildcard.matchPath(value, rule); - } - }; + InExRuleMatcher WILDCARD_PATH_RULE_MATCHER = + (value, rule, include) -> Wildcard.matchPath(value, rule); /** - * Match the value against the rule. + * Matches the value against the rule. */ boolean accept(T value, R rule, boolean include); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/InExRules.java b/fine-jodd/src/main/java/com/fr/third/jodd/inex/InExRules.java similarity index 83% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/InExRules.java rename to fine-jodd/src/main/java/com/fr/third/jodd/inex/InExRules.java index e83a12c3a..579c64e04 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/InExRules.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/inex/InExRules.java @@ -23,13 +23,13 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.util; +package com.fr.third.jodd.inex; import java.util.ArrayList; import java.util.List; /** - * One-class rule engine for includes/excludes logic. It can be used when + * A single-class rule engine for includes/excludes filtering logic. It can be used when * set of objects has to filtered using includes and excludes rules. * For example, when filtering files by file name etc. *

@@ -50,11 +50,18 @@ import java.util.List; *

* All Jodd classes that filters something uses this class to unify the * behavior across the Jodd library. + *

+ * About generics: rule engine examine Values (V). Rules are defined as Definitions (D). + * They are stored internally as R, that is used with Values. */ -public class InExRules implements InExRuleMatcher { +public class InExRules implements InExRuleMatcher { + + public InExRules create() { + return new InExRules<>(); + } protected List> rules; - protected final InExRuleMatcher inExRuleMatcher; + protected final InExRuleMatcher inExRuleMatcher; protected int includesCount; protected int excludesCount; protected boolean blacklist = true; @@ -69,7 +76,7 @@ public class InExRules implements InExRuleMatcher { /** * Creates instance that uses provided matcher. */ - public InExRules(InExRuleMatcher inExRuleMatcher) { + public InExRules(final InExRuleMatcher inExRuleMatcher) { this.inExRuleMatcher = inExRuleMatcher; } @@ -114,7 +121,7 @@ public class InExRules implements InExRuleMatcher { public final R value; public final boolean include; - public Rule(R value, boolean include) { + public Rule(final R value, final boolean include) { this.value = value; this.include = include; } @@ -125,7 +132,7 @@ public class InExRules implements InExRuleMatcher { } @Override - public boolean equals(Object o) { + public boolean equals(final Object o) { if (this == o) { return true; } @@ -156,7 +163,7 @@ public class InExRules implements InExRuleMatcher { /** * Returns rule's value on given index. */ - public R getRule(int index) { + public R getRule(final int index) { return rules.get(index).value; } @@ -211,7 +218,7 @@ public class InExRules implements InExRuleMatcher { * * Should be called after all the rules are set, before matching starts. */ - public void smartMode() { + public void detectMode() { if (excludesCount == 0 && includesCount > 0) { whitelist(); } @@ -223,23 +230,23 @@ public class InExRules implements InExRuleMatcher { /** * Adds include rule. */ - public void include(R rule) { + public void include(final D rule) { addRule(rule, true); } /** * Adds exclude rule. */ - public void exclude(R rule) { + public void exclude(final D rule) { addRule(rule, false); } /** * Adds a rule. Duplicates are not allowed and will be ignored. */ - protected void addRule(R rule, boolean include) { + protected void addRule(final D ruleDefinition, final boolean include) { if (rules == null) { - rules = new ArrayList>(); + rules = new ArrayList<>(); } if (include) { @@ -248,7 +255,7 @@ public class InExRules implements InExRuleMatcher { excludesCount++; } - Rule newRule = new Rule(rule, include); + Rule newRule = new Rule<>(makeRule(ruleDefinition), include); if (rules.contains(newRule)) { return; @@ -257,16 +264,20 @@ public class InExRules implements InExRuleMatcher { rules.add(newRule); } + protected R makeRule(final D rule) { + return (R) rule; + } + /** * Matches value against the set of rules using current white/black list mode. */ - public boolean match(T value) { + public boolean match(final V value) { return match(value, blacklist); } /** * Matches value against the set of rules using provided white/black list mode. */ - public boolean match(T value, boolean blacklist) { + public boolean match(final V value, final boolean blacklist) { if (rules == null) { return blacklist; } @@ -289,7 +300,7 @@ public class InExRules implements InExRuleMatcher { * Applies rules on given flag using current black/white list mode. * @see #apply(Object, boolean, boolean) */ - public boolean apply(T value, boolean flag) { + public boolean apply(final V value, final boolean flag) { return apply(value, blacklist, flag); } @@ -299,7 +310,7 @@ public class InExRules implements InExRuleMatcher { * chain several rules and have the rule engine change the flag * only when a rule is matched. */ - public boolean apply(T value, final boolean blacklist, boolean flag) { + public boolean apply(final V value, final boolean blacklist, boolean flag) { if (rules == null) { return flag; } @@ -319,7 +330,7 @@ public class InExRules implements InExRuleMatcher { /** * Process includes rules. */ - protected boolean processIncludes(T value, boolean include) { + protected boolean processIncludes(final V value, boolean include) { if (includesCount > 0) { if (!include) { for (Rule rule : rules) { @@ -340,7 +351,7 @@ public class InExRules implements InExRuleMatcher { /** * Process excludes rules. */ - protected boolean processExcludes(T value, boolean include) { + protected boolean processExcludes(final V value, boolean include) { if (excludesCount > 0) { if (include) { for (Rule rule : rules) { @@ -362,7 +373,8 @@ public class InExRules implements InExRuleMatcher { * Matches value against single rule. By default performs equals on value * against the rule. */ - public boolean accept(T value, R rule, boolean include) { + @Override + public boolean accept(final V value, final R rule, final boolean include) { return value.equals(rule); } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/filter/package-info.java b/fine-jodd/src/main/java/com/fr/third/jodd/inex/package-info.java similarity index 95% rename from fine-jodd/src/main/java/com/fr/third/jodd/io/filter/package-info.java rename to fine-jodd/src/main/java/com/fr/third/jodd/inex/package-info.java index a1722ff41..bcd7e7694 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/filter/package-info.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/inex/package-info.java @@ -24,6 +24,6 @@ // POSSIBILITY OF SUCH DAMAGE. /** - * Various file filters. + * Include-Exclude rules engine. */ -package com.fr.third.jodd.io.filter; \ No newline at end of file +package com.fr.third.jodd.inex; \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/CachingIntrospector.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/CachingIntrospector.java index a00160e8f..a1fb1342e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/CachingIntrospector.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/CachingIntrospector.java @@ -25,18 +25,17 @@ package com.fr.third.jodd.introspector; -import java.util.HashMap; -import java.util.Map; +import com.fr.third.jodd.cache.TypeCache; /** - * Default {@link com.fr.third.jodd.introspector.Introspector introspector} that caches all class descriptors. + * Default {@link com.fr.third.jodd.introspector.ClassIntrospector introspector} that caches all class descriptors. * It can examine either accessible or supported fields/methods/constructors. *

* It simply caches all class descriptors. */ -public class CachingIntrospector implements Introspector { +public class CachingIntrospector implements ClassIntrospector { - protected final Map cache; + protected final TypeCache cache; protected final boolean scanAccessible; protected final boolean enhancedProperties; protected final boolean includeFieldsAsProperties; @@ -50,12 +49,12 @@ public class CachingIntrospector implements Introspector { } /** - * Creates new caching {@link Introspector}. It may scan + * Creates new caching {@link ClassIntrospector}. It may scan * accessible or supported fields, methods or * constructors. */ - public CachingIntrospector(boolean scanAccessible, boolean enhancedProperties, boolean includeFieldsAsProperties, String[] propertyFieldPrefix) { - this.cache = new HashMap(); + public CachingIntrospector(final boolean scanAccessible, final boolean enhancedProperties, final boolean includeFieldsAsProperties, final String[] propertyFieldPrefix) { + this.cache = TypeCache.createDefault(); this.scanAccessible = scanAccessible; this.enhancedProperties = enhancedProperties; this.includeFieldsAsProperties = includeFieldsAsProperties; @@ -65,39 +64,23 @@ public class CachingIntrospector implements Introspector { /** * {@inheritDoc} */ - public ClassDescriptor lookup(Class type) { - ClassDescriptor cd = cache.get(type); - if (cd != null) { - cd.increaseUsageCount(); - return cd; - } - cd = describeClass(type); - cache.put(type, cd); - return cd; - } - - /** - * {@inheritDoc} - */ - public ClassDescriptor register(Class type) { - ClassDescriptor cd = describeClass(type); - cache.put(type, cd); - return cd; - } - - /** - * Describes a class by creating a new instance of {@link ClassDescriptor} - * that examines all accessible methods and fields. - */ - protected ClassDescriptor describeClass(Class type) { - return new ClassDescriptor(type, scanAccessible, enhancedProperties, includeFieldsAsProperties, propertyFieldPrefix); + @Override + public ClassDescriptor lookup(final Class type) { + return cache.get(type, (t) -> + new ClassDescriptor( + t, + scanAccessible, + enhancedProperties, + includeFieldsAsProperties, + propertyFieldPrefix)); } /** * {@inheritDoc} */ + @Override public void reset() { cache.clear(); } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassDescriptor.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassDescriptor.java index 4cd821741..a63608000 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassDescriptor.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassDescriptor.java @@ -25,13 +25,13 @@ package com.fr.third.jodd.introspector; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; -import java.util.Map; +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Set; -import java.util.Collection; +import java.util.function.Supplier; /** * A descriptor class for all methods/fields/properties/constructors of a class. @@ -59,9 +59,8 @@ public class ClassDescriptor { protected final String[] propertyFieldPrefix; protected final Class[] interfaces; protected final Class[] superclasses; - protected int usageCount; - public ClassDescriptor(Class type, boolean scanAccessible, boolean extendedProperties, boolean includeFieldsAsProperties, String[] propertyFieldPrefix) { + public ClassDescriptor(final Class type, final boolean scanAccessible, final boolean extendedProperties, final boolean includeFieldsAsProperties, final String[] propertyFieldPrefix) { this.type = type; this.scanAccessible = scanAccessible; this.extendedProperties = extendedProperties; @@ -69,13 +68,17 @@ public class ClassDescriptor { this.propertyFieldPrefix = propertyFieldPrefix; isArray = type.isArray(); - isMap = ReflectUtil.isTypeOf(type, Map.class); - isList = ReflectUtil.isTypeOf(type, List.class); - isSet = ReflectUtil.isTypeOf(type, Set.class); - isCollection = ReflectUtil.isTypeOf(type, Collection.class); + isMap = ClassUtil.isTypeOf(type, Map.class); + isList = ClassUtil.isTypeOf(type, List.class); + isSet = ClassUtil.isTypeOf(type, Set.class); + isCollection = ClassUtil.isTypeOf(type, Collection.class); + isSupplier = ClassUtil.isTypeOf(type, Supplier.class); + + interfaces = ClassUtil.resolveAllInterfaces(type); + superclasses = ClassUtil.resolveAllSuperclasses(type); - interfaces = ReflectUtil.resolveAllInterfaces(type); - superclasses = ReflectUtil.resolveAllSuperclasses(type); + isSystemClass = type.getName().startsWith("java.") && + !type.getName().startsWith("java.awt.geom."); } /** @@ -118,23 +121,6 @@ public class ClassDescriptor { return propertyFieldPrefix; } - /** - * Increases usage count. - */ - protected void increaseUsageCount() { - usageCount++; - } - - /** - * Returns number of class descriptor usages. That is number - * of times when class descriptor for some class has been - * lookuped. Higher usage count means that some class is - * more frequently being used. - */ - public int getUsageCount() { - return usageCount; - } - // ---------------------------------------------------------------- special private final boolean isArray; @@ -177,6 +163,25 @@ public class ClassDescriptor { return isCollection; } + private final boolean isSupplier; + + /** + * Returns true if type is a supplier. + */ + public boolean isSupplier() { + return isSupplier; + } + + private boolean isSystemClass; + + /** + * Returns true is class is a system class and should not + * expose fields or declared methods. + */ + public boolean isSystemClass() { + return isSystemClass; + } + // ---------------------------------------------------------------- fields private Fields fields; @@ -195,8 +200,8 @@ public class ClassDescriptor { /** * Returns field descriptor. */ - public FieldDescriptor getFieldDescriptor(String name, boolean declared) { - FieldDescriptor fieldDescriptor = getFields().getFieldDescriptor(name); + public FieldDescriptor getFieldDescriptor(final String name, final boolean declared) { + final FieldDescriptor fieldDescriptor = getFields().getFieldDescriptor(name); if (fieldDescriptor != null) { if (!fieldDescriptor.matchDeclared(declared)) { @@ -232,8 +237,8 @@ public class ClassDescriptor { /** * Returns {@link MethodDescriptor method descriptor} identified by name and parameters. */ - public MethodDescriptor getMethodDescriptor(String name, boolean declared) { - MethodDescriptor methodDescriptor = getMethods().getMethodDescriptor(name); + public MethodDescriptor getMethodDescriptor(final String name, final boolean declared) { + final MethodDescriptor methodDescriptor = getMethods().getMethodDescriptor(name); if ((methodDescriptor != null) && methodDescriptor.matchDeclared(declared)) { return methodDescriptor; @@ -246,8 +251,8 @@ public class ClassDescriptor { /** * Returns {@link MethodDescriptor method descriptor} identified by name and parameters. */ - public MethodDescriptor getMethodDescriptor(String name, Class[] params, boolean declared) { - MethodDescriptor methodDescriptor = getMethods().getMethodDescriptor(name, params); + public MethodDescriptor getMethodDescriptor(final String name, final Class[] params, final boolean declared) { + final MethodDescriptor methodDescriptor = getMethods().getMethodDescriptor(name, params); if ((methodDescriptor != null) && methodDescriptor.matchDeclared(declared)) { return methodDescriptor; @@ -259,7 +264,7 @@ public class ClassDescriptor { /** * Returns an array of all methods with the same name. */ - public MethodDescriptor[] getAllMethodDescriptors(String name) { + public MethodDescriptor[] getAllMethodDescriptors(final String name) { return getMethods().getAllMethodDescriptors(name); } @@ -289,7 +294,7 @@ public class ClassDescriptor { * Returns property descriptor. Declared flag is matched on both read and write * methods. */ - public PropertyDescriptor getPropertyDescriptor(String name, boolean declared) { + public PropertyDescriptor getPropertyDescriptor(final String name, final boolean declared) { PropertyDescriptor propertyDescriptor = getProperties().getPropertyDescriptor(name); if ((propertyDescriptor != null) && propertyDescriptor.matchDeclared(declared)) { @@ -324,7 +329,7 @@ public class ClassDescriptor { /** * Returns the default ctor or null if not found. */ - public CtorDescriptor getDefaultCtorDescriptor(boolean declared) { + public CtorDescriptor getDefaultCtorDescriptor(final boolean declared) { CtorDescriptor defaultCtor = getCtors().getDefaultCtor(); if ((defaultCtor != null) && defaultCtor.matchDeclared(declared)) { @@ -336,7 +341,7 @@ public class ClassDescriptor { /** * Returns the constructor identified by arguments or null if not found. */ - public CtorDescriptor getCtorDescriptor(Class[] args, boolean declared) { + public CtorDescriptor getCtorDescriptor(final Class[] args, final boolean declared) { CtorDescriptor ctorDescriptor = getCtors().getCtorDescriptor(args); if ((ctorDescriptor != null) && ctorDescriptor.matchDeclared(declared)) { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassIntrospector.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassIntrospector.java index 604e54707..8885fa872 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassIntrospector.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassIntrospector.java @@ -26,31 +26,41 @@ package com.fr.third.jodd.introspector; +import java.util.Objects; + /** - * Default class {@link Introspector} simply delegates method calls for + * Default class {@link ClassIntrospector} simply delegates method calls for * more convenient usage. */ -public class ClassIntrospector { +public interface ClassIntrospector { + + class Implementation { + private static ClassIntrospector classIntrospector = new CachingIntrospector(); + + /** + * Sets default implementation. + */ + public static void set(final ClassIntrospector classIntrospector) { + Objects.requireNonNull(classIntrospector); + Implementation.classIntrospector = classIntrospector; + } + } /** - * Returns class descriptor for specified type. + * Returns default implementation. */ - public static ClassDescriptor lookup(Class type) { - return JoddIntrospector.introspector.lookup(type); + static ClassIntrospector get() { + return Implementation.classIntrospector; } /** - * Registers new type. + * Returns class descriptor for specified type. */ - public static ClassDescriptor register(Class type) { - return JoddIntrospector.introspector.register(type); - } + ClassDescriptor lookup(Class type); /** * Clears all cached data. */ - public static void reset() { - JoddIntrospector.introspector.reset(); - } + void reset(); } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/CtorDescriptor.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/CtorDescriptor.java index 3c60bde84..cea1a4cc7 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/CtorDescriptor.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/CtorDescriptor.java @@ -25,8 +25,7 @@ package com.fr.third.jodd.introspector; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; import java.lang.reflect.Constructor; @@ -38,12 +37,12 @@ public class CtorDescriptor extends Descriptor { protected final Constructor constructor; protected final Class[] parameters; - public CtorDescriptor(ClassDescriptor classDescriptor, Constructor constructor) { - super(classDescriptor, ReflectUtil.isPublic(constructor)); + public CtorDescriptor(final ClassDescriptor classDescriptor, final Constructor constructor) { + super(classDescriptor, ClassUtil.isPublic(constructor)); this.constructor = constructor; this.parameters = constructor.getParameterTypes(); - ReflectUtil.forceAccess(constructor); + ClassUtil.forceAccess(constructor); } /** diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Ctors.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Ctors.java index d7fc4f837..a48a13734 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Ctors.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Ctors.java @@ -36,7 +36,7 @@ public class Ctors { protected final CtorDescriptor[] allCtors; protected CtorDescriptor defaultCtor; - public Ctors(ClassDescriptor classDescriptor) { + public Ctors(final ClassDescriptor classDescriptor) { this.classDescriptor = classDescriptor; this.allCtors = inspectConstructors(); } @@ -67,7 +67,7 @@ public class Ctors { /** * Creates new {@link CtorDescriptor}. */ - protected CtorDescriptor createCtorDescriptor(Constructor ctor) { + protected CtorDescriptor createCtorDescriptor(final Constructor ctor) { return new CtorDescriptor(classDescriptor, ctor); } @@ -83,7 +83,7 @@ public class Ctors { /** * Finds constructor description that matches given argument types. */ - public CtorDescriptor getCtorDescriptor(Class... args) { + public CtorDescriptor getCtorDescriptor(final Class... args) { ctors: for (CtorDescriptor ctorDescriptor : allCtors) { Class[] arg = ctorDescriptor.getParameters(); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Descriptor.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Descriptor.java index 0145b0a21..f8d79da0d 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Descriptor.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Descriptor.java @@ -33,7 +33,7 @@ public abstract class Descriptor { protected final ClassDescriptor classDescriptor; protected final boolean isPublic; - protected Descriptor(ClassDescriptor classDescriptor, boolean isPublic) { + protected Descriptor(final ClassDescriptor classDescriptor, final boolean isPublic) { this.classDescriptor = classDescriptor; this.isPublic = isPublic; } @@ -55,7 +55,7 @@ public abstract class Descriptor { /** * Returns true if descriptor content matches required declared flag. */ - public boolean matchDeclared(boolean declared) { + public boolean matchDeclared(final boolean declared) { if (!declared) { return isPublic; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/FieldDescriptor.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/FieldDescriptor.java index 7692ff42d..7388726b7 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/FieldDescriptor.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/FieldDescriptor.java @@ -25,36 +25,37 @@ package com.fr.third.jodd.introspector; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; /** * Field descriptor. Holds additional field data, * that might be specific to implementation class. */ -public class FieldDescriptor extends Descriptor implements Getter, Setter { +public class FieldDescriptor extends Descriptor { + + public static final FieldDescriptor[] EMPTY_ARRAY = new FieldDescriptor[0]; protected final Field field; protected final Type type; protected final Class rawType; protected final Class rawComponentType; protected final Class rawKeyComponentType; + protected final MapperFunction mapperFunction; /** * Creates new field descriptor and resolve all additional field data. * Also, forces access to a field. */ - public FieldDescriptor(ClassDescriptor classDescriptor, Field field) { - super(classDescriptor, ReflectUtil.isPublic(field)); + public FieldDescriptor(final ClassDescriptor classDescriptor, final Field field) { + super(classDescriptor, ClassUtil.isPublic(field)); this.field = field; this.type = field.getGenericType(); - this.rawType = ReflectUtil.getRawType(type, classDescriptor.getType()); + this.rawType = ClassUtil.getRawType(type, classDescriptor.getType()); - Class[] componentTypes = ReflectUtil.getComponentTypes(type, classDescriptor.getType()); + final Class[] componentTypes = ClassUtil.getComponentTypes(type, classDescriptor.getType()); if (componentTypes != null) { this.rawComponentType = componentTypes[componentTypes.length - 1]; this.rawKeyComponentType = componentTypes[0]; @@ -63,7 +64,19 @@ public class FieldDescriptor extends Descriptor implements Getter, Setter { this.rawKeyComponentType = null; } - ReflectUtil.forceAccess(field); + // force access + + ClassUtil.forceAccess(field); + + // mapper + + final Mapper mapper = field.getAnnotation(Mapper.class); + + if (mapper != null) { + mapperFunction = MapperFunctionInstances.get().lookup(mapper.value()); + } else { + mapperFunction = null; + } } /** @@ -108,37 +121,7 @@ public class FieldDescriptor extends Descriptor implements Getter, Setter { * Resolves raw component type for given index. This value is NOT cached. */ public Class[] resolveRawComponentTypes() { - return ReflectUtil.getComponentTypes(type, classDescriptor.getType()); - } - - // ---------------------------------------------------------------- getter/setter - - public Object invokeGetter(Object target) throws InvocationTargetException, IllegalAccessException { - return field.get(target); - } - - public Class getGetterRawType() { - return getRawType(); - } - - public Class getGetterRawComponentType() { - return getRawComponentType(); - } - - public Class getGetterRawKeyComponentType() { - return getRawKeyComponentType(); - } - - public void invokeSetter(Object target, Object argument) throws IllegalAccessException { - field.set(target, argument); - } - - public Class getSetterRawType() { - return getRawType(); - } - - public Class getSetterRawComponentType() { - return getRawComponentType(); + return ClassUtil.getComponentTypes(type, classDescriptor.getType()); } // ---------------------------------------------------------------- toString diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Fields.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Fields.java index 416b6ca70..33a1cc876 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Fields.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Fields.java @@ -25,13 +25,14 @@ package com.fr.third.jodd.introspector; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; import java.lang.reflect.Field; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Map; /** * Collection of {@link FieldDescriptor field descriptors}. @@ -39,7 +40,7 @@ import java.util.HashMap; public class Fields { protected final ClassDescriptor classDescriptor; - protected final HashMap fieldsMap; + protected final Map fieldsMap; // cache private FieldDescriptor[] allFields; @@ -47,7 +48,7 @@ public class Fields { /** * Creates new fields collection. */ - public Fields(ClassDescriptor classDescriptor) { + public Fields(final ClassDescriptor classDescriptor) { this.classDescriptor = classDescriptor; this.fieldsMap = inspectFields(); } @@ -55,16 +56,19 @@ public class Fields { /** * Inspects fields and returns map of {@link FieldDescriptor field descriptors}. */ - protected HashMap inspectFields() { - boolean scanAccessible = classDescriptor.isScanAccessible(); - Class type = classDescriptor.getType(); + private Map inspectFields() { + if (classDescriptor.isSystemClass()) { + return emptyFields(); + } + final boolean scanAccessible = classDescriptor.isScanAccessible(); + final Class type = classDescriptor.getType(); - Field[] fields = scanAccessible ? ReflectUtil.getAccessibleFields(type) : ReflectUtil.getSupportedFields(type); + final Field[] fields = scanAccessible ? ClassUtil.getAccessibleFields(type) : ClassUtil.getSupportedFields(type); - HashMap map = new HashMap(fields.length); + final HashMap map = new HashMap<>(fields.length); - for (Field field : fields) { - String fieldName = field.getName(); + for (final Field field : fields) { + final String fieldName = field.getName(); if (fieldName.equals("serialVersionUID")) { continue; @@ -76,10 +80,18 @@ public class Fields { return map; } + /** + * Defines empty fields for special cases. + */ + private Map emptyFields() { + allFields = FieldDescriptor.EMPTY_ARRAY; + return Collections.emptyMap(); + } + /** * Creates new {@code FieldDescriptor}. */ - protected FieldDescriptor createFieldDescriptor(Field field) { + protected FieldDescriptor createFieldDescriptor(final Field field) { return new FieldDescriptor(classDescriptor, field); } @@ -90,7 +102,7 @@ public class Fields { * Returns {@link FieldDescriptor field descriptor} for given field name * or null if field does not exist. */ - public FieldDescriptor getFieldDescriptor(String name) { + public FieldDescriptor getFieldDescriptor(final String name) { return fieldsMap.get(name); } @@ -108,11 +120,7 @@ public class Fields { index++; } - Arrays.sort(allFields, new Comparator() { - public int compare(FieldDescriptor fd1, FieldDescriptor fd2) { - return fd1.getField().getName().compareTo(fd2.getField().getName()); - } - }); + Arrays.sort(allFields, Comparator.comparing(fd -> fd.getField().getName())); this.allFields = allFields; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Getter.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Getter.java index cc8b1acfe..2c62afc99 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Getter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Getter.java @@ -32,6 +32,57 @@ import java.lang.reflect.InvocationTargetException; */ public interface Getter { + static Getter of(final MethodDescriptor methodDescriptor) { + + return new Getter() { + + @Override + public Object invokeGetter(final Object target) throws InvocationTargetException, IllegalAccessException { + return methodDescriptor.method.invoke(target); + } + + @Override + public Class getGetterRawType() { + return methodDescriptor.getRawReturnType(); + } + + @Override + public Class getGetterRawComponentType() { + return methodDescriptor.getRawReturnComponentType(); + } + + @Override + public Class getGetterRawKeyComponentType() { + return methodDescriptor.getRawReturnKeyComponentType(); + } + }; + } + + static Getter of(final FieldDescriptor fieldDescriptor) { + return new Getter() { + + @Override + public Object invokeGetter(final Object target) throws IllegalAccessException { + return fieldDescriptor.field.get(target); + } + + @Override + public Class getGetterRawType() { + return fieldDescriptor.getRawType(); + } + + @Override + public Class getGetterRawComponentType() { + return fieldDescriptor.getRawComponentType(); + } + + @Override + public Class getGetterRawKeyComponentType() { + return fieldDescriptor.getRawKeyComponentType(); + } + }; + } + Object invokeGetter(Object target) throws InvocationTargetException, IllegalAccessException; Class getGetterRawType(); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/JoddIntrospector.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Mapper.java similarity index 75% rename from fine-jodd/src/main/java/com/fr/third/jodd/introspector/JoddIntrospector.java rename to fine-jodd/src/main/java/com/fr/third/jodd/introspector/Mapper.java index 713e40056..27c5c477b 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/JoddIntrospector.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Mapper.java @@ -25,24 +25,19 @@ package com.fr.third.jodd.introspector; -import com.fr.third.jodd.Jodd; -import com.fr.third.jodd.Jodd; - -public class JoddIntrospector { - - /** - * Default {@link Introspector} implementation. - */ - public static Introspector introspector = new CachingIntrospector(); - - // ---------------------------------------------------------------- module - - static { - init(); - } - - public static void init() { - Jodd.init(JoddIntrospector.class); - } - -} \ No newline at end of file +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Value or type mapper. + * @see MapperFunction + */ +@Documented +@Retention(value = RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +public @interface Mapper { + Class value(); +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MapperFunction.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MapperFunction.java new file mode 100644 index 000000000..436473bcf --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MapperFunction.java @@ -0,0 +1,37 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.introspector; + +import java.util.function.Function; + +/** + * Mapper function allows object to be converted before actually used - usually before injected using + * {@link com.fr.third.jodd.bean.BeanUtil}. + */ +@FunctionalInterface +public interface MapperFunction extends Function { + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MapperFunctionInstances.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MapperFunctionInstances.java new file mode 100644 index 000000000..06ad30946 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MapperFunctionInstances.java @@ -0,0 +1,57 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.introspector; + +import com.fr.third.jodd.cache.TypeCache; +import com.fr.third.jodd.util.ClassUtil; + +/** + * Simple cache of {@link MapperFunction} instances. + */ +public class MapperFunctionInstances { + + private static final MapperFunctionInstances MAPPER_FUNCTION_INSTANCES = new MapperFunctionInstances(); + + /** + * Returns the instance. + */ + public static MapperFunctionInstances get() { + return MAPPER_FUNCTION_INSTANCES; + } + + protected TypeCache typeCache = TypeCache.createDefault(); + + public MapperFunction lookup(final Class mapperFunctionClass) { + return typeCache.get(mapperFunctionClass, (c) -> { + try { + return ClassUtil.newInstance(mapperFunctionClass); + } catch (final Exception ex) { + throw new IllegalArgumentException("Invalid mapper class " + c, ex); + } + }); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodDescriptor.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodDescriptor.java index 974e5b0e2..4b45abbff 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodDescriptor.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodDescriptor.java @@ -25,10 +25,8 @@ package com.fr.third.jodd.introspector; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; @@ -36,23 +34,27 @@ import java.lang.reflect.Type; * Method descriptor. Holds additional method data, * that might be specific to implementation class. */ -public class MethodDescriptor extends Descriptor implements Getter, Setter { +public class MethodDescriptor extends Descriptor { + + private static final MethodParamDescriptor[] NO_PARAMS = new MethodParamDescriptor[0]; protected final Method method; protected final Type returnType; protected final Class rawReturnType; protected final Class rawReturnComponentType; protected final Class rawReturnKeyComponentType; - protected final Class[] rawParameterTypes; - protected final Class[] rawParameterComponentTypes; + protected final MethodParamDescriptor[] parameters; + protected final MapperFunction mapperFunction; + +// protected final Function getterFunction; - public MethodDescriptor(ClassDescriptor classDescriptor, Method method) { - super(classDescriptor, ReflectUtil.isPublic(method)); + public MethodDescriptor(final ClassDescriptor classDescriptor, final Method method) { + super(classDescriptor, ClassUtil.isPublic(method)); this.method = method; this.returnType = method.getGenericReturnType(); - this.rawReturnType = ReflectUtil.getRawType(returnType, classDescriptor.getType()); + this.rawReturnType = ClassUtil.getRawType(returnType, classDescriptor.getType()); - Class[] componentTypes = ReflectUtil.getComponentTypes(returnType, classDescriptor.getType()); + final Class[] componentTypes = ClassUtil.getComponentTypes(returnType, classDescriptor.getType()); if (componentTypes != null) { this.rawReturnComponentType = componentTypes[componentTypes.length - 1]; this.rawReturnKeyComponentType = componentTypes[0]; @@ -61,26 +63,68 @@ public class MethodDescriptor extends Descriptor implements Getter, Setter { this.rawReturnKeyComponentType = null; } - ReflectUtil.forceAccess(method); + // force access + + ClassUtil.forceAccess(method); + + // mapper - Type[] params = method.getGenericParameterTypes(); - Type[] genericParams = method.getGenericParameterTypes(); + final Mapper mapper = method.getAnnotation(Mapper.class); + + if (mapper != null) { + mapperFunction = MapperFunctionInstances.get().lookup(mapper.value()); + } else { + mapperFunction = null; + } - rawParameterTypes = new Class[params.length]; - rawParameterComponentTypes = genericParams.length == 0 ? null : new Class[params.length]; + // parameters - for (int i = 0; i < params.length; i++) { - Type type = params[i]; - rawParameterTypes[i] = ReflectUtil.getRawType(type, classDescriptor.getType()); - if (rawParameterComponentTypes != null) { - rawParameterComponentTypes[i] = ReflectUtil.getComponentType(genericParams[i], classDescriptor.getType(), -1); + if (method.getParameterCount() == 0) { + parameters = NO_PARAMS; + } + else { + parameters = new MethodParamDescriptor[method.getParameterCount()]; + + Class[] params = method.getParameterTypes(); + Type[] genericParams = method.getGenericParameterTypes(); + + for (int i = 0; i < params.length; i++) { + final Class parameterType = params[i]; + final Class rawParameterType = genericParams.length == 0 ? + parameterType : + ClassUtil.getRawType(genericParams[i], classDescriptor.getType()); + final Class rawParameterComponentType = genericParams.length == 0 ? + null : + ClassUtil.getComponentType(genericParams[i], classDescriptor.getType(), -1); + + parameters[i] = new MethodParamDescriptor(parameterType, rawParameterType, rawParameterComponentType); } } + +// try { +// MethodHandles.Lookup lookup = MethodHandles.lookup(); +// CallSite callSite = LambdaMetafactory.metafactory(lookup, +// "apply", +// MethodType.methodType(Function.class), +// MethodType.methodType(Object.class, Object.class), +// lookup.findVirtual( +// classDescriptor.getType(), +// method.getName(), +// MethodType.methodType(method.getReturnType())), +// MethodType.methodType(method.getReturnType(), classDescriptor.type) +// ); +// +// this.getterFunction = (Function) callSite.getTarget().invokeExact(); +// } +// catch (Throwable ex) { +// throw new IllegalArgumentException(ex); +// } } /** * Returns method name. */ + @Override public String getName() { return method.getName(); } @@ -122,56 +166,21 @@ public class MethodDescriptor extends Descriptor implements Getter, Setter { * This value is NOT cached. */ public Class[] resolveRawReturnComponentTypes() { - return ReflectUtil.getComponentTypes(returnType, classDescriptor.getType()); + return ClassUtil.getComponentTypes(returnType, classDescriptor.getType()); } /** - * Returns raw parameter types. + * Returns {@link MethodParamDescriptor method parameteres}. */ - public Class[] getRawParameterTypes() { - return rawParameterTypes; + public MethodParamDescriptor[] getParameters() { + return parameters; } /** - * Returns raw parameter component types. Returns null - * if data does not exist. + * Returns number of parameters. */ - public Class[] getRawParameterComponentTypes() { - return rawParameterComponentTypes; - } - - // ---------------------------------------------------------------- getter/setter - - public Object invokeGetter(Object target) throws InvocationTargetException, IllegalAccessException { - return method.invoke(target, null); - } - - public Class getGetterRawType() { - return getRawReturnType(); - } - - public Class getGetterRawComponentType() { - return getRawReturnComponentType(); - } - - public Class getGetterRawKeyComponentType() { - return getRawReturnKeyComponentType(); - } - - public void invokeSetter(Object target, Object argument) throws IllegalAccessException, InvocationTargetException { - method.invoke(target, argument); - } - - public Class getSetterRawType() { - return getRawParameterTypes()[0]; - } - - public Class getSetterRawComponentType() { - Class[] ts = getRawParameterComponentTypes(); - if (ts == null) { - return null; - } - return ts[0]; + public int getParameterCount() { + return parameters.length; } // ---------------------------------------------------------------- toString diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Introspector.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodParamDescriptor.java similarity index 71% rename from fine-jodd/src/main/java/com/fr/third/jodd/introspector/Introspector.java rename to fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodParamDescriptor.java index 9f563e6e8..7ee278c52 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Introspector.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodParamDescriptor.java @@ -25,27 +25,29 @@ package com.fr.third.jodd.introspector; - /** - * Provides introspection analysis against any java class. - * Implementations may cache {@link ClassDescriptor} objects to improve performance. - * @see CachingIntrospector + * Method parameter descriptor. */ -public interface Introspector { - /** - * Returns the {@link ClassDescriptor} object for specified class. - */ - ClassDescriptor lookup(Class type); +public class MethodParamDescriptor { + protected final Class type; + protected final Class rawType; + protected final Class rawComponentType; + + public MethodParamDescriptor(final Class parameterType, final Class rawParameterType, final Class rawParameterComponentType) { + this.type = parameterType; + this.rawType = rawParameterType; + this.rawComponentType = rawParameterComponentType; + } - /** - * Registers new class type. If type already registered, it will be - * reset and registered again with new class descriptor. - */ - ClassDescriptor register(Class type); + public Class getType() { + return type; + } - /** - * Resets current cache. - */ - void reset(); + public Class getRawType() { + return rawType; + } + public Class getRawComponentType() { + return rawComponentType; + } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Methods.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Methods.java index efefef68b..75e8e267d 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Methods.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Methods.java @@ -26,9 +26,7 @@ package com.fr.third.jodd.introspector; import com.fr.third.jodd.util.ArraysUtil; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.util.ArraysUtil; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; import java.lang.reflect.Method; import java.util.ArrayList; @@ -49,7 +47,7 @@ public class Methods { // cache private MethodDescriptor[] allMethods; - public Methods(ClassDescriptor classDescriptor) { + public Methods(final ClassDescriptor classDescriptor) { this.classDescriptor = classDescriptor; this.methodsMap = inspectMethods(); } @@ -59,14 +57,19 @@ public class Methods { */ protected HashMap inspectMethods() { boolean scanAccessible = classDescriptor.isScanAccessible(); - Class type = classDescriptor.getType(); - Method[] methods = scanAccessible ? ReflectUtil.getAccessibleMethods(type) : ReflectUtil.getSupportedMethods(type); + if (classDescriptor.isSystemClass()) { + scanAccessible = false; + } + + final Class type = classDescriptor.getType(); + + final Method[] methods = scanAccessible ? ClassUtil.getAccessibleMethods(type) : ClassUtil.getSupportedMethods(type); - HashMap map = new HashMap(methods.length); + final HashMap map = new HashMap<>(methods.length); - for (Method method : methods) { - String methodName = method.getName(); + for (final Method method : methods) { + final String methodName = method.getName(); MethodDescriptor[] mds = map.get(methodName); @@ -87,7 +90,7 @@ public class Methods { /** * Creates new {@code MethodDescriptor}. */ - protected MethodDescriptor createMethodDescriptor(Method method) { + protected MethodDescriptor createMethodDescriptor(final Method method) { return new MethodDescriptor(classDescriptor, method); } @@ -98,14 +101,14 @@ public class Methods { * Returns a method that matches given name and parameter types. * Returns null if method is not found. */ - public MethodDescriptor getMethodDescriptor(String name, Class[] paramTypes) { - MethodDescriptor[] methodDescriptors = methodsMap.get(name); + public MethodDescriptor getMethodDescriptor(final String name, final Class[] paramTypes) { + final MethodDescriptor[] methodDescriptors = methodsMap.get(name); if (methodDescriptors == null) { return null; } for (MethodDescriptor methodDescriptor : methodDescriptors) { - Method m = methodDescriptor.getMethod(); - if (ReflectUtil.compareParameters(m.getParameterTypes(), paramTypes)) { + final Method m = methodDescriptor.getMethod(); + if (ClassUtil.compareParameters(m.getParameterTypes(), paramTypes)) { return methodDescriptor; } } @@ -118,8 +121,8 @@ public class Methods { * Returns null if no method exist in this collection by given name. * @see #getMethodDescriptor(String, Class[]) */ - public MethodDescriptor getMethodDescriptor(String name) { - MethodDescriptor[] methodDescriptors = methodsMap.get(name); + public MethodDescriptor getMethodDescriptor(final String name) { + final MethodDescriptor[] methodDescriptors = methodsMap.get(name); if (methodDescriptors == null) { return null; } @@ -132,7 +135,7 @@ public class Methods { /** * Returns all methods for given name. Returns null if method not found. */ - public MethodDescriptor[] getAllMethodDescriptors(String name) { + public MethodDescriptor[] getAllMethodDescriptors(final String name) { return methodsMap.get(name); } @@ -141,19 +144,15 @@ public class Methods { */ public MethodDescriptor[] getAllMethodDescriptors() { if (allMethods == null) { - List allMethodsList = new ArrayList(); + final List allMethodsList = new ArrayList<>(); for (MethodDescriptor[] methodDescriptors : methodsMap.values()) { Collections.addAll(allMethodsList, methodDescriptors); } - MethodDescriptor[] allMethods = allMethodsList.toArray(new MethodDescriptor[allMethodsList.size()]); + final MethodDescriptor[] allMethods = allMethodsList.toArray(new MethodDescriptor[0]); - Arrays.sort(allMethods, new Comparator() { - public int compare(MethodDescriptor md1, MethodDescriptor md2) { - return md1.getMethod().getName().compareTo(md2.getMethod().getName()); - } - }); + Arrays.sort(allMethods, Comparator.comparing(md -> md.getMethod().getName())); this.allMethods = allMethods; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Properties.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Properties.java index ed7676eaa..1fbca304f 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Properties.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Properties.java @@ -25,8 +25,7 @@ package com.fr.third.jodd.introspector; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.util.ReflectUtil; +import com.fr.third.jodd.util.ClassUtil; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -35,8 +34,8 @@ import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; -import static com.fr.third.jodd.util.ReflectUtil.METHOD_GET_PREFIX; -import static com.fr.third.jodd.util.ReflectUtil.METHOD_IS_PREFIX; +import static com.fr.third.jodd.util.ClassUtil.METHOD_GET_PREFIX; +import static com.fr.third.jodd.util.ClassUtil.METHOD_IS_PREFIX; /** * Bean properties collection. Property in Java is defined as a pair of @@ -52,7 +51,7 @@ public class Properties { // cache private PropertyDescriptor[] allProperties; - public Properties(ClassDescriptor classDescriptor) { + public Properties(final ClassDescriptor classDescriptor) { this.classDescriptor = classDescriptor; this.propertyDescriptors = inspectProperties(); } @@ -64,9 +63,9 @@ public class Properties { boolean scanAccessible = classDescriptor.isScanAccessible(); Class type = classDescriptor.getType(); - HashMap map = new HashMap(); + HashMap map = new HashMap<>(); - Method[] methods = scanAccessible ? ReflectUtil.getAccessibleMethods(type) : ReflectUtil.getSupportedMethods(type); + Method[] methods = scanAccessible ? ClassUtil.getAccessibleMethods(type) : ClassUtil.getSupportedMethods(type); for (int iteration = 0; iteration < 2; iteration++) { // first find the getters, and then the setters! @@ -81,13 +80,13 @@ public class Properties { String propertyName; if (iteration == 0) { - propertyName = ReflectUtil.getBeanPropertyGetterName(method); + propertyName = ClassUtil.getBeanPropertyGetterName(method); if (propertyName != null) { add = true; issetter = false; } } else { - propertyName = ReflectUtil.getBeanPropertySetterName(method); + propertyName = ClassUtil.getBeanPropertySetterName(method); if (propertyName != null) { add = true; issetter = true; @@ -140,7 +139,7 @@ public class Properties { * Adds a setter and/or getter method to the property. * If property is already defined, the new, updated, definition will be created. */ - protected void addProperty(HashMap map, String name, MethodDescriptor methodDescriptor, boolean isSetter) { + protected void addProperty(final HashMap map, final String name, final MethodDescriptor methodDescriptor, final boolean isSetter) { MethodDescriptor setterMethod = isSetter ? methodDescriptor : null; MethodDescriptor getterMethod = isSetter ? null : methodDescriptor; @@ -170,8 +169,8 @@ public class Properties { String existingMethodName = existingMethodDescriptor.getMethod().getName(); if ( - existingMethodName.startsWith(ReflectUtil.METHOD_IS_PREFIX) && - methodName.startsWith(ReflectUtil.METHOD_GET_PREFIX)) { + existingMethodName.startsWith(METHOD_IS_PREFIX) && + methodName.startsWith(METHOD_GET_PREFIX)) { // ignore getter when ister exist return; @@ -207,15 +206,15 @@ public class Properties { * up to three times (depends on use case) for the same property. Each time when * a property is updated, a new definition is created with updated information. */ - protected PropertyDescriptor createPropertyDescriptor(String name, MethodDescriptor getterMethod, MethodDescriptor setterMethod) { + protected PropertyDescriptor createPropertyDescriptor(final String name, final MethodDescriptor getterMethod, final MethodDescriptor setterMethod) { return new PropertyDescriptor(classDescriptor, name, getterMethod, setterMethod); } /** * Creates new field-only {@link PropertyDescriptor}. It will be invoked only once. */ - protected PropertyDescriptor createPropertyDescriptor(String name, FieldDescriptor fieldDescriptor) { - return new PropertyDescriptor(classDescriptor, name, fieldDescriptor); + protected PropertyDescriptor createPropertyDescriptor(final String name, final FieldDescriptor fieldDescriptor) { + return new PropertyDescriptor(classDescriptor, name, fieldDescriptor); } // ---------------------------------------------------------------- get @@ -223,7 +222,7 @@ public class Properties { /** * Returns {@link PropertyDescriptor property descriptor}. */ - public PropertyDescriptor getPropertyDescriptor(String name) { + public PropertyDescriptor getPropertyDescriptor(final String name) { return propertyDescriptors.get(name); } @@ -242,7 +241,8 @@ public class Properties { } Arrays.sort(allProperties, new Comparator() { - public int compare(PropertyDescriptor pd1, PropertyDescriptor pd2) { + @Override + public int compare(final PropertyDescriptor pd1, final PropertyDescriptor pd2) { return pd1.getName().compareTo(pd2.getName()); } }); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/PropertyDescriptor.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/PropertyDescriptor.java index e5397eec5..b6fdb3034 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/PropertyDescriptor.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/PropertyDescriptor.java @@ -39,7 +39,7 @@ public class PropertyDescriptor extends Descriptor { /** * Creates field-only property descriptor. */ - public PropertyDescriptor(ClassDescriptor classDescriptor, String propertyName, FieldDescriptor fieldDescriptor) { + public PropertyDescriptor(final ClassDescriptor classDescriptor, final String propertyName, final FieldDescriptor fieldDescriptor) { super(classDescriptor, false); this.name = propertyName; this.readMethodDescriptor = null; @@ -50,7 +50,7 @@ public class PropertyDescriptor extends Descriptor { /** * Creates property descriptor. */ - public PropertyDescriptor(ClassDescriptor classDescriptor, String propertyName, MethodDescriptor readMethod, MethodDescriptor writeMethod) { + public PropertyDescriptor(final ClassDescriptor classDescriptor, final String propertyName, final MethodDescriptor readMethod, final MethodDescriptor writeMethod) { super(classDescriptor, ((readMethod == null) || readMethod.isPublic()) & (writeMethod == null || writeMethod.isPublic()) ); @@ -86,7 +86,7 @@ public class PropertyDescriptor extends Descriptor { * Locates property field. Field is being searched also in all * superclasses of current class. */ - protected FieldDescriptor findField(String fieldName) { + protected FieldDescriptor findField(final String fieldName) { FieldDescriptor fieldDescriptor = classDescriptor.getFieldDescriptor(fieldName, true); if (fieldDescriptor != null) { @@ -100,7 +100,7 @@ public class PropertyDescriptor extends Descriptor { for (Class superclass : superclasses) { - ClassDescriptor classDescriptor = ClassIntrospector.lookup(superclass); + ClassDescriptor classDescriptor = ClassIntrospector.get().lookup(superclass); fieldDescriptor = classDescriptor.getFieldDescriptor(fieldName, true); @@ -116,6 +116,7 @@ public class PropertyDescriptor extends Descriptor { /** * Returns property name. */ + @Override public String getName() { return name; } @@ -179,10 +180,12 @@ public class PropertyDescriptor extends Descriptor { type = fieldDescriptor.getRawType(); } else if (readMethodDescriptor != null) { - type = readMethodDescriptor.getGetterRawType(); + type = getGetter(true).getGetterRawType(); + //type = readMethodDescriptor.getGetterRawType(); } else if (writeMethodDescriptor != null) { - type = writeMethodDescriptor.getSetterRawType(); + type = getSetter(true).getSetterRawType(); + //type = writeMethodDescriptor.getSetterRawType(); } } @@ -198,7 +201,7 @@ public class PropertyDescriptor extends Descriptor { * Returns {@link Getter}. May return null * if no matched getter is found. */ - public Getter getGetter(boolean declared) { + public Getter getGetter(final boolean declared) { if (getters == null) { getters = new Getter[] { createGetter(false), @@ -212,15 +215,15 @@ public class PropertyDescriptor extends Descriptor { /** * Creates a {@link Getter}. */ - protected Getter createGetter(boolean declared) { + protected Getter createGetter(final boolean declared) { if (readMethodDescriptor != null) { if (readMethodDescriptor.matchDeclared(declared)) { - return readMethodDescriptor; + return Getter.of(readMethodDescriptor); } } if (fieldDescriptor != null) { if (fieldDescriptor.matchDeclared(declared)) { - return fieldDescriptor; + return Getter.of(fieldDescriptor); } } return null; @@ -231,7 +234,7 @@ public class PropertyDescriptor extends Descriptor { * Returns {@link Setter}. May return null * if no matched setter is found. */ - public Setter getSetter(boolean declared) { + public Setter getSetter(final boolean declared) { if (setters == null) { setters = new Setter[] { createSetter(false), @@ -245,15 +248,15 @@ public class PropertyDescriptor extends Descriptor { /** * Creates a {@link Setter}. */ - protected Setter createSetter(boolean declared) { + protected Setter createSetter(final boolean declared) { if (writeMethodDescriptor != null) { if (writeMethodDescriptor.matchDeclared(declared)) { - return writeMethodDescriptor; + return Setter.of(writeMethodDescriptor); } } if (fieldDescriptor != null) { if (fieldDescriptor.matchDeclared(declared)) { - return fieldDescriptor; + return Setter.of(fieldDescriptor); } } return null; @@ -264,7 +267,7 @@ public class PropertyDescriptor extends Descriptor { /** * Resolves key type for given property descriptor. */ - public Class resolveKeyType(boolean declared) { + public Class resolveKeyType(final boolean declared) { Class keyType = null; Getter getter = getGetter(declared); @@ -287,7 +290,7 @@ public class PropertyDescriptor extends Descriptor { /** * Resolves component type for given property descriptor. */ - public Class resolveComponentType(boolean declared) { + public Class resolveComponentType(final boolean declared) { Class componentType = null; Getter getter = getGetter(declared); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Setter.java b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Setter.java index b8683f8fc..a03e9f6ff 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Setter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/introspector/Setter.java @@ -32,10 +32,59 @@ import java.lang.reflect.InvocationTargetException; */ public interface Setter { + static Setter of(final MethodDescriptor methodDescriptor) { + return new Setter() { + @Override + public void invokeSetter(final Object target, final Object argument) throws InvocationTargetException, IllegalAccessException { + methodDescriptor.method.invoke(target, argument); + } + + @Override + public Class getSetterRawType() { + return methodDescriptor.getParameters()[0].getRawType(); + } + + @Override + public Class getSetterRawComponentType() { + return methodDescriptor.getParameters()[0].getRawComponentType(); + } + + @Override + public MapperFunction getMapperFunction() { + return methodDescriptor.mapperFunction; + } + }; + } + + static Setter of(final FieldDescriptor fieldDescriptor) { + return new Setter() { + @Override + public void invokeSetter(final Object target, final Object argument) throws IllegalAccessException { + fieldDescriptor.field.set(target, argument); + } + + @Override + public Class getSetterRawType() { + return fieldDescriptor.getRawType(); + } + + @Override + public Class getSetterRawComponentType() { + return fieldDescriptor.getRawComponentType(); + } + + @Override + public MapperFunction getMapperFunction() { + return fieldDescriptor.mapperFunction; + } + }; + } + void invokeSetter(Object target, Object argument) throws IllegalAccessException, InvocationTargetException; Class getSetterRawType(); Class getSetterRawComponentType(); + MapperFunction getMapperFunction(); } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/AppendableWriter.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/AppendableWriter.java new file mode 100644 index 000000000..18c460b99 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/AppendableWriter.java @@ -0,0 +1,121 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +package com.fr.third.jodd.io; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.Writer; +import java.nio.CharBuffer; + +/** + * Appendable writer adapter. + */ +public class AppendableWriter extends Writer { + + private final Appendable appendable; + private final boolean flushable; + private boolean closed; + + public AppendableWriter(final Appendable appendable) { + this.appendable = appendable; + this.flushable = appendable instanceof Flushable; + this.closed = false; + } + + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + checkNotClosed(); + appendable.append(CharBuffer.wrap(cbuf), off, off + len); + } + + @Override + public void write(final int c) throws IOException { + checkNotClosed(); + appendable.append((char) c); + } + + @Override + public Writer append(final char c) throws IOException { + checkNotClosed(); + appendable.append(c); + return this; + } + + @Override + public Writer append(final CharSequence csq, final int start, final int end) throws IOException { + checkNotClosed(); + appendable.append(csq, start, end); + return this; + } + + @Override + public Writer append(final CharSequence csq) throws IOException { + checkNotClosed(); + appendable.append(csq); + return this; + } + + @Override + public void write(final String str, final int off, final int len) throws IOException { + checkNotClosed(); + appendable.append(str, off, off + len); + } + + @Override + public void write(final String str) throws IOException { + appendable.append(str); + } + + @Override + public void write(final char[] cbuf) throws IOException { + appendable.append(CharBuffer.wrap(cbuf)); + } + + @Override + public void flush() throws IOException { + checkNotClosed(); + if (flushable) { + ((Flushable) appendable).flush(); + } + } + + private void checkNotClosed() throws IOException { + if (closed) { + throw new IOException("Cannot write to closed writer " + this); + } + } + + @Override + public void close() throws IOException { + if (!closed) { + flush(); + if (appendable instanceof Closeable) { + ((Closeable) appendable).close(); + } + closed = true; + } + } +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/CharBufferReader.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/CharBufferReader.java index a9ffa377c..9a5b6a38e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/CharBufferReader.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/CharBufferReader.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.io; -import java.io.IOException; import java.io.Reader; import java.nio.CharBuffer; @@ -36,26 +35,26 @@ public class CharBufferReader extends Reader { private final CharBuffer charBuffer; - public CharBufferReader(CharBuffer charBuffer) { - // duplicate so to allow to move independently, - // but share the same underlying data. + public CharBufferReader(final CharBuffer charBuffer) { + // duplicate so to allow to move independently, + // but share the same underlying data. this.charBuffer = charBuffer.duplicate(); } @Override - public int read(char[] chars, int offset, int length) throws IOException { + public int read(final char[] chars, final int offset, final int length) { int read = Math.min(charBuffer.remaining(), length); charBuffer.get(chars, offset, read); return read; } @Override - public int read() throws IOException { + public int read() { return charBuffer.position() < charBuffer.limit() ? charBuffer.get() : -1; } @Override - public void close() throws IOException { + public void close() { } } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/FastByteArrayOutputStream.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/FastByteArrayOutputStream.java index d09f6971a..69efb4ee8 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/FastByteArrayOutputStream.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/FastByteArrayOutputStream.java @@ -25,41 +25,39 @@ package com.fr.third.jodd.io; -import com.fr.third.jodd.util.buffer.FastByteBuffer; -import com.fr.third.jodd.util.buffer.FastByteBuffer; +import com.fr.third.jodd.util.StringUtil; +import com.fr.third.jodd.buffer.FastByteBuffer; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; /** - * This class implements an output stream in which the data is + * This class implements an {@link OutputStream} in which the data is * written into a byte array. The buffer automatically grows as data * is written to it. *

- * The data can be retrieved using toByteArray() and - * toString(). + * The data can be retrieved using {@link #toByteArray()} and {@link #toString}. *

- * Closing a FastByteArrayOutputStream has no effect. The methods in + * Closing a {@link FastByteArrayOutputStream} has no effect. The methods in * this class can be called after the stream has been closed without - * generating an IOException. + * generating an {@link IOException}. *

- * This is an alternative implementation of the java.io.FastByteArrayOutputStream + * This is an alternative implementation of the {@code java.io.FastByteArrayOutputStream} * class. The original implementation only allocates 32 bytes at the beginning. * As this class is designed for heavy duty it starts at 1024 bytes. In contrast * to the original it doesn't reallocate the whole memory block but allocates * additional buffers. This way no buffers need to be garbage collected and * the contents don't have to be copied to the new buffer. This class is * designed to behave exactly like the original. The only exception is the - * depreciated toString(int) method that has been ignored. - * + * deprecated {@code java.io.FastByteArrayOutputStream#toString(int)} method that has been ignored. */ public class FastByteArrayOutputStream extends OutputStream { private final FastByteBuffer buffer; /** - * Creates a new byte array output stream. The buffer capacity is + * Creates a new byte array {@link OutputStream}. The buffer capacity is * initially 1024 bytes, though its size increases if necessary. */ public FastByteArrayOutputStream() { @@ -73,15 +71,15 @@ public class FastByteArrayOutputStream extends OutputStream { * @param size the initial size. * @throws IllegalArgumentException if size is negative. */ - public FastByteArrayOutputStream(int size) { + public FastByteArrayOutputStream(final int size) { buffer = new FastByteBuffer(size); } /** - * @see java.io.OutputStream#write(byte[], int, int) + * @see OutputStream#write(byte[], int, int) */ @Override - public void write(byte[] b, int off, int len) { + public void write(final byte[] b, final int off, final int len) { buffer.append(b, off, len); } @@ -89,21 +87,21 @@ public class FastByteArrayOutputStream extends OutputStream { * Writes single byte. */ @Override - public void write(int b) { + public void write(final int b) { buffer.append((byte) b); } /** - * @see java.io.ByteArrayOutputStream#size() + * @see ByteArrayOutputStream#size() */ public int size() { return buffer.size(); } /** - * Closing a FastByteArrayOutputStream has no effect. The methods in + * Closing a {@link FastByteArrayOutputStream} has no effect. The methods in * this class can be called after the stream has been closed without - * generating an IOException. + * generating an {@link IOException}. */ @Override public void close() { @@ -111,33 +109,28 @@ public class FastByteArrayOutputStream extends OutputStream { } /** - * @see java.io.ByteArrayOutputStream#reset() + * @see ByteArrayOutputStream#reset() */ public void reset() { buffer.clear(); } /** - * @see java.io.ByteArrayOutputStream#writeTo(OutputStream) + * @see ByteArrayOutputStream#writeTo(OutputStream) */ - public void writeTo(OutputStream out) throws IOException { - int index = buffer.index(); - for (int i = 0; i < index; i++) { - byte[] buf = buffer.array(i); - out.write(buf); - } - out.write(buffer.array(index), 0, buffer.offset()); + public void writeTo(final OutputStream out) throws IOException { + out.write(buffer.toArray()); } /** - * @see java.io.ByteArrayOutputStream#toByteArray() + * @see ByteArrayOutputStream#toByteArray() */ public byte[] toByteArray() { return buffer.toArray(); } /** - * @see java.io.ByteArrayOutputStream#toString() + * @see ByteArrayOutputStream#toString() */ @Override public String toString() { @@ -145,10 +138,9 @@ public class FastByteArrayOutputStream extends OutputStream { } /** - * @see java.io.ByteArrayOutputStream#toString(String) + * @see ByteArrayOutputStream#toString(String) */ - public String toString(String enc) throws UnsupportedEncodingException { - return new String(toByteArray(), enc); + public String toString(final String enc) { + return StringUtil.newString(toByteArray(), enc); } - } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/FastCharArrayWriter.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/FastCharArrayWriter.java index b8a124e74..cc4a690bb 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/FastCharArrayWriter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/FastCharArrayWriter.java @@ -25,17 +25,17 @@ package com.fr.third.jodd.io; -import com.fr.third.jodd.util.buffer.FastCharBuffer; -import com.fr.third.jodd.util.buffer.FastCharBuffer; +import com.fr.third.jodd.buffer.FastCharBuffer; +import java.io.CharArrayWriter; import java.io.IOException; import java.io.Writer; /** - * Similar as {@link FastByteArrayOutputStream} but for {@link Writer}. + * Similar to {@link FastByteArrayOutputStream} but for {@link Writer}. */ public class FastCharArrayWriter extends Writer { - + private final FastCharBuffer buffer; /** @@ -47,21 +47,21 @@ public class FastCharArrayWriter extends Writer { } /** - * Creates a new char array writer, with a buffer capacity of + * Creates a new char array {@link Writer}, with a buffer capacity of * the specified size, in bytes. * * @param size the initial size. * @throws IllegalArgumentException if size is negative. */ - public FastCharArrayWriter(int size) { + public FastCharArrayWriter(final int size) { buffer = new FastCharBuffer(size); } /** - * @see java.io.Writer#write(char[], int, int) + * @see Writer#write(char[], int, int) */ @Override - public void write(char[] b, int off, int len) { + public void write(final char[] b, final int off, final int len) { buffer.append(b, off, len); } @@ -69,26 +69,26 @@ public class FastCharArrayWriter extends Writer { * Writes single byte. */ @Override - public void write(int b) { + public void write(final int b) { buffer.append((char) b); } @Override - public void write(String s, int off, int len) { + public void write(final String s, final int off, final int len) { write(s.toCharArray(), off, len); } /** - * @see java.io.CharArrayWriter#size() + * @see CharArrayWriter#size() */ public int size() { return buffer.size(); } /** - * Closing a FastCharArrayWriter has no effect. The methods in + * Closing a {@link FastCharArrayWriter} has no effect. The methods in * this class can be called after the stream has been closed without - * generating an IOException. + * generating an {@link IOException}. */ @Override public void close() { @@ -96,7 +96,7 @@ public class FastCharArrayWriter extends Writer { } /** - * Flushing a FastCharArrayWriter has no effects. + * Flushing a {@link FastCharArrayWriter} has no effects. */ @Override public void flush() { @@ -104,33 +104,28 @@ public class FastCharArrayWriter extends Writer { } /** - * @see java.io.CharArrayWriter#reset() + * @see CharArrayWriter#reset() */ public void reset() { buffer.clear(); } /** - * @see java.io.CharArrayWriter#writeTo(java.io.Writer) + * @see CharArrayWriter#writeTo(Writer) */ - public void writeTo(Writer out) throws IOException { - int index = buffer.index(); - for (int i = 0; i < index; i++) { - char[] buf = buffer.array(i); - out.write(buf); - } - out.write(buffer.array(index), 0, buffer.offset()); + public void writeTo(final Writer out) throws IOException { + out.write(buffer.toArray()); } /** - * @see java.io.CharArrayWriter#toCharArray() + * @see CharArrayWriter#toCharArray() */ public char[] toCharArray() { return buffer.toArray(); } /** - * @see java.io.CharArrayWriter#toString() + * @see CharArrayWriter#toString() */ @Override public String toString() { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/FileNameUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/FileNameUtil.java index a4ee70dd4..dccab081a 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/FileNameUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/FileNameUtil.java @@ -25,10 +25,8 @@ package com.fr.third.jodd.io; +import com.fr.third.jodd.system.SystemUtil; import com.fr.third.jodd.util.StringPool; -import com.fr.third.jodd.util.SystemUtil; -import com.fr.third.jodd.util.StringPool; -import com.fr.third.jodd.util.SystemUtil; import java.io.File; @@ -122,13 +120,13 @@ public class FileNameUtil { /** * Checks if the character is a separator. */ - private static boolean isSeparator(char ch) { + private static boolean isSeparator(final char ch) { return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR); } // ---------------------------------------------------------------- normalization - public static String normalize(String filename) { + public static String normalize(final String filename) { return doNormalize(filename, SYSTEM_SEPARATOR, true); } @@ -154,7 +152,7 @@ public class FileNameUtil { * /foo/../bar --> /bar * /foo/../bar/ --> /bar/ * /foo/../bar/../baz --> /baz - * //foo//./bar --> /foo/bar + * //foo//./bar --> /foo/bar * /../ --> null * ../foo --> null * foo/bar/.. --> foo/ @@ -172,12 +170,12 @@ public class FileNameUtil { * @param filename the filename to normalize, null returns null * @return the normalized filename, or null if invalid */ - public static String normalize(String filename, boolean unixSeparator) { + public static String normalize(final String filename, final boolean unixSeparator) { char separator = (unixSeparator ? UNIX_SEPARATOR : WINDOWS_SEPARATOR); return doNormalize(filename, separator, true); } - public static String normalizeNoEndSeparator(String filename) { + public static String normalizeNoEndSeparator(final String filename) { return doNormalize(filename, SYSTEM_SEPARATOR, false); } @@ -222,7 +220,7 @@ public class FileNameUtil { * @param filename the filename to normalize, null returns null * @return the normalized filename, or null if invalid */ - public static String normalizeNoEndSeparator(String filename, boolean unixSeparator) { + public static String normalizeNoEndSeparator(final String filename, final boolean unixSeparator) { char separator = (unixSeparator ? UNIX_SEPARATOR : WINDOWS_SEPARATOR); return doNormalize(filename, separator, false); } @@ -235,7 +233,7 @@ public class FileNameUtil { * @param keepSeparator true to keep the final separator * @return normalized filename */ - private static String doNormalize(String filename, char separator, boolean keepSeparator) { + private static String doNormalize(final String filename, final char separator, final boolean keepSeparator) { if (filename == null) { return null; } @@ -262,7 +260,7 @@ public class FileNameUtil { // add extra separator on the end to simplify code below boolean lastIsDirectory = true; if (array[size - 1] != separator) { - array[size++] = separator; + array[size++] = separator; lastIsDirectory = false; } @@ -369,14 +367,14 @@ public class FileNameUtil { * @param fullFilenameToAdd the filename (or path) to attach to the base * @return the concatenated path, or null if invalid */ - public static String concat(String basePath, String fullFilenameToAdd) { + public static String concat(final String basePath, final String fullFilenameToAdd) { return doConcat(basePath, fullFilenameToAdd, SYSTEM_SEPARATOR); } - public static String concat(String basePath, String fullFilenameToAdd, boolean unixSeparator) { + public static String concat(final String basePath, final String fullFilenameToAdd, final boolean unixSeparator) { char separator = (unixSeparator ? UNIX_SEPARATOR : WINDOWS_SEPARATOR); return doConcat(basePath, fullFilenameToAdd, separator); } - public static String doConcat(String basePath, String fullFilenameToAdd, char separator) { + public static String doConcat(final String basePath, final String fullFilenameToAdd, final char separator) { int prefix = getPrefixLength(fullFilenameToAdd); if (prefix < 0) { return null; @@ -407,7 +405,7 @@ public class FileNameUtil { * @param path the path to be changed, null ignored * @return the updated path */ - public static String separatorsToUnix(String path) { + public static String separatorsToUnix(final String path) { if (path == null || path.indexOf(WINDOWS_SEPARATOR) == -1) { return path; } @@ -420,7 +418,7 @@ public class FileNameUtil { * @param path the path to be changed, null ignored * @return the updated path */ - public static String separatorsToWindows(String path) { + public static String separatorsToWindows(final String path) { if (path == null || path.indexOf(UNIX_SEPARATOR) == -1) { return path; } @@ -433,7 +431,7 @@ public class FileNameUtil { * @param path the path to be changed, null ignored * @return the updated path */ - public static String separatorsToSystem(String path) { + public static String separatorsToSystem(final String path) { if (path == null) { return null; } @@ -476,7 +474,7 @@ public class FileNameUtil { * @param filename the filename to find the prefix in, null returns -1 * @return the length of the prefix, -1 if invalid or null */ - public static int getPrefixLength(String filename) { + public static int getPrefixLength(final String filename) { if (filename == null) { return -1; } @@ -541,7 +539,7 @@ public class FileNameUtil { * @param filename the filename to find the last path separator in, null returns -1 * @return the index of the last separator character, or -1 if there is no such character */ - public static int indexOfLastSeparator(String filename) { + public static int indexOfLastSeparator(final String filename) { if (filename == null) { return -1; } @@ -563,7 +561,7 @@ public class FileNameUtil { * @return the index of the last separator character, or -1 if there * is no such character */ - public static int indexOfExtension(String filename) { + public static int indexOfExtension(final String filename) { if (filename == null) { return -1; } @@ -575,7 +573,7 @@ public class FileNameUtil { /** * Returns true if file has extension. */ - public static boolean hasExtension(String filename) { + public static boolean hasExtension(final String filename) { return indexOfExtension(filename) != -1; } @@ -610,7 +608,7 @@ public class FileNameUtil { * @param filename the filename to query, null returns null * @return the prefix of the file, null if invalid */ - public static String getPrefix(String filename) { + public static String getPrefix(final String filename) { if (filename == null) { return null; } @@ -646,7 +644,7 @@ public class FileNameUtil { * @param filename the filename to query, null returns null * @return the path of the file, an empty string if none exists, null if invalid */ - public static String getPath(String filename) { + public static String getPath(final String filename) { return doGetPath(filename, 1); } @@ -673,7 +671,7 @@ public class FileNameUtil { * @param filename the filename to query, null returns null * @return the path of the file, an empty string if none exists, null if invalid */ - public static String getPathNoEndSeparator(String filename) { + public static String getPathNoEndSeparator(final String filename) { return doGetPath(filename, 0); } @@ -684,7 +682,7 @@ public class FileNameUtil { * @param separatorAdd 0 to omit the end separator, 1 to return it * @return the path */ - private static String doGetPath(String filename, int separatorAdd) { + private static String doGetPath(final String filename, final int separatorAdd) { if (filename == null) { return null; } @@ -693,11 +691,11 @@ public class FileNameUtil { return null; } int index = indexOfLastSeparator(filename); - int endIndex = index + separatorAdd; - if (prefix >= filename.length() || index < 0 || prefix >= endIndex) { + int endIndex = index + separatorAdd; + if (prefix >= filename.length() || index < 0 || prefix >= endIndex) { return StringPool.EMPTY; } - return filename.substring(prefix, endIndex); + return filename.substring(prefix, endIndex); } /** @@ -725,7 +723,7 @@ public class FileNameUtil { * @param filename the filename to query, null returns null * @return the path of the file, an empty string if none exists, null if invalid */ - public static String getFullPath(String filename) { + public static String getFullPath(final String filename) { return doGetFullPath(filename, true); } @@ -755,7 +753,7 @@ public class FileNameUtil { * @param filename the filename to query, null returns null * @return the path of the file, an empty string if none exists, null if invalid */ - public static String getFullPathNoEndSeparator(String filename) { + public static String getFullPathNoEndSeparator(final String filename) { return doGetFullPath(filename, false); } @@ -766,7 +764,7 @@ public class FileNameUtil { * @param includeSeparator true to include the end separator * @return the path */ - private static String doGetFullPath(String filename, boolean includeSeparator) { + private static String doGetFullPath(final String filename, final boolean includeSeparator) { if (filename == null) { return null; } @@ -786,9 +784,9 @@ public class FileNameUtil { return filename.substring(0, prefix); } int end = index + (includeSeparator ? 1 : 0); - if (end == 0) { - end++; - } + if (end == 0) { + end++; + } return filename.substring(0, end); } @@ -809,7 +807,7 @@ public class FileNameUtil { * @param filename the filename to query, null returns null * @return the name of the file without the path, or an empty string if none exists */ - public static String getName(String filename) { + public static String getName(final String filename) { if (filename == null) { return null; } @@ -834,7 +832,7 @@ public class FileNameUtil { * @param filename the filename to query, null returns null * @return the name of the file without the path, or an empty string if none exists */ - public static String getBaseName(String filename) { + public static String getBaseName(final String filename) { return removeExtension(getName(filename)); } @@ -855,7 +853,7 @@ public class FileNameUtil { * @param filename the filename to retrieve the extension of. * @return the extension of the file or an empty string if none exists. */ - public static String getExtension(String filename) { + public static String getExtension(final String filename) { if (filename == null) { return null; } @@ -886,7 +884,7 @@ public class FileNameUtil { * @param filename the filename to query, null returns null * @return the filename minus the extension */ - public static String removeExtension(String filename) { + public static String removeExtension(final String filename) { if (filename == null) { return null; } @@ -903,14 +901,14 @@ public class FileNameUtil { /** * Checks whether two filenames are equal exactly. */ - public static boolean equals(String filename1, String filename2) { + public static boolean equals(final String filename1, final String filename2) { return equals(filename1, filename2, false); } /** * Checks whether two filenames are equal using the case rules of the system. */ - public static boolean equalsOnSystem(String filename1, String filename2) { + public static boolean equalsOnSystem(final String filename1, final String filename2) { return equals(filename1, filename2, true); } @@ -923,7 +921,7 @@ public class FileNameUtil { * @param system whether to use the system (windows or unix) * @return true if the filenames are equal, null equals null */ - private static boolean equals(String filename1, String filename2, boolean system) { + private static boolean equals(final String filename1, final String filename2, final boolean system) { //noinspection StringEquality if (filename1 == filename2) { return true; @@ -944,7 +942,7 @@ public class FileNameUtil { * Splits filename into a array of four Strings containing prefix, path, basename and extension. * Path will contain ending separator. */ - public static String[] split(String filename) { + public static String[] split(final String filename) { String prefix = getPrefix(filename); if (prefix == null) { prefix = StringPool.EMPTY; @@ -983,19 +981,26 @@ public class FileNameUtil { /** * Resolve ~ in the path. */ - public static String resolveHome(String path) { + public static String resolveHome(final String path) { if (path.length() == 1) { if (path.charAt(0) == '~') { - return SystemUtil.userHome(); + return SystemUtil.info().getHomeDir(); } return path; } if (path.length() >= 2) { if ((path.charAt(0) == '~') && (path.charAt(1) == File.separatorChar)) { - return SystemUtil.userHome() + path.substring(1); + return SystemUtil.info().getHomeDir() + path.substring(1); } } return path; } + /** + * Calculates relative path of target path on base path. + */ + public static String relativePath(final String targetPath, final String basePath) { + return new File(basePath).toPath().relativize(new File(targetPath).toPath()).toString(); + } + } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtil.java index bee428b7c..d29cb00de 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtil.java @@ -26,40 +26,29 @@ package com.fr.third.jodd.io; import com.fr.third.jodd.core.JoddCore; +import com.fr.third.jodd.net.URLDecoder; +import com.fr.third.jodd.system.SystemUtil; +import com.fr.third.jodd.util.DigestEngine; import com.fr.third.jodd.util.StringPool; import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.util.SystemUtil; -import com.fr.third.jodd.util.URLDecoder; -import com.fr.third.jodd.core.JoddCore; -import com.fr.third.jodd.util.StringPool; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.util.SystemUtil; -import com.fr.third.jodd.util.URLDecoder; -import java.io.BufferedInputStream; +import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; +import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStreamWriter; import java.io.RandomAccessFile; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.FileFilter; import java.io.Writer; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.List; +import java.nio.file.Files; import java.util.ArrayList; - -import static com.fr.third.jodd.core.JoddCore.fileUtilParams; +import java.util.List; /** * File utilities. @@ -70,50 +59,59 @@ public class FileUtil { private static final String MSG_CANT_CREATE = "Can't create: "; private static final String MSG_NOT_FOUND = "Not found: "; private static final String MSG_NOT_A_FILE = "Not a file: "; - private static final String MSG_ALREADY_EXISTS = "Already exists: "; private static final String MSG_UNABLE_TO_DELETE = "Unable to delete: "; + private static final int ZERO = 0; + private static final int NEGATIVE_ONE = -1; + private static final String FILE_PROTOCOL = "file"; + private static final String USER_HOME = "~"; + /** - * Simple factory for File objects. + * Simple factory for {@link File} objects but with home resolving. */ - private static File file(String fileName) { + public static File file(String fileName) { + fileName = StringUtil.replace(fileName, USER_HOME, SystemUtil.info().getHomeDir()); return new File(fileName); } /** - * Simple factory for File objects. + * Simple factory for {@link File} objects. */ - private static File file(File parent, String fileName) { + private static File file(final File parent, final String fileName) { return new File(parent, fileName); } // ---------------------------------------------------------------- misc shortcuts /** - * Checks if two files points to the same file. + * @see #equals(File, File) */ - public static boolean equals(String file1, String file2) { - return equals(file(file1), file(file2)); + public static boolean equals(final String one, final String two) { + return equals(file(one), file(two)); } /** - * Checks if two files points to the same file. + * Checks if two {@link File}s point to the same {@link File}. + * + * @param one {@link File} one. + * @param two {@link File} two. + * @return {@code true} if the {@link File}s match. */ - public static boolean equals(File file1, File file2) { + public static boolean equals(File one, File two) { try { - file1 = file1.getCanonicalFile(); - file2 = file2.getCanonicalFile(); - } catch (IOException ignore) { + one = one.getCanonicalFile(); + two = two.getCanonicalFile(); + } catch (final IOException ignore) { return false; } - return file1.equals(file2); + return one.equals(two); } /** - * Converts file URLs to file. Ignores other schemes and returns null. + * Converts {@link File} {@link URL}s to {@link File}. Ignores other schemes and returns {@code null}. */ - public static File toFile(URL url) { - String fileName = toFileName(url); + public static File toFile(final URL url) { + final String fileName = toFileName(url); if (fileName == null) { return null; } @@ -121,342 +119,247 @@ public class FileUtil { } /** - * Converts file to URL in a correct way. - * Returns null in case of error. + * Converts {@link File} to {@link URL} in a correct way. + * + * @return {@link URL} or {@code null} in case of error. + * @throws MalformedURLException if {@link File} cannot be converted. */ - public static URL toURL(File file) throws MalformedURLException { + public static URL toURL(final File file) throws MalformedURLException { return file.toURI().toURL(); } /** - * Converts file URLs to file name. Accepts only URLs with 'file' protocol. - * Otherwise, for other schemes returns null. + * Converts {@link File} {@link URL}s to file name. Accepts only {@link URL}s with 'file' protocol. + * Otherwise, for other schemes returns {@code null}. + * + * @param url {@link URL} to convert + * @return file name */ - public static String toFileName(URL url) { - if ((url == null) || !(url.getProtocol().equals("file"))) { + public static String toFileName(final URL url) { + if ((url == null) || !(url.getProtocol().equals(FILE_PROTOCOL))) { return null; } - String filename = url.getFile().replace('/', File.separatorChar); + final String filename = url.getFile().replace('/', File.separatorChar); - return URLDecoder.decode(filename, JoddCore.encoding); + return URLDecoder.decode(filename, encoding()); } /** * Returns a file of either a folder or a containing archive. */ - public static File toContainerFile(URL url) { - String protocol = url.getProtocol(); - if (protocol.equals("file")) { + public static File toContainerFile(final URL url) { + final String protocol = url.getProtocol(); + if (protocol.equals(FILE_PROTOCOL)) { return toFile(url); } - String path = url.getPath(); + final String path = url.getPath(); return new File(URI.create( - path.substring(0, path.lastIndexOf("!/")))); + path.substring(ZERO, path.lastIndexOf("!/")))); } /** - * Returns true if file exists. + * Returns {@code true} if {@link File} exists. */ - public static boolean isExistingFile(File file) { - if (file == null) { - return false; - } - return file.exists() && file.isFile(); + public static boolean isExistingFile(final File file) { + return file != null && file.exists() && file.isFile(); } /** - * Returns true if folder exists. + * Returns {@code true} if directory exists. */ - public static boolean isExistingFolder(File folder) { - if (folder == null) { - return false; - } - return folder.exists() && folder.isDirectory(); + public static boolean isExistingFolder(final File folder) { + return folder != null && folder.exists() && folder.isDirectory(); } // ---------------------------------------------------------------- mkdirs /** - * Creates all folders at once. - * @see #mkdirs(java.io.File) + * @see #mkdirs(File) */ - public static void mkdirs(String dirs) throws IOException { - mkdirs(file(dirs)); + public static File mkdirs(final String dirs) throws IOException { + return mkdirs(file(dirs)); } + /** - * Creates all folders at once. + * Creates all directories at once. + * + * @param dirs Directories to make. + * @throws IOException if cannot create directory. */ - public static void mkdirs(File dirs) throws IOException { + public static File mkdirs(final File dirs) throws IOException { if (dirs.exists()) { - if (!dirs.isDirectory()) { - throw new IOException(MSG_NOT_A_DIRECTORY + dirs); - } - return; - } - if (!dirs.mkdirs()) { - throw new IOException(MSG_CANT_CREATE + dirs); + checkIsDirectory(dirs); + return dirs; } + return checkCreateDirectory(dirs); } /** - * Creates single folder. - * @see #mkdir(java.io.File) + * @see #mkdir(File) */ - public static void mkdir(String dir) throws IOException { - mkdir(file(dir)); + public static File mkdir(final String dir) throws IOException { + return mkdir(file(dir)); } + /** - * Creates single folders. + * Creates single directory. + * + * @throws IOException if cannot create directory. */ - public static void mkdir(File dir) throws IOException { + public static File mkdir(final File dir) throws IOException { if (dir.exists()) { - if (!dir.isDirectory()) { - throw new IOException(MSG_NOT_A_DIRECTORY + dir); - } - return; - } - if (!dir.mkdir()) { - throw new IOException(MSG_CANT_CREATE + dir); + checkIsDirectory(dir); + return dir; } + return checkCreateDirectory(dir); } // ---------------------------------------------------------------- touch /** - * @see #touch(java.io.File) + * @see #touch(File) */ - public static void touch(String file) throws IOException { + public static void touch(final String file) throws IOException { touch(file(file)); } + /** - * Implements the Unix "touch" utility. It creates a new file - * with size 0 or, if the file exists already, it is opened and - * closed without modifying it, but updating the file date and time. + * Implements the Unix "touch" utility. It creates a new {@link File} + * with size 0 or, if the {@link File} exists already, it is opened and + * closed without modifying it, but updating the {@link File} date and time. */ - public static void touch(File file) throws IOException { + public static void touch(final File file) throws IOException { if (!file.exists()) { - StreamUtil.close(new FileOutputStream(file)); + StreamUtil.close(new FileOutputStream(file, false)); } file.setLastModified(System.currentTimeMillis()); } - - // ---------------------------------------------------------------- params - - /** - * Creates new {@link FileUtilParams} instance by cloning current default params. - */ - public static FileUtilParams cloneParams() { - try { - return JoddCore.fileUtilParams.clone(); - } catch (CloneNotSupportedException ignore) { - return null; - } - } - - /** - * Creates new {@link FileUtilParams} instance with default values. - */ - public static FileUtilParams params() { - return new FileUtilParams(); - } - // ---------------------------------------------------------------- copy file to file /** - * @see #copyFile(java.io.File, java.io.File, FileUtilParams) + * @see #copyFile(File, File) */ - public static void copyFile(String src, String dest) throws IOException { - copyFile(file(src), file(dest), JoddCore.fileUtilParams); - } - /** - * @see #copyFile(java.io.File, java.io.File, FileUtilParams) - */ - public static void copyFile(String src, String dest, FileUtilParams params) throws IOException { - copyFile(file(src), file(dest), params); - } - /** - * @see #copyFile(java.io.File, java.io.File, FileUtilParams) - */ - public static void copyFile(File src, File dest) throws IOException { - copyFile(src, dest, JoddCore.fileUtilParams); + public static void copyFile(final String srcFile, final String destFile) throws IOException { + copyFile(file(srcFile), file(destFile)); } /** - * Copies a file to another file with specified {@link FileUtilParams copy params}. + * Copies a {@link File} to another {@link File}. + * + * @param srcFile Source {@link File}. + * @param destFile Destination {@link File}. + * @throws IOException if cannot copy */ - public static void copyFile(File src, File dest, FileUtilParams params) throws IOException { - checkFileCopy(src, dest, params); - doCopyFile(src, dest, params); - } - - private static void checkFileCopy(File src, File dest, FileUtilParams params) throws IOException { - if (!src.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + src); - } - if (!src.isFile()) { - throw new IOException(MSG_NOT_A_FILE + src); - } - if (equals(src, dest)) { - throw new IOException("Files '" + src + "' and '" + dest + "' are equal"); - } - - File destParent = dest.getParentFile(); - if (destParent != null && !destParent.exists()) { - if (!params.createDirs) { - throw new IOException(MSG_NOT_FOUND + destParent); - } - if (!destParent.mkdirs()) { - throw new IOException(MSG_CANT_CREATE + destParent); - } - } + public static void copyFile(final File srcFile, final File destFile) throws IOException { + checkFileCopy(srcFile, destFile); + _copyFile(srcFile, destFile); } /** * Internal file copy when most of the pre-checking has passed. + * + * @param srcFile Source {@link File}. + * @param destFile Destination {@link File}. + * @throws IOException if cannot copy */ - private static void doCopyFile(File src, File dest, FileUtilParams params) throws IOException { - if (dest.exists()) { - if (dest.isDirectory()) { - throw new IOException("Destination '" + dest + "' is a directory"); - } - if (!params.overwrite) { - throw new IOException(MSG_ALREADY_EXISTS + dest); + private static void _copyFile(final File srcFile, final File destFile) throws IOException { + if (destFile.exists()) { + if (destFile.isDirectory()) { + throw new IOException("Destination '" + destFile + "' is a directory"); } } // do copy file - FileInputStream input = new FileInputStream(src); + FileInputStream input = null; + FileOutputStream output = null; try { - FileOutputStream output = new FileOutputStream(dest); - try { - StreamUtil.copy(input, output); - } finally { - StreamUtil.close(output); - } + input = new FileInputStream(srcFile); + output = new FileOutputStream(destFile, false); + StreamUtil.copy(input, output); } finally { + StreamUtil.close(output); StreamUtil.close(input); } // done - if (src.length() != dest.length()) { - throw new IOException("Copy file failed of '" + src + "' to '" + dest + "' due to different sizes"); - } - if (params.preserveDate) { - dest.setLastModified(src.lastModified()); + if (srcFile.length() != destFile.length()) { + throw new IOException("Copy file failed of '" + srcFile + "' to '" + destFile + "' due to different sizes"); } + destFile.setLastModified(srcFile.lastModified()); } // ---------------------------------------------------------------- copy file to directory /** - * @see #copyFileToDir(java.io.File, java.io.File, FileUtilParams) - */ - public static File copyFileToDir(String src, String destDir) throws IOException { - return copyFileToDir(file(src), file(destDir), JoddCore.fileUtilParams); - } - /** - * @see #copyFileToDir(java.io.File, java.io.File, FileUtilParams) + * @see #copyFileToDir(File, File) */ - public static File copyFileToDir(String src, String destDir, FileUtilParams params) throws IOException { - return copyFileToDir(file(src), file(destDir), params); - } - /** - * @see #copyFileToDir(java.io.File, java.io.File, FileUtilParams) - */ - public static File copyFileToDir(File src, File destDir) throws IOException { - return copyFileToDir(src, destDir, JoddCore.fileUtilParams); + public static File copyFileToDir(final String srcFile, final String destDir) throws IOException { + return copyFileToDir(file(srcFile), file(destDir)); } + /** - * Copies a file to folder with specified copy params and returns copied destination. + * Copies a {@link File} to directory with specified copy params and returns copied destination. */ - public static File copyFileToDir(File src, File destDir, FileUtilParams params) throws IOException { - if (destDir.exists() && !destDir.isDirectory()) { - throw new IOException(MSG_NOT_A_DIRECTORY + destDir); - } - File dest = file(destDir, src.getName()); - copyFile(src, dest, params); - return dest; + public static File copyFileToDir(final File srcFile, final File destDir) throws IOException { + checkExistsAndDirectory(destDir); + final File destFile = file(destDir, srcFile.getName()); + copyFile(srcFile, destFile); + return destFile; } - // ---------------------------------------------------------------- copy dir - - public static void copyDir(String srcDir, String destDir) throws IOException { - copyDir(file(srcDir), file(destDir), JoddCore.fileUtilParams); - } - - public static void copyDir(String srcDir, String destDir, FileUtilParams params) throws IOException { - copyDir(file(srcDir), file(destDir), params); - } - - public static void copyDir(File srcDir, File destDir) throws IOException { - copyDir(srcDir, destDir, JoddCore.fileUtilParams); + /** + * @see #copyDir(File, File) + */ + public static void copyDir(final String srcDir, final String destDir) throws IOException { + copyDir(file(srcDir), file(destDir)); } /** * Copies directory with specified copy params. + * + * @see #_copyDirectory(File, File) */ - public static void copyDir(File srcDir, File destDir, FileUtilParams params) throws IOException { + public static void copyDir(final File srcDir, final File destDir) throws IOException { checkDirCopy(srcDir, destDir); - doCopyDirectory(srcDir, destDir, params); + _copyDirectory(srcDir, destDir); } - private static void checkDirCopy(File srcDir, File destDir) throws IOException { - if (!srcDir.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + srcDir); - } - if (!srcDir.isDirectory()) { - throw new IOException(MSG_NOT_A_DIRECTORY + srcDir); - } - if (equals(srcDir, destDir)) { - throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are equal"); - } - } - - private static void doCopyDirectory(File srcDir, File destDir, FileUtilParams params) throws IOException { + /** + * @param srcDir + * @param destDir + * @throws IOException + */ + private static void _copyDirectory(final File srcDir, final File destDir) throws IOException { if (destDir.exists()) { - if (!destDir.isDirectory()) { - throw new IOException(MSG_NOT_A_DIRECTORY + destDir); - } + checkIsDirectory(destDir); } else { - if (!params.createDirs) { - throw new IOException(MSG_NOT_FOUND + destDir); - } - if (!destDir.mkdirs()) { - throw new IOException(MSG_CANT_CREATE + destDir); - } - if (params.preserveDate) { - destDir.setLastModified(srcDir.lastModified()); - } + checkCreateDirectory(destDir); + destDir.setLastModified(srcDir.lastModified()); } - File[] files = srcDir.listFiles(); + final File[] files = srcDir.listFiles(); if (files == null) { throw new IOException("Failed to list contents of: " + srcDir); } IOException exception = null; - for (File file : files) { - File destFile = file(destDir, file.getName()); + for (final File file : files) { + final File destFile = file(destDir, file.getName()); try { + if (file.isDirectory()) { - if (params.recursive) { - doCopyDirectory(file, destFile, params); - } + _copyDirectory(file, destFile); } else { - doCopyFile(file, destFile, params); - } - } catch (IOException ioex) { - if (params.continueOnError) { - exception = ioex; - continue; + _copyFile(file, destFile); } - throw ioex; + } catch (final IOException ioex) { + exception = ioex; } } @@ -465,179 +368,177 @@ public class FileUtil { } } - - // ---------------------------------------------------------------- move file - public static File moveFile(String src, String dest) throws IOException { - return moveFile(file(src), file(dest), JoddCore.fileUtilParams); - } - - public static File moveFile(String src, String dest, FileUtilParams params) throws IOException { - return moveFile(file(src), file(dest), params); - } - - public static File moveFile(File src, File dest) throws IOException { - return moveFile(src, dest, JoddCore.fileUtilParams); + /** + * @see #moveFile(File, File) + */ + public static File moveFile(final String srcFile, final String destFile) throws IOException { + return moveFile(file(srcFile), file(destFile)); } - public static File moveFile(File src, File dest, FileUtilParams params) throws IOException { - checkFileCopy(src, dest, params); - doMoveFile(src, dest, params); - return dest; + /** + * @see #_moveFile(File, File) + */ + public static File moveFile(final File srcFile, final File destFile) throws IOException { + checkFileCopy(srcFile, destFile); + _moveFile(srcFile, destFile); + return destFile; } - private static void doMoveFile(File src, File dest, FileUtilParams params) throws IOException { - if (dest.exists()) { - if (!dest.isFile()) { - throw new IOException(MSG_NOT_A_FILE + dest); - } - if (!params.overwrite) { - throw new IOException(MSG_ALREADY_EXISTS + dest); - } - dest.delete(); + /** + * Moves a {@link File}. + * + * @param srcFile Source {@link File}. + * @param destFile Destination directory. + * @throws IOException + */ + private static void _moveFile(final File srcFile, final File destFile) throws IOException { + if (destFile.exists()) { + checkIsFile(destFile); + destFile.delete(); } - final boolean rename = src.renameTo(dest); + final boolean rename = srcFile.renameTo(destFile); if (!rename) { - doCopyFile(src, dest, params); - src.delete(); + _copyFile(srcFile, destFile); + srcFile.delete(); } } // ---------------------------------------------------------------- move file to dir - - public static File moveFileToDir(String src, String destDir) throws IOException { - return moveFileToDir(file(src), file(destDir), JoddCore.fileUtilParams); - } - public static File moveFileToDir(String src, String destDir, FileUtilParams params) throws IOException { - return moveFileToDir(file(src), file(destDir), params); + /** + * @see #moveFileToDir(File, File) + */ + public static File moveFileToDir(final String srcFile, final String destDir) throws IOException { + return moveFileToDir(file(srcFile), file(destDir)); } - public static File moveFileToDir(File src, File destDir) throws IOException { - return moveFileToDir(src, destDir, JoddCore.fileUtilParams); - } - public static File moveFileToDir(File src, File destDir, FileUtilParams params) throws IOException { - if (destDir.exists() && !destDir.isDirectory()) { - throw new IOException(MSG_NOT_A_DIRECTORY + destDir); - } - return moveFile(src, file(destDir, src.getName()), params); + /** + * Moves a file to a directory. + * + * @param srcFile Source {@link File}. + * @param destDir Destination directory. + * @throws IOException if there is an error during move. + */ + public static File moveFileToDir(final File srcFile, final File destDir) throws IOException { + checkExistsAndDirectory(destDir); + return moveFile(srcFile, file(destDir, srcFile.getName())); } - // ---------------------------------------------------------------- move dir - public static File moveDir(String srcDir, String destDir) throws IOException { + /** + * @see #moveDir(File, File) + */ + public static File moveDir(final String srcDir, final String destDir) throws IOException { return moveDir(file(srcDir), file(destDir)); } - public static File moveDir(File srcDir, File destDir) throws IOException { + + /** + * @see #_moveDirectory(File, File) + */ + public static File moveDir(final File srcDir, final File destDir) throws IOException { checkDirCopy(srcDir, destDir); - doMoveDirectory(srcDir, destDir); + _moveDirectory(srcDir, destDir); return destDir; } - private static void doMoveDirectory(File src, File dest) throws IOException { - if (dest.exists()) { - if (!dest.isDirectory()) { - throw new IOException(MSG_NOT_A_DIRECTORY + dest); - } - dest = file(dest, dest.getName()); - dest.mkdir(); + /** + * Moves a directory. + * + * @param srcDest Source directory + * @param destDir Destination directory. + * @throws IOException if there is an error during move. + */ + private static void _moveDirectory(final File srcDest, File destDir) throws IOException { + if (destDir.exists()) { + checkIsDirectory(destDir); + destDir = file(destDir, destDir.getName()); + destDir.mkdir(); } - final boolean rename = src.renameTo(dest); + final boolean rename = srcDest.renameTo(destDir); if (!rename) { - doCopyDirectory(src, dest, params()); - deleteDir(src); + _copyDirectory(srcDest, destDir); + deleteDir(srcDest); } } // ---------------------------------------------------------------- delete file - public static void deleteFile(String dest) throws IOException { - deleteFile(file(dest)); + /** + * @see #deleteFile(File) + */ + public static void deleteFile(final String destFile) throws IOException { + deleteFile(file(destFile)); } - public static void deleteFile(File dest) throws IOException { - if (!dest.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + dest); - } - if (!dest.isFile()) { - throw new IOException(MSG_NOT_A_FILE + dest); - } - if (!dest.delete()) { - throw new IOException(MSG_UNABLE_TO_DELETE + dest); - } + /** + * Deletes a {@link File}. + * + * @param destFile Destination to delete. + * @throws IOException if there was an error deleting. + */ + public static void deleteFile(final File destFile) throws IOException { + checkIsFile(destFile); + checkDeleteSuccessful(destFile); } - // ---------------------------------------------------------------- delete dir - public static void deleteDir(String dest) throws IOException { - deleteDir(file(dest), JoddCore.fileUtilParams); - } - public static void deleteDir(String dest, FileUtilParams params) throws IOException { - deleteDir(file(dest), params); - } - public static void deleteDir(File dest) throws IOException { - deleteDir(dest, JoddCore.fileUtilParams); + /** + * @see #deleteDir(File) + */ + public static void deleteDir(final String destDir) throws IOException { + deleteDir(file(destDir)); } + /** * Deletes a directory. + * + * @param destDir Destination to delete. + * @throws IOException if there was an error deleting. */ - public static void deleteDir(File dest, FileUtilParams params) throws IOException { - cleanDir(dest, params); - if (!dest.delete()) { - throw new IOException(MSG_UNABLE_TO_DELETE + dest); - } + public static void deleteDir(final File destDir) throws IOException { + cleanDir(destDir); + checkDeleteSuccessful(destDir); } - - - public static void cleanDir(String dest) throws IOException { - cleanDir(file(dest), JoddCore.fileUtilParams); - } - public static void cleanDir(String dest, FileUtilParams params) throws IOException { - cleanDir(file(dest), params); - } - public static void cleanDir(File dest) throws IOException { - cleanDir(dest, JoddCore.fileUtilParams); + /** + * @see #cleanDir(File) + */ + public static void cleanDir(final String dest) throws IOException { + cleanDir(file(dest)); } /** * Cleans a directory without deleting it. + * + * @param destDir destination to clean. + * @throws IOException if something went wrong. */ - public static void cleanDir(File dest, FileUtilParams params) throws IOException { - if (!dest.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + dest); - } - - if (!dest.isDirectory()) { - throw new IOException(MSG_NOT_A_DIRECTORY + dest); - } + public static void cleanDir(final File destDir) throws IOException { + checkExists(destDir); + checkIsDirectory(destDir); - File[] files = dest.listFiles(); + final File[] files = destDir.listFiles(); if (files == null) { - throw new IOException("Failed to list contents of: " + dest); + throw new IOException("Failed to list contents of: " + destDir); } IOException exception = null; - for (File file : files) { + for (final File file : files) { try { if (file.isDirectory()) { - if (params.recursive) { - deleteDir(file, params); - } + deleteDir(file); } else { file.delete(); } - } catch (IOException ioex) { - if (params.continueOnError) { - exception = ioex; - continue; - } - throw ioex; + } catch (final IOException ioex) { + exception = ioex; + continue; } } @@ -648,102 +549,117 @@ public class FileUtil { // ---------------------------------------------------------------- read/write chars - public static char[] readUTFChars(String fileName) throws IOException { + /** + * @see #readUTFChars(File) + */ + public static char[] readUTFChars(final String fileName) throws IOException { return readUTFChars(file(fileName)); } + /** * Reads UTF file content as char array. - * @see UnicodeInputStream + * + * @param file {@link File} to read. + * @return array of characters. + * @throws IOException if something went wrong. */ - public static char[] readUTFChars(File file) throws IOException { - if (!file.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + file); - } - if (!file.isFile()) { - throw new IOException(MSG_NOT_A_FILE + file); - } - long len = file.length(); - if (len >= Integer.MAX_VALUE) { - len = Integer.MAX_VALUE; + public static char[] readUTFChars(final File file) throws IOException { + checkExists(file); + checkIsFile(file); + + final UnicodeInputStream in = unicodeInputStreamOf(file); + try { + return StreamUtil.readChars(in, detectEncoding(in)); + } finally { + StreamUtil.close(in); } - UnicodeInputStream in = null; + } + + /** + * Reads file content as char array. + * + * @param file {@link File} to read. + * @param encoding Encoding to use. + * @return array of characters. + * @throws IOException if something went wrong. + */ + public static char[] readChars(final File file, final String encoding) throws IOException { + checkExists(file); + checkIsFile(file); + + final InputStream in = streamOf(file, encoding); try { - in = new UnicodeInputStream(new FileInputStream(file), null); - FastCharArrayWriter fastCharArrayWriter = new FastCharArrayWriter((int) len); - String encoding = in.getDetectedEncoding(); - if (encoding == null) { - encoding = StringPool.UTF_8; - } - StreamUtil.copy(in, fastCharArrayWriter, encoding); - return fastCharArrayWriter.toCharArray(); + return StreamUtil.readChars(in, encoding); } finally { StreamUtil.close(in); } } - public static char[] readChars(String fileName) throws IOException { - return readChars(file(fileName), JoddCore.fileUtilParams.encoding); + /** + * @see #readChars(String, String) + */ + public static char[] readChars(final String fileName) throws IOException { + return readChars(fileName, encoding()); } - public static char[] readChars(File file) throws IOException { - return readChars(file, JoddCore.fileUtilParams.encoding); + /** + * @see #readChars(File, String) + */ + public static char[] readChars(final File file) throws IOException { + return readChars(file, encoding()); } - public static char[] readChars(String fileName, String encoding) throws IOException { + /** + * @see #readChars(File, String) + */ + public static char[] readChars(final String fileName, final String encoding) throws IOException { return readChars(file(fileName), encoding); } /** - * Reads file content as char array. + * @see #writeChars(File, char[], String) */ - public static char[] readChars(File file, String encoding) throws IOException { - if (!file.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + file); - } - if (!file.isFile()) { - throw new IOException(MSG_NOT_A_FILE + file); - } - long len = file.length(); - if (len >= Integer.MAX_VALUE) { - len = Integer.MAX_VALUE; - } - - InputStream in = null; - try { - in = new FileInputStream(file); - if (encoding.startsWith("UTF")) { - in = new UnicodeInputStream(in, encoding); - } - FastCharArrayWriter fastCharArrayWriter = new FastCharArrayWriter((int) len); - StreamUtil.copy(in, fastCharArrayWriter, encoding); - return fastCharArrayWriter.toCharArray(); - } finally { - StreamUtil.close(in); - } + public static void writeChars(final File dest, final char[] data) throws IOException { + writeChars(dest, data, encoding()); } - - public static void writeChars(File dest, char[] data) throws IOException { - outChars(dest, data, JoddCore.encoding, false); + /** + * @see #writeChars(File, char[]) + */ + public static void writeChars(final String dest, final char[] data) throws IOException { + writeChars(file(dest), data); } - public static void writeChars(String dest, char[] data) throws IOException { - outChars(file(dest), data, JoddCore.encoding, false); + + /** + * @see #writeChars(File, char[], String) + */ + public static void writeChars(final String dest, final char[] data, final String encoding) throws IOException { + writeChars(file(dest), data, encoding); } - public static void writeChars(File dest, char[] data, String encoding) throws IOException { + /** + * Write characters. append = false + * + * @see #outChars(File, char[], String, boolean) + */ + public static void writeChars(final File dest, final char[] data, final String encoding) throws IOException { outChars(dest, data, encoding, false); } - public static void writeChars(String dest, char[] data, String encoding) throws IOException { - outChars(file(dest), data, encoding, false); - } - - protected static void outChars(File dest, char[] data, String encoding, boolean append) throws IOException { + + /** + * Writes characters to {@link File} destination. + * + * @param dest destination {@link File} + * @param data Data as a {@link String} + * @param encoding Encoding as a {@link String} + * @param append {@code true} if appending; {@code false} if {@link File} should be overwritten. + * @throws IOException if something went wrong. + */ + protected static void outChars(final File dest, final char[] data, final String encoding, final boolean append) throws IOException { if (dest.exists()) { - if (!dest.isFile()) { - throw new IOException(MSG_NOT_A_FILE + dest); - } + checkIsFile(dest); } - Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dest, append), encoding)); + final Writer out = new BufferedWriter(StreamUtil.outputStreamWriterOf(new FileOutputStream(dest, append), encoding)); try { out.write(data); } finally { @@ -751,145 +667,169 @@ public class FileUtil { } } - // ---------------------------------------------------------------- read/write string - public static String readUTFString(String fileName) throws IOException { + /** + * @see #readUTFString(File) + */ + public static String readUTFString(final String fileName) throws IOException { return readUTFString(file(fileName)); } /** - * Detects optional BOM and reads UTF string from a file. + * Detects optional BOM and reads UTF {@link String} from a {@link File}. * If BOM is missing, UTF-8 is assumed. - * @see UnicodeInputStream + * + * @param file {@link File} to read. + * @return String in UTF encoding. + * @throws IOException if copy to {@link InputStream} errors. + * @see #unicodeInputStreamOf(File) + * @see StreamUtil#copy(InputStream, String) */ - public static String readUTFString(File file) throws IOException { - if (!file.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + file); - } - if (!file.isFile()) { - throw new IOException(MSG_NOT_A_FILE + file); - } - long len = file.length(); - if (len >= Integer.MAX_VALUE) { - len = Integer.MAX_VALUE; - } - UnicodeInputStream in = null; + public static String readUTFString(final File file) throws IOException { + final UnicodeInputStream in = unicodeInputStreamOf(file); try { - in = new UnicodeInputStream(new FileInputStream(file), null); - FastCharArrayWriter out = new FastCharArrayWriter((int) len); - String encoding = in.getDetectedEncoding(); - if (encoding == null) { - encoding = StringPool.UTF_8; - } - StreamUtil.copy(in, out, encoding); - return out.toString(); + return StreamUtil.copy(in, detectEncoding(in)).toString(); } finally { StreamUtil.close(in); } } /** - * Detects optional BOM and reads UTF string from an input stream. + * Detects optional BOM and reads UTF {@link String} from an {@link InputStream}. * If BOM is missing, UTF-8 is assumed. + * + * @param inputStream {@link InputStream} to read. + * @return String in UTF encoding. + * @throws IOException if copy to {@link InputStream} errors. + * @see #unicodeInputStreamOf(File) + * @see StreamUtil#copy(InputStream, String) */ - public static String readUTFString(InputStream inputStream) throws IOException { + public static String readUTFString(final InputStream inputStream) throws IOException { UnicodeInputStream in = null; try { in = new UnicodeInputStream(inputStream, null); - FastCharArrayWriter out = new FastCharArrayWriter(); - String encoding = in.getDetectedEncoding(); - if (encoding == null) { - encoding = StringPool.UTF_8; - } - StreamUtil.copy(in, out, encoding); - return out.toString(); + return StreamUtil.copy(in, detectEncoding(in)).toString(); } finally { StreamUtil.close(in); } } - - public static String readString(String source) throws IOException { - return readString(file(source), JoddCore.fileUtilParams.encoding); - } - - public static String readString(String source, String encoding) throws IOException { - return readString(file(source), encoding); - } - - public static String readString(File source) throws IOException { - return readString(source, JoddCore.fileUtilParams.encoding); - } - /** - * Reads file content as string encoded in provided encoding. + * Reads {@link File} content as {@link String} encoded in provided encoding. * For UTF encoded files, detects optional BOM characters. + * + * @param file {@link File} to read. + * @param encoding Encoding to use. + * @return String representing {@link File} content. + * @throws IOException if copy to {@link InputStream} errors. + * @see #streamOf(File, String) + * @see StreamUtil#copy(InputStream, String) */ - public static String readString(File file, String encoding) throws IOException { - if (!file.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + file); - } - if (!file.isFile()) { - throw new IOException(MSG_NOT_A_FILE + file); - } - long len = file.length(); - if (len >= Integer.MAX_VALUE) { - len = Integer.MAX_VALUE; - } - InputStream in = null; + public static String readString(final File file, final String encoding) throws IOException { + checkExists(file); + checkIsFile(file); + final InputStream in = streamOf(file, encoding); try { - in = new FileInputStream(file); - if (encoding.startsWith("UTF")) { - in = new UnicodeInputStream(in, encoding); - } - FastCharArrayWriter out = new FastCharArrayWriter((int) len); - StreamUtil.copy(in, out, encoding); - return out.toString(); + return StreamUtil.copy(in, encoding).toString(); } finally { StreamUtil.close(in); } } + /** + * @see #readString(String, String) + */ + public static String readString(final String source) throws IOException { + return readString(source, encoding()); + } + + /** + * @see #readString(File, String) + */ + public static String readString(final String source, final String encoding) throws IOException { + return readString(file(source), encoding); + } + + /** + * @see #readString(File, String) + */ + public static String readString(final File source) throws IOException { + return readString(source, encoding()); + } - public static void writeString(String dest, String data) throws IOException { - outString(file(dest), data, JoddCore.fileUtilParams.encoding, false); + /** + * @see #writeString(File, String, String) + */ + public static void writeString(final String dest, final String data) throws IOException { + writeString(file(dest), data, encoding()); } - public static void writeString(String dest, String data, String encoding) throws IOException { - outString(file(dest), data, encoding, false); + /** + * @see #writeString(File, String, String) + */ + public static void writeString(final String dest, final String data, final String encoding) throws IOException { + writeString(file(dest), data, encoding); } - public static void writeString(File dest, String data) throws IOException { - outString(dest, data, JoddCore.fileUtilParams.encoding, false); + /** + * @see #writeString(File, String, String) + */ + public static void writeString(final File dest, final String data) throws IOException { + writeString(dest, data, encoding()); } - public static void writeString(File dest, String data, String encoding) throws IOException { + /** + * Writes String. append = false + * + * @see #outString(File, String, String, boolean) + */ + public static void writeString(final File dest, final String data, final String encoding) throws IOException { outString(dest, data, encoding, false); } - - public static void appendString(String dest, String data) throws IOException { - outString(file(dest), data, JoddCore.fileUtilParams.encoding, true); + /** + * @see #appendString(File, String) + */ + public static void appendString(final String dest, final String data) throws IOException { + appendString(file(dest), data); } - public static void appendString(String dest, String data, String encoding) throws IOException { - outString(file(dest), data, encoding, true); + /** + * @see #appendString(File, String, String) + */ + public static void appendString(final String dest, final String data, final String encoding) throws IOException { + appendString(file(dest), data, encoding); } - public static void appendString(File dest, String data) throws IOException { - outString(dest, data, JoddCore.fileUtilParams.encoding, true); + /** + * @see #appendString(File, String, String) + */ + public static void appendString(final File dest, final String data) throws IOException { + appendString(dest, data, encoding()); } - public static void appendString(File dest, String data, String encoding) throws IOException { + /** + * Appends String. append = true + * + * @see #outString(File, String, String, boolean) + */ + public static void appendString(final File dest, final String data, final String encoding) throws IOException { outString(dest, data, encoding, true); } - protected static void outString(File dest, String data, String encoding, boolean append) throws IOException { + /** + * Writes data using encoding to {@link File}. + * + * @param dest destination {@link File} + * @param data Data as a {@link String} + * @param encoding Encoding as a {@link String} + * @param append {@code true} if appending; {@code false} if {@link File} should be overwritten. + * @throws IOException if something went wrong. + */ + protected static void outString(final File dest, final String data, final String encoding, final boolean append) throws IOException { if (dest.exists()) { - if (!dest.isFile()) { - throw new IOException(MSG_NOT_A_FILE + dest); - } + checkIsFile(dest); } FileOutputStream out = null; try { @@ -902,145 +842,204 @@ public class FileUtil { // ---------------------------------------------------------------- stream - public static void writeStream(File dest, InputStream in) throws IOException { - FileOutputStream out = null; - try { - out = new FileOutputStream(dest); - StreamUtil.copy(in, out); - } finally { - StreamUtil.close(out); - } + + /** + * @see #writeStream(File, InputStream) + */ + public static void writeStream(final String dest, final InputStream in) throws IOException { + writeStream(file(dest), in); } - public static void writeStream(String dest, InputStream in) throws IOException { - FileOutputStream out = null; + /** + * @see #writeStream(FileOutputStream, InputStream) + */ + public static void writeStream(final File dest, final InputStream in) throws IOException { + writeStream(new FileOutputStream(dest, false), in); + } + + /** + * Write {@link InputStream} in to {@link FileOutputStream}. + * + * @param out {@link FileOutputStream} to write to. + * @param in {@link InputStream} to read. + * @throws IOException if there is an issue reading/writing. + */ + public static void writeStream(final FileOutputStream out, final InputStream in) throws IOException { try { - out = new FileOutputStream(dest); StreamUtil.copy(in, out); } finally { StreamUtil.close(out); } } - // ---------------------------------------------------------------- read/write string lines - - public static String[] readLines(String source) throws IOException { - return readLines(file(source), JoddCore.fileUtilParams.encoding); + /** + * @see #readLines(String, String) + */ + public static String[] readLines(final String source) throws IOException { + return readLines(source, encoding()); } - public static String[] readLines(String source, String encoding) throws IOException { + + /** + * @see #readLines(File, String) + */ + public static String[] readLines(final String source, final String encoding) throws IOException { return readLines(file(source), encoding); } - public static String[] readLines(File source) throws IOException { - return readLines(source, JoddCore.fileUtilParams.encoding); + + /** + * @see #readLines(File, String) + */ + public static String[] readLines(final File source) throws IOException { + return readLines(source, encoding()); } /** - * Reads lines from source files. + * Reads lines from source {@link File} with specified encoding and returns lines as {@link String}s in array. + * + * @param file {@link File} to read. + * @param encoding Endoing to use. + * @return array of Strings which represents lines in the {@link File}. + * @throws IOException if {@link File} does not exist or is not a {@link File} or there is an issue reading + * the {@link File}. */ - public static String[] readLines(File file, String encoding) throws IOException { - if (!file.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + file); - } - if (!file.isFile()) { - throw new IOException(MSG_NOT_A_FILE + file); - } - List list = new ArrayList(); + public static String[] readLines(final File file, final String encoding) throws IOException { + checkExists(file); + checkIsFile(file); + final List list = new ArrayList<>(); - InputStream in = null; + final InputStream in = streamOf(file, encoding); try { - in = new FileInputStream(file); - if (encoding.startsWith("UTF")) { - in = new UnicodeInputStream(in, encoding); - } - BufferedReader br = new BufferedReader(new InputStreamReader(in, encoding)); + final BufferedReader br = new BufferedReader(StreamUtil.inputStreamReadeOf(in, encoding)); String strLine; - while ((strLine = br.readLine()) != null) { + while ((strLine = br.readLine()) != null) { list.add(strLine); } } finally { StreamUtil.close(in); } - return list.toArray(new String[list.size()]); + return list.toArray(new String[0]); } + // ---------------------------------------------------------------- read/write byte array - - // ---------------------------------------------------------------- read/write bytearray - - - public static byte[] readBytes(String file) throws IOException { + /** + * @see #readBytes(File) + */ + public static byte[] readBytes(final String file) throws IOException { return readBytes(file(file)); } - public static byte[] readBytes(File file) throws IOException { - return readBytes(file, -1); + /** + * @see #readBytes(File, int) + */ + public static byte[] readBytes(final File file) throws IOException { + return readBytes(file, NEGATIVE_ONE); } - public static byte[] readBytes(File file, int fixedLength) throws IOException { - if (!file.exists()) { - throw new FileNotFoundException(MSG_NOT_FOUND + file); - } - if (!file.isFile()) { - throw new IOException(MSG_NOT_A_FILE + file); - } - long len = file.length(); - if (len >= Integer.MAX_VALUE) { + + /** + * Read file and returns byte array with contents. + * + * @param file {@link File} to read + * @param count number of bytes to read + * @return byte array from {@link File} contents. + * @throws IOException if not a {@link File} or {@link File} does not exist or file size is + * larger than {@link Integer#MAX_VALUE}. + */ + public static byte[] readBytes(final File file, final int count) throws IOException { + checkExists(file); + checkIsFile(file); + long numToRead = file.length(); + if (numToRead >= Integer.MAX_VALUE) { throw new IOException("File is larger then max array size"); } - if (fixedLength > -1 && fixedLength < len) { - len = fixedLength; + if (count > NEGATIVE_ONE && count < numToRead) { + numToRead = count; } - byte[] bytes = new byte[(int) len]; - RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); + final byte[] bytes = new byte[(int) numToRead]; + final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); randomAccessFile.readFully(bytes); randomAccessFile.close(); return bytes; } - - - public static void writeBytes(String dest, byte[] data) throws IOException { - outBytes(file(dest), data, 0, data.length, false); + /** + * @see #writeBytes(File, byte[]) + */ + public static void writeBytes(final String dest, final byte[] data) throws IOException { + writeBytes(file(dest), data); } - public static void writeBytes(String dest, byte[] data, int off, int len) throws IOException { - outBytes(file(dest), data, off, len, false); + /** + * @see #writeBytes(File, byte[], int, int) + */ + public static void writeBytes(final File dest, final byte[] data) throws IOException { + writeBytes(dest, data, ZERO, data.length); } - public static void writeBytes(File dest, byte[] data) throws IOException { - outBytes(dest, data, 0, data.length, false); + /** + * @see #writeBytes(File, byte[], int, int) + */ + public static void writeBytes(final String dest, final byte[] data, final int off, final int len) throws IOException { + writeBytes(file(dest), data, off, len); } - public static void writeBytes(File dest, byte[] data, int off, int len) throws IOException { + /** + * Write bytes. append = false + * + * @see #outBytes(File, byte[], int, int, boolean) + */ + public static void writeBytes(final File dest, final byte[] data, final int off, final int len) throws IOException { outBytes(dest, data, off, len, false); } - - public static void appendBytes(String dest, byte[] data) throws IOException { - outBytes(file(dest), data, 0, data.length, true); + /** + * @see #appendBytes(File, byte[]) + */ + public static void appendBytes(final String dest, final byte[] data) throws IOException { + appendBytes(file(dest), data); } - public static void appendBytes(String dest, byte[] data, int off, int len) throws IOException { - outBytes(file(dest), data, off, len, true); + /** + * @see #appendBytes(File, byte[], int, int) + */ + public static void appendBytes(final String dest, final byte[] data, final int off, final int len) throws IOException { + appendBytes(file(dest), data, off, len); } - public static void appendBytes(File dest, byte[] data) throws IOException { - outBytes(dest, data, 0, data.length, true); + /** + * @see #appendBytes(File, byte[], int, int) + */ + public static void appendBytes(final File dest, final byte[] data) throws IOException { + appendBytes(dest, data, ZERO, data.length); } - public static void appendBytes(File dest, byte[] data, int off, int len) throws IOException { + /** + * Appends bytes. append = true + * + * @see #outBytes(File, byte[], int, int, boolean) + */ + public static void appendBytes(final File dest, final byte[] data, final int off, final int len) throws IOException { outBytes(dest, data, off, len, true); } - protected static void outBytes(File dest, byte[] data, int off, int len, boolean append) throws IOException { + /** + * Writes data to {@link File} destination. + * + * @param dest destination {@link File} + * @param data Data as a {@link String} + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @param append {@code true} if appending; {@code false} if {@link File} should be overwritten. + * @throws IOException if something went wrong. + */ + protected static void outBytes(final File dest, final byte[] data, final int off, final int len, final boolean append) throws IOException { if (dest.exists()) { - if (!dest.isFile()) { - throw new IOException(MSG_NOT_A_FILE + dest); - } + checkIsFile(dest); } FileOutputStream out = null; try { @@ -1053,23 +1052,23 @@ public class FileUtil { // ---------------------------------------------------------------- equals content - public static boolean compare(String file1, String file2) throws IOException { + public static boolean compare(final String file1, final String file2) throws IOException { return compare(file(file1), file(file2)); } /** - * Compare the contents of two files to determine if they are equal or + * Compare the contents of two {@link File}s to determine if they are equal or * not. *

- * This method checks to see if the two files are different lengths - * or if they point to the same file, before resorting to byte-by-byte + * This method checks to see if the two {@link File}s are different lengths + * or if they point to the same {@link File}, before resorting to byte-by-byte * comparison of the contents. *

* Code origin: Avalon */ - public static boolean compare(File file1, File file2) throws IOException { - boolean file1Exists = file1.exists(); - if (file1Exists != file2.exists()) { + public static boolean compare(final File one, final File two) throws IOException { + final boolean file1Exists = one.exists(); + if (file1Exists != two.exists()) { return false; } @@ -1077,23 +1076,23 @@ public class FileUtil { return true; } - if ((!file1.isFile()) || (!file2.isFile())) { + if ((!one.isFile()) || (!two.isFile())) { throw new IOException("Only files can be compared"); } - if (file1.length() != file2.length()) { + if (one.length() != two.length()) { return false; } - if (equals(file1, file2)) { + if (equals(one, two)) { return true; } InputStream input1 = null; InputStream input2 = null; try { - input1 = new FileInputStream(file1); - input2 = new FileInputStream(file2); + input1 = new FileInputStream(one); + input2 = new FileInputStream(two); return StreamUtil.compare(input1, input2); } finally { StreamUtil.close(input1); @@ -1103,151 +1102,166 @@ public class FileUtil { // ---------------------------------------------------------------- time - public static boolean isNewer(String file, String reference) { + /** + * @see #isOlder(File, File) + */ + public static boolean isOlder(final String file, final String reference) { + return isOlder(file(file), file(reference)); + } + + /** + * @see #isNewer(File, File) + */ + public static boolean isNewer(final String file, final String reference) { return isNewer(file(file), file(reference)); } /** - * Test if specified File is newer than the reference File. + * Uses {@link File#lastModified()} for reference. * - * @param file the File of which the modification date must be compared - * @param reference the File of which the modification date is used - * @return true if the File exists and has been modified more - * recently than the reference File. + * @see #isNewer(File, long) */ - public static boolean isNewer(File file, File reference) { - if (!reference.exists()) { - throw new IllegalArgumentException("Reference file not found: " + reference); - } + public static boolean isNewer(final File file, final File reference) { + checkReferenceExists(reference); return isNewer(file, reference.lastModified()); } - - public static boolean isOlder(String file, String reference) { - return isOlder(file(file), file(reference)); - } - - public static boolean isOlder(File file, File reference) { - if (!reference.exists()) { - throw new IllegalArgumentException("Reference file not found: " + reference); - } + /** + * Uses {@link File#lastModified()} for reference. + * + * @see #isOlder(File, long) + */ + public static boolean isOlder(final File file, final File reference) { + checkReferenceExists(reference); return isOlder(file, reference.lastModified()); } /** - * Tests if the specified File is newer than the specified time reference. + * Tests if the specified {@link File} is newer than the specified time reference. * - * @param file the File of which the modification date must be compared. - * @param timeMillis the time reference measured in milliseconds since the - * epoch (00:00:00 GMT, January 1, 1970) - * @return true if the File exists and has been modified after - * the given time reference. + * @param file the {@link File} of which the modification date must be compared. + * @param timeMillis the time reference measured in milliseconds since the + * epoch (00:00:00 GMT, January 1, 1970) + * @return {@code true} if the {@link File} exists and has been modified after + * the given time reference. */ - public static boolean isNewer(File file, long timeMillis) { - if (!file.exists()) { - return false; - } - return file.lastModified() > timeMillis; + public static boolean isNewer(final File file, final long timeMillis) { + return file.exists() && file.lastModified() > timeMillis; } - public static boolean isNewer(String file, long timeMillis) { + /** + * @see #isNewer(File, long) + */ + public static boolean isNewer(final String file, final long timeMillis) { return isNewer(file(file), timeMillis); } - - public static boolean isOlder(File file, long timeMillis) { - if (!file.exists()) { - return false; - } - return file.lastModified() < timeMillis; + /** + * Tests if the specified {@link File} is older than the specified time reference. + * + * @param file the {@link File} of which the modification date must be compared. + * @param timeMillis the time reference measured in milliseconds since the + * epoch (00:00:00 GMT, January 1, 1970) + * @return {@code true} if the {@link File} exists and has been modified after + * the given time reference. + */ + public static boolean isOlder(final File file, final long timeMillis) { + return file.exists() && file.lastModified() < timeMillis; } - public static boolean isOlder(String file, long timeMillis) { + /** + * @see #isOlder(File, long) + */ + public static boolean isOlder(final String file, final long timeMillis) { return isOlder(file(file), timeMillis); } - // ---------------------------------------------------------------- smart copy - public static void copy(String src, String dest) throws IOException { - copy(file(src), file(dest), JoddCore.fileUtilParams); - } - - public static void copy(String src, String dest, FileUtilParams params) throws IOException { - copy(file(src), file(dest), params); + /** + * @see #copy(File, File) + */ + public static void copy(final String src, final String dest) throws IOException { + copy(file(src), file(dest)); } - public static void copy(File src, File dest) throws IOException { - copy(src, dest, JoddCore.fileUtilParams); - } /** * Smart copy. If source is a directory, copy it to destination. * Otherwise, if destination is directory, copy source file to it. * Otherwise, try to copy source file to destination file. + * + * @param src source {@link File} + * @param dest destination {@link File} + * @throws IOException if there is an error copying. + * @see #copyDir(File, File) + * @see #copyFileToDir(File, File) + * @see #copyFile(File, File) */ - public static void copy(File src, File dest, FileUtilParams params) throws IOException { + public static void copy(final File src, final File dest) throws IOException { if (src.isDirectory()) { - copyDir(src, dest, params); + copyDir(src, dest); return; } if (dest.isDirectory()) { - copyFileToDir(src, dest, params); + copyFileToDir(src, dest); return; } - copyFile(src, dest, params); + copyFile(src, dest); } // ---------------------------------------------------------------- smart move - public static void move(String src, String dest) throws IOException { - move(file(src), file(dest), JoddCore.fileUtilParams); - } - - public static void move(String src, String dest, FileUtilParams params) throws IOException { - move(file(src), file(dest), params); + /** + * @see #move(File, File) + */ + public static void move(final String src, final String dest) throws IOException { + move(file(src), file(dest)); } - public static void move(File src, File dest) throws IOException { - move(src, dest, JoddCore.fileUtilParams); - } /** * Smart move. If source is a directory, move it to destination. - * Otherwise, if destination is directory, move source file to it. - * Otherwise, try to move source file to destination file. + * Otherwise, if destination is directory, move source {@link File} to it. + * Otherwise, try to move source {@link File} to destination {@link File}. + * + * @param src source {@link File} + * @param dest destination {@link File} + * @throws IOException if there is an error moving. + * @see #moveDir(File, File) + * @see #moveFileToDir(File, File) + * @see #moveFile(File, File) */ - public static void move(File src, File dest, FileUtilParams params) throws IOException { + public static void move(final File src, final File dest) throws IOException { if (src.isDirectory()) { moveDir(src, dest); return; } if (dest.isDirectory()) { - moveFileToDir(src, dest, params); + moveFileToDir(src, dest); return; } - moveFile(src, dest, params); + moveFile(src, dest); } // ---------------------------------------------------------------- smart delete - public static void delete(String dest) throws IOException { - delete(file(dest), JoddCore.fileUtilParams); - } - - public static void delete(String dest, FileUtilParams params) throws IOException { - delete(file(dest), params); - } - - public static void delete(File dest) throws IOException { - delete(dest, JoddCore.fileUtilParams); + /** + * @see #delete(File) + */ + public static void delete(final String dest) throws IOException { + delete(file(dest)); } /** * Smart delete of destination file or directory. + * + * @throws IOException if there is an issue deleting the file/directory. + * @see #deleteFile(File) + * @see #deleteDir(File) */ - public static void delete(File dest, FileUtilParams params) throws IOException { + public static void delete(final File dest) throws IOException { if (dest.isDirectory()) { - deleteDir(dest, params); + deleteDir(dest); return; } deleteFile(dest); @@ -1256,13 +1270,13 @@ public class FileUtil { // ---------------------------------------------------------------- misc /** - * Check if one file is an ancestor of second one. + * Check if one {@link File} is an ancestor of second one. * - * @param strict if false then this method returns true if ancestor - * and file are equal - * @return true if ancestor is parent of file; false otherwise + * @param strict if c then this method returns {@code true} if ancestor + * and {@link File} are equal + * @return {@code true} if ancestor is parent of {@link File}; otherwise, {@code false} */ - public static boolean isAncestor(File ancestor, File file, boolean strict) { + public static boolean isAncestor(final File ancestor, final File file, final boolean strict) { File parent = strict ? getParentFile(file) : file; while (true) { if (parent == null) { @@ -1277,12 +1291,15 @@ public class FileUtil { /** * Returns parent for the file. The method correctly - * processes "." and ".." in file names. The name + * processes "." and ".." in {@link File} names. The name * remains relative if was relative before. - * Returns null if the file has no parent. + * Returns {@code null} if the {@link File} has no parent. + * + * @param file {@link File} + * @return {@code null} if the {@link File} has no parent. */ public static File getParentFile(final File file) { - int skipCount = 0; + int skipCount = ZERO; File parentFile = file; while (true) { parentFile = parentFile.getParentFile(); @@ -1296,7 +1313,7 @@ public class FileUtil { skipCount++; continue; } - if (skipCount > 0) { + if (skipCount > ZERO) { skipCount--; continue; } @@ -1304,7 +1321,14 @@ public class FileUtil { } } - public static boolean isFilePathAcceptable(File file, FileFilter fileFilter) { + /** + * Checks if file and its ancestors are acceptable by using {@link FileFilter#accept(File)}. + * + * @param file {@link File} to check. + * @param fileFilter {@link FileFilter} to use. + * @return if file and its ancestors are acceptable + */ + public static boolean isFilePathAcceptable(File file, final FileFilter fileFilter) { do { if (fileFilter != null && !fileFilter.accept(file)) { return false; @@ -1316,42 +1340,57 @@ public class FileUtil { // ---------------------------------------------------------------- temp + /** + * @see #createTempDirectory(String, String) + */ public static File createTempDirectory() throws IOException { - return createTempDirectory(JoddCore.tempFilePrefix, null, null); + return createTempDirectory(tempPrefix(), null); } /** - * Creates temporary directory. + * @see #createTempDirectory(String, String, File) */ - public static File createTempDirectory(String prefix, String suffix) throws IOException { + public static File createTempDirectory(final String prefix, final String suffix) throws IOException { return createTempDirectory(prefix, suffix, null); } /** * Creates temporary directory. + * + * @see #createTempFile(String, String, File) */ - public static File createTempDirectory(String prefix, String suffix, File tempDir) throws IOException { - File file = createTempFile(prefix, suffix, tempDir); + public static File createTempDirectory(final String prefix, final String suffix, final File tempDir) throws IOException { + final File file = createTempFile(prefix, suffix, tempDir); file.delete(); file.mkdir(); return file; } /** - * Simple method that creates temp file. + * @see #createTempFile(String, String, File, boolean) */ public static File createTempFile() throws IOException { - return createTempFile(JoddCore.tempFilePrefix, null, null, true); + return createTempFile(tempPrefix(), null, null, true); } /** - * Creates temporary file. - * If create is set to true file will be - * physically created on the file system. Otherwise, it will be created and then - * deleted - trick that will make temp file exist only if they are used. + * Creates temporary {@link File}. + * + * @param prefix The prefix string to be used in generating the file's + * name; must be at least three characters long + * @param suffix The suffix string to be used in generating the file's + * name; may be {@code null}, in which case the + * suffix {@code ".tmp"} will be used + * @param tempDir The directory in which the file is to be created, or + * {@code null} if the default temporary-file + * directory is to be used + * @param create If {@code create} is set to {@code true} {@link File} will be + * physically created on the file system. Otherwise, it will be created and then + * deleted - trick that will make temp {@link File} exist only if they are used. + * @return File */ - public static File createTempFile(String prefix, String suffix, File tempDir, boolean create) throws IOException { - File file = createTempFile(prefix, suffix, tempDir); + public static File createTempFile(final String prefix, final String suffix, final File tempDir, final boolean create) throws IOException { + final File file = createTempFile(prefix, suffix, tempDir); file.delete(); if (create) { file.createNewFile(); @@ -1360,15 +1399,24 @@ public class FileUtil { } /** - * Creates temporary file. Wraps java method and repeat creation several time - * if something fail. + * Creates temporary {@link File}. Wraps Java method and repeats creation several times + * if something fails. + * + * @param prefix The prefix string to be used in generating the file's + * name; must be at least three characters long + * @param suffix The suffix string to be used in generating the file's + * name; may be {@code null}, in which case the + * suffix {@code ".tmp"} will be used + * @param tempDir The directory in which the file is to be created, or + * {@code null} if the default temporary-file + * directory is to be used */ - public static File createTempFile(String prefix, String suffix, File dir) throws IOException { - int exceptionsCount = 0; + public static File createTempFile(final String prefix, final String suffix, final File tempDir) throws IOException { + int exceptionsCount = ZERO; while (true) { try { - return File.createTempFile(prefix, suffix, dir).getCanonicalFile(); - } catch (IOException ioex) { // fixes java.io.WinNTFileSystem.createFileExclusively access denied + return File.createTempFile(prefix, suffix, tempDir).getCanonicalFile(); + } catch (final IOException ioex) { // fixes java.io.WinNTFileSystem.createFileExclusively access denied if (++exceptionsCount >= 50) { throw ioex; } @@ -1380,104 +1428,258 @@ public class FileUtil { /** * Determines whether the specified file is a symbolic link rather than an actual file. - * Always returns false on Windows. + * + * @deprecated {@link java.nio.file.Files#isSymbolicLink(java.nio.file.Path)} provides this functionality natively as of Java 1.7. */ - public static boolean isSymlink(final File file) throws IOException { - if (SystemUtil.isHostWindows()) { - return false; - } + @Deprecated + public static boolean isSymlink(final File file) { + return Files.isSymbolicLink(file.toPath()); + } - File fileInCanonicalDir; + // ---------------------------------------------------------------- digests - if (file.getParent() == null) { - fileInCanonicalDir = file; - } else { - File canonicalDir = file.getParentFile().getCanonicalFile(); - fileInCanonicalDir = new File(canonicalDir, file.getName()); - } + /** + * Creates MD5 digest of a {@link File}. + * + * @param file {@link File} to create digest of. + * @return MD5 digest of the {@link File}. + */ + public static String md5(final File file) throws IOException { + return DigestEngine.md5().digestString(file); + } - return !fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile()); + /** + * Creates SHA-256 digest of a file. + * + * @param file {@link File} to create digest of. + * @return SHA-256 digest of the {@link File}. + */ + public static String sha256(final File file) throws IOException { + return DigestEngine.sha256().digestString(file); } - // ---------------------------------------------------------------- digests + /** + * Creates SHA-512 digest of a file. + * + * @param file {@link File} to create digest of. + * @return SHA-512 digest of the {@link File}. + */ + public static String sha512(final File file) throws IOException { + return DigestEngine.sha512().digestString(file); + } /** - * Calculates digest for a file using provided algorithm. + * Checks the start of the file for ASCII control characters + * + * @param file {@link File} + * @return true if the the start of the {@link File} is ASCII control characters. */ - public static byte[] digest(final File file, MessageDigest algorithm) throws IOException { - algorithm.reset(); - FileInputStream fis = new FileInputStream(file); - BufferedInputStream bis = new BufferedInputStream(fis); - DigestInputStream dis = new DigestInputStream(bis, algorithm); + public static boolean isBinary(final File file) throws IOException { + final byte[] bytes = readBytes(file, 128); - try { - while (dis.read() != -1) { + for (final byte b : bytes) { + if (b < 32 && b != 9 && b != 10 && b != 13) { + return true; } } - finally { - StreamUtil.close(fis); - } - return algorithm.digest(); + return false; } /** - * Creates MD5 digest of a file. + * @see #unicodeInputStreamOf(InputStream, String) + * @see #checkExists(File) + * @see #checkIsFile(File) */ - public static String md5(final File file) throws IOException { - MessageDigest md5Digest = null; - try { - md5Digest = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException ignore) { + private static UnicodeInputStream unicodeInputStreamOf(final File file) throws IOException { + checkExists(file); + checkIsFile(file); + return unicodeInputStreamOf(new FileInputStream(file), null); + } + + /** + * Returns new {@link UnicodeInputStream} using {@link InputStream} and targetEncoding. + * + * @param input {@link InputStream} + * @param targetEncoding Encoding to use. + * @return new {@link UnicodeInputStream}. + */ + private static UnicodeInputStream unicodeInputStreamOf(final InputStream input, final String targetEncoding) { + return new UnicodeInputStream(input, targetEncoding); + } + + /** + * Returns either new {@link FileInputStream} or new {@link UnicodeInputStream}. + * + * @return either {@link FileInputStream} or {@link UnicodeInputStream}. + * @throws IOException if something went wrong. + * @see #unicodeInputStreamOf(InputStream, String) + */ + private static InputStream streamOf(final File file, final String encoding) throws IOException { + InputStream in = new FileInputStream(file); + if (encoding.startsWith("UTF")) { + in = unicodeInputStreamOf(in, encoding); } + return in; + } - byte[] digest = digest(file, md5Digest); + /** + * Detect encoding on {@link UnicodeInputStream} by using {@link UnicodeInputStream#getDetectedEncoding()}. + * + * @param in {@link UnicodeInputStream} + * @return UTF encoding as a String. If encoding could not be detected, defaults to {@link StringPool#UTF_8}. + * @see UnicodeInputStream#getDetectedEncoding() + */ + private static String detectEncoding(final UnicodeInputStream in) { + String encoding = in.getDetectedEncoding(); + if (encoding == null) { + encoding = StringPool.UTF_8; + } + return encoding; + } - return StringUtil.toHexString(digest); + /** + * Checks if {@link File} exists. Throws IOException if not. + * + * @param file {@link File} + * @throws FileNotFoundException if file does not exist. + */ + private static void checkExists(final File file) throws FileNotFoundException { + if (!file.exists()) { + throw new FileNotFoundException(MSG_NOT_FOUND + file); + } } /** - * Creates SHA-1 digest of a file. + * Checks if {@link File} exists. Throws IllegalArgumentException if not. + * + * @param file {@link File} + * @throws IllegalArgumentException if file does not exist. */ - public static String sha(final File file) throws IOException { - MessageDigest md5Digest = null; + private static void checkReferenceExists(final File file) throws IllegalArgumentException { try { - md5Digest = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException ignore) { + checkExists(file); + } catch (final FileNotFoundException e) { + throw new IllegalArgumentException("Reference file not found: " + file); } + } - byte[] digest = digest(file, md5Digest); + /** + * Checks if {@link File} is a file. Throws IOException if not. + * + * @param file {@link File} + * @throws IOException if {@link File} is not a file. + */ + private static void checkIsFile(final File file) throws IOException { + if (!file.isFile()) { + throw new IOException(MSG_NOT_A_FILE + file); + } + } - return StringUtil.toHexString(digest); + /** + * Checks if {@link File} is a directory. Throws IOException if not. + * + * @param dir Directory + * @throws IOException if {@link File} is not a directory. + */ + private static void checkIsDirectory(final File dir) throws IOException { + if (!dir.isDirectory()) { + throw new IOException(MSG_NOT_A_DIRECTORY + dir); + } } /** - * Creates SHA-256 digest of a file. + * Checks if directory exists. Throws IOException if it does not. + * + * @param dir Directory + * @throws IOException if directory does not exist. + * @see #checkIsDirectory(File) */ - public static String sha256(final File file) throws IOException { - MessageDigest md5Digest = null; - try { - md5Digest = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException ignore) { + private static void checkExistsAndDirectory(final File dir) throws IOException { + if (dir.exists()) { + checkIsDirectory(dir); + } + } + + /** + * Checks if directory can be created. Throws IOException if it cannot. + *

+ * This actually creates directory (and its ancestors) (as per {@link File#mkdirs()} }). + * + * @param dir Directory + * @throws IOException if directory cannot be created. + */ + private static File checkCreateDirectory(final File dir) throws IOException { + if (!dir.mkdirs()) { + throw new IOException(MSG_CANT_CREATE + dir); } + return dir; + } - byte[] digest = digest(file, md5Digest); + /** + * Checks if directory can be deleted. Throws IOException if it cannot. + * This actually deletes directory (as per {@link File#delete()}). + * + * @param dir Directory + * @throws IOException if directory cannot be created. + */ + private static void checkDeleteSuccessful(final File dir) throws IOException { + if (!dir.delete()) { + throw new IOException(MSG_UNABLE_TO_DELETE + dir); + } + } - return StringUtil.toHexString(digest); + /** + * Checks that srcDir exists, that it is a directory and if srcDir and destDir are not equal. + * + * @param srcDir Source directory + * @param destDir Destination directory + * @throws IOException if any of the above conditions are not true. + */ + private static void checkDirCopy(final File srcDir, final File destDir) throws IOException { + checkExists(srcDir); + checkIsDirectory(srcDir); + if (equals(srcDir, destDir)) { + throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are equal"); + } } /** - * Checks the start of the file for ASCII control characters + * Checks that file copy can occur. + * + * @param srcFile Source {@link File} + * @param destFile Destination {@link File} + * @throws IOException if srcFile does not exist or is not a file or + * srcFile and destFile are equal or cannot create ancestor directories. */ - public static boolean isBinary(final File file) throws IOException { - byte[] bytes = readBytes(file, 128); + private static void checkFileCopy(final File srcFile, final File destFile) throws IOException { + checkExists(srcFile); + checkIsFile(srcFile); + if (equals(srcFile, destFile)) { + throw new IOException("Files '" + srcFile + "' and '" + destFile + "' are equal"); + } - for (byte b : bytes) { - if (b < 32) { - return true; - } + final File destParent = destFile.getParentFile(); + if (destParent != null && !destParent.exists()) { + checkCreateDirectory(destParent); } + } - return false; + // ---------------------------------------------------------------- configs + + /** + * Returns default encoding. + * @return default encoding. + */ + private static String encoding() { + return JoddCore.encoding; + } + + /** + * Returns default prefix for temp files. + * @return default prefix for temp files. + */ + private static String tempPrefix() { + return JoddCore.tempFilePrefix; } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtilParams.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtilParams.java deleted file mode 100644 index c0bbfb0ed..000000000 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtilParams.java +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2003-present, Jodd Team (http://jodd.org) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package com.fr.third.jodd.io; - -import com.fr.third.jodd.core.JoddCore; -import com.fr.third.jodd.core.JoddCore; - -/** - * {@link FileUtil File utilities} parameters. - */ -public class FileUtilParams implements Cloneable { - - protected boolean preserveDate = true; // should destination file have the same timestamp as source - protected boolean overwrite = true; // overwrite existing destination - protected boolean createDirs = true; // create missing subdirectories of destination - protected boolean recursive = true; // use recursive directory copying and deleting - protected boolean continueOnError = true; // don't stop on error and continue job as much as possible - protected String encoding = JoddCore.encoding; // default encoding for reading/writing strings - - - public boolean isPreserveDate() { - return preserveDate; - } - public FileUtilParams setPreserveDate(boolean preserveDate) { - this.preserveDate = preserveDate; - return this; - } - - public boolean isOverwrite() { - return overwrite; - } - public FileUtilParams setOverwrite(boolean overwrite) { - this.overwrite = overwrite; - return this; - } - - public boolean isCreateDirs() { - return createDirs; - } - public FileUtilParams setCreateDirs(boolean createDirs) { - this.createDirs = createDirs; - return this; - } - - public boolean isRecursive() { - return recursive; - } - public FileUtilParams setRecursive(boolean recursive) { - this.recursive = recursive; - return this; - } - - public boolean isContinueOnError() { - return continueOnError; - } - public FileUtilParams setContinueOnError(boolean continueOnError) { - this.continueOnError = continueOnError; - return this; - } - - - public String getEncoding() { - return encoding; - } - public FileUtilParams setEncoding(String encoding) { - this.encoding = encoding; - return this; - } - - // ------------------------------------------------------------ clone - - @Override - public FileUtilParams clone() throws CloneNotSupportedException { - return (FileUtilParams) super.clone(); - } - -} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/NetUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/NetUtil.java index 626cb274d..200036324 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/NetUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/NetUtil.java @@ -25,19 +25,22 @@ package com.fr.third.jodd.io; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.util.StringUtil; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.HttpURLConnection; import java.net.Inet4Address; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; import java.nio.channels.Channels; +import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; +import java.nio.file.StandardOpenOption; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Network utilities. @@ -49,11 +52,10 @@ public class NetUtil { public static final String DEFAULT_MASK = "255.255.255.0"; public static final int INT_VALUE_127_0_0_1 = 0x7f000001; - /** * Resolves IP address from a hostname. */ - public static String resolveIpAddress(String hostname) { + public static String resolveIpAddress(final String hostname) { try { InetAddress netAddress; @@ -71,7 +73,7 @@ public class NetUtil { /** * Returns IP address as integer. */ - public static int getIpAsInt(String ipAddress) { + public static int getIpAsInt(final String ipAddress) { int ipIntValue = 0; String[] tokens = StringUtil.splitc(ipAddress, '.'); for (String token : tokens) { @@ -84,13 +86,13 @@ public class NetUtil { } public static int getMaskAsInt(String mask) { - if (!validateHostIp(mask)) { + if (!validateIPv4(mask)) { mask = DEFAULT_MASK; } return getIpAsInt(mask); } - public static boolean isSocketAccessAllowed(int localIp, int socketIp, int mask) { + public static boolean isSocketAccessAllowed(final int localIp, final int socketIp, final int mask) { boolean _retVal = false; if (socketIp == INT_VALUE_127_0_0_1 || (localIp & mask) == (socketIp & mask)) { @@ -99,44 +101,24 @@ public class NetUtil { return _retVal; } + private static final Pattern ip4RegExp = Pattern.compile("^((?:1?[1-9]?\\d|2(?:[0-4]\\d|5[0-5]))\\.){4}$"); + /** - * Validates IP address given as a string. + * Checks given string against IP address v4 format. + * + * @param input an ip address - may be null + * @return true if param has a valid ip v4 format false otherwise + * @see ip address v4 */ - public static boolean validateHostIp(String host) { - boolean retVal = false; - if (host == null) { - return retVal; - } - - int hitDots = 0; - char[] data = host.toCharArray(); - for (int i = 0; i < data.length; i++) { - char c = data[i]; - int b = 0; - do { - if (c < '0' || c > '9') { - return false; - } - b = (b * 10 + c) - 48; - if (++i >= data.length) { - break; - } - c = data[i]; - } while (c != '.'); - - if (b > 255) { - return false; - } - hitDots++; - } - - return hitDots == 4; + public static boolean validateIPv4(final String input) { + Matcher m = ip4RegExp.matcher(input + '.'); + return m.matches(); } /** * Resolves host name from IP address bytes. */ - public static String resolveHostName(byte[] ip) { + public static String resolveHostName(final byte[] ip) { try { InetAddress address = InetAddress.getByAddress(ip); return address.getHostName(); @@ -150,34 +132,63 @@ public class NetUtil { /** * Downloads resource as byte array. */ - public static byte[] downloadBytes(String url) throws IOException { - InputStream inputStream = new URL(url).openStream(); - return StreamUtil.readBytes(inputStream); + public static byte[] downloadBytes(final String url) throws IOException { + try (InputStream inputStream = new URL(url).openStream()) { + return StreamUtil.readBytes(inputStream); + } } /** * Downloads resource as String. */ - public static String downloadString(String url, String encoding) throws IOException { - InputStream inputStream = new URL(url).openStream(); - return new String(StreamUtil.readChars(inputStream, encoding)); + public static String downloadString(final String url, final String encoding) throws IOException { + try (InputStream inputStream = new URL(url).openStream()) { + return new String(StreamUtil.readChars(inputStream, encoding)); + } } /** * Downloads resource as String. */ - public static String downloadString(String url) throws IOException { - InputStream inputStream = new URL(url).openStream(); - return new String(StreamUtil.readChars(inputStream)); + public static String downloadString(final String url) throws IOException { + try (InputStream inputStream = new URL(url).openStream()) { + return new String(StreamUtil.readChars(inputStream)); + } } /** * Downloads resource to a file, potentially very efficiently. */ - public static void downloadFile(String url, File file) throws IOException { - InputStream inputStream = new URL(url).openStream(); - ReadableByteChannel rbc = Channels.newChannel(inputStream); - FileOutputStream fos = new FileOutputStream(file); - fos.getChannel().transferFrom(rbc, 0, 1 << 24); + public static void downloadFile(final String url, final File file) throws IOException { + try ( + InputStream inputStream = new URL(url).openStream(); + ReadableByteChannel rbc = Channels.newChannel(inputStream); + FileChannel fileChannel = FileChannel.open( + file.toPath(), + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.WRITE) + ) { + fileChannel.transferFrom(rbc, 0, Long.MAX_VALUE); + } + } + + /** + * Get remote file size. Returns -1 if the content length is unknown + * + * @param url remote file url + * @return file size + * @throws IOException JDK-IOException + */ + public static long getRemoteFileSize(String url) throws IOException { + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) new URL(url).openConnection(); + return connection.getContentLengthLong(); + } finally { + if (connection != null) { + connection.disconnect(); + } + } } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/PathUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/PathUtil.java index 793695290..c9eed8f3a 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/PathUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/PathUtil.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.io; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.util.StringUtil; import java.io.BufferedReader; @@ -46,14 +45,14 @@ public class PathUtil { * a separator it gets resolved as a full path, ignoring the base. * This method acts different. */ - public static Path resolve(Path base, String child) { + public static Path resolve(final Path base, String child) { if (StringUtil.startsWithChar(child, File.separatorChar)) { child = child.substring(1); } return base.resolve(child); } - public static Path resolve(Path path, String... childs) { + public static Path resolve(Path path, final String... childs) { for (String child : childs) { path = resolve(path, child); } @@ -63,29 +62,27 @@ public class PathUtil { /** * Reads path content. */ - public static String readString(Path path) throws IOException { - BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8); - - StringWriter writer = new StringWriter(); - - StreamUtil.copy(reader, writer); - - return writer.toString(); + public static String readString(final Path path) throws IOException { + try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + StringWriter writer = new StringWriter(); // flush & close not needed for StringWriter-instance + StreamUtil.copy(reader, writer); + return writer.toString(); + } } /** * Deletes a directory recursively. */ - public static void deleteFileTree(Path directory) throws IOException { + public static void deleteFileTree(final Path directory) throws IOException { Files.walkFileTree(directory, new SimpleFileVisitor() { @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/StreamGobbler.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/StreamGobbler.java index 6f8bf9370..feca3f53e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/StreamGobbler.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/StreamGobbler.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.io; -import com.fr.third.jodd.util.StringPool; import com.fr.third.jodd.util.StringPool; import java.io.BufferedReader; @@ -50,15 +49,15 @@ public class StreamGobbler extends Thread { protected final Object lock = new Object(); protected boolean end = false; - public StreamGobbler(InputStream is) { + public StreamGobbler(final InputStream is) { this(is, null, null); } - public StreamGobbler(InputStream is, OutputStream output) { + public StreamGobbler(final InputStream is, final OutputStream output) { this(is, output, null); } - public StreamGobbler(InputStream is, OutputStream output, String prefix) { + public StreamGobbler(final InputStream is, final OutputStream output, final String prefix) { this.is = is; this.prefix = prefix; this.out = output; @@ -119,6 +118,7 @@ public class StreamGobbler extends Thread { } } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/StreamUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/StreamUtil.java index e9f3704ec..1c6054c3e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/StreamUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/StreamUtil.java @@ -25,11 +25,9 @@ package com.fr.third.jodd.io; -import static com.fr.third.jodd.core.JoddCore.ioBufferSize; - -import com.fr.third.jodd.core.JoddCore; import com.fr.third.jodd.core.JoddCore; +import javax.activation.DataSource; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.Closeable; @@ -40,6 +38,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; +import java.io.UnsupportedEncodingException; import java.io.Writer; /** @@ -47,17 +46,21 @@ import java.io.Writer; */ public class StreamUtil { + private static final int ZERO = 0; + private static final int NEGATIVE_ONE = -1; + private static final int ALL = -1; + // ---------------------------------------------------------------- silent close /** - * Closes silently the closable object. If it is FLushable, it + * Closes silently the closable object. If it is {@link Flushable}, it * will be flushed first. No exception will be thrown if an I/O error occurs. */ - public static void close(Closeable closeable) { + public static void close(final Closeable closeable) { if (closeable != null) { if (closeable instanceof Flushable) { try { - ((Flushable)closeable).flush(); + ((Flushable) closeable).flush(); } catch (IOException ignored) { } } @@ -72,288 +75,582 @@ public class StreamUtil { // ---------------------------------------------------------------- copy /** - * Copies input stream to output stream using buffer. Streams don't have - * to be wrapped to buffered, since copying is already optimized. + * Copies bytes from {@link Reader} to {@link Writer} using buffer. + * {@link Reader} and {@link Writer} don't have to be wrapped to buffered, since copying is already optimized. + * + * @param input {@link Reader} to read. + * @param output {@link Writer} to write to. + * @return The total number of characters read. + * @throws IOException if there is an error reading or writing. */ - public static int copy(InputStream input, OutputStream output) throws IOException { - byte[] buffer = new byte[JoddCore.ioBufferSize]; - int count = 0; + public static int copy(final Reader input, final Writer output) throws IOException { + int numToRead = bufferSize(); + char[] buffer = new char[numToRead]; + + int totalRead = ZERO; int read; - while (true) { - read = input.read(buffer, 0, JoddCore.ioBufferSize); - if (read == -1) { + + while ((read = input.read(buffer, ZERO, numToRead)) >= ZERO) { + output.write(buffer, ZERO, read); + totalRead = totalRead + read; + } + + output.flush(); + return totalRead; + } + + /** + * Copies bytes from {@link InputStream} to {@link OutputStream} using buffer. + * {@link InputStream} and {@link OutputStream} don't have to be wrapped to buffered, + * since copying is already optimized. + * + * @param input {@link InputStream} to read. + * @param output {@link OutputStream} to write to. + * @return The total number of bytes read. + * @throws IOException if there is an error reading or writing. + */ + public static int copy(final InputStream input, final OutputStream output) throws IOException { + int numToRead = bufferSize(); + byte[] buffer = new byte[numToRead]; + + int totalRead = ZERO; + int read; + + while ((read = input.read(buffer, ZERO, numToRead)) >= ZERO) { + output.write(buffer, ZERO, read); + totalRead = totalRead + read; + } + + output.flush(); + return totalRead; + } + + /** + * Copies specified number of characters from {@link Reader} to {@link Writer} using buffer. + * {@link Reader} and {@link Writer} don't have to be wrapped to buffered, since copying is already optimized. + * + * @param input {@link Reader} to read. + * @param output {@link Writer} to write to. + * @param count The number of characters to read. + * @return The total number of characters read. + * @throws IOException if there is an error reading or writing. + */ + public static int copy(final Reader input, final Writer output, final int count) throws IOException { + if (count == ALL) { + return copy(input, output); + } + + int numToRead = count; + char[] buffer = new char[numToRead]; + + int totalRead = ZERO; + int read; + + while (numToRead > ZERO) { + read = input.read(buffer, ZERO, bufferSize(numToRead)); + if (read == NEGATIVE_ONE) { break; } - output.write(buffer, 0, read); - count += read; + output.write(buffer, ZERO, read); + + numToRead = numToRead - read; + totalRead = totalRead + read; } - return count; + + output.flush(); + return totalRead; } + /** - * Copies specified number of bytes from input stream to output stream using buffer. + * Copies specified number of bytes from {@link InputStream} to {@link OutputStream} using buffer. + * {@link InputStream} and {@link OutputStream} don't have to be wrapped to buffered, since copying is already optimized. + * + * @param input {@link InputStream} to read. + * @param output {@link OutputStream} to write to. + * @param count The number of bytes to read. + * @return The total number of bytes read. + * @throws IOException if there is an error reading or writing. */ - public static int copy(InputStream input, OutputStream output, int byteCount) throws IOException { - int bufferSize = (byteCount > JoddCore.ioBufferSize) ? JoddCore.ioBufferSize : byteCount; + public static int copy(final InputStream input, final OutputStream output, final int count) throws IOException { + if (count == ALL) { + return copy(input, output); + } + + int numToRead = count; + byte[] buffer = new byte[numToRead]; - byte[] buffer = new byte[bufferSize]; - int count = 0; + int totalRead = ZERO; int read; - while (byteCount > 0) { - if (byteCount < bufferSize) { - read = input.read(buffer, 0, byteCount); - } else { - read = input.read(buffer, 0, bufferSize); - } - if (read == -1) { + + while (numToRead > ZERO) { + read = input.read(buffer, ZERO, bufferSize(numToRead)); + if (read == NEGATIVE_ONE) { break; } - byteCount -= read; - count += read; - output.write(buffer, 0, read); + output.write(buffer, ZERO, read); + + numToRead = numToRead - read; + totalRead = totalRead + read; } - return count; + + output.flush(); + return totalRead; } + // ---------------------------------------------------------------- read bytes + /** + * Reads all available bytes from {@link InputStream} as a byte array. + * Uses {@link InputStream#available()} to determine the size of input stream. + * This is the fastest method for reading {@link InputStream} to byte array, but + * depends on {@link InputStream} implementation of {@link InputStream#available()}. + * + * @param input {@link InputStream} to read. + * @return byte[] + * @throws IOException if total read is less than {@link InputStream#available()}; + */ + public static byte[] readAvailableBytes(final InputStream input) throws IOException { + int numToRead = input.available(); + byte[] buffer = new byte[numToRead]; + int totalRead = ZERO; + int read; + + while ((totalRead < numToRead) && (read = input.read(buffer, totalRead, numToRead - totalRead)) >= ZERO) { + totalRead = totalRead + read; + } + + if (totalRead < numToRead) { + throw new IOException("Failed to completely read InputStream"); + } + + return buffer; + } + + // ---------------------------------------------------------------- copy to OutputStream /** - * Copies input stream to writer using buffer. + * @see #copy(Reader, OutputStream, String) */ - public static void copy(InputStream input, Writer output) throws IOException { - copy(input, output, JoddCore.encoding); + public static T copy(final Reader input, final T output) throws IOException { + return copy(input, output, encoding()); } + /** - * Copies specified number of bytes from input stream to writer using buffer. + * @see #copy(Reader, OutputStream, String, int) */ - public static void copy(InputStream input, Writer output, int byteCount) throws IOException { - copy(input, output, JoddCore.encoding, byteCount); + public static T copy(final Reader input, final T output, final int count) throws IOException { + return copy(input, output, encoding(), count); } + /** - * Copies input stream to writer using buffer and specified encoding. + * @see #copy(Reader, OutputStream, String, int) */ - public static void copy(InputStream input, Writer output, String encoding) throws IOException { - copy(new InputStreamReader(input, encoding), output); + public static T copy(final Reader input, final T output, final String encoding) throws IOException { + return copy(input, output, encoding, ALL); } + /** - * Copies specified number of bytes from input stream to writer using buffer and specified encoding. + * Copies {@link Reader} to {@link OutputStream} using buffer and specified encoding. + * + * @see #copy(Reader, Writer, int) */ - public static void copy(InputStream input, Writer output, String encoding, int byteCount) throws IOException { - copy(new InputStreamReader(input, encoding), output, byteCount); + public static T copy(final Reader input, final T output, final String encoding, final int count) throws IOException { + try (Writer out = outputStreamWriterOf(output, encoding)) { + copy(input, out, count); + return output; + } } - /** - * Copies reader to writer using buffer. - * Streams don't have to be wrapped to buffered, since copying is already optimized. + /** + * Copies data from {@link DataSource} to a new {@link FastByteArrayOutputStream} and returns this. + * + * @param input {@link DataSource} to copy from. + * @return new {@link FastByteArrayOutputStream} with data from input. + * @see #copyToOutputStream(InputStream) */ - public static int copy(Reader input, Writer output) throws IOException { - char[] buffer = new char[JoddCore.ioBufferSize]; - int count = 0; - int read; - while ((read = input.read(buffer, 0, JoddCore.ioBufferSize)) >= 0) { - output.write(buffer, 0, read); - count += read; + public static FastByteArrayOutputStream copyToOutputStream(final DataSource input) throws IOException { + return copyToOutputStream(input.getInputStream()); + } + + /** + * @see #copyToOutputStream(InputStream, int) + */ + public static FastByteArrayOutputStream copyToOutputStream(final InputStream input) throws IOException { + return copyToOutputStream(input, ALL); + } + + /** + * Copies {@link InputStream} to a new {@link FastByteArrayOutputStream} using buffer and specified encoding. + * + * @see #copy(InputStream, OutputStream, int) + */ + public static FastByteArrayOutputStream copyToOutputStream(final InputStream input, final int count) throws IOException { + try (FastByteArrayOutputStream output = createFastByteArrayOutputStream()) { + copy(input, output, count); + return output; } - output.flush(); - return count; } + /** - * Copies specified number of characters from reader to writer using buffer. + * @see #copyToOutputStream(Reader, String) */ - public static int copy(Reader input, Writer output, int charCount) throws IOException { - int bufferSize = (charCount > JoddCore.ioBufferSize) ? JoddCore.ioBufferSize : charCount; + public static FastByteArrayOutputStream copyToOutputStream(final Reader input) throws IOException { + return copyToOutputStream(input, encoding()); + } - char[] buffer = new char[bufferSize]; - int count = 0; - int read; - while (charCount > 0) { - if (charCount < bufferSize) { - read = input.read(buffer, 0, charCount); - } else { - read = input.read(buffer, 0, bufferSize); - } - if (read == -1) { - break; - } - charCount -= read; - count += read; - output.write(buffer, 0, read); + /** + * @see #copyToOutputStream(Reader, String, int) + */ + public static FastByteArrayOutputStream copyToOutputStream(final Reader input, final String encoding) throws IOException { + return copyToOutputStream(input, encoding, ALL); + } + + /** + * @see #copyToOutputStream(Reader, String, int) + */ + public static FastByteArrayOutputStream copyToOutputStream(final Reader input, final int count) throws IOException { + return copyToOutputStream(input, encoding(), count); + } + + /** + * Copies {@link Reader} to a new {@link FastByteArrayOutputStream} using buffer and specified encoding. + * + * @see #copy(Reader, OutputStream, String, int) + */ + public static FastByteArrayOutputStream copyToOutputStream(final Reader input, final String encoding, final int count) throws IOException { + try (FastByteArrayOutputStream output = createFastByteArrayOutputStream()) { + copy(input, output, encoding, count); + return output; } - return count; } + // ---------------------------------------------------------------- copy to Writer + /** + * @see #copy(InputStream, Writer, String) + */ + public static T copy(final InputStream input, final T output) throws IOException { + return copy(input, output, encoding()); + } /** - * Copies reader to output stream using buffer. + * @see #copy(InputStream, Writer, String, int) */ - public static void copy(Reader input, OutputStream output) throws IOException { - copy(input, output, JoddCore.encoding); + public static T copy(final InputStream input, final T output, final int count) throws IOException { + return copy(input, output, encoding(), count); } + /** - * Copies specified number of characters from reader to output stream using buffer. + * @see #copy(InputStream, Writer, String, int) */ - public static void copy(Reader input, OutputStream output, int charCount) throws IOException { - copy(input, output, JoddCore.encoding, charCount); + public static T copy(final InputStream input, final T output, final String encoding) throws IOException { + return copy(input, output, encoding, ALL); } + /** - * Copies reader to output stream using buffer and specified encoding. + * Copies {@link InputStream} to {@link Writer} using buffer and specified encoding. + * + * @see #copy(Reader, Writer, int) */ - public static void copy(Reader input, OutputStream output, String encoding) throws IOException { - Writer out = new OutputStreamWriter(output, encoding); - copy(input, out); - out.flush(); + public static T copy(final InputStream input, final T output, final String encoding, final int count) throws IOException { + copy(inputStreamReadeOf(input, encoding), output, count); + return output; } + /** - * Copies specified number of characters from reader to output stream using buffer and specified encoding. + * @see #copy(InputStream, String) */ - public static void copy(Reader input, OutputStream output, String encoding, int charCount) throws IOException { - Writer out = new OutputStreamWriter(output, encoding); - copy(input, out, charCount); - out.flush(); + public static FastCharArrayWriter copy(final InputStream input) throws IOException { + return copy(input, encoding()); } + /** + * @see #copy(InputStream, String, int) + */ + public static FastCharArrayWriter copy(final InputStream input, final int count) throws IOException { + return copy(input, encoding(), count); + } - // ---------------------------------------------------------------- read bytes + /** + * @see #copy(InputStream, String, int) + */ + public static FastCharArrayWriter copy(final InputStream input, final String encoding) throws IOException { + return copy(input, encoding, ALL); + } /** - * Reads all available bytes from InputStream as a byte array. - * Uses in.available() to determine the size of input stream. - * This is the fastest method for reading input stream to byte array, but - * depends on stream implementation of available(). - * Buffered internally. + * Copies {@link InputStream} to a new {@link FastCharArrayWriter} using buffer and specified encoding. + * + * @see #copy(InputStream, Writer, String, int) */ - public static byte[] readAvailableBytes(InputStream in) throws IOException { - int l = in.available(); - byte[] byteArray = new byte[l]; - int i = 0, j; - while ((i < l) && (j = in.read(byteArray, i, l - i)) >= 0) { - i +=j; + public static FastCharArrayWriter copy(final InputStream input, final String encoding, final int count) throws IOException { + try (FastCharArrayWriter output = createFastCharArrayWriter()) { + copy(input, output, encoding, count); + return output; } - if (i < l) { - throw new IOException("Failed to completely read input stream"); + } + + /** + * @see #copy(Reader, int) + */ + public static FastCharArrayWriter copy(final Reader input) throws IOException { + return copy(input, ALL); + } + + /** + * Copies {@link Reader} to a new {@link FastCharArrayWriter} using buffer and specified encoding. + * + * @see #copy(Reader, Writer, int) + */ + public static FastCharArrayWriter copy(final Reader input, final int count) throws IOException { + try (FastCharArrayWriter output = createFastCharArrayWriter()) { + copy(input, output, count); + return output; } - return byteArray; } - public static byte[] readBytes(InputStream input) throws IOException { - FastByteArrayOutputStream output = new FastByteArrayOutputStream(); - copy(input, output); - return output.toByteArray(); + /** + * Copies data from {@link DataSource} to a new {@link FastCharArrayWriter} and returns this. + * + * @param input {@link DataSource} to copy from. + * @return new {@link FastCharArrayWriter} with data from input. + * @see #copy(InputStream) + */ + public static FastCharArrayWriter copy(final DataSource input) throws IOException { + return copy(input.getInputStream()); + } + + // ---------------------------------------------------------------- read bytes + + /** + * @see #readBytes(InputStream, int) + */ + public static byte[] readBytes(final InputStream input) throws IOException { + return readBytes(input, ALL); } - public static byte[] readBytes(InputStream input, int byteCount) throws IOException { - FastByteArrayOutputStream output = new FastByteArrayOutputStream(); - copy(input, output, byteCount); - return output.toByteArray(); + + /** + * @see #copyToOutputStream(InputStream, int) + */ + public static byte[] readBytes(final InputStream input, final int count) throws IOException { + return copyToOutputStream(input, count).toByteArray(); } - public static byte[] readBytes(Reader input) throws IOException { - FastByteArrayOutputStream output = new FastByteArrayOutputStream(); - copy(input, output); - return output.toByteArray(); + /** + * @see #readBytes(Reader, String) + */ + public static byte[] readBytes(final Reader input) throws IOException { + return readBytes(input, encoding()); } - public static byte[] readBytes(Reader input, int byteCount) throws IOException { - FastByteArrayOutputStream output = new FastByteArrayOutputStream(); - copy(input, output, byteCount); - return output.toByteArray(); + + /** + * @see #readBytes(Reader, String, int) + */ + public static byte[] readBytes(final Reader input, final int count) throws IOException { + return readBytes(input, encoding(), count); } - public static byte[] readBytes(Reader input, String encoding) throws IOException { - FastByteArrayOutputStream output = new FastByteArrayOutputStream(); - copy(input, output, encoding); - return output.toByteArray(); + + /** + * @see #readBytes(Reader, String, int) + */ + public static byte[] readBytes(final Reader input, final String encoding) throws IOException { + return readBytes(input, encoding, ALL); } - public static byte[] readBytes(Reader input, String encoding, int byteCount) throws IOException { - FastByteArrayOutputStream output = new FastByteArrayOutputStream(); - copy(input, output, encoding, byteCount); - return output.toByteArray(); + + /** + * @see #copyToOutputStream(Reader, String, int) + */ + public static byte[] readBytes(final Reader input, final String encoding, final int count) throws IOException { + return copyToOutputStream(input, encoding, count).toByteArray(); } // ---------------------------------------------------------------- read chars - public static char[] readChars(InputStream input) throws IOException { - FastCharArrayWriter output = new FastCharArrayWriter(); - copy(input, output); - return output.toCharArray(); + /** + * @see #readChars(Reader, int) + */ + public static char[] readChars(final Reader input) throws IOException { + return readChars(input, ALL); } - public static char[] readChars(InputStream input, int charCount) throws IOException { - FastCharArrayWriter output = new FastCharArrayWriter(); - copy(input, output, charCount); - return output.toCharArray(); + + /** + * @see #copy(Reader, int) + */ + public static char[] readChars(final Reader input, final int count) throws IOException { + return copy(input, count).toCharArray(); } - public static char[] readChars(InputStream input, String encoding) throws IOException { - FastCharArrayWriter output = new FastCharArrayWriter(); - copy(input, output, encoding); - return output.toCharArray(); - } - public static char[] readChars(InputStream input, String encoding, int charCount) throws IOException { - FastCharArrayWriter output = new FastCharArrayWriter(); - copy(input, output, encoding, charCount); - return output.toCharArray(); - } + /** + * @see #readChars(InputStream, int) + */ + public static char[] readChars(final InputStream input) throws IOException { + return readChars(input, ALL); + } - public static char[] readChars(Reader input) throws IOException { - FastCharArrayWriter output = new FastCharArrayWriter(); - copy(input, output); - return output.toCharArray(); + /** + * @see #readChars(InputStream, String, int) + */ + public static char[] readChars(final InputStream input, final String encoding) throws IOException { + return readChars(input, encoding, ALL); } - public static char[] readChars(Reader input, int charCount) throws IOException { - FastCharArrayWriter output = new FastCharArrayWriter(); - copy(input, output, charCount); - return output.toCharArray(); + + /** + * @see #readChars(InputStream, String, int) + */ + public static char[] readChars(final InputStream input, final int count) throws IOException { + return readChars(input, encoding(), count); } + /** + * @see #copy(InputStream, String, int) + */ + public static char[] readChars(final InputStream input, final String encoding, final int count) throws IOException { + return copy(input, encoding, count).toCharArray(); + } // ---------------------------------------------------------------- compare content /** - * Compares the content of two byte streams. + * Compares the content of two byte streams ({@link InputStream}s). * - * @return true if the content of the first stream is equal - * to the content of the second stream. + * @return {@code true} if the content of the first {@link InputStream} is equal + * to the content of the second {@link InputStream}. */ public static boolean compare(InputStream input1, InputStream input2) throws IOException { - if (!(input1 instanceof BufferedInputStream)) { - input1 = new BufferedInputStream(input1); - } - if (!(input2 instanceof BufferedInputStream)) { - input2 = new BufferedInputStream(input2); - } - int ch = input1.read(); - while (ch != -1) { - int ch2 = input2.read(); - if (ch != ch2) { - return false; - } - ch = input1.read(); - } - int ch2 = input2.read(); - return (ch2 == -1); - } - /** - * Compares the content of two character streams. + if (!(input1 instanceof BufferedInputStream)) { + input1 = new BufferedInputStream(input1); + } + if (!(input2 instanceof BufferedInputStream)) { + input2 = new BufferedInputStream(input2); + } + int ch = input1.read(); + while (ch != NEGATIVE_ONE) { + int ch2 = input2.read(); + if (ch != ch2) { + return false; + } + ch = input1.read(); + } + int ch2 = input2.read(); + return (ch2 == NEGATIVE_ONE); + } + + /** + * Compares the content of two character streams ({@link Reader}s). * - * @return true if the content of the first stream is equal - * to the content of the second stream. + * @return {@code true} if the content of the first {@link Reader} is equal + * to the content of the second {@link Reader}. */ public static boolean compare(Reader input1, Reader input2) throws IOException { - if (!(input1 instanceof BufferedReader)) { - input1 = new BufferedReader(input1); - } - if (!(input2 instanceof BufferedReader)) { - input2 = new BufferedReader(input2); - } - - int ch = input1.read(); - while (ch != -1) { - int ch2 = input2.read(); - if (ch != ch2) { - return false; - } - ch = input1.read(); - } - int ch2 = input2.read(); - return (ch2 == -1); - } + if (!(input1 instanceof BufferedReader)) { + input1 = new BufferedReader(input1); + } + if (!(input2 instanceof BufferedReader)) { + input2 = new BufferedReader(input2); + } + + int ch = input1.read(); + while (ch != NEGATIVE_ONE) { + int ch2 = input2.read(); + if (ch != ch2) { + return false; + } + ch = input1.read(); + } + int ch2 = input2.read(); + return (ch2 == NEGATIVE_ONE); + } + + // ---------------------------------------------------------------- defaults + + /** + * Returns default IO buffer size. + * + * @return default IO buffer size. + */ + private static int bufferSize() { + return JoddCore.ioBufferSize; + } + + /** + * Returns either count or default IO buffer size (whichever is smaller). + * + * @param count Number of characters or bytes to retrieve. + * @return buffer size (either count or default IO buffer size, whichever is smaller). + */ + private static int bufferSize(final int count) { + final int ioBufferSize = JoddCore.ioBufferSize; + if (count < ioBufferSize) { + return count; + } else { + return ioBufferSize; + } + } + + /** + * Returns default encoding. + * @return default encoding. + */ + private static String encoding() { + return JoddCore.encoding; + } + + // ---------------------------------------------------------------- wrappers + + /** + * Returns new {@link FastCharArrayWriter} using default IO buffer size. + * + * @return new {@link FastCharArrayWriter} using default IO buffer size. + */ + private static FastCharArrayWriter createFastCharArrayWriter() { + return new FastCharArrayWriter(bufferSize()); + } + + /** + * Returns new {@link FastByteArrayOutputStream} using default IO buffer size. + * + * @return new {@link FastByteArrayOutputStream} using default IO buffer size. + */ + private static FastByteArrayOutputStream createFastByteArrayOutputStream() { + return new FastByteArrayOutputStream(bufferSize()); + } + + /** + * @see #inputStreamReadeOf(InputStream, String) + */ + public static InputStreamReader inputStreamReadeOf(final InputStream input) throws UnsupportedEncodingException { + return inputStreamReadeOf(input, encoding()); + } + /** + * Returns new {@link InputStreamReader} using specified {@link InputStream} and encoding. + * + * @param input {@link InputStream} + * @param encoding Encoding as {@link String} to use for {@link InputStreamReader}. + * @return new {@link InputStreamReader} + * @throws UnsupportedEncodingException if encoding is not valid. + */ + public static InputStreamReader inputStreamReadeOf(final InputStream input, final String encoding) throws UnsupportedEncodingException { + return new InputStreamReader(input, encoding); + } + + /** + * @see #outputStreamWriterOf(OutputStream, String) + */ + public static OutputStreamWriter outputStreamWriterOf(final OutputStream output) throws UnsupportedEncodingException { + return outputStreamWriterOf(output, encoding()); + } + + /** + * Returns new {@link OutputStreamWriter} using specified {@link OutputStream} and encoding. + * + * @param output {@link OutputStream} + * @param encoding Encoding as {@link String} to use for {@link OutputStreamWriter}. + * @return new {@link OutputStreamWriter} + * @throws UnsupportedEncodingException if encoding is not valid. + */ + public static OutputStreamWriter outputStreamWriterOf(final OutputStream output, final String encoding) throws UnsupportedEncodingException { + return new OutputStreamWriter(output, encoding); + } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/UnicodeInputStream.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/UnicodeInputStream.java index 4e7b88bcd..d56c9296c 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/UnicodeInputStream.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/UnicodeInputStream.java @@ -60,13 +60,13 @@ public class UnicodeInputStream extends InputStream { * Read mode is active when target encoding is set. Then this stream reads * optional BOM for given encoding. If BOM doesn't exist, nothing is skipped. */ - public UnicodeInputStream(InputStream in, String targetEncoding) { + public UnicodeInputStream(final InputStream in, final String targetEncoding) { internalInputStream = new PushbackInputStream(in, MAX_BOM_SIZE); this.targetEncoding = targetEncoding; } /** - * Returns detected UTF encoding or null if no UTF encoding has been detected (i.e. no BOM). + * Returns detected UTF encoding or {@code null} if no UTF encoding has been detected (i.e. no BOM). * If stream is not read yet, it will be {@link #init() initalized} first. */ public String getDetectedEncoding() { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/ZipBuilder.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/ZipBuilder.java index 66caf2dd9..8a5073f7b 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/ZipBuilder.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/ZipBuilder.java @@ -26,13 +26,12 @@ package com.fr.third.jodd.io; import com.fr.third.jodd.util.StringPool; -import com.fr.third.jodd.util.StringPool; +import com.fr.third.jodd.util.StringUtil; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.util.zip.ZipOutputStream; /** @@ -44,10 +43,10 @@ public class ZipBuilder { private final File targetZipFile; private final ByteArrayOutputStream targetBaos; - public static ZipBuilder createZipFile(File zipFile) throws IOException { + public static ZipBuilder createZipFile(final File zipFile) throws IOException { return new ZipBuilder(zipFile); } - public static ZipBuilder createZipFile(String zipFile) throws IOException { + public static ZipBuilder createZipFile(final String zipFile) throws IOException { return new ZipBuilder(new File(zipFile)); } @@ -57,7 +56,7 @@ public class ZipBuilder { // ---------------------------------------------------------------- ctors - protected ZipBuilder(File zipFile) throws IOException { + protected ZipBuilder(final File zipFile) throws IOException { if (!FileUtil.isExistingFile(zipFile)) { FileUtil.touch(zipFile); } @@ -97,7 +96,7 @@ public class ZipBuilder { // ---------------------------------------------------------------- add file to zip - public AddFileToZip add(File source) { + public AddFileToZip add(final File source) { return new AddFileToZip(source); } @@ -107,14 +106,14 @@ public class ZipBuilder { private String comment; private boolean recursive = true; - private AddFileToZip(File file) { + private AddFileToZip(final File file) { this.file = file; } /** * Defines optional entry path. */ - public AddFileToZip path(String path) { + public AddFileToZip path(final String path) { this.path = path; return this; } @@ -122,7 +121,7 @@ public class ZipBuilder { /** * Defines optional comment. */ - public AddFileToZip comment(String comment) { + public AddFileToZip comment(final String comment) { this.comment = comment; return this; } @@ -146,16 +145,11 @@ public class ZipBuilder { // ---------------------------------------------------------------- add content - public AddContentToZip add(String content) { - try { - return new AddContentToZip(content.getBytes(StringPool.UTF_8)); - } - catch (UnsupportedEncodingException ignore) { - return null; - } + public AddContentToZip add(final String content) { + return new AddContentToZip(StringUtil.getBytes(content, StringPool.UTF_8)); } - public AddContentToZip add(byte[] content) { + public AddContentToZip add(final byte[] content) { return new AddContentToZip(content); } @@ -164,14 +158,14 @@ public class ZipBuilder { private String path; private String comment; - private AddContentToZip(byte[] content) { + private AddContentToZip(final byte[] content) { this.bytes = content; } /** * Defines optional entry path. */ - public AddContentToZip path(String path) { + public AddContentToZip path(final String path) { this.path = path; return this; } @@ -179,7 +173,7 @@ public class ZipBuilder { /** * Defines optional comment. */ - public AddContentToZip comment(String comment) { + public AddContentToZip comment(final String comment) { this.comment = comment; return this; } @@ -195,7 +189,7 @@ public class ZipBuilder { // ---------------------------------------------------------------- folder - public ZipBuilder addFolder(String folderName) throws IOException { + public ZipBuilder addFolder(final String folderName) throws IOException { ZipUtil.addFolderToZip(zos, folderName, null); return this; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/ZipUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/ZipUtil.java index e7248f3f3..fcb10f43e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/ZipUtil.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/ZipUtil.java @@ -25,22 +25,22 @@ package com.fr.third.jodd.io; -import com.fr.third.jodd.util.StringPool; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.util.Wildcard; import com.fr.third.jodd.util.StringPool; import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.util.Wildcard; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; +import java.util.List; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPInputStream; @@ -69,14 +69,14 @@ public class ZipUtil { /** * Compresses a file into zlib archive. */ - public static File zlib(String file) throws IOException { + public static File zlib(final String file) throws IOException { return zlib(new File(file)); } /** * Compresses a file into zlib archive. */ - public static File zlib(File file) throws IOException { + public static File zlib(final File file) throws IOException { if (file.isDirectory()) { throw new IOException("Can't zlib folder"); } @@ -98,18 +98,18 @@ public class ZipUtil { } // ---------------------------------------------------------------- gzip - + /** * Compresses a file into gzip archive. */ - public static File gzip(String fileName) throws IOException { + public static File gzip(final String fileName) throws IOException { return gzip(new File(fileName)); } /** * Compresses a file into gzip archive. */ - public static File gzip(File file) throws IOException { + public static File gzip(final File file) throws IOException { if (file.isDirectory()) { throw new IOException("Can't gzip folder"); } @@ -131,14 +131,14 @@ public class ZipUtil { /** * Decompress gzip archive. */ - public static File ungzip(String file) throws IOException { + public static File ungzip(final String file) throws IOException { return ungzip(new File(file)); } /** * Decompress gzip archive. */ - public static File ungzip(File file) throws IOException { + public static File ungzip(final File file) throws IOException { String outFileName = FileNameUtil.removeExtension(file.getAbsolutePath()); File out = new File(outFileName); out.createNewFile(); @@ -161,28 +161,47 @@ public class ZipUtil { * Zips a file or a folder. * @see #zip(java.io.File) */ - public static File zip(String file) throws IOException { + public static File zip(final String file) throws IOException { return zip(new File(file)); } /** * Zips a file or a folder. If adding a folder, all its content will be added. */ - public static File zip(File file) throws IOException { + public static File zip(final File file) throws IOException { String zipFile = file.getAbsolutePath() + ZIP_EXT; return ZipBuilder.createZipFile(zipFile) - .add(file).recursive().save() + .add(file).recursive().save() .toZipFile(); } // ---------------------------------------------------------------- unzip + /** + * Lists zip content. + */ + public static List listZip(final File zipFile) throws IOException { + List entries = new ArrayList<>(); + + ZipFile zip = new ZipFile(zipFile); + Enumeration zipEntries = zip.entries(); + + while (zipEntries.hasMoreElements()) { + ZipEntry entry = (ZipEntry) zipEntries.nextElement(); + String entryName = entry.getName(); + + entries.add(entryName); + } + + return Collections.unmodifiableList(entries); + } + /** * Extracts zip file content to the target directory. * @see #unzip(java.io.File, java.io.File, String...) */ - public static void unzip(String zipFile, String destDir, String... patterns) throws IOException { + public static void unzip(final String zipFile, final String destDir, final String... patterns) throws IOException { unzip(new File(zipFile), new File(destDir), patterns); } @@ -194,7 +213,7 @@ public class ZipUtil { * @param destDir destination directory * @param patterns optional wildcard patterns of files to extract, may be null */ - public static void unzip(File zipFile, File destDir, String... patterns) throws IOException { + public static void unzip(final File zipFile, final File destDir, final String... patterns) throws IOException { ZipFile zip = new ZipFile(zipFile); Enumeration zipEntries = zip.entries(); @@ -208,7 +227,14 @@ public class ZipUtil { } } - File file = (destDir != null) ? new File(destDir, entryName) : new File(entryName); + final File file = (destDir != null) ? new File(destDir, entryName) : new File(entryName); + + // check for Zip slip FLAW + final File rootDir = destDir != null ? destDir : new File("."); + if (!FileUtil.isAncestor(rootDir, file, true)) { + throw new IOException("Unzipping"); + } + if (entry.isDirectory()) { if (!file.mkdirs()) { if (!file.isDirectory()) { @@ -251,7 +277,7 @@ public class ZipUtil { * @param comment optional comment * @param recursive when set to true content of added folders will be added, too */ - public static void addToZip(ZipOutputStream zos, File file, String path, String comment, boolean recursive) throws IOException { + public static void addToZip(final ZipOutputStream zos, final File file, String path, final String comment, final boolean recursive) throws IOException { if (!file.exists()) { throw new FileNotFoundException(file.toString()); } @@ -318,7 +344,7 @@ public class ZipUtil { /** * Adds byte content into the zip as a file. */ - public static void addToZip(ZipOutputStream zos, byte[] content, String path, String comment) throws IOException { + public static void addToZip(final ZipOutputStream zos, final byte[] content, String path, final String comment) throws IOException { while (path.length() != 0 && path.charAt(0) == '/') { path = path.substring(1); } @@ -346,7 +372,7 @@ public class ZipUtil { zos.closeEntry(); } - public static void addFolderToZip(ZipOutputStream zos, String path, String comment) throws IOException { + public static void addFolderToZip(final ZipOutputStream zos, String path, final String comment) throws IOException { while (path.length() != 0 && path.charAt(0) == '/') { path = path.substring(1); } @@ -376,7 +402,7 @@ public class ZipUtil { /** * Closes zip file safely. */ - public static void close(ZipFile zipFile) { + public static void close(final ZipFile zipFile) { if (zipFile != null) { try { zipFile.close(); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassFinder.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassFinder.java deleted file mode 100644 index e5dd8e1cc..000000000 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassFinder.java +++ /dev/null @@ -1,584 +0,0 @@ -// Copyright (c) 2003-present, Jodd Team (http://jodd.org) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package com.fr.third.jodd.io.findfile; - -import com.fr.third.jodd.util.ArraysUtil; -import com.fr.third.jodd.util.ClassLoaderUtil; -import com.fr.third.jodd.util.InExRuleMatcher; -import com.fr.third.jodd.util.InExRules; -import com.fr.third.jodd.util.Wildcard; -import com.fr.third.jodd.io.FileNameUtil; -import com.fr.third.jodd.util.ClassLoaderUtil; -import com.fr.third.jodd.util.InExRules; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.util.Wildcard; -import com.fr.third.jodd.util.ArraysUtil; -import com.fr.third.jodd.io.FileUtil; -import com.fr.third.jodd.io.StreamUtil; -import com.fr.third.jodd.io.ZipUtil; - -import java.net.URL; -import java.util.zip.ZipFile; -import java.util.zip.ZipEntry; -import java.util.Enumeration; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.FileNotFoundException; - -import static com.fr.third.jodd.util.InExRuleMatcher.WILDCARD_PATH_RULE_MATCHER; -import static com.fr.third.jodd.util.InExRuleMatcher.WILDCARD_RULE_MATCHER; - -/** - * Simple utility that scans URLs for classes. - * Its purpose is to help scanning class paths for some classes. - * Content of Jar files is also examined. - *

- * Scanning starts in included all mode (blacklist mode) for both jars and lists. - * User can set explicit excludes. Of course, mode can be changed. - *

- * All paths are matched using {@link Wildcard#matchPath(String, String) path-style} - * wildcard matcher. All entries are matched using {@link Wildcard#match(CharSequence, CharSequence)} common-style} - * wildcard matcher. - * - * @see ClassScanner - */ -public abstract class ClassFinder { - - private static final String CLASS_FILE_EXT = ".class"; - private static final String JAR_FILE_EXT = ".jar"; - - // ---------------------------------------------------------------- excluded jars - - /** - * Array of system jars that are excluded from the search. - * By default, these paths are common for linux, windows and mac. - */ - protected static String[] systemJars = new String[] { - "**/jre/lib/*.jar", - "**/jre/lib/ext/*.jar", - "**/Java/Extensions/*.jar", - "**/Classes/*.jar" - }; - - protected final InExRules rulesJars = createJarRules(); - - /** - * Creates JAR rules. By default, excludes all system jars. - */ - protected InExRules createJarRules() { - InExRules rulesJars = new InExRules(InExRuleMatcher.WILDCARD_PATH_RULE_MATCHER); - - for (String systemJar : systemJars) { - rulesJars.exclude(systemJar); - } - - return rulesJars; - } - - /** - * Returns system jars. - */ - public static String[] getSystemJars() { - return systemJars; - } - - /** - * Specify excluded jars. - */ - public void setExcludedJars(String... excludedJars) { - for (String excludedJar : excludedJars) { - rulesJars.exclude(excludedJar); - } - } - - /** - * Specify included jars. - */ - public void setIncludedJars(String... includedJars) { - for (String includedJar : includedJars) { - rulesJars.include(includedJar); - } - } - - /** - * Sets white/black list mode for jars. - */ - public void setIncludeAllJars(boolean blacklist) { - if (blacklist) { - rulesJars.blacklist(); - } else { - rulesJars.whitelist(); - } - } - - /** - * Sets white/black list mode for jars. - */ - public void setExcludeAllJars(boolean whitelist) { - if (whitelist) { - rulesJars.whitelist(); - } else { - rulesJars.blacklist(); - } - } - - // ---------------------------------------------------------------- included entries - - protected final InExRules rulesEntries = createEntriesRules(); - - protected InExRules createEntriesRules() { - return new InExRules(InExRuleMatcher.WILDCARD_RULE_MATCHER); - } - - /** - * Sets included set of names that will be considered during configuration. - * @see InExRules - */ - public void setIncludedEntries(String... includedEntries) { - for (String includedEntry : includedEntries) { - rulesEntries.include(includedEntry); - } - } - - /** - * Sets white/black list mode for entries. - */ - public void setIncludeAllEntries(boolean blacklist) { - if (blacklist) { - rulesEntries.blacklist(); - } else { - rulesEntries.whitelist(); - } - } - /** - * Sets white/black list mode for entries. - */ - public void setExcludeAllEntries(boolean whitelist) { - if (whitelist) { - rulesEntries.whitelist(); - } else { - rulesEntries.blacklist(); - } - } - - /** - * Sets excluded names that narrows included set of packages. - * @see InExRules - */ - public void setExcludedEntries(String... excludedEntries) { - for (String excludedEntry : excludedEntries) { - rulesEntries.exclude(excludedEntry); - } - } - - // ---------------------------------------------------------------- implementation - - /** - * If set to true all files will be scanned and not only classes. - */ - protected boolean includeResources; - /** - * If set to true exceptions for entry scans are ignored. - */ - protected boolean ignoreException; - - - public boolean isIncludeResources() { - return includeResources; - } - - public void setIncludeResources(boolean includeResources) { - this.includeResources = includeResources; - } - - public boolean isIgnoreException() { - return ignoreException; - } - - /** - * Sets if exceptions during scanning process should be ignored or not. - */ - public void setIgnoreException(boolean ignoreException) { - this.ignoreException = ignoreException; - } - - // ---------------------------------------------------------------- scan - - /** - * Scans several URLs. If (#ignoreExceptions} is set, exceptions - * per one URL will be ignored and loops continues. - */ - protected void scanUrls(URL... urls) { - for (URL path : urls) { - scanUrl(path); - } - } - - /** - * Scans single URL for classes and jar files. - * Callback {@link #onEntry(EntryData)} is called on - * each class name. - */ - protected void scanUrl(URL url) { - File file = FileUtil.toFile(url); - if (file == null) { - if (!ignoreException) { - throw new FindFileException("URL is not a valid file: " + url); - } - } - scanPath(file); - } - - - protected void scanPaths(File... paths) { - for (File path : paths) { - scanPath(path); - } - } - - protected void scanPaths(String... paths) { - for (String path : paths) { - scanPath(path); - } - } - - protected void scanPath(String path) { - scanPath(new File(path)); - } - - /** - * Returns true if some JAR file has to be accepted. - */ - protected boolean acceptJar(File jarFile) { - String path = jarFile.getAbsolutePath(); - path = FileNameUtil.separatorsToUnix(path); - - return rulesJars.match(path); - } - - /** - * Scans single path. - */ - protected void scanPath(File file) { - String path = file.getAbsolutePath(); - - if (StringUtil.endsWithIgnoreCase(path, JAR_FILE_EXT)) { - - if (!acceptJar(file)) { - return; - } - scanJarFile(file); - } else if (file.isDirectory()) { - scanClassPath(file); - } - } - - // ---------------------------------------------------------------- internal - - /** - * Scans classes inside single JAR archive. Archive is scanned as a zip file. - * @see #onEntry(EntryData) - */ - protected void scanJarFile(File file) { - ZipFile zipFile; - try { - zipFile = new ZipFile(file); - } catch (IOException ioex) { - if (!ignoreException) { - throw new FindFileException("Invalid zip: " + file.getName(), ioex); - } - return; - } - Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { - ZipEntry zipEntry = (ZipEntry) entries.nextElement(); - String zipEntryName = zipEntry.getName(); - try { - if (StringUtil.endsWithIgnoreCase(zipEntryName, CLASS_FILE_EXT)) { - String entryName = prepareEntryName(zipEntryName, true); - EntryData entryData = new EntryData(entryName, zipFile, zipEntry); - try { - scanEntry(entryData); - } finally { - entryData.closeInputStreamIfOpen(); - } - } else if (includeResources) { - String entryName = prepareEntryName(zipEntryName, false); - EntryData entryData = new EntryData(entryName, zipFile, zipEntry); - try { - scanEntry(entryData); - } finally { - entryData.closeInputStreamIfOpen(); - } - } - } catch (RuntimeException rex) { - if (!ignoreException) { - ZipUtil.close(zipFile); - throw rex; - } - } - } - ZipUtil.close(zipFile); - } - - /** - * Scans single classpath directory. - * @see #onEntry(EntryData) - */ - protected void scanClassPath(File root) { - String rootPath = root.getAbsolutePath(); - if (!rootPath.endsWith(File.separator)) { - rootPath += File.separatorChar; - } - - FindFile ff = new FindFile().setIncludeDirs(false).setRecursive(true).searchPath(rootPath); - File file; - while ((file = ff.nextFile()) != null) { - String filePath = file.getAbsolutePath(); - try { - if (StringUtil.endsWithIgnoreCase(filePath, CLASS_FILE_EXT)) { - scanClassFile(filePath, rootPath, file, true); - } else if (includeResources) { - scanClassFile(filePath, rootPath, file, false); - } - } catch (RuntimeException rex) { - if (!ignoreException) { - throw rex; - } - } - } - } - - protected void scanClassFile(String filePath, String rootPath, File file, boolean isClass) { - if (StringUtil.startsWithIgnoreCase(filePath, rootPath)) { - String entryName = prepareEntryName(filePath.substring(rootPath.length()), isClass); - EntryData entryData = new EntryData(entryName, file); - try { - scanEntry(entryData); - } finally { - entryData.closeInputStreamIfOpen(); - } - } - } - - /** - * Prepares resource and class names. For classes, it strips '.class' from the end and converts - * all (back)slashes to dots. For resources, it replaces all backslashes to slashes. - */ - protected String prepareEntryName(String name, boolean isClass) { - String entryName = name; - if (isClass) { - entryName = name.substring(0, name.length() - 6); // 6 == ".class".length() - entryName = StringUtil.replaceChar(entryName, '/', '.'); - entryName = StringUtil.replaceChar(entryName, '\\', '.'); - } else { - entryName = '/' + StringUtil.replaceChar(entryName, '\\', '/'); - } - return entryName; - } - - /** - * Returns true if some entry name has to be accepted. - * @see #prepareEntryName(String, boolean) - * @see #scanEntry(EntryData) - */ - protected boolean acceptEntry(String entryName) { - return rulesEntries.match(entryName); - } - - - /** - * If entry name is {@link #acceptEntry(String) accepted} invokes {@link #onEntry(EntryData)} a callback}. - */ - protected void scanEntry(EntryData entryData) { - if (!acceptEntry(entryData.getName())) { - return; - } - try { - onEntry(entryData); - } catch (Exception ex) { - throw new FindFileException("Scan entry error: " + entryData, ex); - } - } - - - // ---------------------------------------------------------------- callback - - /** - * Called during classpath scanning when class or resource is found. - *

    - *
  • Class name is java-alike class name (pk1.pk2.class) that may be immediately used - * for dynamic loading.
  • - *
  • Resource name starts with '\' and represents either jar path (\pk1/pk2/res) or relative file path (\pk1\pk2\res).
  • - *
- * - * InputStream is provided by InputStreamProvider and opened lazy. - * Once opened, input stream doesn't have to be closed - this is done by this class anyway. - */ - protected abstract void onEntry(EntryData entryData) throws Exception; - - // ---------------------------------------------------------------- utilities - - /** - * Returns type signature bytes used for searching in class file. - */ - protected byte[] getTypeSignatureBytes(Class type) { - String name = 'L' + type.getName().replace('.', '/') + ';'; - return name.getBytes(); - } - - /** - * Returns true if class contains {@link #getTypeSignatureBytes(Class) type signature}. - * It searches the class content for bytecode signature. This is the fastest way of finding if come - * class uses some type. Please note that if signature exists it still doesn't means that class uses - * it in expected way, therefore, class should be loaded to complete the scan. - */ - protected boolean isTypeSignatureInUse(InputStream inputStream, byte[] bytes) { - try { - byte[] data = StreamUtil.readBytes(inputStream); - int index = ArraysUtil.indexOf(data, bytes); - return index != -1; - } catch (IOException ioex) { - throw new FindFileException("Read error", ioex); - } - } - - // ---------------------------------------------------------------- class loading - - /** - * Loads class by its name. If {@link #ignoreException} is set, - * no exception is thrown, but null is returned. - */ - protected Class loadClass(String className) throws ClassNotFoundException { - try { - return ClassLoaderUtil.loadClass(className); - } catch (ClassNotFoundException cnfex) { - if (ignoreException) { - return null; - } - throw cnfex; - } catch (Error error) { - if (ignoreException) { - return null; - } - throw error; - } - } - - - // ---------------------------------------------------------------- provider - - /** - * Provides input stream on demand. Input stream is not open until get(). - */ - protected static class EntryData { - - private final File file; - private final ZipFile zipFile; - private final ZipEntry zipEntry; - private final String name; - - EntryData(String name, ZipFile zipFile, ZipEntry zipEntry) { - this.name = name; - this.zipFile = zipFile; - this.zipEntry = zipEntry; - this.file = null; - inputStream = null; - } - EntryData(String name, File file) { - this.name = name; - this.file = file; - this.zipEntry = null; - this.zipFile = null; - inputStream = null; - } - - private InputStream inputStream; - - /** - * Returns entry name. - */ - public String getName() { - return name; - } - - /** - * Returns true if archive. - */ - public boolean isArchive() { - return zipFile != null; - } - - /** - * Returns archive name or null if entry is not inside archived file. - */ - public String getArchiveName() { - if (zipFile != null) { - return zipFile.getName(); - } - return null; - } - - /** - * Opens zip entry or plain file and returns its input stream. - */ - public InputStream openInputStream() { - if (zipFile != null) { - try { - inputStream = zipFile.getInputStream(zipEntry); - return inputStream; - } catch (IOException ioex) { - throw new FindFileException("Input stream error: '" + zipFile.getName() - + "', entry: '" + zipEntry.getName() + "'." , ioex); - } - } - try { - inputStream = new FileInputStream(file); - return inputStream; - } catch (FileNotFoundException fnfex) { - throw new FindFileException("Unable to open: " + file.getAbsolutePath(), fnfex); - } - } - - /** - * Closes input stream if opened. - */ - void closeInputStreamIfOpen() { - if (inputStream == null) { - return; - } - StreamUtil.close(inputStream); - inputStream = null; - } - - @Override - public String toString() { - return "EntryData{" + name + '\'' +'}'; - } - } -} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassScanner.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassScanner.java index 9cd006818..3c8df19ea 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassScanner.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassScanner.java @@ -25,44 +25,615 @@ package com.fr.third.jodd.io.findfile; +import com.fr.third.jodd.inex.InExRules; +import com.fr.third.jodd.io.FileNameUtil; +import com.fr.third.jodd.io.FileUtil; +import com.fr.third.jodd.io.StreamUtil; +import com.fr.third.jodd.io.ZipUtil; +import com.fr.third.jodd.util.ArraysUtil; import com.fr.third.jodd.util.ClassLoaderUtil; -import com.fr.third.jodd.util.ClassLoaderUtil; +import com.fr.third.jodd.util.Consumers; +import com.fr.third.jodd.util.StringUtil; -import java.net.URL; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.function.Consumer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static com.fr.third.jodd.inex.InExRuleMatcher.WILDCARD_PATH_RULE_MATCHER; +import static com.fr.third.jodd.inex.InExRuleMatcher.WILDCARD_RULE_MATCHER; /** - * A class scanner, user-friendly version of {@link ClassFinder} a class scanner. - * Offers public scan() methods that can be directly used. + * Convenient class path scanner. */ -public abstract class ClassScanner extends ClassFinder { +public class ClassScanner { + + private static final String CLASS_FILE_EXT = ".class"; + private static final String JAR_FILE_EXT = ".jar"; + + /** + * Create new class scanner. + */ + public static ClassScanner create() { + return new ClassScanner(); + } + + public ClassScanner() { + this.rulesJars = new InExRules<>(WILDCARD_PATH_RULE_MATCHER); + this.rulesEntries = new InExRules<>(WILDCARD_RULE_MATCHER); + + excludeJars(SYSTEM_JARS); + } + + // ---------------------------------------------------------------- excluded jars + + /** + * Array of system jars that are excluded from the search. + * By default, these paths are common for linux, windows and mac. + */ + protected static final String[] SYSTEM_JARS = new String[] { + "**/jre/lib/*.jar", + "**/jre/lib/ext/*.jar", + "**/Java/Extensions/*.jar", + "**/Classes/*.jar" + }; + + protected static final String[] COMMONLY_EXCLUDED_JARS = new String[] { + "**/tomcat*", + "**/jetty*", + "**/javafx*", + "**/junit*", + "**/javax*", + "**/org.eclipse.*", + "**/ant*", + "**/idea_rt.jar", + }; + + protected final InExRules rulesJars; + + /** + * Specify excluded jars. + */ + public ClassScanner excludeJars(final String... excludedJars) { + for (final String excludedJar : excludedJars) { + rulesJars.exclude(excludedJar); + } + return this; + } + + /** + * Exclude some commonly unused jars. + */ + public ClassScanner excludeCommonJars() { + return excludeJars(COMMONLY_EXCLUDED_JARS); + } + + /** + * Specify included jars. + */ + public ClassScanner includeJars(final String... includedJars) { + for (final String includedJar : includedJars) { + rulesJars.include(includedJar); + } + return this; + } + + /** + * Sets white/black list mode for jars. + */ + public ClassScanner includeAllJars(final boolean blacklist) { + if (blacklist) { + rulesJars.blacklist(); + } else { + rulesJars.whitelist(); + } + return this; + } + + /** + * Sets white/black list mode for jars. + */ + public ClassScanner excludeAllJars(final boolean whitelist) { + if (whitelist) { + rulesJars.whitelist(); + } else { + rulesJars.blacklist(); + } + return this; + } + + // ---------------------------------------------------------------- included entries + + protected static final String[] COMMONLY_EXCLUDED_ENTRIES = new String[] { + "java.*", + "ch.qos.logback.*", + "sun.*", + "com.sun.*", + "org.eclipse.*", + }; + + protected final InExRules rulesEntries; + protected boolean detectEntriesMode = false; + + /** + * Sets included set of names that will be considered during configuration. + * @see InExRules + */ + public ClassScanner includeEntries(final String... includedEntries) { + for (final String includedEntry : includedEntries) { + rulesEntries.include(includedEntry); + } + return this; + } + + /** + * Sets white/black list mode for entries. + */ + public ClassScanner includeAllEntries(final boolean blacklist) { + if (blacklist) { + rulesEntries.blacklist(); + } else { + rulesEntries.whitelist(); + } + return this; + } + /** + * Sets white/black list mode for entries. + */ + public ClassScanner excludeAllEntries(final boolean whitelist) { + if (whitelist) { + rulesEntries.whitelist(); + } else { + rulesEntries.blacklist(); + } + return this; + } + + /** + * Sets excluded names that narrows included set of packages. + * @see InExRules + */ + public ClassScanner excludeEntries(final String... excludedEntries) { + for (final String excludedEntry : excludedEntries) { + rulesEntries.exclude(excludedEntry); + } + return this; + } + + /** + * Excludes some commonly skipped packages. + */ + public ClassScanner excludeCommonEntries() { + return excludeEntries(COMMONLY_EXCLUDED_ENTRIES); + } + + public ClassScanner detectEntriesMode(final boolean detectMode) { + this.detectEntriesMode = detectMode; + return this; + } + + // ---------------------------------------------------------------- implementation + + /** + * If set to true all files will be scanned and not only classes. + */ + protected boolean includeResources; + /** + * If set to true exceptions for entry scans are ignored. + */ + protected boolean ignoreException; + + public ClassScanner includeResources(final boolean includeResources) { + this.includeResources = includeResources; + return this; + } + + /** + * Sets if exceptions during scanning process should be ignored or not. + */ + public ClassScanner ignoreException(final boolean ignoreException) { + this.ignoreException = ignoreException; + return this; + } + + // ---------------------------------------------------------------- scan + /** - * Scans provided classpath. + * Returns true if some JAR file has to be accepted. */ - public void scan(URL... urls) { - scanUrls(urls); + protected boolean acceptJar(final File jarFile) { + String path = jarFile.getAbsolutePath(); + path = FileNameUtil.separatorsToUnix(path); + + return rulesJars.match(path); } + // ---------------------------------------------------------------- internal + /** - * Scans {@link ClassLoaderUtil#getDefaultClasspath() default class path}. + * Scans classes inside single JAR archive. Archive is scanned as a zip file. + * @see #onEntry(ClassPathEntry) */ - public void scanDefaultClasspath() { - scan(ClassLoaderUtil.getDefaultClasspath()); + protected void scanJarFile(final File file) { + final ZipFile zipFile; + try { + zipFile = new ZipFile(file); + } catch (final IOException ioex) { + if (!ignoreException) { + throw new FindFileException("Invalid zip: " + file.getName(), ioex); + } + return; + } + final Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + final ZipEntry zipEntry = (ZipEntry) entries.nextElement(); + final String zipEntryName = zipEntry.getName(); + try { + if (StringUtil.endsWithIgnoreCase(zipEntryName, CLASS_FILE_EXT)) { + final String entryName = prepareEntryName(zipEntryName, true); + final ClassPathEntry classPathEntry = new ClassPathEntry(entryName, zipFile, zipEntry); + try { + scanEntry(classPathEntry); + } finally { + classPathEntry.closeInputStream(); + } + } else if (includeResources) { + final String entryName = prepareEntryName(zipEntryName, false); + final ClassPathEntry classPathEntry = new ClassPathEntry(entryName, zipFile, zipEntry); + try { + scanEntry(classPathEntry); + } finally { + classPathEntry.closeInputStream(); + } + } + } catch (final RuntimeException rex) { + if (!ignoreException) { + ZipUtil.close(zipFile); + throw rex; + } + } + } + ZipUtil.close(zipFile); + } + + /** + * Scans single classpath directory. + * @see #onEntry(ClassPathEntry) + */ + protected void scanClassPath(final File root) { + String rootPath = root.getAbsolutePath(); + if (!rootPath.endsWith(File.separator)) { + rootPath += File.separatorChar; + } + + final FindFile ff = FindFile.create().includeDirs(false).recursive(true).searchPath(rootPath); + File file; + while ((file = ff.nextFile()) != null) { + final String filePath = file.getAbsolutePath(); + try { + if (StringUtil.endsWithIgnoreCase(filePath, CLASS_FILE_EXT)) { + scanClassFile(filePath, rootPath, file, true); + } else if (includeResources) { + scanClassFile(filePath, rootPath, file, false); + } + } catch (final RuntimeException rex) { + if (!ignoreException) { + throw rex; + } + } + } + } + + protected void scanClassFile(final String filePath, final String rootPath, final File file, final boolean isClass) { + if (StringUtil.startsWithIgnoreCase(filePath, rootPath)) { + final String entryName = prepareEntryName(filePath.substring(rootPath.length()), isClass); + final ClassPathEntry classPathEntry = new ClassPathEntry(entryName, file); + try { + scanEntry(classPathEntry); + } finally { + classPathEntry.closeInputStream(); + } + } + } + + /** + * Prepares resource and class names. For classes, it strips '.class' from the end and converts + * all (back)slashes to dots. For resources, it replaces all backslashes to slashes. + */ + protected String prepareEntryName(final String name, final boolean isClass) { + String entryName = name; + if (isClass) { + entryName = name.substring(0, name.length() - 6); // 6 == ".class".length() + entryName = StringUtil.replaceChar(entryName, '/', '.'); + entryName = StringUtil.replaceChar(entryName, '\\', '.'); + } else { + entryName = '/' + StringUtil.replaceChar(entryName, '\\', '/'); + } + return entryName; + } + + /** + * Returns true if some entry name has to be accepted. + * @see #prepareEntryName(String, boolean) + * @see #scanEntry(ClassPathEntry) + */ + protected boolean acceptEntry(final String entryName) { + return rulesEntries.match(entryName); + } + + /** + * If entry name is {@link #acceptEntry(String) accepted} invokes {@link #onEntry(ClassPathEntry)} a callback}. + */ + protected void scanEntry(final ClassPathEntry classPathEntry) { + if (!acceptEntry(classPathEntry.name())) { + return; + } + try { + onEntry(classPathEntry); + } catch (final Exception ex) { + throw new FindFileException("Scan entry error: " + classPathEntry, ex); + } + } + + + // ---------------------------------------------------------------- callback + + private final Consumers entryDataConsumers = Consumers.empty(); + + /** + * Registers a {@link ClassPathEntry class path entry} consumer. + * It will be called on each loaded entry. + */ + public ClassScanner registerEntryConsumer(final Consumer entryDataConsumer) { + entryDataConsumers.add(entryDataConsumer); + return this; + } + + /** + * Called during classpath scanning when class or resource is found. + *
    + *
  • Class name is java-alike class name (pk1.pk2.class) that may be immediately used + * for dynamic loading.
  • + *
  • Resource name starts with '\' and represents either jar path (\pk1/pk2/res) or relative file path (\pk1\pk2\res).
  • + *
+ * + * InputStream is provided by InputStreamProvider and opened lazy. + * Once opened, input stream doesn't have to be closed - this is done by this class anyway. + */ + protected void onEntry(final ClassPathEntry classPathEntry) { + entryDataConsumers.accept(classPathEntry); + } + + // ---------------------------------------------------------------- utilities + + /** + * Returns type signature bytes used for searching in class file. + */ + public static byte[] bytecodeSignatureOfType(final Class type) { + final String name = 'L' + type.getName().replace('.', '/') + ';'; + return name.getBytes(); + } + + // ---------------------------------------------------------------- provider + + /** + * Provides input stream on demand. Input stream is not open until get(). + */ + public class ClassPathEntry { + + private final File file; + private final ZipFile zipFile; + private final ZipEntry zipEntry; + private final String name; + + ClassPathEntry(final String name, final ZipFile zipFile, final ZipEntry zipEntry) { + this.name = name; + this.zipFile = zipFile; + this.zipEntry = zipEntry; + this.file = null; + this.inputStream = null; + } + ClassPathEntry(final String name, final File file) { + this.name = name; + this.file = file; + this.zipEntry = null; + this.zipFile = null; + this.inputStream = null; + } + + private InputStream inputStream; + private byte[] inputStreamBytes; + + /** + * Returns entry name. + */ + public String name() { + return name; + } + + /** + * Returns true if archive. + */ + public boolean isArchive() { + return zipFile != null; + } + + /** + * Returns archive name or null if entry is not inside archived file. + */ + public String archiveName() { + if (zipFile != null) { + return zipFile.getName(); + } + return null; + } + + /** + * Returns true if class contains {@link #bytecodeSignatureOfType(Class) type signature}. + * It searches the class content for bytecode signature. This is the fastest way of finding if come + * class uses some type. Please note that if signature exists it still doesn't means that class uses + * it in expected way, therefore, class should be loaded to complete the scan. + */ + public boolean isTypeSignatureInUse(final byte[] bytes) { + try { + final byte[] data = readBytes(); + final int index = ArraysUtil.indexOf(data, bytes); + return index != -1; + } catch (final IOException ioex) { + throw new FindFileException("Read error", ioex); + } + } + + /** + * Reads stream bytes. Since stream can be read only once, the byte content + * is cached. + */ + public byte[] readBytes() throws IOException { + openInputStream(); + + if (inputStreamBytes == null) { + inputStreamBytes = StreamUtil.readBytes(inputStream); + } + return inputStreamBytes; + } + + /** + * Opens zip entry or plain file and returns its input stream. + */ + public InputStream openInputStream() { + if (inputStream != null) { + return inputStream; + } + if (zipFile != null && zipEntry != null) { + try { + inputStream = zipFile.getInputStream(zipEntry); + return inputStream; + } catch (final IOException ioex) { + throw new FindFileException("Input stream error: '" + zipFile.getName() + + "', entry: '" + zipEntry.getName() + "'." , ioex); + } + } + if (file != null) { + try { + inputStream = new FileInputStream(file); + return inputStream; + } catch (final FileNotFoundException fnfex) { + throw new FindFileException("Unable to open: " + file.getAbsolutePath(), fnfex); + } + } + throw new FindFileException("Unable to open stream: " + name()); + } + + /** + * Closes input stream if opened. + */ + public void closeInputStream() { + if (inputStream == null) { + return; + } + StreamUtil.close(inputStream); + inputStream = null; + inputStreamBytes = null; + } + + /** + * Loads class by its name. If {@link #ignoreException} is set, + * no exception is thrown, but null is returned. + */ + public Class loadClass() throws ClassNotFoundException { + try { + return ClassLoaderUtil.loadClass(name); + } catch (final ClassNotFoundException | Error cnfex) { + if (ignoreException) { + return null; + } + throw cnfex; + } + } + + @Override + public String toString() { + return "ClassPathEntry{" + name + '\'' +'}'; + } + } + + // ---------------------------------------------------------------- public scanning + + private final Set filesToScan = new LinkedHashSet<>(); + + /** + * Scans URLs. If (#ignoreExceptions} is set, exceptions + * per one URL will be ignored and loops continues. + */ + public ClassScanner scan(final URL... urls) { + for (final URL url : urls) { + final File file = FileUtil.toContainerFile(url); + if (file == null) { + if (!ignoreException) { + throw new FindFileException("URL is not a valid file: " + url); + } + } + else { + filesToScan.add(file); + } + } + return this; + } + + /** + * Scans {@link com.fr.third.jodd.util.ClassLoaderUtil#getDefaultClasspath() default class path}. + */ + public ClassScanner scanDefaultClasspath() { + return scan(ClassLoaderUtil.getDefaultClasspath()); } /** * Scans provided paths. */ - public void scan(File... paths) { - scanPaths(paths); + public ClassScanner scan(final File... paths) { + filesToScan.addAll(Arrays.asList(paths)); + return this; } /** * Scans provided paths. */ - public void scan(String... paths) { - scanPaths(paths); + public ClassScanner scan(final String... paths) { + for (final String path : paths) { + filesToScan.add(new File(path)); + } + return this; + } + + /** + * Starts with the scanner. + */ + public void start() { + if (detectEntriesMode) { + rulesEntries.detectMode(); + } + + filesToScan.forEach(file -> { + final String path = file.getAbsolutePath(); + if (StringUtil.endsWithIgnoreCase(path, JAR_FILE_EXT)) { + if (!acceptJar(file)) { + return; + } + scanJarFile(file); + } else if (file.isDirectory()) { + scanClassPath(file); + } + }); } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileExtensionComparator.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileExtensionComparator.java new file mode 100644 index 000000000..195d25ff4 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileExtensionComparator.java @@ -0,0 +1,58 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.findfile; + +import com.fr.third.jodd.io.FileNameUtil; + +import java.io.File; +import java.io.Serializable; +import java.util.Comparator; + +public class FileExtensionComparator implements Comparator, Serializable { + protected final int order; + + public FileExtensionComparator(final boolean ascending) { + if (ascending) { + order = 1; + } else { + order = -1; + } + } + + @Override + public int compare(final File file1, final File file2) { + String ext1 = FileNameUtil.getExtension(file1.getName()); + String ext2 = FileNameUtil.getExtension(file2.getName()); + long diff = ext1.compareToIgnoreCase(ext2); + if (diff == 0) { + return 0; + } + if (diff > 0) { + return order; + } + return -order; + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileLastModifiedTimeComparator.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileLastModifiedTimeComparator.java new file mode 100644 index 000000000..4f610f614 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileLastModifiedTimeComparator.java @@ -0,0 +1,54 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.findfile; + +import java.io.File; +import java.io.Serializable; +import java.util.Comparator; + +public class FileLastModifiedTimeComparator implements Comparator, Serializable { + protected final int order; + + public FileLastModifiedTimeComparator(final boolean ascending) { + if (ascending) { + order = 1; + } else { + order = -1; + } + } + + @Override + public int compare(final File file1, final File file2) { + long diff = file1.lastModified() - file2.lastModified(); + if (diff == 0) { + return 0; + } + if (diff > 0) { + return order; + } + return -order; + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileNameComparator.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileNameComparator.java new file mode 100644 index 000000000..c980f71d5 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileNameComparator.java @@ -0,0 +1,57 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.findfile; + +import com.fr.third.jodd.util.NaturalOrderComparator; + +import java.io.File; +import java.io.Serializable; +import java.util.Comparator; + +public class FileNameComparator implements Comparator, Serializable { + protected final int order; + protected NaturalOrderComparator naturalOrderComparator = new NaturalOrderComparator<>(true, true, true); + + public FileNameComparator(final boolean ascending) { + if (ascending) { + order = 1; + } else { + order = -1; + } + } + + @Override + public int compare(final File file1, final File file2) { + int result = naturalOrderComparator.compare(file1.getName(), file2.getName()); + if (result == 0) { + return result; + } + if (result > 0) { + return order; + } + return -order; + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFile.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFile.java index 6f0d00e05..2a9a1c8e4 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFile.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFile.java @@ -25,29 +25,24 @@ package com.fr.third.jodd.io.findfile; +import com.fr.third.jodd.inex.InExRules; +import com.fr.third.jodd.io.FileNameUtil; +import com.fr.third.jodd.io.FileUtil; +import com.fr.third.jodd.util.Consumers; +import com.fr.third.jodd.util.MultiComparator; +import com.fr.third.jodd.util.StringUtil; + import java.io.File; -import java.io.Serializable; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; - -import com.fr.third.jodd.util.InExRules; -import com.fr.third.jodd.util.MultiComparator; -import com.fr.third.jodd.util.NaturalOrderComparator; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.util.collection.JoddArrayList; -import com.fr.third.jodd.io.FileNameUtil; -import com.fr.third.jodd.io.FileUtil; -import com.fr.third.jodd.util.InExRules; -import com.fr.third.jodd.util.MultiComparator; -import com.fr.third.jodd.util.NaturalOrderComparator; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.util.collection.JoddArrayList; +import java.util.function.Consumer; /** * Generic iterative file finder. Searches all files on specified search path. @@ -59,8 +54,17 @@ import com.fr.third.jodd.util.collection.JoddArrayList; * @see RegExpFindFile * @see InExRules */ -@SuppressWarnings("unchecked") -public class FindFile { +public class FindFile implements Iterable { + + public static WildcardFindFile createWildcardFF() { + return new WildcardFindFile(); + } + public static RegExpFindFile createRegExpFF() { + return new RegExpFindFile(); + } + public static FindFile create() { + return new FindFile(); + } /** * Match type. @@ -90,44 +94,28 @@ public class FindFile { protected boolean walking = true; protected Match matchType = Match.FULL_PATH; - public boolean isRecursive() { - return recursive; - } - /** * Activates recursive search. */ - public T setRecursive(boolean recursive) { + public FindFile recursive(final boolean recursive) { this.recursive = recursive; - return (T) this; - } - - public boolean isIncludeDirs() { - return includeDirs; + return this; } /** * Include directories in search. */ - public T setIncludeDirs(boolean includeDirs) { + public FindFile includeDirs(final boolean includeDirs) { this.includeDirs = includeDirs; - return (T) this; - } - - public boolean isIncludeFiles() { - return includeFiles; + return this; } /** * Include files in search. */ - public T setIncludeFiles(boolean includeFiles) { + public FindFile includeFiles(final boolean includeFiles) { this.includeFiles = includeFiles; - return (T) this; - } - - public boolean isWalking() { - return walking; + return this; } /** @@ -139,23 +127,49 @@ public class FindFile { * When walking mode is turned off, folders are processed once * all files are processed, one after the other. The order is * not natural, but memory consumption is optimal. - * @see #setRecursive(boolean) + * @see #recursive(boolean) */ - public T setWalking(boolean walking) { + public FindFile walking(final boolean walking) { this.walking = walking; - return (T) this; - } - - public Match getMatchType() { - return matchType; + return this; } /** * Set {@link Match matching type}. */ - public T setMatchType(Match match) { + public FindFile matchType(final Match match) { this.matchType = match; - return (T) this; + return this; + } + + public FindFile matchOnlyFileName() { + this.matchType = Match.NAME; + return this; + } + public FindFile matchFullPath() { + this.matchType = Match.FULL_PATH; + return this; + } + public FindFile matchRelativePath() { + this.matchType = Match.RELATIVE_PATH; + return this; + } + + // ---------------------------------------------------------------- consumer + + private Consumers consumers; + + /** + * Registers file consumer + */ + public FindFile onFile(final Consumer fileConsumer) { + if (consumers == null) { + consumers = Consumers.of(fileConsumer); + } + else { + consumers.add(fileConsumer); + } + return this; } // ---------------------------------------------------------------- search path @@ -163,95 +177,95 @@ public class FindFile { /** * Specifies single search path. */ - public T searchPath(File searchPath) { + public FindFile searchPath(final File searchPath) { addPath(searchPath); - return (T) this; + return this; } /** * Specifies a set of search paths. */ - public T searchPath(File... searchPath) { - for (File file : searchPath) { + public FindFile searchPath(final File... searchPath) { + for (final File file : searchPath) { addPath(file); } - return (T) this; + return this; } /** * Specifies the search path. If provided path contains * {@link File#pathSeparator} than string will be tokenized - * and each part will be added separately as a search path. + * and each part will be added separately as a search path. */ - public T searchPath(String searchPath) { + public FindFile searchPath(final String searchPath) { if (searchPath.indexOf(File.pathSeparatorChar) != -1) { - String[] paths = StringUtil.split(searchPath, File.pathSeparator); - for (String path : paths) { + final String[] paths = StringUtil.split(searchPath, File.pathSeparator); + for (final String path : paths) { addPath(new File(path)); } } else { addPath(new File(searchPath)); } - return (T) this; + return this; } /** * Specifies search paths. - * @see #searchPath(String) + * @see #searchPath(String) */ - public T searchPath(String... searchPaths) { - for (String searchPath : searchPaths) { + public FindFile searchPaths(final String... searchPaths) { + for (final String searchPath : searchPaths) { searchPath(searchPath); } - return (T) this; + return this; } /** * Specifies the search path. Throws an exception if URI is invalid. */ - public T searchPath(URI searchPath) { - File file; + public FindFile searchPath(final URI searchPath) { + final File file; try { file = new File(searchPath); - } catch (Exception ex) { + } catch (final Exception ex) { throw new FindFileException("URI error: " + searchPath, ex); } addPath(file); - return (T) this; + return this; } /** * Specifies the search path. */ - public T searchPath(URI... searchPath) { - for (URI uri : searchPath) { + public FindFile searchPaths(final URI... searchPath) { + for (final URI uri : searchPath) { searchPath(uri); } - return (T) this; + return this; } /** * Specifies the search path. Throws an exception if URL is invalid. */ - public T searchPath(URL searchPath) { - File file = FileUtil.toFile(searchPath); + public FindFile searchPath(final URL searchPath) { + final File file = FileUtil.toContainerFile(searchPath); if (file == null) { throw new FindFileException("URL error: " + searchPath); } addPath(file); - return (T) this; + return this; } /** * Specifies the search path. */ - public T searchPath(URL... searchPath) { - for (URL url : searchPath) { + public FindFile searchPaths(final URL... searchPath) { + for (final URL url : searchPath) { searchPath(url); } - return (T) this; + return this; } // ---------------------------------------------------------------- files iterator @@ -266,13 +280,13 @@ public class FindFile { protected final String[] fileNames; protected final File[] files; - public FilesIterator(File folder) { + public FilesIterator(final File folder) { this.folder = folder; if (sortComparators != null) { this.files = folder.listFiles(); if (this.files != null) { - Arrays.sort(this.files, new MultiComparator(sortComparators)); + Arrays.sort(this.files, new MultiComparator<>(sortComparators)); } this.fileNames = null; @@ -282,14 +296,14 @@ public class FindFile { } } - public FilesIterator(String[] fileNames) { + public FilesIterator(final String[] fileNames) { this.folder = null; if (sortComparators != null) { - int fileNamesLength = fileNames.length; + final int fileNamesLength = fileNames.length; this.files = new File[fileNamesLength]; for (int i = 0; i < fileNamesLength; i++) { - String fileName = fileNames[i]; + final String fileName = fileNames[i]; if (fileName != null) { this.files[i] = new File(fileName); } @@ -320,7 +334,7 @@ public class FindFile { protected File nextFileName() { while (index < fileNames.length) { - String fileName = fileNames[index]; + final String fileName = fileNames[index]; if (fileName == null) { index++; @@ -329,7 +343,7 @@ public class FindFile { fileNames[index] = null; index++; - File file; + final File file; if (folder == null) { file = new File(fileName); } else { @@ -352,7 +366,7 @@ public class FindFile { protected File nextFile() { while (index < files.length) { - File file = files[index]; + final File file = files[index]; if (file == null) { index++; @@ -378,65 +392,65 @@ public class FindFile { // ---------------------------------------------------------------- matching - protected final InExRules rules = createRulesEngine(); + protected final InExRules rules = createRulesEngine(); /** * Creates rule engine. */ - protected InExRules createRulesEngine() { - return new InExRules(); + protected InExRules createRulesEngine() { + return new InExRules<>(); } /** * Defines include pattern. */ - public T include(String pattern) { + public FindFile include(final String pattern) { rules.include(pattern); - return (T) this; + return this; } /** * Defines include patterns. */ - public T include(String... patterns) { - for (String pattern : patterns) { + public FindFile include(final String... patterns) { + for (final String pattern : patterns) { rules.include(pattern); } - return (T) this; + return this; } /** * Enables whitelist mode. */ - public T excludeAll() { + public FindFile excludeAll() { rules.whitelist(); - return (T) this; + return this; } /** * Enables blacklist mode. */ - public T includeAll() { + public FindFile includeAll() { rules.blacklist(); - return (T) this; + return this; } /** * Defines exclude pattern. */ - public T exclude(String pattern) { + public FindFile exclude(final String pattern) { rules.exclude(pattern); - return (T) this; + return this; } /** * Defines exclude patterns. */ - public T exclude(String... patterns) { - for (String pattern : patterns) { + public FindFile exclude(final String... patterns) { + for (final String pattern : patterns) { rules.exclude(pattern); } - return (T) this; + return this; } /** @@ -446,17 +460,23 @@ public class FindFile { * File is matched using {@link #getMatchingFilePath(java.io.File) matching file path}. * @see InExRules */ - protected boolean acceptFile(File file) { - String matchingFilePath = getMatchingFilePath(file); + protected boolean acceptFile(final File file) { + final String matchingFilePath = getMatchingFilePath(file); - return rules.match(matchingFilePath); + if (rules.match(matchingFilePath)) { + if (consumers != null) { + consumers.accept(file); + } + return true; + } + return false; } /** * Resolves file path depending on {@link Match matching type} * Returned path is formatted in unix style. */ - protected String getMatchingFilePath(File file) { + protected String getMatchingFilePath(final File file) { String path = null; @@ -479,10 +499,10 @@ public class FindFile { // ---------------------------------------------------------------- next file - protected JoddArrayList pathList; - protected JoddArrayList pathListOriginal; - protected JoddArrayList todoFolders; - protected JoddArrayList todoFiles; + protected LinkedList pathList; + protected LinkedList pathListOriginal; + protected LinkedList todoFolders; + protected LinkedList todoFiles; protected File lastFile; protected File rootFile; @@ -501,12 +521,12 @@ public class FindFile { * Non existing files are ignored. * If path is a folder, it will be scanned for all files. */ - protected void addPath(File path) { + protected void addPath(final File path) { if (!path.exists()) { return; } if (pathList == null) { - pathList = new JoddArrayList(); + pathList = new LinkedList<>(); } pathList.add(path); @@ -538,8 +558,8 @@ public class FindFile { // iterate files if (!todoFiles.isEmpty()) { - FilesIterator filesIterator = todoFiles.getLast(); - File nextFile = filesIterator.next(); + final FilesIterator filesIterator = todoFiles.getLast(); + final File nextFile = filesIterator.next(); if (nextFile == null) { todoFiles.removeLast(); @@ -570,7 +590,7 @@ public class FindFile { // process folders - File folder; + final File folder; boolean initialDir = false; if (todoFolders.isEmpty()) { @@ -603,53 +623,41 @@ public class FindFile { } /** - * Performs scanning. + * Finds all files and returns list of founded files. */ - @SuppressWarnings("StatementWithEmptyBody") - public void find() { - while (nextFile() != null) { - } - } - - /** - * Finds a file. - */ - public void find(FileConsumer fileConsumer) { - File f; - while ((f = nextFile()) != null) { - boolean next = fileConsumer.onFile(f); - - if (!next) { - break; - } + public List findAll() { + final List allFiles = new ArrayList<>(); + File file; + while ((file = nextFile()) != null) { + allFiles.add(file); } + return allFiles; } /** * Initializes file walking. * Separates input files and folders. */ - @SuppressWarnings("unchecked") protected void init() { - rules.smartMode(); + rules.detectMode(); - todoFiles = new JoddArrayList(); - todoFolders = new JoddArrayList(); + todoFiles = new LinkedList<>(); + todoFolders = new LinkedList<>(); if (pathList == null) { - pathList = new JoddArrayList(); + pathList = new LinkedList<>(); return; } if (pathListOriginal == null) { - pathListOriginal = (JoddArrayList) pathList.clone(); + pathListOriginal = (LinkedList) pathList.clone(); } - String[] files = new String[pathList.size()]; + final String[] files = new String[pathList.size()]; int index = 0; - Iterator iterator = pathList.iterator(); + final Iterator iterator = pathList.iterator(); while (iterator.hasNext()) { - File file = iterator.next(); + final File file = iterator.next(); if (file.isFile()) { files[index++] = file.getAbsolutePath(); @@ -658,7 +666,7 @@ public class FindFile { } if (index != 0) { - FilesIterator filesIterator = new FilesIterator(files); + final FilesIterator filesIterator = new FilesIterator(files); todoFiles.add(filesIterator); } } @@ -666,16 +674,19 @@ public class FindFile { /** * Returns file walking iterator. */ + @Override public Iterator iterator() { return new Iterator() { private File nextFile; + @Override public boolean hasNext() { nextFile = nextFile(); return nextFile != null; } + @Override public File next() { if (nextFile == null) { throw new NoSuchElementException(); @@ -683,6 +694,7 @@ public class FindFile { return nextFile; } + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -693,9 +705,9 @@ public class FindFile { protected List> sortComparators; - protected void addComparator(Comparator comparator) { + protected void addComparator(final Comparator comparator) { if (sortComparators == null) { - sortComparators = new ArrayList>(4); + sortComparators = new ArrayList<>(4); } sortComparators.add(comparator); } @@ -703,184 +715,81 @@ public class FindFile { /** * Removes ALL sorting options. */ - public T sortNone() { + public FindFile sortNone() { sortComparators = null; - return (T) this; + return this; } /** * Adds generic sorting. */ - public T sortWith(Comparator fileComparator) { + public FindFile sortWith(final Comparator fileComparator) { addComparator(fileComparator); - return (T) this; + return this; } /** * Puts folders before files. */ - public T sortFoldersFirst() { + public FindFile sortFoldersFirst() { addComparator(new FolderFirstComparator(true)); - return (T) this; + return this; } /** * Puts files before folders. */ - public T sortFoldersLast() { + public FindFile sortFoldersLast() { addComparator(new FolderFirstComparator(false)); - return (T) this; + return this; } /** * Sorts files by file name, using natural sort. */ - public T sortByName() { + public FindFile sortByName() { addComparator(new FileNameComparator(true)); - return (T) this; + return this; } /** * Sorts files by file names descending, using natural sort. */ - public T sortByNameDesc() { + public FindFile sortByNameDesc() { addComparator(new FileNameComparator(false)); - return (T) this; + return this; } /** * Sorts files by file extension. */ - public T sortByExtension() { + public FindFile sortByExtension() { addComparator(new FileExtensionComparator(true)); - return (T) this; + return this; } /** * Sorts files by file extension descending. */ - public T sortByExtensionDesc() { + public FindFile sortByExtensionDesc() { addComparator(new FileExtensionComparator(false)); - return (T) this; + return this; } /** * Sorts files by last modified time. */ - public T sortByTime() { + public FindFile sortByTime() { addComparator(new FileLastModifiedTimeComparator(true)); - return (T) this; + return this; } /** * Sorts files by last modified time descending. */ - public T sortByTimeDesc() { + public FindFile sortByTimeDesc() { addComparator(new FileLastModifiedTimeComparator(false)); - return (T) this; - } - - // ---------------------------------------------------------------- comparators - - public static class FolderFirstComparator implements Comparator, Serializable { - private static final long serialVersionUID = 1; - - protected final int order; - - public FolderFirstComparator(boolean foldersFirst) { - if (foldersFirst) { - order = 1; - } else { - order = -1; - } - } - - public int compare(File file1, File file2) { - if (file1.isFile() && file2.isDirectory()) { - return order; - } - if (file1.isDirectory() && file2.isFile()) { - return -order; - } - return 0; - } + return this; } - public static class FileNameComparator implements Comparator, Serializable { - private static final long serialVersionUID = 1; - - protected final int order; - protected NaturalOrderComparator naturalOrderComparator = new NaturalOrderComparator(true); - - public FileNameComparator(boolean ascending) { - if (ascending) { - order = 1; - } else { - order = -1; - } - } - - public int compare(File file1, File file2) { - int result = naturalOrderComparator.compare(file1.getName(), file2.getName()); - if (result == 0) { - return result; - } - if (result > 0) { - return order; - } - return -order; - } - } - - public static class FileExtensionComparator implements Comparator, Serializable { - private static final long serialVersionUID = 1; - - protected final int order; - - public FileExtensionComparator(boolean ascending) { - if (ascending) { - order = 1; - } else { - order = -1; - } - } - - public int compare(File file1, File file2) { - String ext1 = FileNameUtil.getExtension(file1.getName()); - String ext2 = FileNameUtil.getExtension(file2.getName()); - long diff = ext1.compareToIgnoreCase(ext2); - if (diff == 0) { - return 0; - } - if (diff > 0) { - return order; - } - return -order; - } - } - - public static class FileLastModifiedTimeComparator implements Comparator, Serializable { - private static final long serialVersionUID = 1; - - protected final int order; - - public FileLastModifiedTimeComparator(boolean ascending) { - if (ascending) { - order = 1; - } else { - order = -1; - } - } - - public int compare(File file1, File file2) { - long diff = file1.lastModified() - file2.lastModified(); - if (diff == 0) { - return 0; - } - if (diff > 0) { - return order; - } - return -order; - } - } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFileException.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFileException.java index 74f6535fb..b5a142141 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFileException.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFileException.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.io.findfile; -import com.fr.third.jodd.exception.UncheckedException; import com.fr.third.jodd.exception.UncheckedException; /** @@ -33,15 +32,11 @@ import com.fr.third.jodd.exception.UncheckedException; */ public class FindFileException extends UncheckedException { - public FindFileException(Throwable t) { - super(t); - } - - public FindFileException(String message) { + public FindFileException(final String message) { super(message); } - public FindFileException(String message, Throwable t) { + public FindFileException(final String message, final Throwable t) { super(message, t); } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FolderFirstComparator.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FolderFirstComparator.java new file mode 100644 index 000000000..4f1ec062f --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FolderFirstComparator.java @@ -0,0 +1,53 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.findfile; + +import java.io.File; +import java.io.Serializable; +import java.util.Comparator; + +public class FolderFirstComparator implements Comparator, Serializable { + protected final int order; + + public FolderFirstComparator(final boolean foldersFirst) { + if (foldersFirst) { + order = 1; + } else { + order = -1; + } + } + + @Override + public int compare(final File file1, final File file2) { + if (file1.isFile() && file2.isDirectory()) { + return order; + } + if (file1.isDirectory() && file2.isFile()) { + return -order; + } + return 0; + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/RegExpFindFile.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/RegExpFindFile.java index b71af5bb9..b1ff9f5a9 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/RegExpFindFile.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/RegExpFindFile.java @@ -25,30 +25,32 @@ package com.fr.third.jodd.io.findfile; -import com.fr.third.jodd.util.InExRules; -import com.fr.third.jodd.util.InExRules; +import com.fr.third.jodd.inex.InExRules; import java.util.regex.Pattern; /** * Simple {@link FindFile} that matches file names with regular expression pattern. - * @see jodd.io.findfile.WildcardFindFile + * @see com.fr.third.jodd.io.findfile.WildcardFindFile */ -public class RegExpFindFile extends FindFile { +public class RegExpFindFile extends FindFile { + + public static RegExpFindFile create() { + return new RegExpFindFile(); + } @Override - protected InExRules createRulesEngine() { - return new InExRules() { + protected InExRules createRulesEngine() { + return new InExRules() { @Override - protected void addRule(Object rule, boolean include) { - Pattern pattern = Pattern.compile((String) rule); - super.addRule(pattern, include); + protected Pattern makeRule(final String rule) { + return Pattern.compile(rule); } @Override - public boolean accept(String path, Object pattern, boolean include) { - return ((Pattern) pattern).matcher(path).matches(); + public boolean accept(final String path, final Pattern pattern, final boolean include) { + return pattern.matcher(path).matches(); } }; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/WildcardFindFile.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/WildcardFindFile.java index 2314bf48e..6c0f83cc4 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/WildcardFindFile.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/WildcardFindFile.java @@ -25,22 +25,24 @@ package com.fr.third.jodd.io.findfile; -import com.fr.third.jodd.util.InExRuleMatcher; -import com.fr.third.jodd.util.InExRules; -import com.fr.third.jodd.util.InExRuleMatcher; -import com.fr.third.jodd.util.InExRules; +import com.fr.third.jodd.inex.InExRuleMatcher; +import com.fr.third.jodd.inex.InExRules; /** * {@link FindFile} that matches file names using *, ? * and ** wildcards. * - * @see RegExpFindFile + * @see com.fr.third.jodd.io.findfile.RegExpFindFile */ -public class WildcardFindFile extends FindFile { +public class WildcardFindFile extends FindFile { + + public static WildcardFindFile create() { + return new WildcardFindFile(); + } @Override - protected InExRules createRulesEngine() { - return new InExRules(InExRuleMatcher.WILDCARD_PATH_RULE_MATCHER); + protected InExRules createRulesEngine() { + return new InExRules<>(InExRuleMatcher.WILDCARD_PATH_RULE_MATCHER); } } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUpload.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUpload.java new file mode 100644 index 000000000..34050cee2 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUpload.java @@ -0,0 +1,143 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Encapsulates base for uploaded file. Its instance may be + * either valid, when it represent an uploaded file, or invalid + * when uploaded file doesn't exist or there was a problem with it. + */ +public abstract class FileUpload { + + protected final MultipartRequestInputStream input; + protected final int maxFileSize; + protected final FileUploadHeader header; + + protected FileUpload(final MultipartRequestInputStream input, final int maxFileSize) { + this.input = input; + this.header = input.lastHeader; + this.maxFileSize = maxFileSize; + } + + // ---------------------------------------------------------------- header + + /** + * Returns {@link FileUploadHeader} of uploaded file. + */ + public FileUploadHeader getHeader() { + return header; + } + + // ---------------------------------------------------------------- data + + /** + * Returns all bytes of uploaded file. + */ + public abstract byte[] getFileContent() throws IOException; + + /** + * Returns input stream of uploaded file. + */ + public abstract InputStream getFileInputStream() throws IOException; + + // ---------------------------------------------------------------- size and validity + + protected boolean valid; + + protected int size = -1; + + protected boolean fileTooBig; + + /** + * Returns the file upload size or -1 if file was not uploaded. + */ + public int getSize() { + return size; + } + + /** + * Returns true if file was uploaded. + */ + public boolean isUploaded() { + return size != -1; + } + + /** + * Returns true if upload process went correctly. + * This still does not mean that file is uploaded, e.g. when file + * was not specified on the form. + */ + public boolean isValid() { + return valid; + } + + /** + * Returns max file size or -1 if there is no max file size. + */ + public int getMaxFileSize() { + return maxFileSize; + } + + /** + * Returns true if file is too big. File will be marked as invalid. + */ + public boolean isFileTooBig() { + return fileTooBig; + } + + // ---------------------------------------------------------------- status + + /** + * Returns true if uploaded file content is stored in memory. + */ + public abstract boolean isInMemory(); + + // ---------------------------------------------------------------- process + + /** + * Process request input stream. Note that file size is unknown at this point. + * Therefore, the implementation should set the {@link #getSize() size} + * attribute after successful processing. This method also must set the + * {@link #isValid() valid} attribute. + * + * @see MultipartRequestInputStream + */ + protected abstract void processStream() throws IOException; + + // ---------------------------------------------------------------- toString + + /** + * Returns basic information about the uploaded file. + */ + @Override + public String toString() { + return "FileUpload: uploaded=[" + isUploaded() + "] valid=[" + valid + "] field=[" + + header.getFormFieldName() + "] name=[" + header.getFileName() + "] size=[" + size + ']'; + } +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileConsumer.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUploadFactory.java similarity index 78% rename from fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileConsumer.java rename to fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUploadFactory.java index 2bff90df7..4015c80fd 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileConsumer.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUploadFactory.java @@ -23,15 +23,17 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.io.findfile; +package com.fr.third.jodd.io.upload; -import java.io.File; - -public interface FileConsumer { +/** + * {@link FileUpload} factory for handling uploaded files. Implementations may + * handle uploaded files differently: to store them to memory, directly to disk + * or something else. + */ +public interface FileUploadFactory { /** - * Accepts the file and returns boolean that defines - * if the processing should stop. + * Creates new instance of {@link FileUpload uploaded file}. */ - public boolean onFile(File file); -} + FileUpload create(MultipartRequestInputStream input); +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUploadHeader.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUploadHeader.java new file mode 100644 index 000000000..74ffb63f3 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/FileUploadHeader.java @@ -0,0 +1,200 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload; + +import com.fr.third.jodd.io.FileNameUtil; +import com.fr.third.jodd.util.StringPool; + +/** + * Parses file upload header. + */ +public class FileUploadHeader { + + String dataHeader; + String formFieldName; + + String formFileName; + String path; + String fileName; + + boolean isFile; + String contentType; + String mimeType; + String mimeSubtype; + String contentDisposition; + + + FileUploadHeader(final String dataHeader) { + this.dataHeader = dataHeader; + isFile = dataHeader.indexOf("filename") > 0; + formFieldName = getDataFieldValue(dataHeader, "name"); + if (isFile) { + formFileName = getDataFieldValue(dataHeader, "filename"); + if (formFileName == null) { + return; + } + if (formFileName.length() == 0) { + path = StringPool.EMPTY; + fileName = StringPool.EMPTY; + } + int ls = FileNameUtil.indexOfLastSeparator(formFileName); + if (ls == -1) { + path = StringPool.EMPTY; + fileName = formFileName; + } else { + path = formFileName.substring(0, ls); + fileName = formFileName.substring(ls + 1); + } + if (fileName.length() > 0) { + this.contentType = getContentType(dataHeader); + mimeType = getMimeType(contentType); + mimeSubtype = getMimeSubtype(contentType); + contentDisposition = getContentDisposition(dataHeader); + } + } + } + + // ---------------------------------------------------------------- utilities + + /** + * Gets value of data field or null if field not found. + */ + private String getDataFieldValue(final String dataHeader, final String fieldName) { + String value = null; + String token = String.valueOf((new StringBuffer(String.valueOf(fieldName))).append('=').append('"')); + int pos = dataHeader.indexOf(token); + if (pos > 0) { + int start = pos + token.length(); + int end = dataHeader.indexOf('"', start); + if ((start > 0) && (end > 0)) { + value = dataHeader.substring(start, end); + } + } + return value; + } + + /** + * Strips content type information from requests data header. + * @param dataHeader data header string + * @return content type or an empty string if no content type defined + */ + private String getContentType(final String dataHeader) { + String token = "Content-Type:"; + int start = dataHeader.indexOf(token); + if (start == -1) { + return StringPool.EMPTY; + } + start += token.length(); + return dataHeader.substring(start).trim(); + } + + private String getContentDisposition(final String dataHeader) { + int start = dataHeader.indexOf(':') + 1; + int end = dataHeader.indexOf(';'); + return dataHeader.substring(start, end); + } + + private String getMimeType(final String ContentType) { + int pos = ContentType.indexOf('/'); + if (pos == -1) { + return ContentType; + } + return ContentType.substring(1, pos); + } + + private String getMimeSubtype(final String ContentType) { + int start = ContentType.indexOf('/'); + if (start == -1) { + return ContentType; + } + start++; + return ContentType.substring(start); + } + + + // ---------------------------------------------------------------- public interface + + /** + * Returns true if uploaded data are correctly marked as a file. + * This is true if header contains string 'filename'. + */ + public boolean isFile() { + return isFile; + } + + /** + * Returns form field name. + */ + public String getFormFieldName() { + return formFieldName; + } + + /** + * Returns complete file name as specified at client side. + */ + public String getFormFilename() { + return formFileName; + } + + /** + * Returns file name (base name and extension, without full path data). + */ + public String getFileName() { + return fileName; + } + + /** + * Returns uploaded content type. It is usually in the following form:
+ * mime_type/mime_subtype. + * + * @see #getMimeType() + * @see #getMimeSubtype() + */ + public String getContentType() { + return contentType; + } + + /** + * Returns file types MIME. + */ + public String getMimeType() { + return mimeType; + } + + /** + * Returns file sub type MIME. + */ + public String getMimeSubtype() { + return mimeSubtype; + } + + /** + * Returns content disposition. Usually it is 'form-data'. + */ + public String getContentDisposition() { + return contentDisposition; + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/MultipartRequestInputStream.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/MultipartRequestInputStream.java new file mode 100644 index 000000000..5e88b6631 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/MultipartRequestInputStream.java @@ -0,0 +1,225 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload; + +import com.fr.third.jodd.io.FastByteArrayOutputStream; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Extended input stream based on buffered requests input stream. + * It provides some more functions that might be useful when working + * with uploaded fies. + */ +public class MultipartRequestInputStream extends BufferedInputStream { + + public MultipartRequestInputStream(final InputStream in) { + super(in); + } + + /** + * Reads expected byte. Throws exception on streams end. + */ + public byte readByte() throws IOException { + int i = super.read(); + if (i == -1) { + throw new IOException("End of HTTP request stream reached"); + } + return (byte) i; + } + + /** + * Skips specified number of bytes. + */ + public void skipBytes(final int i) throws IOException { + long len = super.skip(i); + if (len != i) { + throw new IOException("Failed to skip data in HTTP request"); + } + } + + // ---------------------------------------------------------------- boundary + + protected byte[] boundary; + + /** + * Reads boundary from the input stream. + */ + public byte[] readBoundary() throws IOException { + FastByteArrayOutputStream boundaryOutput = new FastByteArrayOutputStream(); + byte b; + // skip optional whitespaces + while ((b = readByte()) <= ' ') { + } + boundaryOutput.write(b); + + // now read boundary chars + while ((b = readByte()) != '\r') { + boundaryOutput.write(b); + } + if (boundaryOutput.size() == 0) { + throw new IOException("Problems with parsing request: invalid boundary"); + } + skipBytes(1); + boundary = new byte[boundaryOutput.size() + 2]; + System.arraycopy(boundaryOutput.toByteArray(), 0, boundary, 2, boundary.length - 2); + boundary[0] = '\r'; + boundary[1] = '\n'; + return boundary; + } + + // ---------------------------------------------------------------- data header + + protected FileUploadHeader lastHeader; + + public FileUploadHeader getLastHeader() { + return lastHeader; + } + + /** + * Reads data header from the input stream. When there is no more + * headers (i.e. end of stream reached), returns null + */ + public FileUploadHeader readDataHeader(final String encoding) throws IOException { + String dataHeader = readDataHeaderString(encoding); + if (dataHeader != null) { + lastHeader = new FileUploadHeader(dataHeader); + } else { + lastHeader = null; + } + return lastHeader; + } + + + protected String readDataHeaderString(final String encoding) throws IOException { + FastByteArrayOutputStream data = new FastByteArrayOutputStream(); + byte b; + while (true) { + // end marker byte on offset +0 and +2 must be 13 + if ((b = readByte()) != '\r') { + data.write(b); + continue; + } + mark(4); + skipBytes(1); + int i = read(); + if (i == -1) { + // reached end of stream + return null; + } + if (i == '\r') { + reset(); + break; + } + reset(); + data.write(b); + } + skipBytes(3); + if (encoding != null) { + return data.toString(encoding); + } else { + return data.toString(); + } + } + + + // ---------------------------------------------------------------- copy + + /** + * Copies bytes from this stream to some output until boundary is + * reached. Returns number of copied bytes. It will throw an exception + * for any irregular behaviour. + */ + public int copyAll(final OutputStream out) throws IOException { + int count = 0; + while (true) { + byte b = readByte(); + if (isBoundary(b)) { + break; + } + out.write(b); + count++; + } + return count; + } + + /** + * Copies max or less number of bytes to output stream. Useful for determining + * if uploaded file is larger then expected. + */ + public int copyMax(final OutputStream out, final int maxBytes) throws IOException { + int count = 0; + while (true) { + byte b = readByte(); + if (isBoundary(b)) { + break; + } + out.write(b); + count++; + if (count == maxBytes) { + return count; + } + } + return count; + } + + /** + * Skips to the boundary and returns total number of bytes skipped. + */ + public int skipToBoundary() throws IOException { + int count = 0; + while (true) { + byte b = readByte(); + count++; + if (isBoundary(b)) { + break; + } + } + return count; + } + + /** + * Checks if the current byte (i.e. one that was read last) represents + * the very first byte of the boundary. + */ + public boolean isBoundary(byte b) throws IOException { + int boundaryLen = boundary.length; + mark(boundaryLen + 1); + int bpos = 0; + while (b == boundary[bpos]) { + b = readByte(); + bpos++; + if (bpos == boundaryLen) { + return true; // boundary found! + } + } + reset(); + return false; + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/MultipartStreamParser.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/MultipartStreamParser.java new file mode 100644 index 000000000..faafc010f --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/MultipartStreamParser.java @@ -0,0 +1,246 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload; + +import com.fr.third.jodd.io.FastByteArrayOutputStream; +import com.fr.third.jodd.io.upload.impl.MemoryFileUploadFactory; +import com.fr.third.jodd.util.ArraysUtil; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Generic, serlvets-free multipart request input stream parser. + */ +public class MultipartStreamParser { + + protected FileUploadFactory fileUploadFactory; + protected Map requestParameters; + protected Map requestFiles; + + public MultipartStreamParser() { + this(null); + } + + public MultipartStreamParser(final FileUploadFactory fileUploadFactory) { + this.fileUploadFactory = (fileUploadFactory == null ? new MemoryFileUploadFactory() : fileUploadFactory); + } + + private boolean parsed; + + /** + * Sets the loaded flag that indicates that input stream is loaded and parsed. + * Throws an exception if stream already loaded. + */ + protected void setParsed() throws IOException { + if (parsed) { + throw new IOException("Multi-part request already parsed"); + } + parsed = true; + } + + /** + * Returns true if multi-part request is already loaded. + */ + public boolean isParsed() { + return parsed; + } + + // ---------------------------------------------------------------- load and extract + + protected void putFile(final String name, final FileUpload value) { + if (requestFiles == null) { + requestFiles = new HashMap<>(); + } + + FileUpload[] fileUploads = requestFiles.get(name); + + if (fileUploads != null) { + fileUploads = ArraysUtil.append(fileUploads, value); + } else { + fileUploads = new FileUpload[] {value}; + } + + requestFiles.put(name, fileUploads); + } + + protected void putParameters(final String name, final String[] values) { + if (requestParameters == null) { + requestParameters = new HashMap<>(); + } + requestParameters.put(name, values); + } + + protected void putParameter(final String name, final String value) { + if (requestParameters == null) { + requestParameters = new HashMap<>(); + } + + String[] params = requestParameters.get(name); + + if (params != null) { + params = ArraysUtil.append(params, value); + } else { + params = new String[] {value}; + } + + requestParameters.put(name, params); + } + + /** + * Extracts uploaded files and parameters from the request data. + */ + public void parseRequestStream(final InputStream inputStream, final String encoding) throws IOException { + setParsed(); + + MultipartRequestInputStream input = new MultipartRequestInputStream(inputStream); + input.readBoundary(); + while (true) { + FileUploadHeader header = input.readDataHeader(encoding); + if (header == null) { + break; + } + + if (header.isFile) { + String fileName = header.fileName; + if (fileName.length() > 0) { + if (header.contentType.indexOf("application/x-macbinary") > 0) { + input.skipBytes(128); + } + } + FileUpload newFile = fileUploadFactory.create(input); + newFile.processStream(); + if (fileName.length() == 0) { + // file was specified, but no name was provided, therefore it was not uploaded + if (newFile.getSize() == 0) { + newFile.size = -1; + } + } + putFile(header.formFieldName, newFile); + } else { + // no file, therefore it is regular form parameter. + FastByteArrayOutputStream fbos = new FastByteArrayOutputStream(); + input.copyAll(fbos); + String value = encoding != null ? new String(fbos.toByteArray(), encoding) : new String(fbos.toByteArray()); + putParameter(header.formFieldName, value); + } + + input.skipBytes(1); + input.mark(1); + + // read byte, but may be end of stream + int nextByte = input.read(); + if (nextByte == -1 || nextByte == '-') { + input.reset(); + break; + } + input.reset(); + } + } + + // ---------------------------------------------------------------- parameters + + + /** + * Returns single value of a parameter. If parameter name is used for + * more then one parameter, only the first one will be returned. + * + * @return parameter value, or null if not found + */ + public String getParameter(final String paramName) { + if (requestParameters == null) { + return null; + } + String[] values = requestParameters.get(paramName); + if ((values != null) && (values.length > 0)) { + return values[0]; + } + return null; + } + + /** + * Returns the names of the parameters contained in this request. + */ + public Set getParameterNames() { + if (requestParameters == null) { + return Collections.emptySet(); + } + return requestParameters.keySet(); + } + + /** + * Returns all values all of the values the given request parameter has. + */ + public String[] getParameterValues(final String paramName) { + if (requestParameters == null) { + return null; + } + return requestParameters.get(paramName); + } + + + /** + * Returns uploaded file. + * @param paramName parameter name of the uploaded file + * @return uploaded file or null if parameter name not found + */ + public FileUpload getFile(final String paramName) { + if (requestFiles == null) { + return null; + } + FileUpload[] values = requestFiles.get(paramName); + if ((values != null) && (values.length > 0)) { + return values[0]; + } + return null; + } + + + /** + * Returns all uploaded files the given request parameter has. + */ + public FileUpload[] getFiles(final String paramName) { + if (requestFiles == null) { + return null; + } + return requestFiles.get(paramName); + } + + /** + * Returns parameter names of all uploaded files. + */ + public Set getFileParameterNames() { + if (requestFiles == null) { + return Collections.emptySet(); + } + return requestFiles.keySet(); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/AdaptiveFileUpload.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/AdaptiveFileUpload.java new file mode 100644 index 000000000..48ff6759b --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/AdaptiveFileUpload.java @@ -0,0 +1,261 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload.impl; + +import com.fr.third.jodd.core.JoddCore; +import com.fr.third.jodd.io.FastByteArrayOutputStream; +import com.fr.third.jodd.io.FileNameUtil; +import com.fr.third.jodd.io.FileUtil; +import com.fr.third.jodd.io.StreamUtil; +import com.fr.third.jodd.io.upload.FileUpload; +import com.fr.third.jodd.io.upload.MultipartRequestInputStream; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Smart {@link FileUpload} implementation that defer the action of what to do with uploaded file + * for later. Internally, it stores uploaded file either in memory if it is small, or, in all + * other cases, it stores them in TEMP folder. + */ +public class AdaptiveFileUpload extends FileUpload { + + protected static final String TMP_FILE_SUFFIX = ".upload.tmp"; + + protected final int memoryThreshold; + protected final File uploadPath; + protected final boolean breakOnError; + protected final String[] fileExtensions; + protected final boolean allowFileExtensions; + + AdaptiveFileUpload(final MultipartRequestInputStream input, final int memoryThreshold, final File uploadPath, final int maxFileSize, final boolean breakOnError, final String[] extensions, final boolean allowed) { + super(input, maxFileSize); + this.memoryThreshold = memoryThreshold; + this.uploadPath = uploadPath; + this.breakOnError = breakOnError; + this.fileExtensions = extensions; + this.allowFileExtensions = allowed; + } + + // ---------------------------------------------------------------- settings + + public int getMemoryThreshold() { + return memoryThreshold; + } + + public File getUploadPath() { + return uploadPath; + } + + public boolean isBreakOnError() { + return breakOnError; + } + + public String[] getFileExtensions() { + return fileExtensions; + } + + public boolean isAllowFileExtensions() { + return allowFileExtensions; + } + + // ---------------------------------------------------------------- properties + + protected File tempFile; + protected byte[] data; + + /** + * Returns true if file upload resides in memory. + */ + @Override + public boolean isInMemory() { + return data != null; + } + + // ---------------------------------------------------------------- process + + + protected boolean matchFileExtension() throws IOException { + String fileNameExtension = FileNameUtil.getExtension(getHeader().getFileName()); + for (String fileExtension : fileExtensions) { + if (fileNameExtension.equalsIgnoreCase(fileExtension)) { + if (!allowFileExtensions) { // extension matched and it is not allowed + if (breakOnError) { + throw new IOException("Upload filename extension not allowed: " + fileNameExtension); + } + size = input.skipToBoundary(); + return false; + } + return true; // extension matched and it is allowed. + } + } + if (allowFileExtensions) { // extension is not one of the allowed ones. + if (breakOnError) { + throw new IOException("Upload filename extension not allowed: " + fileNameExtension); + } + size = input.skipToBoundary(); + return false; + } + return true; + } + + /** + * Determines if upload is allowed. + */ + protected boolean checkUpload() throws IOException { + if (fileExtensions != null) { + if (!matchFileExtension()) { + return false; + } + } + return true; + } + + @Override + protected void processStream() throws IOException { + if (!checkUpload()) { + return; + } + size = 0; + if (memoryThreshold > 0) { + FastByteArrayOutputStream fbaos = new FastByteArrayOutputStream(memoryThreshold + 1); + int written = input.copyMax(fbaos, memoryThreshold + 1); + data = fbaos.toByteArray(); + if (written <= memoryThreshold) { + size = data.length; + valid = true; + return; + } + } + + tempFile = FileUtil.createTempFile(JoddCore.tempFilePrefix, TMP_FILE_SUFFIX, uploadPath); + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile)); + if (data != null) { + size = data.length; + out.write(data); + data = null; // not needed anymore + } + boolean deleteTempFile = false; + try { + if (maxFileSize == -1) { + size += input.copyAll(out); + } else { + size += input.copyMax(out, maxFileSize - size + 1); // one more byte to detect larger files + if (size > maxFileSize) { + deleteTempFile = true; + fileTooBig = true; + valid = false; + if (breakOnError) { + throw new IOException("File upload (" + header.getFileName() + ") too big, > " + maxFileSize); + } + input.skipToBoundary(); + return; + } + } + valid = true; + } finally { + StreamUtil.close(out); + if (deleteTempFile) { + tempFile.delete(); + tempFile = null; + } + } + } + + // ---------------------------------------------------------------- operations + + + /** + * Deletes file uploaded item from disk or memory. + */ + public void delete() { + if (tempFile != null) { + tempFile.delete(); + } + if (data != null) { + data = null; + } + } + + /** + * Writes file uploaded item. + */ + public File write(final String destination) throws IOException { + return write(new File(destination)); + } + + /** + * Writes file upload item to destination folder or to destination file. + * Returns the destination file. + */ + public File write(File destination) throws IOException { + if (destination.isDirectory()) { + destination = new File(destination, this.header.getFileName()); + } + if (data != null) { + FileUtil.writeBytes(destination, data); + } else { + if (tempFile != null) { + FileUtil.move(tempFile, destination); + } + } + return destination; + } + + /** + * Returns the content of file upload item. + */ + @Override + public byte[] getFileContent() throws IOException { + if (data != null) { + return data; + } + if (tempFile != null) { + return FileUtil.readBytes(tempFile); + } + return null; + } + + @Override + public InputStream getFileInputStream() throws IOException { + if (data != null) { + return new BufferedInputStream(new ByteArrayInputStream(data)); + } + if (tempFile != null) { + return new BufferedInputStream(new FileInputStream(tempFile)); + } + return null; + } + + + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/AdaptiveFileUploadFactory.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/AdaptiveFileUploadFactory.java new file mode 100644 index 000000000..d91ac56bd --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/AdaptiveFileUploadFactory.java @@ -0,0 +1,124 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload.impl; + +import com.fr.third.jodd.io.upload.FileUpload; +import com.fr.third.jodd.io.upload.FileUploadFactory; +import com.fr.third.jodd.io.upload.MultipartRequestInputStream; + +import java.io.File; + +/** + * + * Factory for {@link AdaptiveFileUpload}. + */ +public class AdaptiveFileUploadFactory implements FileUploadFactory { + + protected int memoryThreshold = 8192; + protected File uploadPath; + protected int maxFileSize = 102400; + protected boolean breakOnError; + protected String[] fileExtensions; + protected boolean allowFileExtensions = true; + + /** + * {@inheritDoc} + */ + @Override + public FileUpload create(final MultipartRequestInputStream input) { + return new AdaptiveFileUpload(input, memoryThreshold, uploadPath, maxFileSize, breakOnError, fileExtensions, allowFileExtensions); + } + + // ---------------------------------------------------------------- properties + + public int getMemoryThreshold() { + return memoryThreshold; + } + /** + * Specifies per file memory limit for keeping uploaded files in the memory. + */ + public AdaptiveFileUploadFactory setMemoryThreshold(final int memoryThreshold) { + if (memoryThreshold >= 0) { + this.memoryThreshold = memoryThreshold; + } + return this; + } + + public File getUploadPath() { + return uploadPath; + } + + /** + * Specifies the upload path. If set to null default + * system TEMP path will be used. + */ + public AdaptiveFileUploadFactory setUploadPath(final File uploadPath) { + this.uploadPath = uploadPath; + return this; + } + + public int getMaxFileSize() { + return maxFileSize; + } + + /** + * Sets maximum file upload size. Setting to -1 + * disables this constraint. + */ + public AdaptiveFileUploadFactory setMaxFileSize(final int maxFileSize) { + this.maxFileSize = maxFileSize; + return this; + } + + public boolean isBreakOnError() { + return breakOnError; + } + + public AdaptiveFileUploadFactory setBreakOnError(final boolean breakOnError) { + this.breakOnError = breakOnError; + return this; + } + + /** + * Specifies if upload should break on error. + */ + public AdaptiveFileUploadFactory breakOnError(final boolean breakOnError) { + this.breakOnError = breakOnError; + return this; + } + + /** + * Allow or disallow set of file extensions. Only one rule can be active at time, + * which means user can only specify extensions that are either allowed or disallowed. + * Setting this value to null will turn this feature off. + */ + public AdaptiveFileUploadFactory setFileExtensions(final String[] fileExtensions, final boolean allow) { + this.fileExtensions = fileExtensions; + this.allowFileExtensions = allow; + return this; + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/DiskFileUpload.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/DiskFileUpload.java new file mode 100644 index 000000000..9edf18988 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/DiskFileUpload.java @@ -0,0 +1,119 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload.impl; + +import com.fr.third.jodd.io.FileUtil; +import com.fr.third.jodd.io.StreamUtil; +import com.fr.third.jodd.io.upload.FileUpload; +import com.fr.third.jodd.io.upload.MultipartRequestInputStream; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * {@link FileUpload} that saves uploaded files directly to destination folder. + */ +public class DiskFileUpload extends FileUpload { + + protected final File destFolder; + + DiskFileUpload(final MultipartRequestInputStream input, final File destinationFolder, final int maxFileSize) { + super(input, maxFileSize); + this.destFolder = destinationFolder; + } + + /** + * Returns false as uploaded file is stored on disk. + */ + @Override + public boolean isInMemory() { + return false; + } + + /** + * Returns destination folder. + */ + public File getDestinationFolder() { + return destFolder; + } + + /** + * Returns uploaded and saved file. + */ + public File getFile() { + return file; + } + + protected File file; + + /** + * Returns files content from disk file. + * If error occurs, it returns null + */ + @Override + public byte[] getFileContent() throws IOException { + return FileUtil.readBytes(file); + } + + /** + * Returns new buffered file input stream. + */ + @Override + public InputStream getFileInputStream() throws IOException { + return new BufferedInputStream(new FileInputStream(file)); + } + + @Override + protected void processStream() throws IOException { + file = new File(destFolder, header.getFileName()); + final OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); + + size = 0; + try { + if (maxFileSize == -1) { + size = input.copyAll(out); + } else { + size = input.copyMax(out, maxFileSize + 1); // one more byte to detect larger files + if (size > maxFileSize) { + fileTooBig = true; + valid = false; + input.skipToBoundary(); + return; + } + } + valid = true; + } finally { + StreamUtil.close(out); + } + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/DiskFileUploadFactory.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/DiskFileUploadFactory.java new file mode 100644 index 000000000..7bdabfaa4 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/DiskFileUploadFactory.java @@ -0,0 +1,95 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload.impl; + +import com.fr.third.jodd.io.upload.FileUpload; +import com.fr.third.jodd.io.upload.FileUploadFactory; +import com.fr.third.jodd.io.upload.MultipartRequestInputStream; +import com.fr.third.jodd.system.SystemUtil; + +import java.io.File; +import java.io.IOException; + +/** + * Factory for {@link com.fr.third.jodd.io.upload.impl.DiskFileUpload} + */ +public class DiskFileUploadFactory implements FileUploadFactory { + + protected File destFolder; + + protected int maxFileSize = 102400; + + public DiskFileUploadFactory() throws IOException { + this(SystemUtil.info().getTempDir()); + } + + public DiskFileUploadFactory(final String destFolder) throws IOException { + this(destFolder, 102400); + + } + + public DiskFileUploadFactory(final String destFolder, final int maxFileSize) throws IOException { + setUploadDir(destFolder); + this.maxFileSize = maxFileSize; + } + + + public DiskFileUploadFactory setUploadDir(String destFolder) throws IOException { + if (destFolder == null) { + destFolder = SystemUtil.info().getTempDir(); + } + File destination = new File(destFolder); + if (!destination.exists()) { + destination.mkdirs(); + } + if (!destination.isDirectory()) { + throw new IOException("Invalid destination folder: " + destFolder); + } + this.destFolder = destination; + return this; + } + + public int getMaxFileSize() { + return maxFileSize; + } + + /** + * Sets maximum file upload size. Setting to -1 will disable this constraint. + */ + public DiskFileUploadFactory setMaxFileSize(final int maxFileSize) { + this.maxFileSize = maxFileSize; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public FileUpload create(final MultipartRequestInputStream input) { + return new DiskFileUpload(input, destFolder, maxFileSize); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/MemoryFileUpload.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/MemoryFileUpload.java new file mode 100644 index 000000000..02c687466 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/MemoryFileUpload.java @@ -0,0 +1,96 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload.impl; + +import com.fr.third.jodd.io.FastByteArrayOutputStream; +import com.fr.third.jodd.io.upload.FileUpload; +import com.fr.third.jodd.io.upload.MultipartRequestInputStream; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * {@link FileUpload} that stores uploaded files in memory byte array. + */ +public class MemoryFileUpload extends FileUpload { + + MemoryFileUpload(final MultipartRequestInputStream input, final int maxFileSize) { + super(input, maxFileSize); + } + + // ---------------------------------------------------------------- logic + + protected byte[] data; + + /** + * Returns byte array containing uploaded file data. + */ + @Override + public byte[] getFileContent() { + return data; + } + + /** + * Returns true as uploaded file is stored in memory. + */ + @Override + public boolean isInMemory() { + return true; + } + + /** + * Returns byte array input stream. + */ + @Override + public InputStream getFileInputStream() { + return new ByteArrayInputStream(data); + } + + /** + * Reads data from input stream into byte array and stores file size. + */ + @Override + public void processStream() throws IOException { + FastByteArrayOutputStream out = new FastByteArrayOutputStream(); + size = 0; + if (maxFileSize == -1) { + size += input.copyAll(out); + } else { + size += input.copyMax(out, maxFileSize + 1); // one more byte to detect larger files + if (size > maxFileSize) { + fileTooBig = true; + valid = false; + input.skipToBoundary(); + return; + } + } + data = out.toByteArray(); + size = data.length; + valid = true; + } + +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/MemoryFileUploadFactory.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/MemoryFileUploadFactory.java new file mode 100644 index 000000000..6290b7ce5 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/MemoryFileUploadFactory.java @@ -0,0 +1,59 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.io.upload.impl; + +import com.fr.third.jodd.io.upload.FileUpload; +import com.fr.third.jodd.io.upload.FileUploadFactory; +import com.fr.third.jodd.io.upload.MultipartRequestInputStream; + +/** + * Factory for {@link com.fr.third.jodd.io.upload.impl.MemoryFileUpload}. + */ +public class MemoryFileUploadFactory implements FileUploadFactory { + + protected int maxFileSize = 102400; + + public int getMaxFileSize() { + return maxFileSize; + } + + /** + * Sets maximum file upload size. Setting to -1 will disable this constraint. + */ + public MemoryFileUploadFactory setMaxFileSize(final int maxFileSize) { + this.maxFileSize = maxFileSize; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public FileUpload create(final MultipartRequestInputStream input) { + return new MemoryFileUpload(input, maxFileSize); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/package-info.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/package-info.java new file mode 100644 index 000000000..53d7a6805 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/impl/package-info.java @@ -0,0 +1,29 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +/** + * Various implementations of uploaded files and their factories. + */ +package com.fr.third.jodd.io.upload.impl; \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/package-info.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/package-info.java new file mode 100644 index 000000000..7a6e30147 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/upload/package-info.java @@ -0,0 +1,29 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +/** + * Multipart streams and file uploads. + */ +package com.fr.third.jodd.io.upload; \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcher.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcher.java index ae16a505c..006c30c38 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcher.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcher.java @@ -25,45 +25,33 @@ package com.fr.third.jodd.io.watch; -import com.fr.third.jodd.mutable.MutableLong; -import com.fr.third.jodd.util.StringPool; -import com.fr.third.jodd.util.Wildcard; import com.fr.third.jodd.io.FileUtil; import com.fr.third.jodd.mutable.MutableLong; +import com.fr.third.jodd.util.Consumers; import com.fr.third.jodd.util.StringPool; import com.fr.third.jodd.util.Wildcard; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Timer; import java.util.TimerTask; +import java.util.function.Consumer; public class DirWatcher { - /** - * Events that describes file change. - */ - public enum Event { - CREATED, - DELETED, - MODIFIED - } - protected final File dir; - protected HashMap map = new HashMap(); + protected HashMap map = new HashMap<>(); protected int filesCount; - protected List listeners = new ArrayList(); + protected Consumers listeners = Consumers.empty(); protected String[] patterns; /** * Creates new watcher on specified directory. * You can set file patterns {@link #monitor(String...) later}. */ - public DirWatcher(String dir) { + public DirWatcher(final String dir) { this(dir, null); } @@ -71,7 +59,7 @@ public class DirWatcher { * Creates new watched on specified directory with given set of * wildcard patterns for file names. */ - public DirWatcher(String dirName, String... patterns) { + public DirWatcher(final String dirName, final String... patterns) { this.dir = new File(dirName); if (!dir.exists() || !dir.isDirectory()) { @@ -86,14 +74,14 @@ public class DirWatcher { * from watched folder. */ protected void init() { - File[] filesArray = dir.listFiles(); + final File[] filesArray = dir.listFiles(); filesCount = 0; if (filesArray != null) { filesCount = filesArray.length; - for (File file : filesArray) { + for (final File file : filesArray) { if (!acceptFile(file)) { continue; } @@ -111,17 +99,17 @@ public class DirWatcher { /** * Enables or disables if dot files should be watched. */ - public DirWatcher ignoreDotFiles(boolean ignoreDotFiles) { + public DirWatcher ignoreDotFiles(final boolean ignoreDotFiles) { this.ignoreDotFiles = ignoreDotFiles; return this; } /** * Defines if watcher should start blank and consider all present - * files as {@link jodd.io.watch.DirWatcher.Event#CREATED created}. + * files as {@link com.fr.third.jodd.io.watch.DirWatcherEvent.Type#CREATED created}. * By default all existing files will consider as existing ones. */ - public DirWatcher startBlank(boolean startBlank) { + public DirWatcher startBlank(final boolean startBlank) { this.startBlank = startBlank; return this; } @@ -129,7 +117,7 @@ public class DirWatcher { /** * Defines patterns to scan. */ - public DirWatcher monitor(String... patterns) { + public DirWatcher monitor(final String... patterns) { this.patterns = patterns; return this; } @@ -139,12 +127,12 @@ public class DirWatcher { /** * Accepts if a file is going to be watched. */ - protected boolean acceptFile(File file) { + protected boolean acceptFile(final File file) { if (!file.isFile()) { return false; // ignore non-files } - String fileName = file.getName(); + final String fileName = file.getName(); if (ignoreDotFiles) { if (fileName.startsWith(StringPool.DOT)) { @@ -174,13 +162,13 @@ public class DirWatcher { /** * Enables usage of provided watch file. */ - public DirWatcher useWatchFile(String name) { + public DirWatcher useWatchFile(final String name) { watchFile = new File(dir, name); if (!watchFile.isFile() || !watchFile.exists()) { try { FileUtil.touch(watchFile); - } catch (IOException ioex) { + } catch (final IOException ioex) { throw new DirWatcherException("Invalid watch file: " + name, ioex); } } @@ -198,7 +186,7 @@ public class DirWatcher { /** * Starts the watcher. */ - public void start(long pollingInterval) { + public void start(final long pollingInterval) { if (timer == null) { if (!startBlank) { init(); @@ -223,6 +211,7 @@ public class DirWatcher { protected boolean running; + @Override public final void run() { if (running) { // if one task takes too long, don't fire another one @@ -232,7 +221,7 @@ public class DirWatcher { if (watchFile != null) { // wait for watch file changes - long last = watchFile.lastModified(); + final long last = watchFile.lastModified(); if (last <= watchFileLastAccessTime) { running = false; @@ -243,7 +232,7 @@ public class DirWatcher { // scan! - File[] filesArray = dir.listFiles(); + final File[] filesArray = dir.listFiles(); if (filesArray == null) { running = false; @@ -254,42 +243,42 @@ public class DirWatcher { // check if there might be a delete file if (filesArray.length < filesCount) { - deletedFiles = new HashSet(map.keySet()); + deletedFiles = new HashSet<>(map.keySet()); } filesCount = filesArray.length; // scan the files and check for modification/addition - for (File file : filesArray) { + for (final File file : filesArray) { if (!acceptFile(file)) { continue; } - MutableLong currentTime = map.get(file); + final MutableLong currentTime = map.get(file); if (deletedFiles != null) { deletedFiles.remove(file); } - long lastModified = file.lastModified(); + final long lastModified = file.lastModified(); if (currentTime == null) { // new file map.put(file, new MutableLong(lastModified)); - onChange(file, Event.CREATED); + onChange(DirWatcherEvent.Type.CREATED, file); } else if (currentTime.longValue() != lastModified) { // modified file - currentTime.setValue(lastModified); - onChange(file, Event.MODIFIED); + currentTime.set(lastModified); + onChange(DirWatcherEvent.Type.MODIFIED, file); } } // check for deleted files if (deletedFiles != null) { - for (File deletedFile : deletedFiles) { + for (final File deletedFile : deletedFiles) { map.remove(deletedFile); - onChange(deletedFile, Event.DELETED); + onChange(DirWatcherEvent.Type.DELETED, deletedFile); } } @@ -301,28 +290,31 @@ public class DirWatcher { /** * Triggers listeners on file change. */ - protected void onChange(File file, Event event) { - for (DirWatcherListener listener : listeners) { - listener.onChange(file, event); - } + protected void onChange(final DirWatcherEvent.Type type, final File file) { + listeners.accept(new DirWatcherEvent(type, file)); } // ---------------------------------------------------------------- listeners /** - * Registers {@link DirWatcherListener listener}. + * Registers {@link com.fr.third.jodd.io.watch.DirWatcherEvent consumer}. */ - public void register(DirWatcherListener dirWatcherListener) { - if (!listeners.contains(dirWatcherListener)) { - listeners.add(dirWatcherListener); - } + public void register(final Consumer dirWatcherListener) { + listeners.add(dirWatcherListener); } /** - * Removes registered {@link DirWatcherListener listener}. + * Removes registered {@link com.fr.third.jodd.io.watch.DirWatcherEvent consumer}. */ - public void remove(DirWatcherListener dirWatcherListener) { + public void remove(final Consumer dirWatcherListener) { listeners.remove(dirWatcherListener); } -} \ No newline at end of file + /** + * Removes all event consumers.. + */ + public void clear() { + listeners.clear(); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherEvent.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherEvent.java new file mode 100644 index 000000000..380335d7d --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherEvent.java @@ -0,0 +1,70 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +package com.fr.third.jodd.io.watch; + +import java.io.File; + +public class DirWatcherEvent { + + private final Type type; + private final File target; + private final long timestamp; + + /** + * Event type that describes file change. + */ + public enum Type { + CREATED, + DELETED, + MODIFIED + } + + DirWatcherEvent(final Type type, final File target) { + this.type = type; + this.target = target; + this.timestamp = System.currentTimeMillis(); + } + + /** + * Returns event type. + */ + public Type type() { + return type; + } + + /** + * Returns event target. + */ + public File target() { + return target; + } + + /** + * Returns event creation timestamp. + */ + public long timestamp() { + return timestamp; + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherException.java b/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherException.java index 8ee11549d..cd127fe2b 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherException.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherException.java @@ -25,19 +25,18 @@ package com.fr.third.jodd.io.watch; -import com.fr.third.jodd.exception.UncheckedException; import com.fr.third.jodd.exception.UncheckedException; /** - * Exception for {@link DirWatcher}. + * Exception for {@link com.fr.third.jodd.io.watch.DirWatcher}. */ public class DirWatcherException extends UncheckedException { - public DirWatcherException(String message) { + public DirWatcherException(final String message) { super(message); } - public DirWatcherException(String message, Throwable t) { + public DirWatcherException(final String message, final Throwable t) { super(message, t); } } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/ValueHolderWrapper.java b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/LazyValue.java similarity index 66% rename from fine-jodd/src/main/java/com/fr/third/jodd/mutable/ValueHolderWrapper.java rename to fine-jodd/src/main/java/com/fr/third/jodd/mutable/LazyValue.java index ca5e86893..4c2cc4916 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/ValueHolderWrapper.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/LazyValue.java @@ -25,51 +25,45 @@ package com.fr.third.jodd.mutable; +import java.util.function.Supplier; + /** - * Creates {@link ValueHolder} instances. + * Holder of a value that is computed lazy. */ -public class ValueHolderWrapper { +public class LazyValue implements Supplier { - /** - * Creates new empty {@link ValueHolder}. - */ - public static ValueHolder create() { - return new ValueHolderImpl(null); - } + private Supplier supplier; + private volatile boolean initialized; + private T value; /** - * Wraps existing instance to {@link ValueHolder}. + * Creates new instance of LazyValue. */ - public static ValueHolder wrap(final T value) { - return new ValueHolderImpl(value); + public static LazyValue of(final Supplier supplier) { + return new LazyValue<>(supplier); } - static class ValueHolderImpl implements ValueHolder { - - T value; - - ValueHolderImpl(T v) { - this.value = v; - } - - public T value() { - return value; - } - - public void value(T value) { - this.value = value; - } + private LazyValue(final Supplier supplier) { + this.supplier = supplier; + } - /** - * Simple to-string representation. - */ - @Override - public String toString() { - if (value == null) { - return "{null}"; + /** + * Returns the value. Value will be computed on first call. + */ + @Override + public T get() { + if (!initialized) { + synchronized (this) { + if (!initialized) { + final T t = supplier.get(); + value = t; + initialized = true; + supplier = null; + return t; + } } - return '{' + value.toString() + '}'; } - + return value; } -} \ No newline at end of file + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableBoolean.java b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableBoolean.java index c674a5abe..7f7bf5b3e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableBoolean.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableBoolean.java @@ -30,22 +30,26 @@ package com.fr.third.jodd.mutable; */ public final class MutableBoolean implements Comparable, Cloneable { + public static MutableBoolean of(final boolean value) { + return new MutableBoolean(value); + } + public MutableBoolean() { } - public MutableBoolean(boolean value) { + public MutableBoolean(final boolean value) { this.value = value; } - public MutableBoolean(String value) { + public MutableBoolean(final String value) { this.value = Boolean.valueOf(value).booleanValue(); } - public MutableBoolean(Boolean value) { + public MutableBoolean(final Boolean value) { this.value = value.booleanValue(); } - public MutableBoolean(Number number) { + public MutableBoolean(final Number number) { this.value = number.intValue() != 0; } @@ -60,18 +64,22 @@ public final class MutableBoolean implements Comparable, Cloneab /** * Returns mutable value. */ - public boolean getValue() { + public boolean get() { return value; } /** * Sets mutable value. */ - public void setValue(boolean value) { + public void set(final boolean value) { this.value = value; } - public void setValue(Boolean value) { + /** + * Sets mutable value. Throws exception if boolean value is + * null. + */ + public void set(final Boolean value) { this.value = value.booleanValue(); } @@ -102,7 +110,7 @@ public final class MutableBoolean implements Comparable, Cloneab * false otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj != null) { if ( ((Boolean)this.value).getClass() == obj.getClass() ) { return value == ((Boolean) obj).booleanValue(); @@ -113,13 +121,14 @@ public final class MutableBoolean implements Comparable, Cloneab } return false; } - + // ---------------------------------------------------------------- compare /** * Compares value of two same instances. */ - public int compareTo(MutableBoolean o) { + @Override + public int compareTo(final MutableBoolean o) { return (value == o.value) ? 0 : (!value ? -1 : 1); } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableByte.java b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableByte.java index f30b2801f..073182400 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableByte.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableByte.java @@ -30,18 +30,22 @@ package com.fr.third.jodd.mutable; */ public final class MutableByte extends Number implements Comparable, Cloneable { + public static MutableByte of(final byte value) { + return new MutableByte(value); + } + public MutableByte() { } - public MutableByte(byte value) { + public MutableByte(final byte value) { this.value = value; } - public MutableByte(String value) { + public MutableByte(final String value) { this.value = Byte.parseByte(value); } - public MutableByte(Number number) { + public MutableByte(final Number number) { this.value = number.byteValue(); } @@ -55,21 +59,21 @@ public final class MutableByte extends Number implements Comparable /** * Returns mutable value. */ - public byte getValue() { + public byte get() { return value; } /** * Sets mutable value. */ - public void setValue(byte value) { + public void set(final byte value) { this.value = value; } /** * Sets mutable value from a Number. */ - public void setValue(Number value) { + public void set(final Number value) { this.value = value.byteValue(); } @@ -99,7 +103,7 @@ public final class MutableByte extends Number implements Comparable * false otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj != null) { if ( ((Byte)this.value).getClass() == obj.getClass() ) { return value == ((Byte) obj).byteValue(); @@ -150,7 +154,8 @@ public final class MutableByte extends Number implements Comparable /** * Compares value of two same instances. */ - public int compareTo(MutableByte other) { + @Override + public int compareTo(final MutableByte other) { return value < other.value ? -1 : (value == other.value ? 0 : 1); } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableDouble.java b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableDouble.java index b0de2cbb5..0a9b6212e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableDouble.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableDouble.java @@ -30,18 +30,22 @@ package com.fr.third.jodd.mutable; */ public final class MutableDouble extends Number implements Comparable, Cloneable { + public static MutableDouble of(final double value) { + return new MutableDouble(value); + } + public MutableDouble() { } - public MutableDouble(double value) { + public MutableDouble(final double value) { this.value = value; } - public MutableDouble(String value) { + public MutableDouble(final String value) { this.value = Double.parseDouble(value); } - public MutableDouble(Number number) { + public MutableDouble(final Number number) { this.value = number.doubleValue(); } @@ -55,21 +59,21 @@ public final class MutableDouble extends Number implements Comparablefalse otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj != null) { if ( ((Double)this.value).getClass() == obj.getClass() ) { return Double.doubleToLongBits(value) == Double.doubleToLongBits(((Double) obj).doubleValue()); @@ -165,7 +169,8 @@ public final class MutableDouble extends Number implements Comparable, Cloneable { + public static MutableFloat of(final float value) { + return new MutableFloat(value); + } + public MutableFloat() { } - public MutableFloat(float value) { + public MutableFloat(final float value) { this.value = value; } - public MutableFloat(String value) { + public MutableFloat(final String value) { this.value = Float.parseFloat(value); } - public MutableFloat(Number number) { + public MutableFloat(final Number number) { this.value = number.floatValue(); } @@ -55,21 +59,21 @@ public final class MutableFloat extends Number implements Comparablefalse otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj != null) { if ( ((Float)this.value).getClass() == obj.getClass() ) { return Float.floatToIntBits(value) == Float.floatToIntBits(((Float) obj).floatValue()); @@ -164,7 +168,8 @@ public final class MutableFloat extends Number implements Comparable, Cloneable { + public static MutableInteger of(final int value) { + return new MutableInteger(value); + } + public MutableInteger() { } - public MutableInteger(int value) { + public MutableInteger(final int value) { this.value = value; } - public MutableInteger(String value) { + public MutableInteger(final String value) { this.value = Integer.parseInt(value); } - public MutableInteger(Number number) { + public MutableInteger(final Number number) { this.value = number.intValue(); } @@ -55,21 +59,21 @@ public final class MutableInteger extends Number implements Comparablefalse otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj != null) { if ( ((Integer)this.value).getClass() == obj.getClass() ) { return value == ((Integer) obj).intValue(); @@ -150,7 +154,8 @@ public final class MutableInteger extends Number implements Comparable, Cloneable { + public static MutableLong of(final long value) { + return new MutableLong(value); + } + public MutableLong() { } - public MutableLong(long value) { + public MutableLong(final long value) { this.value = value; } - public MutableLong(String value) { + public MutableLong(final String value) { this.value = Long.parseLong(value); } - public MutableLong(Number number) { + public MutableLong(final Number number) { this.value = number.longValue(); } @@ -55,21 +59,21 @@ public final class MutableLong extends Number implements Comparable /** * Returns mutable value. */ - public long getValue() { + public long get() { return value; } /** * Sets mutable value. */ - public void setValue(long value) { + public void set(final long value) { this.value = value; } /** * Sets mutable value from a Number. */ - public void setValue(Number value) { + public void set(final Number value) { this.value = value.longValue(); } @@ -99,7 +103,7 @@ public final class MutableLong extends Number implements Comparable * false otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj != null) { if ( ((Long)this.value).getClass() == obj.getClass() ) { return value == ((Long) obj).longValue(); @@ -150,7 +154,8 @@ public final class MutableLong extends Number implements Comparable /** * Compares value of two same instances. */ - public int compareTo(MutableLong other) { + @Override + public int compareTo(final MutableLong other) { return value < other.value ? -1 : (value == other.value ? 0 : 1); } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableShort.java b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableShort.java index 37ecce08e..f9943315f 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableShort.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/MutableShort.java @@ -30,18 +30,22 @@ package com.fr.third.jodd.mutable; */ public final class MutableShort extends Number implements Comparable, Cloneable { + public static MutableShort of(final short value) { + return new MutableShort(value); + } + public MutableShort() { } - public MutableShort(short value) { + public MutableShort(final short value) { this.value = value; } - public MutableShort(String value) { + public MutableShort(final String value) { this.value = Short.parseShort(value); } - public MutableShort(Number number) { + public MutableShort(final Number number) { this.value = number.shortValue(); } @@ -55,21 +59,21 @@ public final class MutableShort extends Number implements Comparablefalse otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj != null) { if ( ((Short)this.value).getClass() == obj.getClass() ) { return value == ((Short) obj).shortValue(); @@ -150,7 +154,8 @@ public final class MutableShort extends Number implements Comparable extends ValueProvider { +public interface Value extends Supplier { /** - * Returns value. + * Creates default value wrapper. */ - public T value(); + public static Value of(final R value) { + return new ValueImpl<>(value); + } /** * Sets new value. */ - public void value(T value); + public void set(T value); } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/format/Scanf.java b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/ValueImpl.java similarity index 74% rename from fine-jodd/src/main/java/com/fr/third/jodd/format/Scanf.java rename to fine-jodd/src/main/java/com/fr/third/jodd/mutable/ValueImpl.java index 0aad19ba5..4b78e1d53 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/format/Scanf.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/mutable/ValueImpl.java @@ -23,27 +23,37 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.format; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.IOException; +package com.fr.third.jodd.mutable; /** - * Scanf. + * Default implementation of a {@link Value}. */ -public class Scanf { +class ValueImpl implements Value { + + private T value; + + ValueImpl(final T v) { + this.value = v; + } - protected static BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + @Override + public T get() { + return value; + } + + @Override + public void set(final T value) { + this.value = value; + } /** - * Scans input console and returns entered string. + * Simple to-string representation of a value. */ - public static String scanf() { - try { - return in.readLine(); - } catch (IOException ioe) { - return null; + @Override + public String toString() { + if (value == null) { + return "value: {null}"; } + return "value: {" + value.toString() + '}'; } -} +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/HtmlDecoder.java b/fine-jodd/src/main/java/com/fr/third/jodd/net/HtmlDecoder.java similarity index 70% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/HtmlDecoder.java rename to fine-jodd/src/main/java/com/fr/third/jodd/net/HtmlDecoder.java index f100ca531..cab00f18f 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/HtmlDecoder.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/net/HtmlDecoder.java @@ -23,9 +23,12 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.util; +package com.fr.third.jodd.net; import com.fr.third.jodd.io.StreamUtil; +import com.fr.third.jodd.util.BinarySearchBase; +import com.fr.third.jodd.util.CharUtil; +import com.fr.third.jodd.util.StringUtil; import java.io.InputStream; import java.util.Arrays; @@ -44,37 +47,37 @@ public class HtmlDecoder { private static final char[][] ENTITY_NAMES; static { - Properties entityReferences = new Properties(); + final Properties entityReferences = new Properties(); - String propertiesName = HtmlDecoder.class.getSimpleName() + ".properties"; + final String propertiesName = HtmlDecoder.class.getSimpleName() + ".properties"; - InputStream is = HtmlDecoder.class.getResourceAsStream(propertiesName); + final InputStream is = HtmlDecoder.class.getResourceAsStream(propertiesName); try { entityReferences.load(is); } - catch (Exception ex) { - throw new IllegalStateException(ex); + catch (final Exception ex) { + throw new IllegalStateException("Can't load properties", ex); } finally { StreamUtil.close(is); } - ENTITY_MAP = new HashMap(entityReferences.size()); + ENTITY_MAP = new HashMap<>(entityReferences.size()); - Enumeration keys = entityReferences.propertyNames(); + final Enumeration keys = (Enumeration) entityReferences.propertyNames(); while (keys.hasMoreElements()) { - String name = (String) keys.nextElement(); - String values = entityReferences.getProperty(name); - String[] array = StringUtil.splitc(values, ','); + final String name = keys.nextElement(); + final String values = entityReferences.getProperty(name); + final String[] array = StringUtil.splitc(values, ','); - char[] chars; + final char[] chars; - String hex = array[0]; - char value = (char) Integer.parseInt(hex, 16); + final String hex = array[0]; + final char value = (char) Integer.parseInt(hex, 16); if (array.length == 2) { - String hex2 = array[1]; - char value2 = (char) Integer.parseInt(hex2, 16); + final String hex2 = array[1]; + final char value2 = (char) Integer.parseInt(hex2, 16); chars = new char[]{value, value2}; } else { @@ -89,31 +92,27 @@ public class HtmlDecoder { ENTITY_NAMES = new char[ENTITY_MAP.size()][]; int i = 0; - for (String name : ENTITY_MAP.keySet()) { + for (final String name : ENTITY_MAP.keySet()) { ENTITY_NAMES[i++] = name.toCharArray(); } - Arrays.sort(ENTITY_NAMES, new Comparator() { - public int compare(char[] o1, char[] o2) { - return new String(o1).compareTo(new String(o2)); - } - }); + Arrays.sort(ENTITY_NAMES, Comparator.comparing(String::new)); } /** * Decodes HTML text. Assumes that all character references are properly closed with semi-colon. */ - public static String decode(String html) { + public static String decode(final String html) { int ndx = html.indexOf('&'); if (ndx == -1) { return html; } - StringBuilder result = new StringBuilder(html.length()); + final StringBuilder result = new StringBuilder(html.length()); int lastIndex = 0; - int len = html.length(); + final int len = html.length(); mainloop: while (ndx != -1) { result.append(html.substring(lastIndex, ndx)); @@ -129,8 +128,8 @@ mainloop: if (html.charAt(ndx + 1) == '#') { // decimal/hex - char c = html.charAt(ndx + 2); - int radix; + final char c = html.charAt(ndx + 2); + final int radix; if ((c == 'x') || (c == 'X')) { radix = 16; ndx += 3; @@ -139,15 +138,15 @@ mainloop: ndx += 2; } - String number = html.substring(ndx, lastIndex); - int i = Integer.parseInt(number, radix); + final String number = html.substring(ndx, lastIndex); + final int i = Integer.parseInt(number, radix); result.append((char) i); lastIndex++; } else { // token - String encodeToken = html.substring(ndx + 1, lastIndex); + final String encodeToken = html.substring(ndx + 1, lastIndex); - char[] replacement = ENTITY_MAP.get(encodeToken); + final char[] replacement = ENTITY_MAP.get(encodeToken); if (replacement == null) { result.append('&'); lastIndex = ndx + 1; @@ -169,19 +168,20 @@ mainloop: /** * Detects the longest character reference name on given position in char array. + * Returns {@code null} if name not found. */ public static String detectName(final char[] input, int ndx) { final Ptr ptr = new Ptr(); int firstIndex = 0; int lastIndex = ENTITY_NAMES.length - 1; - int len = input.length; + final int len = input.length; char[] lastName = null; - BinarySearchBase binarySearch = new BinarySearchBase() { + final BinarySearchBase binarySearch = new BinarySearchBase() { @Override - protected int compare(int index) { - char[] name = ENTITY_NAMES[index]; + protected int compare(final int index) { + final char[] name = ENTITY_NAMES[index]; if (ptr.offset >= name.length) { return -1; @@ -203,7 +203,7 @@ mainloop: return lastName != null ? new String(lastName) : null; } - char[] element = ENTITY_NAMES[firstIndex]; + final char[] element = ENTITY_NAMES[firstIndex]; if (element.length == ptr.offset + 1) { // total match, remember position, continue for finding the longer name @@ -215,7 +215,7 @@ mainloop: if (firstIndex == lastIndex) { // only one element found, check the rest for (int i = ptr.offset; i < element.length; i++) { - if (element[i] != input[ndx]) { + if (ndx == input.length || element[i] != input[ndx]) { return lastName != null ? new String(lastName) : null; } ndx++; @@ -235,8 +235,8 @@ mainloop: /** * Returns replacement chars for given character reference. */ - public static char[] lookup(String name) { + public static char[] lookup(final String name) { return ENTITY_MAP.get(name); } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/HtmlEncoder.java b/fine-jodd/src/main/java/com/fr/third/jodd/net/HtmlEncoder.java similarity index 90% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/HtmlEncoder.java rename to fine-jodd/src/main/java/com/fr/third/jodd/net/HtmlEncoder.java index 780d49760..bfac3bf20 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/HtmlEncoder.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/net/HtmlEncoder.java @@ -23,7 +23,9 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.util; +package com.fr.third.jodd.net; + +import com.fr.third.jodd.util.StringPool; /** * Encodes text and URL strings in various ways resulting HTML-safe text. @@ -47,7 +49,7 @@ public class HtmlEncoder { private static final char[] GT = ">".toCharArray(); private static final char[] NBSP = " ".toCharArray(); - /** + /* * Creates HTML lookup tables for faster encoding. */ static { @@ -90,10 +92,10 @@ public class HtmlEncoder { *
    *
  • & with &amp;
  • *
  • " with &quot;
  • - *
  • &nbsp;
  • + *
  • *
*/ - public static String attributeDoubleQuoted(CharSequence value) { + public static String attributeDoubleQuoted(final CharSequence value) { return encode(value, ATTR_DQ, LEN); } @@ -103,10 +105,10 @@ public class HtmlEncoder { *
    *
  • & with &amp;
  • *
  • ' with &#39;
  • - *
  • &nbsp;
  • + *
  • *
*/ - public static String attributeSingleQuoted(CharSequence value) { + public static String attributeSingleQuoted(final CharSequence value) { return encode(value, ATTR_SQ, LEN); } @@ -119,20 +121,20 @@ public class HtmlEncoder { *
  • \u00A0 with  
  • * */ - public static String text(CharSequence text) { + public static String text(final CharSequence text) { return encode(text, TEXT, LEN); } /** * Encodes XML string. In XML there are only 5 predefined character entities. */ - public static String xml(CharSequence text) { + public static String xml(final CharSequence text) { return encode(text, TEXT_XML, LEN_XML); } // ---------------------------------------------------------------- private - private static String encode(CharSequence text, char[][] buff, int bufflen) { + private static String encode(final CharSequence text, final char[][] buff, final int bufflen) { int len; if ((text == null) || ((len = text.length()) == 0)) { return StringPool.EMPTY; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/net/HttpMethod.java b/fine-jodd/src/main/java/com/fr/third/jodd/net/HttpMethod.java new file mode 100644 index 000000000..d10d00fc4 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/net/HttpMethod.java @@ -0,0 +1,49 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.net; + +/** + * Http request methods. + * See: http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods + */ +public enum HttpMethod { + CONNECT, + DELETE, + GET, + HEAD, + OPTIONS, + PATCH, + POST, + PUT, + TRACE; + + /** + * Returns {@code true} if method name is equal to provided one. + */ + public boolean equalsName(final String name) { + return name().equalsIgnoreCase(name); + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/net/HttpStatus.java b/fine-jodd/src/main/java/com/fr/third/jodd/net/HttpStatus.java new file mode 100644 index 000000000..9626d5517 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/net/HttpStatus.java @@ -0,0 +1,330 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.net; + +/** + * Simple developer-friendly set of HttpStatus codes and messages. + */ +public class HttpStatus { + + private final int status; + protected String message; + + public HttpStatus(final int status, final String message) { + this.status = status; + this.message = message; + } + public HttpStatus(final int status) { + this.status = status; + } + + /** + * Returns {@code true} is status is error. + */ + public boolean isError() { + return status >= 400; + } + /** + * Returns {@code true} is status is successful. + */ + public boolean isSuccess() { + return status < 400; + } + + /** + * Returns status code. + */ + public int status() { + return status; + } + + /** + * Returns status message. + */ + public String message() { + return message; + } + + // ---------------------------------------------------------------- + + public static class Redirection { + private Redirection() { } + + static Redirection instance = new Redirection(); + + public HttpStatus multipleChoice300() { + return new HttpStatus(300, "The requested resource points to a destination with multiple representations."); + } + + public HttpStatus movedPermanently301() { + return new HttpStatus(301, "The requested resource has been assigned a new permanent URL."); + } + public HttpStatus movedTemporarily302() { + return new HttpStatus(302, "The requested resource has been temporarily moved to a different URL."); + } + public HttpStatus notModified304() { + return new HttpStatus(304, "The contents of the requested web page have not been modified since the last access."); + } + public HttpStatus temporaryRedirect307() { + return new HttpStatus(307, "The requested URL resides temporarily under a different URL"); + } + public HttpStatus permanentRedirect308() { + return new HttpStatus(308, "All future requests should be sent using a different URI."); + } + } + + // ---------------------------------------------------------------- 400 + + public static class HttpStatus400 extends HttpStatus { + + public HttpStatus400() { + super(400); + } + public HttpStatus400 badContent() { + message = "The content type of the request data or the content type of a" + + " part of a multipart request is not supported."; + return this; + } + public HttpStatus400 badRequest() { + message = "The API request is invalid or improperly formed."; + return this; + } + public HttpStatus400 exists() { + message = "Resource already exists."; + return this; + } + public HttpStatus400 invalidDocumentValue() { + message = "The request failed because it contained an invalid parameter or" + + " parameter value for the document. Review the API" + + " documentation to determine which parameters are valid for" + + " your request."; + return this; + } + public HttpStatus400 invalidQuery() { + message = "The request is invalid. Check the API documentation to determine" + + " what parameters are supported for the request and to see if" + + " the request contains an invalid combination of parameters" + + " or an invalid parameter value."; + return this; + } + public HttpStatus400 keyExpired() { + message = "The API key provided in the request is invalid, which means the" + + " API server is unable to make the request."; + return this; + } + public HttpStatus400 required() { + message = "The API request is missing required information. The required" + + " information could be a parameter or resource property."; + return this; + } + public HttpStatus400 validationError() { + message = "Validation of input failed."; + return this; + } + } + + // ---------------------------------------------------------------- 401 + + public static class HttpStatus401 extends HttpStatus { + public HttpStatus401() { + super(401); + } + + public HttpStatus401 unauthorized(String message) { + message = "Access is denied due to invalid credentials."; + return this; + } + } + + // ---------------------------------------------------------------- 403 + + public static class HttpStatus403 extends HttpStatus { + public HttpStatus403() { + super(403); + } + + public HttpStatus403 corsRequestOrigin() { + message = "The CORS request is from an unknown origin."; + return this; + } + + public HttpStatus403 forbidden() { + message = "The requested operation is forbidden and cannot be completed."; + return this; + } + public HttpStatus403 limitExceeded() { + message = "The request cannot be completed due to access or rate limitations."; + return this; + } + public HttpStatus403 quotaExceeded() { + message = "The requested operation requires more resources than the quota" + + " allows."; + return this; + } + public HttpStatus403 rateLimitExceeded() { + message = "Too many requests have been sent within a given time span."; + return this; + } + public HttpStatus403 responseTooLarge() { + message = "The requested resource is too large to return."; + return this; + } + public HttpStatus403 unknownAuth() { + message = "The API server does not recognize the authorization scheme used" + + " for the request."; + return this; + } + } + + // ---------------------------------------------------------------- 404 + + public static class HttpStatus404 extends HttpStatus { + public HttpStatus404() { + super(404); + } + + public HttpStatus404 notFound() { + message = "The requested operation failed because a resource associated" + + " with the request could not be found."; + return this; + } + public HttpStatus404 unsupportedProtocol() { + message = "The protocol used in the request is not supported."; + return this; + } + } + + // ---------------------------------------------------------------- 405 + + public static class HttpStatus405 extends HttpStatus { + public HttpStatus405() { + super(405); + } + + public HttpStatus405 httpMethodNotAllowed() { + message = "The HTTP method associated with the request is not supported."; + return this; + } + } + + // ---------------------------------------------------------------- 408 + + public static class HttpStatus408 extends HttpStatus { + public HttpStatus408() { + super(408); + } + + public HttpStatus408 requestTimeout() { + message = "The server did not produce a response within the time that the " + + "server was prepared to wait."; + return this; + } + } + + // ---------------------------------------------------------------- 409 + + public static class HttpStatus409 extends HttpStatus { + public HttpStatus409() { + super(409); + } + + public HttpStatus409 conflict() { + message = "Indicates that the request could not be processed because of " + + "conflict in the request, such as an edit conflict between " + + "multiple simultaneous updates."; + return this; + } + } + + // ---------------------------------------------------------------- 500 + + public static class HttpStatus500 extends HttpStatus { + public HttpStatus500() { + super(500); + } + + public HttpStatus500 internalError() { + message = "The request failed due to an internal error."; + return this; + } + } + + // ---------------------------------------------------------------- 503 + + public static class HttpStatus503 extends HttpStatus { + public HttpStatus503() { + super(503); + } + + public HttpStatus503 serviceUnavailable() { + message = "The server is currently unavailable (because it is overloaded or down for maintenance)."; + return this; + } + } + + // ---------------------------------------------------------------- static + + public static HttpStatus of(final int status, final String message) { + return new HttpStatus(status, message); + } + + public static HttpStatus ok() { + return new HttpStatus(200, "OK"); + } + + public static Redirection redirection() { + return Redirection.instance; + } + + public static HttpStatus400 error400() { + return new HttpStatus400(); + } + public static HttpStatus401 error401() { + return new HttpStatus401(); + } + public static HttpStatus403 error403() { + return new HttpStatus403(); + } + public static HttpStatus404 error404() { + return new HttpStatus404(); + } + public static HttpStatus405 error405() { + return new HttpStatus405(); + } + public static HttpStatus408 error408() { + return new HttpStatus408(); + } + public static HttpStatus409 error409() { + return new HttpStatus409(); + } + public static HttpStatus500 error500() { + return new HttpStatus500(); + } + public static HttpStatus503 error503() { + return new HttpStatus503(); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/MimeTypes.java b/fine-jodd/src/main/java/com/fr/third/jodd/net/MimeTypes.java similarity index 73% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/MimeTypes.java rename to fine-jodd/src/main/java/com/fr/third/jodd/net/MimeTypes.java index 39fdf1817..44802fd2f 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/MimeTypes.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/net/MimeTypes.java @@ -23,15 +23,18 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.util; +package com.fr.third.jodd.net; import com.fr.third.jodd.io.StreamUtil; +import com.fr.third.jodd.util.StringPool; +import com.fr.third.jodd.util.StringUtil; +import com.fr.third.jodd.util.Wildcard; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; @@ -54,12 +57,12 @@ public class MimeTypes { public static final String MIME_TEXT_PLAIN = "text/plain"; public static final String MIME_TEXT_HTML = "text/html"; - private static final HashMap MIME_TYPE_MAP; // extension -> mime-type map + private static final LinkedHashMap MIME_TYPE_MAP; // extension -> mime-type map static { - Properties mimes = new Properties(); + final Properties mimes = new Properties(); - InputStream is = MimeTypes.class.getResourceAsStream(MimeTypes.class.getSimpleName() + ".properties"); + final InputStream is = MimeTypes.class.getResourceAsStream(MimeTypes.class.getSimpleName() + ".properties"); if (is == null) { throw new IllegalStateException("Mime types file missing"); } @@ -68,17 +71,17 @@ public class MimeTypes { mimes.load(is); } catch (IOException ioex) { - throw new IllegalStateException(ioex.getMessage()); + throw new IllegalStateException("Can't load properties", ioex); } finally { StreamUtil.close(is); } - MIME_TYPE_MAP = new HashMap(mimes.size() * 2); + MIME_TYPE_MAP = new LinkedHashMap<>(mimes.size() * 2); - Enumeration keys = mimes.propertyNames(); + final Enumeration keys = mimes.propertyNames(); while (keys.hasMoreElements()) { String mimeType = (String) keys.nextElement(); - String extensions = mimes.getProperty(mimeType); + final String extensions = mimes.getProperty(mimeType); if (mimeType.startsWith("/")) { mimeType = "application" + mimeType; @@ -92,9 +95,9 @@ public class MimeTypes { mimeType = "video" + mimeType.substring(1); } - String[] allExtensions = StringUtil.splitc(extensions, ' '); + final String[] allExtensions = StringUtil.splitc(extensions, ' '); - for (String extension : allExtensions) { + for (final String extension : allExtensions) { if (MIME_TYPE_MAP.put(extension, mimeType) != null) { throw new IllegalArgumentException("Duplicated extension: " + extension); } @@ -105,7 +108,7 @@ public class MimeTypes { /** * Registers MIME type for provided extension. Existing extension type will be overridden. */ - public static void registerMimeType(String ext, String mimeType) { + public static void registerMimeType(final String ext, final String mimeType) { MIME_TYPE_MAP.put(ext, mimeType); } @@ -113,7 +116,7 @@ public class MimeTypes { * Returns the corresponding MIME type to the given extension. * If no MIME type was found it returns application/octet-stream type. */ - public static String getMimeType(String ext) { + public static String getMimeType(final String ext) { String mimeType = lookupMimeType(ext); if (mimeType == null) { mimeType = MIME_APPLICATION_OCTET_STREAM; @@ -124,7 +127,7 @@ public class MimeTypes { /** * Simply returns MIME type or null if no type is found. */ - public static String lookupMimeType(String ext) { + public static String lookupMimeType(final String ext) { return MIME_TYPE_MAP.get(ext.toLowerCase()); } @@ -134,17 +137,17 @@ public class MimeTypes { * @param mimeType list of mime types, separated by comma * @param useWildcard if set, mime types are wildcard patterns */ - public static String[] findExtensionsByMimeTypes(String mimeType, boolean useWildcard) { - ArrayList extensions = new ArrayList(); + public static String[] findExtensionsByMimeTypes(String mimeType, final boolean useWildcard) { + final ArrayList extensions = new ArrayList<>(); mimeType = mimeType.toLowerCase(); - String[] mimeTypes = StringUtil.splitc(mimeType, ", "); + final String[] mimeTypes = StringUtil.splitc(mimeType, ", "); - for (Map.Entry entry : MIME_TYPE_MAP.entrySet()) { - String entryExtension = entry.getKey(); - String entryMimeType = entry.getValue().toLowerCase(); + for (final Map.Entry entry : MIME_TYPE_MAP.entrySet()) { + final String entryExtension = entry.getKey(); + final String entryMimeType = entry.getValue().toLowerCase(); - int matchResult = useWildcard ? + final int matchResult = useWildcard ? Wildcard.matchOne(entryMimeType, mimeTypes) : StringUtil.equalsOne(entryMimeType, mimeTypes); @@ -157,6 +160,13 @@ public class MimeTypes { return StringPool.EMPTY_ARRAY; } - return extensions.toArray(new String[extensions.size()]); + return extensions.toArray(new String[0]); } -} \ No newline at end of file + + /** + * Returns {@code true} if given value is one of the registered MIME extensions. + */ + public static boolean isRegisteredExtension(final String extension) { + return MIME_TYPE_MAP.containsKey(extension); + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/URLCoder.java b/fine-jodd/src/main/java/com/fr/third/jodd/net/URLCoder.java similarity index 79% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/URLCoder.java rename to fine-jodd/src/main/java/com/fr/third/jodd/net/URLCoder.java index 4ddd92349..fae47414c 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/URLCoder.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/net/URLCoder.java @@ -23,12 +23,13 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.util; +package com.fr.third.jodd.net; import com.fr.third.jodd.core.JoddCore; +import com.fr.third.jodd.util.StringPool; +import com.fr.third.jodd.util.StringUtil; import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -97,9 +98,16 @@ public class URLCoder { */ enum URIPart { + UNRESERVED { + @Override + public boolean isValid(final char c) { + return isUnreserved(c); + } + }, + SCHEME { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { return isAlpha(c) || isDigit(c) || c == '+' || c == '-' || c == '.'; } }, @@ -111,43 +119,43 @@ public class URLCoder { // }, USER_INFO { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { return isUnreserved(c) || isSubDelimiter(c) || c == ':'; } }, HOST { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { return isUnreserved(c) || isSubDelimiter(c); } }, PORT { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { return isDigit(c); } }, PATH { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { return isPchar(c) || c == '/'; } }, PATH_SEGMENT { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { return isPchar(c); } }, QUERY { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { return isPchar(c) || c == '/' || c == '?'; } }, QUERY_PARAM { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { if (c == '=' || c == '+' || c == '&' || c == ';') { return false; } @@ -156,7 +164,7 @@ public class URLCoder { }, FRAGMENT { @Override - public boolean isValid(char c) { + public boolean isValid(final char c) { return isPchar(c) || c == '/' || c == '?'; } }; @@ -176,17 +184,12 @@ public class URLCoder { /** * Encodes single URI component. */ - private static String encodeUriComponent(String source, String encoding, URIPart uriPart) { + private static String encodeUriComponent(final String source, final String encoding, final URIPart uriPart) { if (source == null) { return null; } - byte[] bytes; - try { - bytes = encodeBytes(source.getBytes(encoding), uriPart); - } catch (UnsupportedEncodingException ignore) { - return null; - } + byte[] bytes = encodeBytes(StringUtil.getBytes(source, encoding), uriPart); char[] chars = new char[bytes.length]; for (int i = 0; i < bytes.length; i++) { @@ -198,7 +201,7 @@ public class URLCoder { /** * Encodes byte array using allowed characters from {@link URIPart}. */ - private static byte[] encodeBytes(byte[] source, URIPart uriPart) { + private static byte[] encodeBytes(final byte[] source, final URIPart uriPart) { ByteArrayOutputStream bos = new ByteArrayOutputStream(source.length); for (byte b : source) { if (b < 0) { @@ -219,13 +222,23 @@ public class URLCoder { // ---------------------------------------------------------------- main methods + /** + * Encodes string using default RFCP rules. + */ + public static String encode(final String string, final String encoding) { + return encodeUriComponent(string, encoding, URIPart.UNRESERVED); + } + public static String encode(final String string) { + return encodeUriComponent(string, JoddCore.encoding, URIPart.UNRESERVED); + } + /** * Encodes the given URI scheme with the given encoding. */ - public static String encodeScheme(String scheme, String encoding) { + public static String encodeScheme(final String scheme, final String encoding) { return encodeUriComponent(scheme, encoding, URIPart.SCHEME); } - public static String encodeScheme(String scheme) { + public static String encodeScheme(final String scheme) { return encodeUriComponent(scheme, JoddCore.encoding, URIPart.SCHEME); } @@ -244,80 +257,80 @@ public class URLCoder { /** * Encodes the given URI user info with the given encoding. */ - public static String encodeUserInfo(String userInfo, String encoding) { + public static String encodeUserInfo(final String userInfo, final String encoding) { return encodeUriComponent(userInfo, encoding, URIPart.USER_INFO); } - public static String encodeUserInfo(String userInfo) { + public static String encodeUserInfo(final String userInfo) { return encodeUriComponent(userInfo, JoddCore.encoding, URIPart.USER_INFO); } /** * Encodes the given URI host with the given encoding. */ - public static String encodeHost(String host, String encoding) { + public static String encodeHost(final String host, final String encoding) { return encodeUriComponent(host, encoding, URIPart.HOST); } - public static String encodeHost(String host) { + public static String encodeHost(final String host) { return encodeUriComponent(host, JoddCore.encoding, URIPart.HOST); } /** * Encodes the given URI port with the given encoding. */ - public static String encodePort(String port, String encoding) { + public static String encodePort(final String port, final String encoding) { return encodeUriComponent(port, encoding, URIPart.PORT); } - public static String encodePort(String port) { + public static String encodePort(final String port) { return encodeUriComponent(port, JoddCore.encoding, URIPart.PORT); } /** * Encodes the given URI path with the given encoding. */ - public static String encodePath(String path, String encoding) { + public static String encodePath(final String path, final String encoding) { return encodeUriComponent(path, encoding, URIPart.PATH); } - public static String encodePath(String path) { + public static String encodePath(final String path) { return encodeUriComponent(path, JoddCore.encoding, URIPart.PATH); } /** * Encodes the given URI path segment with the given encoding. */ - public static String encodePathSegment(String segment, String encoding) { + public static String encodePathSegment(final String segment, final String encoding) { return encodeUriComponent(segment, encoding, URIPart.PATH_SEGMENT); } - public static String encodePathSegment(String segment) { + public static String encodePathSegment(final String segment) { return encodeUriComponent(segment, JoddCore.encoding, URIPart.PATH_SEGMENT); } /** * Encodes the given URI query with the given encoding. */ - public static String encodeQuery(String query, String encoding) { + public static String encodeQuery(final String query, final String encoding) { return encodeUriComponent(query, encoding, URIPart.QUERY); } - public static String encodeQuery(String query) { + public static String encodeQuery(final String query) { return encodeUriComponent(query, JoddCore.encoding, URIPart.QUERY); } /** * Encodes the given URI query parameter with the given encoding. */ - public static String encodeQueryParam(String queryParam, String encoding) { + public static String encodeQueryParam(final String queryParam, final String encoding) { return encodeUriComponent(queryParam, encoding, URIPart.QUERY_PARAM); } - public static String encodeQueryParam(String queryParam) { + public static String encodeQueryParam(final String queryParam) { return encodeUriComponent(queryParam, JoddCore.encoding, URIPart.QUERY_PARAM); } /** * Encodes the given URI fragment with the given encoding. */ - public static String encodeFragment(String fragment, String encoding) { + public static String encodeFragment(final String fragment, final String encoding) { return encodeUriComponent(fragment, encoding, URIPart.FRAGMENT); } - public static String encodeFragment(String fragment) { + public static String encodeFragment(final String fragment) { return encodeUriComponent(fragment, JoddCore.encoding, URIPart.FRAGMENT); } @@ -327,7 +340,7 @@ public class URLCoder { /** * @see #encodeUri(String, String) */ - public static String encodeUri(String uri) { + public static String encodeUri(final String uri) { return encodeUri(uri, JoddCore.encoding); } /** @@ -337,7 +350,7 @@ public class URLCoder { * characters in query parameter names and query parameter values because they cannot * be parsed in a reliable way. */ - public static String encodeUri(String uri, String encoding) { + public static String encodeUri(final String uri, final String encoding) { Matcher m = URI_PATTERN.matcher(uri); if (m.matches()) { String scheme = m.group(2); @@ -357,7 +370,7 @@ public class URLCoder { /** * @see #encodeHttpUrl(String, String) */ - public static String encodeHttpUrl(String httpUrl) { + public static String encodeHttpUrl(final String httpUrl) { return encodeHttpUrl(httpUrl, JoddCore.encoding); } /** @@ -369,7 +382,7 @@ public class URLCoder { * characters in query parameter names and query parameter values because they cannot * be parsed in a reliable way. */ - public static String encodeHttpUrl(String httpUrl, String encoding) { + public static String encodeHttpUrl(final String httpUrl, final String encoding) { Matcher m = HTTP_URL_PATTERN.matcher(httpUrl); if (m.matches()) { String scheme = m.group(1); @@ -386,9 +399,9 @@ public class URLCoder { } private static String encodeUriComponents( - String scheme, String authority, String userInfo, - String host, String port, String path, String query, - String fragment, String encoding) { + final String scheme, final String authority, final String userInfo, + final String host, final String port, final String path, final String query, + final String fragment, final String encoding) { StringBuilder sb = new StringBuilder(); @@ -434,7 +447,7 @@ public class URLCoder { * Provided path is parsed and {@link #encodeUri(String) encoded}. * @see #build(String, boolean) */ - public static Builder build(String path) { + public static Builder build(final String path) { return build(path, true); } @@ -447,7 +460,7 @@ public class URLCoder { * The purpose of builder is to help with query parameters. All other URI parts * should be set previously or after the URL is built. */ - public static Builder build(String path, boolean encodePath) { + public static Builder build(final String path, final boolean encodePath) { return new Builder(path, encodePath, JoddCore.encoding); } @@ -456,7 +469,7 @@ public class URLCoder { protected final String encoding; protected boolean hasParams; - public Builder(String path, boolean encodePath, String encoding) { + public Builder(final String path, final boolean encodePath, final String encoding) { this.encoding = encoding; url = new StringBuilder(); if (encodePath) { @@ -467,10 +480,14 @@ public class URLCoder { this.hasParams = url.indexOf(StringPool.QUESTION_MARK) != -1; } + public Builder queryParam(final String name, final Object value) { + return queryParam(name, value.toString()); + } + /** * Appends new query parameter to the url. */ - public Builder queryParam(String name, String value) { + public Builder queryParam(final String name, final String value) { url.append(hasParams ? '&' : '?'); hasParams = true; @@ -486,8 +503,7 @@ public class URLCoder { /** * Returns full URL. */ - @Override - public String toString() { + public String get() { return url.toString(); } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/URLDecoder.java b/fine-jodd/src/main/java/com/fr/third/jodd/net/URLDecoder.java similarity index 84% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/URLDecoder.java rename to fine-jodd/src/main/java/com/fr/third/jodd/net/URLDecoder.java index 43fbc0a36..41f48f9e5 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/URLDecoder.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/net/URLDecoder.java @@ -23,12 +23,12 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.util; +package com.fr.third.jodd.net; import com.fr.third.jodd.core.JoddCore; +import com.fr.third.jodd.util.StringUtil; import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; /** * URL decoder. @@ -38,7 +38,7 @@ public class URLDecoder { /** * Decodes URL elements. */ - public static String decode(String url) { + public static String decode(final String url) { return decode(url, JoddCore.encoding, false); } @@ -48,25 +48,25 @@ public class URLDecoder { * not decode the '+' character. * @see #decodeQuery(String, String) */ - public static String decode(String source, String encoding) { + public static String decode(final String source, final String encoding) { return decode(source, encoding, false); } /** * Decodes query name or value. */ - public static String decodeQuery(String source) { + public static String decodeQuery(final String source) { return decode(source, JoddCore.encoding, true); } /** * Decodes query name or value. */ - public static String decodeQuery(String source, String encoding) { + public static String decodeQuery(final String source, final String encoding) { return decode(source, encoding, true); } - private static String decode(String source, String encoding, boolean decodePlus) { + private static String decode(final String source, final String encoding, final boolean decodePlus) { int length = source.length(); ByteArrayOutputStream bos = new ByteArrayOutputStream(length); @@ -102,11 +102,7 @@ public class URLDecoder { bos.write(ch); } } - try { - return changed ? new String(bos.toByteArray(), encoding) : source; - } catch (UnsupportedEncodingException ignore) { - return null; - } + return changed ? StringUtil.newString(bos.toByteArray(), encoding) : source; } } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/package-info.java b/fine-jodd/src/main/java/com/fr/third/jodd/package-info.java index 2d594a8f2..67475e62b 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/package-info.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/package-info.java @@ -24,6 +24,6 @@ // POSSIBILITY OF SUCH DAMAGE. /** - * Jodd = tools + ioc + mvc + db + aop + tx + html {@literal <} 1.1M + * Jodd = tools + ioc + mvc + db + aop + tx + json + html {@literal <} 1.7Mb */ package com.fr.third.jodd; \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/system/HostInfo.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/HostInfo.java new file mode 100644 index 000000000..8250563ed --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/HostInfo.java @@ -0,0 +1,95 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.system; + +import java.io.File; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Host information. + */ +abstract class HostInfo { + + /** + * Delegate host info to be resolved lazy. + * Android detection will initialize this class too and since {@code InetAddress.getLocalHost()} + * is forbidden in Android, we will get an exception. + */ + private static class HostInfoLazy { + private final String HOST_NAME; + private final String HOST_ADDRESS; + + public HostInfoLazy() { + String hostName; + String hostAddress; + + try { + final InetAddress localhost = InetAddress.getLocalHost(); + + hostName = localhost.getHostName(); + hostAddress = localhost.getHostAddress(); + } catch (final UnknownHostException uhex) { + hostName = "localhost"; + hostAddress = "127.0.0.1"; + } + + this.HOST_NAME = hostName; + this.HOST_ADDRESS = hostAddress; + } + } + + private static HostInfoLazy hostInfoLazy; + + /** + * Returns host name. + */ + public final String getHostName() { + if (hostInfoLazy == null) { + hostInfoLazy = new HostInfoLazy(); + } + return hostInfoLazy.HOST_NAME; + } + + /** + * Returns host IP address. + */ + public final String getHostAddress() { + if (hostInfoLazy == null) { + hostInfoLazy = new HostInfoLazy(); + } + return hostInfoLazy.HOST_ADDRESS; + } + + // ---------------------------------------------------------------- util + + protected String nosep(final String in) { + if (in.endsWith(File.separator)) { + return in.substring(0, in.length() - 1); + } + return in; + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/system/JavaInfo.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/JavaInfo.java new file mode 100644 index 000000000..3be8edcbb --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/JavaInfo.java @@ -0,0 +1,185 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.system; + + +import java.util.ArrayList; + +abstract class JavaInfo extends HostInfo { + + private final String JAVA_VERSION = SystemUtil.get("java.version"); + private final int JAVA_VERSION_NUMBER = detectJavaVersionNumber(); + private final String JAVA_VENDOR = SystemUtil.get("java.vendor"); + private final String JAVA_VENDOR_URL = SystemUtil.get("java.vendor.url"); + private final String JAVA_SPECIFICATION_VERSION = SystemUtil.get("java.specification.version"); + private final String JAVA_SPECIFICATION_NAME = SystemUtil.get("java.specification.name"); + private final String JAVA_SPECIFICATION_VENDOR = SystemUtil.get("java.specification.vendor"); + private final String[] JRE_PACKAGES = buildJrePackages(JAVA_VERSION_NUMBER); + + /** + * Returns Java version string, as specified in system property. + * Returned string contain major version, minor version and revision. + */ + public String getJavaVersion() { + return JAVA_VERSION; + } + + /** + * Returns unified Java version as an integer. + */ + public int getJavaVersionNumber() { + return JAVA_VERSION_NUMBER; + } + + /** + * Returns Java vendor. + */ + public String getJavaVendor() { + return JAVA_VENDOR; + } + + /** + * Returns Java vendor URL. + */ + public String getJavaVendorURL() { + return JAVA_VENDOR_URL; + } + + /** + * Retrieves the version of the currently running JVM. + */ + public String getJavaSpecificationVersion() { + return JAVA_SPECIFICATION_VERSION; + } + + public final String getJavaSpecificationName() { + return JAVA_SPECIFICATION_NAME; + } + + public final String getJavaSpecificationVendor() { + return JAVA_SPECIFICATION_VENDOR; + } + + // ---------------------------------------------------------------- packages + + /** + * Returns list of packages, build into runtime jars. + */ + public String[] getJrePackages() { + return JRE_PACKAGES; + } + + /** + * Builds a set of java core packages. + */ + private String[] buildJrePackages(final int javaVersionNumber) { + final ArrayList packages = new ArrayList<>(); + + switch (javaVersionNumber) { + case 9: + case 8: + case 7: + case 6: + case 5: + // in Java1.5, the apache stuff moved + packages.add("com.sun.org.apache"); + // fall through... + case 4: + if (javaVersionNumber == 4) { + packages.add("org.apache.crimson"); + packages.add("org.apache.xalan"); + packages.add("org.apache.xml"); + packages.add("org.apache.xpath"); + } + packages.add("org.ietf.jgss"); + packages.add("org.w3c.dom"); + packages.add("org.xml.sax"); + // fall through... + case 3: + packages.add("org.omg"); + packages.add("com.sun.corba"); + packages.add("com.sun.jndi"); + packages.add("com.sun.media"); + packages.add("com.sun.naming"); + packages.add("com.sun.org.omg"); + packages.add("com.sun.rmi"); + packages.add("sunw.io"); + packages.add("sunw.util"); + // fall through... + case 2: + packages.add("com.sun.java"); + packages.add("com.sun.image"); + // fall through... + case 1: + default: + // core stuff + packages.add("sun"); + packages.add("java"); + packages.add("javax"); + break; + } + + return packages.toArray(new String[0]); + } + + + + // ---------------------------------------------------------------- java checks + + private int detectJavaVersionNumber() { + String javaVersion = JAVA_VERSION; + + final int lastDashNdx = javaVersion.lastIndexOf('-'); + if (lastDashNdx != -1) { + javaVersion = javaVersion.substring(0, lastDashNdx); + } + + if (javaVersion.startsWith("1.")) { + // up to java 8 + final int index = javaVersion.indexOf('.', 2); + return Integer.parseInt(javaVersion.substring(2, index)); + } else { + final int index = javaVersion.indexOf('.'); + return Integer.parseInt(index == -1 ? javaVersion : javaVersion.substring(0, index)); + } + } + + /** + * Checks if the currently running JVM is at least compliant + * with provided JDK version. + */ + public boolean isAtLeastJavaVersion(final int version) { + return JAVA_VERSION_NUMBER >= version; + } + + /** + * Checks if the currently running JVM is equal to provided version. + */ + public boolean isJavaVersion(final int version) { + return JAVA_VERSION_NUMBER == version; + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/format/RomanNumber.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/JvmInfo.java similarity index 52% rename from fine-jodd/src/main/java/com/fr/third/jodd/format/RomanNumber.java rename to fine-jodd/src/main/java/com/fr/third/jodd/system/JvmInfo.java index fa5c864e6..71ccf7bb3 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/format/RomanNumber.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/JvmInfo.java @@ -23,56 +23,56 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.format; +package com.fr.third.jodd.system; -/** - * Conversion to and from Roman numbers. - */ -public class RomanNumber { +abstract class JvmInfo extends JavaInfo { - private static final int[] VALUES = new int[] {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; - private static final String[] LETTERS = new String[] {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; + private final String JAVA_VM_NAME = SystemUtil.get("java.vm.name"); + private final String JAVA_VM_VERSION = SystemUtil.get("java.vm.version"); + private final String JAVA_VM_VENDOR = SystemUtil.get("java.vm.vendor"); + private final String JAVA_VM_INFO = SystemUtil.get("java.vm.info"); + private final String JAVA_VM_SPECIFICATION_NAME = SystemUtil.get("java.vm.specification.name"); + private final String JAVA_VM_SPECIFICATION_VERSION = SystemUtil.get("java.vm.specification.version"); + private final String JAVA_VM_SPECIFICATION_VENDOR = SystemUtil.get("java.vm.specification.vendor"); /** - * Converts to Roman number. + * Returns JVM name. */ - public static String convertToRoman(int value) { - if (value <= 0) { - throw new IllegalArgumentException(); - } - StringBuilder roman = new StringBuilder(); - int n = value; - for (int i = 0; i < LETTERS.length; i++) { - while (n >= VALUES[i]) { - roman.append(LETTERS[i]); - n -= VALUES[i]; - } - } - return roman.toString(); + public final String getJvmName() { + return JAVA_VM_NAME; } /** - * Converts to Arabic numbers. + * Returns JVM version. */ - public static int convertToArabic(String roman) { - int start = 0, value = 0; - for (int i = 0; i < LETTERS.length; i++) { - while (roman.startsWith(LETTERS[i], start)) { - value += VALUES[i]; - start += LETTERS[i].length(); - } - } - return start == roman.length() ? value : -1; + public final String getJvmVersion() { + return JAVA_VM_VERSION; } /** - * Checks if some string is valid roman number. + * Returns VM vendor. */ - public static boolean isValidRomanNumber(String roman) { - try { - return roman.equals(convertToRoman(convertToArabic(roman))); - } catch (IllegalArgumentException ignore) { - return false; - } + public final String getJvmVendor() { + return JAVA_VM_VENDOR; } + + /** + * Returns additional VM information. + */ + public final String getJvmInfo() { + return JAVA_VM_INFO; + } + + public final String getJvmSpecificationName() { + return JAVA_VM_SPECIFICATION_NAME; + } + + public final String getJvmSpecificationVersion() { + return JAVA_VM_SPECIFICATION_VERSION; + } + + public final String getJvmSpecificationVendor() { + return JAVA_VM_SPECIFICATION_VENDOR; + } + } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/system/OsInfo.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/OsInfo.java new file mode 100644 index 000000000..b654b4623 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/OsInfo.java @@ -0,0 +1,187 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.system; + +import com.fr.third.jodd.util.ClassLoaderUtil; + +abstract class OsInfo extends JvmInfo { + + private final String OS_VERSION = SystemUtil.get("os.version"); + private final String OS_ARCH = SystemUtil.get("os.arch"); + private final String OS_NAME = SystemUtil.get("os.name"); + + private final boolean IS_ANDROID = isAndroid0(); + private final boolean IS_OS_AIX = matchOS("AIX"); + private final boolean IS_OS_HP_UX = matchOS("HP-UX"); + private final boolean IS_OS_IRIX = matchOS("Irix"); + private final boolean IS_OS_LINUX = matchOS("Linux") || matchOS("LINUX"); + private final boolean IS_OS_MAC = matchOS("Mac"); + private final boolean IS_OS_MAC_OSX = matchOS("Mac OS X"); + private final boolean IS_OS_OS2 = matchOS("OS/2"); + private final boolean IS_OS_SOLARIS = matchOS("Solaris"); + private final boolean IS_OS_SUN_OS = matchOS("SunOS"); + private final boolean IS_OS_WINDOWS = matchOS("Windows"); + private final boolean IS_OS_WINDOWS_2000 = matchOS("Windows", "5.0"); + private final boolean IS_OS_WINDOWS_95 = matchOS("Windows 9", "4.0"); + private final boolean IS_OS_WINDOWS_98 = matchOS("Windows 9", "4.1"); + private final boolean IS_OS_WINDOWS_ME = matchOS("Windows", "4.9"); + private final boolean IS_OS_WINDOWS_NT = matchOS("Windows NT"); + private final boolean IS_OS_WINDOWS_XP = matchOS("Windows", "5.1"); + + private final String FILE_SEPARATOR = SystemUtil.get("file.separator"); + private final String LINE_SEPARATOR = SystemUtil.get("line.separator"); + private final String PATH_SEPARATOR = SystemUtil.get("path.separator"); + private final String FILE_ENCODING = SystemUtil.get("file.encoding"); + + public final String getOsArchitecture() { + return OS_ARCH; + } + + public final String getOsName() { + return OS_NAME; + } + + public final String getOsVersion() { + return OS_VERSION; + } + + /** + * Returns true if system is android. + */ + public boolean isAndroid() { + return IS_ANDROID; + } + + private static boolean isAndroid0() { + try { + Class.forName("android.app.Application", false, ClassLoaderUtil.getSystemClassLoader()); + return true; + } catch (Exception e) { + return false; + } + } + + + public final boolean isAix() { + return IS_OS_AIX; + } + + public final boolean isHpUx() { + return IS_OS_HP_UX; + } + + public final boolean isIrix() { + return IS_OS_IRIX; + } + + public final boolean isLinux() { + return IS_OS_LINUX; + } + + public final boolean isMac() { + return IS_OS_MAC; + } + + public final boolean isMacOsX() { + return IS_OS_MAC_OSX; + } + + public final boolean isOs2() { + return IS_OS_OS2; + } + + public final boolean isSolaris() { + return IS_OS_SOLARIS; + } + + public final boolean isSunOS() { + return IS_OS_SUN_OS; + } + + public final boolean isWindows() { + return IS_OS_WINDOWS; + } + + public final boolean isWindows2000() { + return IS_OS_WINDOWS_2000; + } + + public final boolean isWindows95() { + return IS_OS_WINDOWS_95; + } + + public final boolean isWindows98() { + return IS_OS_WINDOWS_98; + } + + public final boolean isWindowsME() { + return IS_OS_WINDOWS_ME; + } + + public final boolean isWindowsNT() { + return IS_OS_WINDOWS_NT; + } + + public final boolean isWindowsXP() { + return IS_OS_WINDOWS_XP; + } + + // ---------------------------------------------------------------- file + + public final String getFileSeparator() { + return FILE_SEPARATOR; + } + + public final String getLineSeparator() { + return LINE_SEPARATOR; + } + + public final String getPathSeparator() { + return PATH_SEPARATOR; + } + + public final String getFileEncoding() { + return FILE_ENCODING; + } + + // ---------------------------------------------------------------- util + + private boolean matchOS(final String osNamePrefix) { + if (OS_NAME == null) { + return false; + } + + return OS_NAME.startsWith(osNamePrefix); + } + + private boolean matchOS(final String osNamePrefix, final String osVersionPrefix) { + if ((OS_NAME == null) || (OS_VERSION == null)) { + return false; + } + + return OS_NAME.startsWith(osNamePrefix) && OS_VERSION.startsWith(osVersionPrefix); + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/system/RuntimeInfo.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/RuntimeInfo.java new file mode 100644 index 000000000..55dafd308 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/RuntimeInfo.java @@ -0,0 +1,83 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.system; + +import java.lang.management.ManagementFactory; + +abstract class RuntimeInfo extends OsInfo { + + private final Runtime runtime = Runtime.getRuntime(); + + /** + * Returns MAX memory. + */ + public final long getMaxMemory(){ + return runtime.maxMemory(); + } + + /** + * Returns TOTAL memory. + */ + public final long getTotalMemory(){ + return runtime.totalMemory(); + } + + /** + * Returns FREE memory. + */ + public final long getFreeMemory(){ + return runtime.freeMemory(); + } + + /** + * Returns usable memory. + */ + public final long getAvailableMemory(){ + return runtime.maxMemory() - runtime.totalMemory() + runtime.freeMemory(); + } + + /** + * Returns used memory. + */ + public final long getUsedMemory(){ + return runtime.totalMemory() - runtime.freeMemory(); + } + + /** + * Returns PID of current Java process. + */ + public final long getCurrentPID() { + return Long.parseLong(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); + } + + /** + * Returns number of CPUs. + */ + public final long getCPUs() { + return runtime.availableProcessors(); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/system/SystemInfo.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/SystemInfo.java new file mode 100644 index 000000000..157f3f487 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/SystemInfo.java @@ -0,0 +1,29 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.system; + +public final class SystemInfo extends UserInfo { +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/system/SystemUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/SystemUtil.java new file mode 100644 index 000000000..2713871f3 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/SystemUtil.java @@ -0,0 +1,138 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.system; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Objects; + +public class SystemUtil { + + /** + * Returns system property or {@code null} if not set. + */ + public static String get(final String name) { + return get(name, null); + } + + /** + * Returns system property. If key is not available, returns the default value. + */ + public static String get(final String name, final String defaultValue) { + Objects.requireNonNull(name); + + String value = null; + try { + if (System.getSecurityManager() == null) { + value = System.getProperty(name); + } else { + value = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty(name)); + } + } catch (final Exception ignore) { + } + + if (value == null) { + return defaultValue; + } + + return value; + } + + /** + * Returns system property as boolean. + */ + public static boolean getBoolean(final String name, final boolean defaultValue) { + String value = get(name); + if (value == null) { + return defaultValue; + } + + value = value.trim().toLowerCase(); + + switch (value) { + case "true" : + case "yes" : + case "1" : + case "on" : + return true; + case "false": + case "no" : + case "0" : + case "off" : + return false; + default: + return defaultValue; + } + } + + /** + * Returns system property as an int. + */ + public static long getInt(final String name, final int defaultValue) { + String value = get(name); + if (value == null) { + return defaultValue; + } + + value = value.trim().toLowerCase(); + try { + return Integer.parseInt(value); + } + catch (final NumberFormatException nfex) { + return defaultValue; + } + } + + /** + * Returns system property as a long. + */ + public static long getLong(final String name, final long defaultValue) { + String value = get(name); + if (value == null) { + return defaultValue; + } + + value = value.trim().toLowerCase(); + try { + return Long.parseLong(value); + } + catch (final NumberFormatException nfex) { + return defaultValue; + } + } + + // ---------------------------------------------------------------- infos + + private static final SystemInfo systemInfo = new SystemInfo(); + + /** + * Returns system information. + */ + public static SystemInfo info() { + return systemInfo; + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/system/UserInfo.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/UserInfo.java new file mode 100644 index 000000000..f081d0c9e --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/UserInfo.java @@ -0,0 +1,78 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.system; + +import com.fr.third.jodd.util.StringUtil; + +import java.io.File; + +/** + * User information. + */ +abstract class UserInfo extends RuntimeInfo { + + private final String USER_NAME = SystemUtil.get("user.name"); + private final String USER_HOME = nosep(SystemUtil.get("user.home")); + private final String USER_DIR = nosep(SystemUtil.get("user.dir")); + private final String USER_LANGUAGE = SystemUtil.get("user.language"); + private final String USER_COUNTRY = ((SystemUtil.get("user.country") == null) ? SystemUtil.get("user.region") : SystemUtil.get("user.country")); + private final String JAVA_IO_TMPDIR = SystemUtil.get("java.io.tmpdir"); + private final String JAVA_HOME = nosep(SystemUtil.get("java.home")); + private final String[] SYSTEM_CLASS_PATH = StringUtil.splitc(SystemUtil.get("java.class.path"), File.pathSeparator); + + public final String getUserName() { + return USER_NAME; + } + + public final String getHomeDir() { + return USER_HOME; + } + + public final String getWorkingDir() { + return USER_DIR; + } + + public final String getTempDir() { + return JAVA_IO_TMPDIR; + } + + public final String getUserLanguage() { + return USER_LANGUAGE; + } + + public final String getUserCountry() { + return USER_COUNTRY; + } + + public String getJavaHomeDir() { + return JAVA_HOME; + } + + public String[] getSystemClasspath() { + return SYSTEM_CLASS_PATH; + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/system/package-info.java b/fine-jodd/src/main/java/com/fr/third/jodd/system/package-info.java new file mode 100644 index 000000000..14a01a5a6 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/system/package-info.java @@ -0,0 +1,29 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +/** + * Various system and runtime information in one or two friendly classes. + */ +package com.fr.third.jodd.system; \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherListener.java b/fine-jodd/src/main/java/com/fr/third/jodd/template/ContextTemplateParser.java similarity index 82% rename from fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherListener.java rename to fine-jodd/src/main/java/com/fr/third/jodd/template/ContextTemplateParser.java index 9be3d95d0..015f2f92b 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/io/watch/DirWatcherListener.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/template/ContextTemplateParser.java @@ -23,18 +23,15 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.io.watch; +package com.fr.third.jodd.template; -import java.io.File; +import java.util.function.Function; /** - * Listener for {@link jodd.io.watch.DirWatcher}. + * Context withing string template parser operates. + * Curried version of {@link StringTemplateParser#parse(String, Function)} */ -public interface DirWatcherListener { - - /** - * Invoked when one of the monitored files is created, deleted or modified. - */ - void onChange(File file, DirWatcher.Event event); - -} \ No newline at end of file +@FunctionalInterface +public interface ContextTemplateParser { + String parse(String template); +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/template/MapTemplateParser.java b/fine-jodd/src/main/java/com/fr/third/jodd/template/MapTemplateParser.java new file mode 100644 index 000000000..c335414c5 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/template/MapTemplateParser.java @@ -0,0 +1,48 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.template; + +import java.util.Map; + +public class MapTemplateParser extends StringTemplateParser { + + /** + * Creates new working template context of a map. + */ + public ContextTemplateParser of(final Map map) { + return template -> parseWithMap(template, map); + } + + public String parseWithMap(final String template, final Map map) { + return super.parse(template, macroName -> { + Object value = map.get(macroName); + if (value == null) { + return null; + } + return value.toString(); + }); + } +} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/util/StringTemplateParser.java b/fine-jodd/src/main/java/com/fr/third/jodd/template/StringTemplateParser.java similarity index 74% rename from fine-jodd/src/main/java/com/fr/third/jodd/util/StringTemplateParser.java rename to fine-jodd/src/main/java/com/fr/third/jodd/template/StringTemplateParser.java index 4edfe24f0..e29b22024 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/util/StringTemplateParser.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/template/StringTemplateParser.java @@ -23,9 +23,13 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.util; +package com.fr.third.jodd.template; -import java.util.Map; +import com.fr.third.jodd.util.CharUtil; +import com.fr.third.jodd.util.StringPool; +import com.fr.third.jodd.util.StringUtil; + +import java.util.function.Function; /** * Parser for string macro templates. On parsing, macro values @@ -35,13 +39,6 @@ import java.util.Map; */ public class StringTemplateParser { - /** - * Static ctor. - */ - public static StringTemplateParser create() { - return new StringTemplateParser(); - } - public static final String DEFAULT_MACRO_PREFIX = "$"; public static final String DEFAULT_MACRO_START = "${"; public static final String DEFAULT_MACRO_END = "}"; @@ -57,77 +54,55 @@ public class StringTemplateParser { protected char escapeChar = '\\'; protected boolean parseValues; - public boolean isReplaceMissingKey() { - return replaceMissingKey; - } - /** * Specifies if missing keys should be resolved at all, * true by default. * If false missing keys will be left as it were, i.e. * they will not be replaced. */ - public void setReplaceMissingKey(boolean replaceMissingKey) { + public StringTemplateParser setReplaceMissingKey(final boolean replaceMissingKey) { this.replaceMissingKey = replaceMissingKey; - } - - public String getMissingKeyReplacement() { - return missingKeyReplacement; + return this; } /** * Specifies replacement for missing keys. If null * exception will be thrown. */ - public StringTemplateParser setMissingKeyReplacement(String missingKeyReplacement) { + public StringTemplateParser setMissingKeyReplacement(final String missingKeyReplacement) { this.missingKeyReplacement = missingKeyReplacement; return this; } - public boolean isResolveEscapes() { - return resolveEscapes; - } - /** * Specifies if escaped values should be resolved. In special usecases, * when the same string has to be processed more then once, * this may be set to false so escaped values * remains. */ - public StringTemplateParser setResolveEscapes(boolean resolveEscapes) { + public StringTemplateParser setResolveEscapes(final boolean resolveEscapes) { this.resolveEscapes = resolveEscapes; return this; } - public String getMacroStart() { - return macroStart; - } - /** * Defines macro start string. */ - public StringTemplateParser setMacroStart(String macroStart) { + public StringTemplateParser setMacroStart(final String macroStart) { this.macroStart = macroStart; return this; } - public String getMacroPrefix() { - return macroPrefix; - } - - public StringTemplateParser setMacroPrefix(String macroPrefix) { + public StringTemplateParser setMacroPrefix(final String macroPrefix) { this.macroPrefix = macroPrefix; return this; } - public String getMacroEnd() { - return macroEnd; - } /** * Defines macro end string. */ - public StringTemplateParser setMacroEnd(String macroEnd) { + public StringTemplateParser setMacroEnd(final String macroEnd) { this.macroEnd = macroEnd; return this; } @@ -140,27 +115,19 @@ public class StringTemplateParser { return this; } - public char getEscapeChar() { - return escapeChar; - } - /** * Defines escape character. */ - public StringTemplateParser setEscapeChar(char escapeChar) { + public StringTemplateParser setEscapeChar(final char escapeChar) { this.escapeChar = escapeChar; return this; } - public boolean isParseValues() { - return parseValues; - } - /** * Defines if macro values has to be parsed, too. * By default, macro values are returned as they are. */ - public StringTemplateParser setParseValues(boolean parseValues) { + public StringTemplateParser setParseValues(final boolean parseValues) { this.parseValues = parseValues; return this; } @@ -171,7 +138,7 @@ public class StringTemplateParser { /** * Parses string template and replaces macros with resolved values. */ - public String parse(String template, MacroResolver macroResolver) { + public String parse(String template, final Function macroResolver) { StringBuilder result = new StringBuilder(template.length()); int i = 0; @@ -191,9 +158,9 @@ public class StringTemplateParser { strict = false; } - int prefixLen = macroPrefix.length(); - int startLen = macroStart.length(); - int endLen = macroEnd.length(); + final int prefixLen = macroPrefix.length(); + final int startLen = macroStart.length(); + final int endLen = macroEnd.length(); while (i < len) { int ndx = template.indexOf(macroPrefix, i); @@ -229,18 +196,18 @@ public class StringTemplateParser { // macro started, detect strict format - boolean strictFormat = strict; + boolean detectedStrictFormat = strict; - if (!strictFormat) { + if (!detectedStrictFormat) { if (StringUtil.isSubstringAt(template, macroStart, ndx)) { - strictFormat = true; + detectedStrictFormat = true; } } int ndx1; int ndx2; - if (!strictFormat) { + if (!detectedStrictFormat) { // not strict format: $foo ndx += prefixLen; @@ -297,7 +264,7 @@ public class StringTemplateParser { Object value; if (missingKeyReplacement != null || !replaceMissingKey) { try { - value = macroResolver.resolve(name); + value = macroResolver.apply(name); } catch (Exception ignore) { value = null; } @@ -306,11 +273,15 @@ public class StringTemplateParser { if (replaceMissingKey) { value = missingKeyReplacement; } else { - value = template.substring(ndx1 - startLen, ndx2 + 1); + if (detectedStrictFormat) { + value = template.substring(ndx1 - startLen, ndx2 + endLen); + } else { + value = template.substring(ndx1 - 1, ndx2); + } } } } else { - value = macroResolver.resolve(name); + value = macroResolver.apply(name); if (value == null) { value = StringPool.EMPTY; } @@ -326,7 +297,7 @@ public class StringTemplateParser { result.append(stringValue); i = ndx2; - if (strictFormat) { + if (detectedStrictFormat) { i += endLen; } } else { @@ -339,37 +310,4 @@ public class StringTemplateParser { return result.toString(); } - // ---------------------------------------------------------------- resolver - - /** - * Macro value resolver. - */ - public interface MacroResolver { - /** - * Resolves macro value for macro name founded in - * string template. null values will - * be replaced with empty strings. - */ - String resolve(String macroName); - - } - - /** - * Creates commonly used {@link MacroResolver} that resolved - * macros in the provided map. - */ - public static MacroResolver createMapMacroResolver(final Map map) { - return new MacroResolver() { - public String resolve(String macroName) { - Object value = map.get(macroName); - - if (value == null) { - return null; - } - - return value.toString(); - } - }; - } - } \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/time/JulianDate.java b/fine-jodd/src/main/java/com/fr/third/jodd/time/JulianDate.java new file mode 100644 index 000000000..d1998d69c --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/time/JulianDate.java @@ -0,0 +1,474 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.time; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Objects; + +import static com.fr.third.jodd.time.TimeUtil.MILLIS_IN_DAY; + +/** + * Julian Date stamp, for high precision calculations. Julian date is a real + * number and it basically consist of two parts: integer and fraction. Integer + * part carries date information, fraction carries time information. + * + *

    + * The Julian day or Julian day number (JDN) is the (integer) number of days that + * have elapsed since Monday, January 1, 4713 BC in the proleptic Julian calendar 1. + * That day is counted as Julian day zero. Thus the multiples of 7 are Mondays. + * Negative values can also be used. + * + *

    + * The Julian Date (JD) is the number of days (with decimal fraction of the day) that + * have elapsed since 12 noon Greenwich Mean Time (UT or TT) of that day. + * Rounding to the nearest integer gives the Julian day number. + *

    + * For calculations that will have time precision of 1e-3 seconds, both + * fraction and integer part must have enough digits in it. The problem is + * that integer part is big and, on the other hand fractional is small, and + * since final julian date is a sum of this two values, some fraction + * numerals may be lost. Therefore, for higher precision both + * fractional and integer part of julian date real number has to be + * preserved. + *

    + * This class stores the unmodified fraction part, but not all digits + * are significant! For 1e-3 seconds precision, only 8 digits after + * the decimal point are significant. + * + * @see TimeUtil + */ +public class JulianDate implements Serializable, Cloneable { + /** + * Julian Date for 1970-01-01T00:00:00 (Thursday). + */ + public static final JulianDate JD_1970 = new JulianDate(2440587, 0.5); + + /** + * Julian Date for 2001-01-01T00:00:00 (Monday). + */ + public static final JulianDate JD_2001 = new JulianDate(2451910, 0.5); + + + public static JulianDate of(final double value) { + return new JulianDate(value); + } + + public static JulianDate of(final LocalDateTime localDateTime) { + return of(localDateTime.getYear(), + localDateTime.getMonth().getValue(), + localDateTime.getDayOfMonth(), + localDateTime.getHour(), + localDateTime.getMinute(), + localDateTime.getSecond(), + localDateTime.getNano() / 1_000_000); + } + + public static JulianDate of(final LocalDate localDate) { + return of(localDate.getYear(), + localDate.getMonth().getValue(), + localDate.getDayOfMonth(), + 0, 0, 0, 0); + } + + public static JulianDate of(final long milliseconds) { + int integer = (int) (milliseconds / MILLIS_IN_DAY); + double fraction = (double)(milliseconds % MILLIS_IN_DAY) / MILLIS_IN_DAY; + integer += JD_1970.integer; + fraction += JD_1970.fraction; + return new JulianDate(integer, fraction); + } + + public static JulianDate of(final int i, final double f) { + return new JulianDate(i, f); + } + + public static JulianDate of(int year, int month, int day, final int hour, final int minute, final int second, final int millisecond) { + // month range fix + if ((month > 12) || (month < -12)) { + month--; + int delta = month / 12; + year += delta; + month -= delta * 12; + month++; + } + if (month < 0) { + year--; + month += 12; + } + + // decimal day fraction + double frac = (hour / 24.0) + (minute / 1440.0) + (second / 86400.0) + (millisecond / 86400000.0); + if (frac < 0) { // negative time fix + int delta = ((int)(-frac)) + 1; + frac += delta; + day -= delta; + } + //double gyr = year + (0.01 * month) + (0.0001 * day) + (0.0001 * frac) + 1.0e-9; + double gyr = year + (0.01 * month) + (0.0001 * (day + frac)) + 1.0e-9; + + // conversion factors + int iy0; + int im0; + if (month <= 2) { + iy0 = year - 1; + im0 = month + 12; + } else { + iy0 = year; + im0 = month; + } + int ia = iy0 / 100; + int ib = 2 - ia + (ia >> 2); + + // calculate julian date + int jd; + if (year <= 0) { + jd = (int)((365.25 * iy0) - 0.75) + (int)(30.6001 * (im0 + 1)) + day + 1720994; + } else { + jd = (int)(365.25 * iy0) + (int)(30.6001 * (im0 + 1)) + day + 1720994; + } + if (gyr >= 1582.1015) { // on or after 15 October 1582 + jd += ib; + } + //return jd + frac + 0.5; + + return new JulianDate(jd, frac + 0.5); + } + + public static JulianDate now() { + return of(LocalDateTime.now()); + } + + /** + * Integer part of the Julian Date (JD). + */ + protected int integer; + + /** + * Returns integer part of the Julian Date (JD). + */ + public int getInteger() { + return integer; + } + + /** + * Fraction part of the Julian Date (JD). + * Should be always in [0.0, 1.0) range. + */ + protected double fraction; + + /** + * Returns the fraction part of Julian Date (JD). + * Returned value is always in [0.0, 1.0) range. + */ + public double getFraction() { + return fraction; + } + + /** + * Calculates and returns significant fraction only as an int. + */ + public int getSignificantFraction() { + return (int) (fraction * 100_000_000); + } + + /** + * Returns JDN. Note that JDN is not equal to {@link #integer}. It is calculated by + * rounding to the nearest integer. + */ + public int getJulianDayNumber() { + if (fraction >= 0.5) { + return integer + 1; + } + return integer; + } + + /** + * Creates JD from a double. + */ + public JulianDate(final double jd) { + integer = (int) jd; + fraction = jd - (double)integer; + } + + /** + * Creates JD from both integer and fractional part using normalization. + * Normalization occurs when fractional part is out of range. + * + * @see #set(int, double) + * + * @param i integer part + * @param f fractional part should be in range [0.0, 1.0) + */ + public JulianDate(final int i, final double f) { + set(i, f); + } + + /** + * Creates JD from BigDecimal. + */ + public JulianDate(final BigDecimal bd) { + double d = bd.doubleValue(); + integer = (int) d; + fraction = bd.subtract(new BigDecimal(integer)).doubleValue(); + } + + + // ---------------------------------------------------------------- conversion + + + /** + * Returns double value of JD. + * CAUTION: double values may not be suit for precision math due to + * loss of precision. + */ + public double doubleValue() { + return (double)integer + fraction; + } + + /** + * Returns BigDecimal value of JD. + */ + public BigDecimal toBigDecimal() { + return new BigDecimal(integer).add(new BigDecimal(fraction)); + } + + /** + * Returns string representation of JD. + * + * @return julian integer as string + */ + @Override + public String toString() { + String s = Double.toString(fraction); + int i = s.indexOf('.'); + s = s.substring(i); + return integer + s; + } + + /** + * Converts to milliseconds. + */ + public long toMilliseconds() { + double then = (fraction - JD_1970.fraction) * MILLIS_IN_DAY; + then += (integer - JD_1970.integer) * MILLIS_IN_DAY; + then += then > 0 ? 1.0e-6 : -1.0e-6; + return (long) then; + } + + + public LocalDateTime toLocalDateTime() { + int year, month, day; + double frac; + int jd, ka, kb, kc, kd, ke, ialp; + + //double JD = jds.doubleValue();//jdate; + //jd = (int)(JD + 0.5); // integer julian date + //frac = JD + 0.5 - (double)jd + 1.0e-10; // day fraction + + ka = (int)(fraction + 0.5); + jd = integer + ka; + frac = fraction + 0.5 - ka + 1.0e-10; + + ka = jd; + if (jd >= 2299161) { + ialp = (int)(((double)jd - 1867216.25) / (36524.25)); + ka = jd + 1 + ialp - (ialp >> 2); + } + kb = ka + 1524; + kc = (int)(((double)kb - 122.1) / 365.25); + kd = (int)((double)kc * 365.25); + ke = (int)((double)(kb - kd) / 30.6001); + day = kb - kd - ((int)((double)ke * 30.6001)); + if (ke > 13) { + month = ke - 13; + } else { + month = ke - 1; + } + if ((month == 2) && (day > 28)){ + day = 29; + } + if ((month == 2) && (day == 29) && (ke == 3)) { + year = kc - 4716; + } else if (month > 2) { + year = kc - 4716; + } else { + year = kc - 4715; + } + + final int time_year = year; + final int time_month = month; + final int time_day = day; + + // hour with minute and second included as fraction + double d_hour = frac * 24.0; + final int time_hour = (int) d_hour; // integer hour + + // minute with second included as a fraction + double d_minute = (d_hour - (double)time_hour) * 60.0; + final int time_minute = (int) d_minute; // integer minute + + double d_second = (d_minute - (double)time_minute) * 60.0; + final int time_second = (int) d_second; // integer seconds + + double d_millis = (d_second - (double)time_second) * 1000.0; + + // fix calculation errors + final int time_millisecond = (int) (((d_millis * 10) + 0.5) / 10); + + return LocalDateTime.of(time_year, time_month, time_day, time_hour, time_minute, time_second, time_millisecond * 1_000_000); + } + + + // ---------------------------------------------------------------- math + + /** + * Adds two JD and returns a new instance. + */ + public JulianDate add(final JulianDate jds) { + int i = this.integer + jds.integer; + double f = this.fraction + jds.fraction; + return new JulianDate(i, f); + } + + /** + * Adds a double delta value and returns a new instance. + */ + public JulianDate add(final double delta) { + return new JulianDate(this.integer, this.fraction + delta); + } + + + /** + * Subtracts a JD from current instance and returns a new instance. + */ + public JulianDate sub(final JulianDate jds) { + int i = this.integer - jds.integer; + double f = this.fraction -jds.fraction; + return new JulianDate(i, f); + } + + /** + * Subtracts a double from current instance and returns a new instance. + */ + public JulianDate sub(final double delta) { + return new JulianDate(this.integer, this.fraction - delta); + } + + /** + * Sets integer and fractional part with normalization. + * Normalization means that if double is out of range, + * values will be correctly fixed. + */ + private void set(final int i, double f) { + integer = i; + int fi = (int) f; + f -= fi; + integer += fi; + if (f < 0) { + f += 1; + integer--; + } + this.fraction = f; + } + + + // ---------------------------------------------------------------- between + + /** + * Calculates the number of days between two dates. Returned value is always positive. + */ + public int daysBetween(final JulianDate otherDate) { + int difference = daysSpan(otherDate); + return difference >= 0 ? difference : -difference; + } + + /** + * Returns span between two days. Returned value may be positive (when this date + * is after the provided one) or negative (when comparing to future date). + */ + public int daysSpan(final JulianDate otherDate) { + int now = getJulianDayNumber(); + int then = otherDate.getJulianDayNumber(); + return now - then; + } + + // ---------------------------------------------------------------- equals & hashCode + + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + if (this.getClass() != object.getClass()) { + return false; + } + JulianDate stamp = (JulianDate) object; + return (stamp.integer == this.integer) && + (Double.compare(stamp.fraction, this.fraction) == 0); + } + + @Override + public int hashCode() { + return Objects.hash(integer, fraction); + } + + // ---------------------------------------------------------------- clone + + @Override + protected JulianDate clone() { + return new JulianDate(this.integer, this.fraction); + } + + // ---------------------------------------------------------------- conversion + + /** + * Returns Reduced Julian Date (RJD), used by astronomers. + * RJD = JD − 2400000 + */ + public JulianDate getReducedJulianDate() { + return new JulianDate(integer - 2400000, fraction); + } + + /** + * Returns Modified Julian Date (MJD), where date starts from midnight rather than noon. + * RJD = JD − 2400000.5 + */ + public JulianDate getModifiedJulianDate() { + return new JulianDate(integer - 2400000, fraction - 0.5); + } + + /** + * Returns Truncated Julian Day (TJD), introduced by NASA for the space program. + * TJD began at midnight at the beginning of May 24, 1968 (Friday). + */ + public JulianDate getTruncatedJulianDate() { + return new JulianDate(integer - 2440000, fraction - 0.5); + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/time/TimeUtil.java b/fine-jodd/src/main/java/com/fr/third/jodd/time/TimeUtil.java new file mode 100644 index 000000000..77a05dc1a --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/time/TimeUtil.java @@ -0,0 +1,132 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.time; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; + +public class TimeUtil { + + public static final int SECONDS_IN_DAY = 60 * 60 * 24; + public static final long MILLIS_IN_DAY = 1000L * SECONDS_IN_DAY; + + public static final SimpleDateFormat HTTP_DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); + + /** + * Converts local date to Date. + */ + public static Date toDate(final LocalDate localDate) { + return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + /** + * Converts local date time to Date. + */ + public static Date toDate(final LocalDateTime localDateTime) { + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * Converts local date time to Calendar. + */ + public static Calendar toCalendar(final LocalDateTime localDateTime) { + return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault())); + } + + /** + * Converts local date time to Calendar and setting time to midnight. + */ + public static Calendar toCalendar(final LocalDate localDate) { + return GregorianCalendar.from(ZonedDateTime.of(localDate, LocalTime.MIDNIGHT, ZoneId.systemDefault())); + } + + /** + * Converts local date time to epoh milliseconds. + */ + public static long toMilliseconds(final LocalDateTime localDateTime) { + return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + /** + * Converts local date time to epoh milliseconds assuming start of the day as time point. + */ + public static long toMilliseconds(final LocalDate localDate) { + return toMilliseconds(localDate.atStartOfDay()); + } + + + public static LocalDateTime fromCalendar(final Calendar calendar) { + final TimeZone tz = calendar.getTimeZone(); + final ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId(); + return LocalDateTime.ofInstant(calendar.toInstant(), zid); + } + + public static LocalDateTime fromDate(final Date date) { + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + } + + public static LocalDateTime fromMilliseconds(final long milliseconds) { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault()); + } + + + /** + * Formats time to HTTP date/time format. Note that number of milliseconds + * is lost. + */ + public static String formatHttpDate(final long millis) { + final Date date = new Date(millis); + return HTTP_DATE_FORMAT.format(date); + } + + /** + * Parses the HTTP date/time format. Returns -1 if given string + * is invalid. + */ + public static long parseHttpTime(final String time) { + if (time == null) { + return -1; + } + + try { + return TimeUtil.HTTP_DATE_FORMAT.parse(time).getTime(); + } + catch (ParseException e) { + return -1; + } + } + +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/time/package-info.java b/fine-jodd/src/main/java/com/fr/third/jodd/time/package-info.java new file mode 100644 index 000000000..3cb62ffa3 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/time/package-info.java @@ -0,0 +1,29 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +/** + * Precise and powerful Julian Date. It's not a LocalDate replacement, it's more an add-on. + */ +package com.fr.third.jodd.time; \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/Convert.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/Convert.java deleted file mode 100644 index 9cabda247..000000000 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/Convert.java +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright (c) 2003-present, Jodd Team (http://jodd.org) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package com.fr.third.jodd.typeconverter; - -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.datetime.JDateTime; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Date; -import java.util.Calendar; - -/** - * Static version of {@link ConvertBean} of default {@link TypeConverterManagerBean}. - */ -public class Convert { - - /** - * Returns default {@link ConvertBean}. We should not store this instance - * in a static variable as default {@link TypeConverterManagerBean} may be changed. - */ - protected static ConvertBean getConvertBean() { - return TypeConverterManager.getDefaultTypeConverterManager().getConvertBean(); - } - - // ---------------------------------------------------------------- @@generated - - public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) { - return getConvertBean().toBigDecimal(value, defaultValue); - } - - public static Boolean toBoolean(Object value) { - return getConvertBean().toBoolean(value); - } - - public static Boolean toBoolean(Object value, Boolean defaultValue) { - return getConvertBean().toBoolean(value, defaultValue); - } - - public static boolean toBooleanValue(Object value, boolean defaultValue) { - return getConvertBean().toBooleanValue(value, defaultValue); - } - - public static boolean toBooleanValue(Object value) { - return getConvertBean().toBooleanValue(value); - } - - public static Integer toInteger(Object value) { - return getConvertBean().toInteger(value); - } - - public static Integer toInteger(Object value, Integer defaultValue) { - return getConvertBean().toInteger(value, defaultValue); - } - - public static int toIntValue(Object value, int defaultValue) { - return getConvertBean().toIntValue(value, defaultValue); - } - - public static int toIntValue(Object value) { - return getConvertBean().toIntValue(value); - } - - public static Long toLong(Object value) { - return getConvertBean().toLong(value); - } - - public static Long toLong(Object value, Long defaultValue) { - return getConvertBean().toLong(value, defaultValue); - } - - public static long toLongValue(Object value, long defaultValue) { - return getConvertBean().toLongValue(value, defaultValue); - } - - public static long toLongValue(Object value) { - return getConvertBean().toLongValue(value); - } - - public static Float toFloat(Object value) { - return getConvertBean().toFloat(value); - } - - public static Float toFloat(Object value, Float defaultValue) { - return getConvertBean().toFloat(value, defaultValue); - } - - public static float toFloatValue(Object value, float defaultValue) { - return getConvertBean().toFloatValue(value, defaultValue); - } - - public static float toFloatValue(Object value) { - return getConvertBean().toFloatValue(value); - } - - public static Double toDouble(Object value) { - return getConvertBean().toDouble(value); - } - - public static Double toDouble(Object value, Double defaultValue) { - return getConvertBean().toDouble(value, defaultValue); - } - - public static double toDoubleValue(Object value, double defaultValue) { - return getConvertBean().toDoubleValue(value, defaultValue); - } - - public static double toDoubleValue(Object value) { - return getConvertBean().toDoubleValue(value); - } - - public static Short toShort(Object value) { - return getConvertBean().toShort(value); - } - - public static Short toShort(Object value, Short defaultValue) { - return getConvertBean().toShort(value, defaultValue); - } - - public static short toShortValue(Object value, short defaultValue) { - return getConvertBean().toShortValue(value, defaultValue); - } - - public static short toShortValue(Object value) { - return getConvertBean().toShortValue(value); - } - - public static Character toCharacter(Object value) { - return getConvertBean().toCharacter(value); - } - - public static Character toCharacter(Object value, Character defaultValue) { - return getConvertBean().toCharacter(value, defaultValue); - } - - public static char toCharValue(Object value, char defaultValue) { - return getConvertBean().toCharValue(value, defaultValue); - } - - public static char toCharValue(Object value) { - return getConvertBean().toCharValue(value); - } - - public static Byte toByte(Object value) { - return getConvertBean().toByte(value); - } - - public static Byte toByte(Object value, Byte defaultValue) { - return getConvertBean().toByte(value, defaultValue); - } - - public static byte toByteValue(Object value, byte defaultValue) { - return getConvertBean().toByteValue(value, defaultValue); - } - - public static byte toByteValue(Object value) { - return getConvertBean().toByteValue(value); - } - - public static boolean[] toBooleanArray(Object value) { - return getConvertBean().toBooleanArray(value); - } - - public static int[] toIntegerArray(Object value) { - return getConvertBean().toIntegerArray(value); - } - - public static long[] toLongArray(Object value) { - return getConvertBean().toLongArray(value); - } - - public static float[] toFloatArray(Object value) { - return getConvertBean().toFloatArray(value); - } - - public static double[] toDoubleArray(Object value) { - return getConvertBean().toDoubleArray(value); - } - - public static short[] toShortArray(Object value) { - return getConvertBean().toShortArray(value); - } - - public static char[] toCharacterArray(Object value) { - return getConvertBean().toCharacterArray(value); - } - - public static String toString(Object value) { - return getConvertBean().toString(value); - } - - public static String toString(Object value, String defaultValue) { - return getConvertBean().toString(value, defaultValue); - } - - public static String[] toStringArray(Object value) { - return getConvertBean().toStringArray(value); - } - - public static Class toClass(Object value) { - return getConvertBean().toClass(value); - } - - public static Class[] toClassArray(Object value) { - return getConvertBean().toClassArray(value); - } - - public static JDateTime toJDateTime(Object value) { - return getConvertBean().toJDateTime(value); - } - - public static JDateTime toJDateTime(Object value, JDateTime defaultValue) { - return getConvertBean().toJDateTime(value, defaultValue); - } - - public static Date toDate(Object value) { - return getConvertBean().toDate(value); - } - - public static Date toDate(Object value, Date defaultValue) { - return getConvertBean().toDate(value, defaultValue); - } - - public static Calendar toCalendar(Object value) { - return getConvertBean().toCalendar(value); - } - - public static Calendar toCalendar(Object value, Calendar defaultValue) { - return getConvertBean().toCalendar(value, defaultValue); - } - - public static BigInteger toBigInteger(Object value) { - return getConvertBean().toBigInteger(value); - } - - public static BigInteger toBigInteger(Object value, BigInteger defaultValue) { - return getConvertBean().toBigInteger(value, defaultValue); - } - - public static BigDecimal toBigDecimal(Object value) { - return getConvertBean().toBigDecimal(value); - } -} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/ConvertBean.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/Converter.java similarity index 50% rename from fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/ConvertBean.java rename to fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/Converter.java index 73531cf6d..076f0d7a0 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/ConvertBean.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/Converter.java @@ -25,90 +25,39 @@ package com.fr.third.jodd.typeconverter; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.datetime.JDateTime; - import java.math.BigDecimal; import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; - -public class ConvertBean { - - /** - * List of common types. Used for faster lookup and when needed to be used - * by other converters. - */ - protected Class[] commonTypes = new Class[] { - Boolean.class, // 0 - boolean.class, - Integer.class, // 2 - int.class, - Long.class, // 4 - long.class, - Float.class, // 6 - float.class, - Double.class, // 8 - double.class, - Short.class, // 10 - short.class, - Character.class, // 12 - char.class, - Byte.class, // 14 - byte.class, - - boolean[].class, // 16 - int[].class, // 17 - long[].class, // 18 - float[].class, // 19 - double[].class, // 20 - short[].class, // 21 - char[].class, // 22 - - String.class, // 23 - String[].class, // 24 - Class.class, // 25 - Class[].class, // 26 - - JDateTime.class, // 27 - Date.class, // 28 - Calendar.class, // 29 - - BigInteger.class, // 30 - BigDecimal.class, // 31 - }; - - /** - * Common type converters, filled up during the registration. - */ - protected TypeConverter[] typeConverters = new TypeConverter[commonTypes.length]; - - - public void register(Class type, TypeConverter typeConverter) { - for (int i = 0; i < commonTypes.length; i++) { - Class commonType = commonTypes[i]; - if (type == commonType) { - typeConverters[i] = typeConverter; - break; - } - } + +/** + * Just a convenient {@link TypeConverterManager} usage. + */ +public class Converter { + + private static final Converter CONVERTER = new Converter(); + + /** + * Returns default instance. + */ + public static Converter get() { + return CONVERTER; } - // ---------------------------------------------------------------- @@generated + // ---------------------------------------------------------------- boolean /** * Converts value to Boolean. */ - public Boolean toBoolean(Object value) { - return (Boolean) typeConverters[0].convert(value); + public Boolean toBoolean(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Boolean.class); + return tc.convert(value); } /** * Converts value to Boolean. Returns default value * when conversion result is null */ - public Boolean toBoolean(Object value, Boolean defaultValue) { - Boolean result = (Boolean) typeConverters[0].convert(value); + public Boolean toBoolean(final Object value, final Boolean defaultValue) { + final Boolean result = toBoolean(value); if (result == null) { return defaultValue; } @@ -119,8 +68,8 @@ public class ConvertBean { * Converts value to boolean. Returns default value * when conversion result is null. */ - public boolean toBooleanValue(Object value, boolean defaultValue) { - Boolean result = (Boolean) typeConverters[1].convert(value); + public boolean toBooleanValue(final Object value, final boolean defaultValue) { + final Boolean result = toBoolean(value); if (result == null) { return defaultValue; } @@ -130,24 +79,26 @@ public class ConvertBean { /** * Converts value to boolean with common default value. */ - public boolean toBooleanValue(Object value) { + public boolean toBooleanValue(final Object value) { return toBooleanValue(value, false); } + // ---------------------------------------------------------------- integer /** * Converts value to Integer. */ - public Integer toInteger(Object value) { - return (Integer) typeConverters[2].convert(value); + public Integer toInteger(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Integer.class); + return tc.convert(value); } /** * Converts value to Integer. Returns default value * when conversion result is null */ - public Integer toInteger(Object value, Integer defaultValue) { - Integer result = (Integer) typeConverters[2].convert(value); + public Integer toInteger(final Object value, final Integer defaultValue) { + final Integer result = toInteger(value); if (result == null) { return defaultValue; } @@ -158,8 +109,8 @@ public class ConvertBean { * Converts value to int. Returns default value * when conversion result is null. */ - public int toIntValue(Object value, int defaultValue) { - Integer result = (Integer) typeConverters[3].convert(value); + public int toIntValue(final Object value, final int defaultValue) { + final Integer result = toInteger(value); if (result == null) { return defaultValue; } @@ -169,24 +120,26 @@ public class ConvertBean { /** * Converts value to int with common default value. */ - public int toIntValue(Object value) { + public int toIntValue(final Object value) { return toIntValue(value, 0); } + // ---------------------------------------------------------------- long /** * Converts value to Long. */ - public Long toLong(Object value) { - return (Long) typeConverters[4].convert(value); + public Long toLong(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Long.class); + return tc.convert(value); } /** * Converts value to Long. Returns default value * when conversion result is null */ - public Long toLong(Object value, Long defaultValue) { - Long result = (Long) typeConverters[4].convert(value); + public Long toLong(final Object value, final Long defaultValue) { + final Long result = toLong(value); if (result == null) { return defaultValue; } @@ -197,8 +150,8 @@ public class ConvertBean { * Converts value to long. Returns default value * when conversion result is null. */ - public long toLongValue(Object value, long defaultValue) { - Long result = (Long) typeConverters[5].convert(value); + public long toLongValue(final Object value, final long defaultValue) { + final Long result = toLong(value); if (result == null) { return defaultValue; } @@ -208,24 +161,26 @@ public class ConvertBean { /** * Converts value to long with common default value. */ - public long toLongValue(Object value) { + public long toLongValue(final Object value) { return toLongValue(value, 0); } + // ---------------------------------------------------------------- float /** * Converts value to Float. */ - public Float toFloat(Object value) { - return (Float) typeConverters[6].convert(value); + public Float toFloat(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Float.class); + return tc.convert(value); } /** * Converts value to Float. Returns default value * when conversion result is null */ - public Float toFloat(Object value, Float defaultValue) { - Float result = (Float) typeConverters[6].convert(value); + public Float toFloat(final Object value, final Float defaultValue) { + final Float result = toFloat(value); if (result == null) { return defaultValue; } @@ -236,8 +191,8 @@ public class ConvertBean { * Converts value to float. Returns default value * when conversion result is null. */ - public float toFloatValue(Object value, float defaultValue) { - Float result = (Float) typeConverters[7].convert(value); + public float toFloatValue(final Object value, final float defaultValue) { + final Float result = toFloat(value); if (result == null) { return defaultValue; } @@ -247,24 +202,26 @@ public class ConvertBean { /** * Converts value to float with common default value. */ - public float toFloatValue(Object value) { + public float toFloatValue(final Object value) { return toFloatValue(value, 0); } + // ---------------------------------------------------------------- double /** * Converts value to Double. */ - public Double toDouble(Object value) { - return (Double) typeConverters[8].convert(value); + public Double toDouble(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Double.class); + return tc.convert(value); } /** * Converts value to Double. Returns default value * when conversion result is null */ - public Double toDouble(Object value, Double defaultValue) { - Double result = (Double) typeConverters[8].convert(value); + public Double toDouble(final Object value, final Double defaultValue) { + final Double result = toDouble(value); if (result == null) { return defaultValue; } @@ -275,8 +232,8 @@ public class ConvertBean { * Converts value to double. Returns default value * when conversion result is null. */ - public double toDoubleValue(Object value, double defaultValue) { - Double result = (Double) typeConverters[9].convert(value); + public double toDoubleValue(final Object value, final double defaultValue) { + final Double result = toDouble(value); if (result == null) { return defaultValue; } @@ -286,24 +243,26 @@ public class ConvertBean { /** * Converts value to double with common default value. */ - public double toDoubleValue(Object value) { + public double toDoubleValue(final Object value) { return toDoubleValue(value, 0); } + // ---------------------------------------------------------------- short /** * Converts value to Short. */ - public Short toShort(Object value) { - return (Short) typeConverters[10].convert(value); + public Short toShort(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Short.class); + return tc.convert(value); } /** * Converts value to Short. Returns default value * when conversion result is null */ - public Short toShort(Object value, Short defaultValue) { - Short result = (Short) typeConverters[10].convert(value); + public Short toShort(final Object value, final Short defaultValue) { + final Short result = toShort(value); if (result == null) { return defaultValue; } @@ -314,8 +273,8 @@ public class ConvertBean { * Converts value to short. Returns default value * when conversion result is null. */ - public short toShortValue(Object value, short defaultValue) { - Short result = (Short) typeConverters[11].convert(value); + public short toShortValue(final Object value, final short defaultValue) { + final Short result = toShort(value); if (result == null) { return defaultValue; } @@ -325,24 +284,26 @@ public class ConvertBean { /** * Converts value to short with common default value. */ - public short toShortValue(Object value) { + public short toShortValue(final Object value) { return toShortValue(value, (short) 0); } + // ---------------------------------------------------------------- character /** * Converts value to Character. */ - public Character toCharacter(Object value) { - return (Character) typeConverters[12].convert(value); + public Character toCharacter(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Character.class); + return tc.convert(value); } /** * Converts value to Character. Returns default value * when conversion result is null */ - public Character toCharacter(Object value, Character defaultValue) { - Character result = (Character) typeConverters[12].convert(value); + public Character toCharacter(final Object value, final Character defaultValue) { + final Character result = toCharacter(value); if (result == null) { return defaultValue; } @@ -353,8 +314,8 @@ public class ConvertBean { * Converts value to char. Returns default value * when conversion result is null. */ - public char toCharValue(Object value, char defaultValue) { - Character result = (Character) typeConverters[13].convert(value); + public char toCharValue(final Object value, final char defaultValue) { + final Character result = toCharacter(value); if (result == null) { return defaultValue; } @@ -364,24 +325,26 @@ public class ConvertBean { /** * Converts value to char with common default value. */ - public char toCharValue(Object value) { + public char toCharValue(final Object value) { return toCharValue(value, (char) 0); } + // ---------------------------------------------------------------- byte /** * Converts value to Byte. */ - public Byte toByte(Object value) { - return (Byte) typeConverters[14].convert(value); + public Byte toByte(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Byte.class); + return tc.convert(value); } /** * Converts value to Byte. Returns default value * when conversion result is null */ - public Byte toByte(Object value, Byte defaultValue) { - Byte result = (Byte) typeConverters[14].convert(value); + public Byte toByte(final Object value, final Byte defaultValue) { + final Byte result = toByte(value); if (result == null) { return defaultValue; } @@ -392,8 +355,8 @@ public class ConvertBean { * Converts value to byte. Returns default value * when conversion result is null. */ - public byte toByteValue(Object value, byte defaultValue) { - Byte result = (Byte) typeConverters[15].convert(value); + public byte toByteValue(final Object value, final byte defaultValue) { + final Byte result = toByte(value); if (result == null) { return defaultValue; } @@ -403,72 +366,86 @@ public class ConvertBean { /** * Converts value to byte with common default value. */ - public byte toByteValue(Object value) { + public byte toByteValue(final Object value) { return toByteValue(value, (byte) 0); } + // ---------------------------------------------------------------- array + /** * Converts value to boolean[]. */ - public boolean[] toBooleanArray(Object value) { - return (boolean[]) typeConverters[16].convert(value); + public boolean[] toBooleanArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(boolean[].class); + return tc.convert(value); } /** * Converts value to int[]. */ - public int[] toIntegerArray(Object value) { - return (int[]) typeConverters[17].convert(value); + public int[] toIntegerArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(int[].class); + return tc.convert(value); + } /** * Converts value to long[]. */ - public long[] toLongArray(Object value) { - return (long[]) typeConverters[18].convert(value); + public long[] toLongArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(long[].class); + return tc.convert(value); + } /** * Converts value to float[]. */ - public float[] toFloatArray(Object value) { - return (float[]) typeConverters[19].convert(value); + public float[] toFloatArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(float[].class); + return tc.convert(value); } /** * Converts value to double[]. */ - public double[] toDoubleArray(Object value) { - return (double[]) typeConverters[20].convert(value); + public double[] toDoubleArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(double[].class); + return tc.convert(value); } /** * Converts value to short[]. */ - public short[] toShortArray(Object value) { - return (short[]) typeConverters[21].convert(value); + public short[] toShortArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(short[].class); + return tc.convert(value); } /** * Converts value to char[]. */ - public char[] toCharacterArray(Object value) { - return (char[]) typeConverters[22].convert(value); + public char[] toCharacterArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(char[].class); + return tc.convert(value); } + // ---------------------------------------------------------------- string + /** * Converts value to String. */ - public String toString(Object value) { - return (String) typeConverters[23].convert(value); + public String toString(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(String.class); + return tc.convert(value); } /** * Converts value to String. Returns default value * when conversion result is null */ - public String toString(Object value, String defaultValue) { - String result = (String) typeConverters[23].convert(value); + public String toString(final Object value, final String defaultValue) { + final String result = toString(value); if (result == null) { return defaultValue; } @@ -478,94 +455,45 @@ public class ConvertBean { /** * Converts value to String[]. */ - public String[] toStringArray(Object value) { - return (String[]) typeConverters[24].convert(value); + public String[] toStringArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(String[].class); + return tc.convert(value); } + // ---------------------------------------------------------------- class + /** * Converts value to Class. */ - public Class toClass(Object value) { - return (Class) typeConverters[25].convert(value); + public Class toClass(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Class.class); + return tc.convert(value); } /** * Converts value to Class[]. */ - public Class[] toClassArray(Object value) { - return (Class[]) typeConverters[26].convert(value); - } - - /** - * Converts value to JDateTime. - */ - public JDateTime toJDateTime(Object value) { - return (JDateTime) typeConverters[27].convert(value); - } - - /** - * Converts value to JDateTime. Returns default value - * when conversion result is null - */ - public JDateTime toJDateTime(Object value, JDateTime defaultValue) { - JDateTime result = (JDateTime) typeConverters[27].convert(value); - if (result == null) { - return defaultValue; - } - return result; - } - - /** - * Converts value to Date. - */ - public Date toDate(Object value) { - return (Date) typeConverters[28].convert(value); - } - - /** - * Converts value to Date. Returns default value - * when conversion result is null - */ - public Date toDate(Object value, Date defaultValue) { - Date result = (Date) typeConverters[28].convert(value); - if (result == null) { - return defaultValue; - } - return result; + public Class[] toClassArray(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(Class[].class); + return tc.convert(value); } - /** - * Converts value to Calendar. - */ - public Calendar toCalendar(Object value) { - return (Calendar) typeConverters[29].convert(value); - } - - /** - * Converts value to Calendar. Returns default value - * when conversion result is null - */ - public Calendar toCalendar(Object value, Calendar defaultValue) { - Calendar result = (Calendar) typeConverters[29].convert(value); - if (result == null) { - return defaultValue; - } - return result; - } + // ---------------------------------------------------------------- bigs /** * Converts value to BigInteger. */ - public BigInteger toBigInteger(Object value) { - return (BigInteger) typeConverters[30].convert(value); + public BigInteger toBigInteger(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(BigInteger.class); + return tc.convert(value); } /** * Converts value to BigInteger. Returns default value * when conversion result is null */ - public BigInteger toBigInteger(Object value, BigInteger defaultValue) { - BigInteger result = (BigInteger) typeConverters[30].convert(value); + public BigInteger toBigInteger(final Object value, final BigInteger defaultValue) { + final BigInteger result = toBigInteger(value); if (result == null) { return defaultValue; } @@ -575,16 +503,17 @@ public class ConvertBean { /** * Converts value to BigDecimal. */ - public BigDecimal toBigDecimal(Object value) { - return (BigDecimal) typeConverters[31].convert(value); + public BigDecimal toBigDecimal(final Object value) { + final TypeConverter tc = TypeConverterManager.get().lookup(BigDecimal.class); + return tc.convert(value); } /** * Converts value to BigDecimal. Returns default value * when conversion result is null */ - public BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) { - BigDecimal result = (BigDecimal) typeConverters[31].convert(value); + public BigDecimal toBigDecimal(final Object value, final BigDecimal defaultValue) { + final BigDecimal result = toBigDecimal(value); if (result == null) { return defaultValue; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConversionException.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConversionException.java index 624006fa4..6b025f5a1 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConversionException.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConversionException.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter; -import com.fr.third.jodd.exception.UncheckedException; import com.fr.third.jodd.exception.UncheckedException; /** @@ -33,23 +32,23 @@ import com.fr.third.jodd.exception.UncheckedException; */ public class TypeConversionException extends UncheckedException { - public TypeConversionException(Throwable t) { + public TypeConversionException(final Throwable t) { super(t); } - public TypeConversionException(String message) { + public TypeConversionException(final String message) { super(message); } - public TypeConversionException(String message, Throwable t) { + public TypeConversionException(final String message, final Throwable t) { super(message, t); } - public TypeConversionException(Object value) { + public TypeConversionException(final Object value) { this("Conversion failed: " + value); } - public TypeConversionException(Object value, Throwable t) { + public TypeConversionException(final Object value, final Throwable t) { this("Conversion failed: " + value, t); } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverter.java index b30a4e00f..358bb8a12 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverter.java @@ -28,8 +28,9 @@ package com.fr.third.jodd.typeconverter; /** * Object converter interface. * - * @see TypeConverterManagerBean + * @see TypeConverterManager */ +@FunctionalInterface public interface TypeConverter { /** @@ -45,4 +46,19 @@ public interface TypeConverter { */ T convert(Object value); + /** + * Converts object and returns default value if conversion fails. + */ + default T convert(final Object value, final T defaultValue) { + if (value == null) { + return defaultValue; + } + try { + return convert(value); + } + catch (Exception e) { + return defaultValue; + } + } + } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverterManager.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverterManager.java index 9d1f9910e..cebbc47ee 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverterManager.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverterManager.java @@ -25,69 +25,361 @@ package com.fr.third.jodd.typeconverter; +import com.fr.third.jodd.cache.TypeCache; +import com.fr.third.jodd.io.upload.FileUpload; +import com.fr.third.jodd.mutable.MutableByte; +import com.fr.third.jodd.mutable.MutableDouble; +import com.fr.third.jodd.mutable.MutableFloat; +import com.fr.third.jodd.mutable.MutableInteger; +import com.fr.third.jodd.mutable.MutableLong; +import com.fr.third.jodd.mutable.MutableShort; +import com.fr.third.jodd.typeconverter.impl.ArrayConverter; +import com.fr.third.jodd.typeconverter.impl.BigDecimalConverter; +import com.fr.third.jodd.typeconverter.impl.BigIntegerConverter; +import com.fr.third.jodd.typeconverter.impl.BooleanArrayConverter; +import com.fr.third.jodd.typeconverter.impl.BooleanConverter; +import com.fr.third.jodd.typeconverter.impl.ByteArrayConverter; +import com.fr.third.jodd.typeconverter.impl.ByteConverter; +import com.fr.third.jodd.typeconverter.impl.CalendarConverter; +import com.fr.third.jodd.typeconverter.impl.CharacterArrayConverter; +import com.fr.third.jodd.typeconverter.impl.CharacterConverter; +import com.fr.third.jodd.typeconverter.impl.ClassArrayConverter; +import com.fr.third.jodd.typeconverter.impl.ClassConverter; +import com.fr.third.jodd.typeconverter.impl.CollectionConverter; +import com.fr.third.jodd.typeconverter.impl.DateConverter; +import com.fr.third.jodd.typeconverter.impl.DoubleArrayConverter; +import com.fr.third.jodd.typeconverter.impl.DoubleConverter; +import com.fr.third.jodd.typeconverter.impl.FileConverter; +import com.fr.third.jodd.typeconverter.impl.FileUploadConverter; +import com.fr.third.jodd.typeconverter.impl.FloatArrayConverter; +import com.fr.third.jodd.typeconverter.impl.FloatConverter; +import com.fr.third.jodd.typeconverter.impl.IntegerArrayConverter; +import com.fr.third.jodd.typeconverter.impl.IntegerConverter; +import com.fr.third.jodd.typeconverter.impl.LocalDateConverter; +import com.fr.third.jodd.typeconverter.impl.LocalDateTimeConverter; +import com.fr.third.jodd.typeconverter.impl.LocalTimeConverter; +import com.fr.third.jodd.typeconverter.impl.LocaleConverter; +import com.fr.third.jodd.typeconverter.impl.LongArrayConverter; +import com.fr.third.jodd.typeconverter.impl.LongConverter; +import com.fr.third.jodd.typeconverter.impl.MutableByteConverter; +import com.fr.third.jodd.typeconverter.impl.MutableDoubleConverter; +import com.fr.third.jodd.typeconverter.impl.MutableFloatConverter; +import com.fr.third.jodd.typeconverter.impl.MutableIntegerConverter; +import com.fr.third.jodd.typeconverter.impl.MutableLongConverter; +import com.fr.third.jodd.typeconverter.impl.MutableShortConverter; +import com.fr.third.jodd.typeconverter.impl.ShortArrayConverter; +import com.fr.third.jodd.typeconverter.impl.ShortConverter; +import com.fr.third.jodd.typeconverter.impl.SqlDateConverter; +import com.fr.third.jodd.typeconverter.impl.SqlTimeConverter; +import com.fr.third.jodd.typeconverter.impl.SqlTimestampConverter; +import com.fr.third.jodd.typeconverter.impl.StringArrayConverter; +import com.fr.third.jodd.typeconverter.impl.StringConverter; +import com.fr.third.jodd.typeconverter.impl.TimeZoneConverter; +import com.fr.third.jodd.typeconverter.impl.URIConverter; +import com.fr.third.jodd.typeconverter.impl.URLConverter; +import com.fr.third.jodd.typeconverter.impl.UUIDConverter; +import com.fr.third.jodd.util.ClassUtil; + +import java.io.File; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Calendar; import java.util.Collection; +import java.util.Locale; +import java.util.TimeZone; +import java.util.UUID; /** * Provides dynamic object conversion to a type. - * Contains a map of registered converters. User may add new converter. - * Static version of {@link TypeConverterManagerBean}. + * Contains a map of registered converters. User may add new converters. */ public class TypeConverterManager { - private static final TypeConverterManagerBean TYPE_CONVERTER_MANAGER_BEAN = new TypeConverterManagerBean(); + private static final TypeConverterManager TYPE_CONVERTER_MANAGER = new TypeConverterManager(); /** - * Returns default {@link TypeConverterManager}. + * Returns default implementation. */ - public static TypeConverterManagerBean getDefaultTypeConverterManager() { - return TYPE_CONVERTER_MANAGER_BEAN; + public static TypeConverterManager get() { + return TYPE_CONVERTER_MANAGER; + } + + private final TypeCache converters = TypeCache.createDefault(); + + // ---------------------------------------------------------------- methods + + public TypeConverterManager() { + registerDefaults(); } /** * Registers default set of converters. */ - public static void registerDefaults() { - TYPE_CONVERTER_MANAGER_BEAN.registerDefaults(); + public void registerDefaults() { + register(String.class, new StringConverter()); + register(String[].class, new StringArrayConverter(this)); + + final IntegerConverter integerConverter = new IntegerConverter(); + register(Integer.class, integerConverter); + register(int.class, integerConverter); + register(MutableInteger.class, new MutableIntegerConverter(this)); + + final ShortConverter shortConverter = new ShortConverter(); + register(Short.class, shortConverter); + register(short.class, shortConverter); + register(MutableShort.class, new MutableShortConverter(this)); + + final LongConverter longConverter = new LongConverter(); + register(Long.class, longConverter); + register(long.class, longConverter); + register(MutableLong.class, new MutableLongConverter(this)); + + final ByteConverter byteConverter = new ByteConverter(); + register(Byte.class, byteConverter); + register(byte.class, byteConverter); + register(MutableByte.class, new MutableByteConverter(this)); + + final FloatConverter floatConverter = new FloatConverter(); + register(Float.class, floatConverter); + register(float.class, floatConverter); + register(MutableFloat.class, new MutableFloatConverter(this)); + + final DoubleConverter doubleConverter = new DoubleConverter(); + register(Double.class, doubleConverter); + register(double.class, doubleConverter); + register(MutableDouble.class, new MutableDoubleConverter(this)); + + final BooleanConverter booleanConverter = new BooleanConverter(); + register(Boolean.class, booleanConverter); + register(boolean.class, booleanConverter); + + final CharacterConverter characterConverter = new CharacterConverter(); + register(Character.class, characterConverter); + register(char.class, characterConverter); + + register(byte[].class, new ByteArrayConverter(this)); + register(short[].class, new ShortArrayConverter(this)); + register(int[].class, new IntegerArrayConverter(this)); + register(long[].class, new LongArrayConverter(this)); + register(float[].class, new FloatArrayConverter(this)); + register(double[].class, new DoubleArrayConverter(this)); + register(boolean[].class, new BooleanArrayConverter(this)); + register(char[].class, new CharacterArrayConverter(this)); + + // we don't really need these, but converters will be cached and not created every time + register(Integer[].class, new ArrayConverter(this, Integer.class) { + @Override + protected Integer[] createArray(final int length) { + return new Integer[length]; + } + }); + register(Long[].class, new ArrayConverter(this, Long.class) { + @Override + protected Long[] createArray(final int length) { + return new Long[length]; + } + }); + register(Byte[].class, new ArrayConverter(this, Byte.class) { + @Override + protected Byte[] createArray(final int length) { + return new Byte[length]; + } + }); + register(Short[].class, new ArrayConverter(this, Short.class) { + @Override + protected Short[] createArray(final int length) { + return new Short[length]; + } + }); + register(Float[].class, new ArrayConverter(this, Float.class) { + @Override + protected Float[] createArray(final int length) { + return new Float[length]; + } + }); + register(Double[].class, new ArrayConverter(this, Double.class) { + @Override + protected Double[] createArray(final int length) { + return new Double[length]; + } + }); + register(Boolean[].class, new ArrayConverter(this, Boolean.class) { + @Override + protected Boolean[] createArray(final int length) { + return new Boolean[length]; + } + }); + register(Character[].class, new ArrayConverter(this, Character.class) { + @Override + protected Character[] createArray(final int length) { + return new Character[length]; + } + }); + + register(MutableInteger[].class, new ArrayConverter<>(this, MutableInteger.class)); + register(MutableLong[].class, new ArrayConverter<>(this, MutableLong.class)); + register(MutableByte[].class, new ArrayConverter<>(this, MutableByte.class)); + register(MutableShort[].class, new ArrayConverter<>(this, MutableShort.class)); + register(MutableFloat[].class, new ArrayConverter<>(this, MutableFloat.class)); + register(MutableDouble[].class, new ArrayConverter<>(this, MutableDouble.class)); + + register(BigDecimal.class, new BigDecimalConverter()); + register(BigInteger.class, new BigIntegerConverter()); + register(BigDecimal[].class, new ArrayConverter<>(this, BigDecimal.class)); + register(BigInteger[].class, new ArrayConverter<>(this, BigInteger.class)); + + register(java.util.Date.class, new DateConverter()); + register(java.sql.Date.class, new SqlDateConverter()); + register(Time.class, new SqlTimeConverter()); + register(Timestamp.class, new SqlTimestampConverter()); + register(Calendar.class, new CalendarConverter()); +// register(GregorianCalendar.class, new CalendarConverter()); + register(LocalDateTime.class, new LocalDateTimeConverter()); + register(LocalDate.class, new LocalDateConverter()); + register(LocalTime.class, new LocalTimeConverter()); + + register(File.class, new FileConverter()); + register(FileUpload.class, new FileUploadConverter()); + + register(Class.class, new ClassConverter()); + register(Class[].class, new ClassArrayConverter(this)); + + register(URI.class, new URIConverter()); + register(URL.class, new URLConverter()); + + register(Locale.class, new LocaleConverter()); + register(TimeZone.class, new TimeZoneConverter()); + + register(UUID.class, new UUIDConverter()); } /** * Registers a converter for specified type. * User must register converter for all super-classes as well. * - * @param type class that converter is for - * @param typeConverter converter for provided class + * @param type class that converter is for + * @param typeConverter converter for provided class */ - public static void register(Class type, TypeConverter typeConverter) { - TYPE_CONVERTER_MANAGER_BEAN.register(type, typeConverter); + public void register(final Class type, final TypeConverter typeConverter) { + converters.put(type, typeConverter); } - public static void unregister(Class type) { - TYPE_CONVERTER_MANAGER_BEAN.unregister(type); + /** + * Un-registers converter for given type. + */ + public void unregister(final Class type) { + converters.remove(type); } + // ---------------------------------------------------------------- lookup + /** * Retrieves converter for provided type. Only registered types are matched, * therefore subclasses must be also registered. * * @return founded converter or null */ - public static TypeConverter lookup(Class type) { - return TYPE_CONVERTER_MANAGER_BEAN.lookup(type); + public TypeConverter lookup(final Class type) { + return converters.get(type); } + // ---------------------------------------------------------------- converter + /** - * Casts an object to destination type using {@link TypeConverterManager type conversion}. - * If destination type is one of common types, consider using {@link jodd.typeconverter.Convert} instead. + * Converts an object to destination type. If type is registered, it's + * {@link TypeConverter} will be used. If not, it scans of destination is + * an array or enum, as those two cases are handled in a special way. + *

    + * If destination type is one of common types, consider using {@link Converter} + * instead for somewhat faster approach (no lookup). */ - public static T convertType(Object value, Class destinationType) { - return TYPE_CONVERTER_MANAGER_BEAN.convertType(value, destinationType); + @SuppressWarnings({"unchecked"}) + public T convertType(final Object value, final Class destinationType) { + if (destinationType == Object.class) { + // no conversion :) + return (T) value; + } + + final TypeConverter converter = lookup(destinationType); + + if (converter != null) { + return (T) converter.convert(value); + } + + // no converter + + if (value == null) { + return null; + } + + // check same instances + if (ClassUtil.isInstanceOf(value, destinationType)) { + return (T) value; + } + + // handle destination arrays + if (destinationType.isArray()) { + final ArrayConverter arrayConverter = new ArrayConverter(this, destinationType.getComponentType()); + + return (T) arrayConverter.convert(value); + } + + // handle enums + if (destinationType.isEnum()) { + final Object[] enums = destinationType.getEnumConstants(); + final String valStr = value.toString(); + for (final Object e : enums) { + if (e.toString().equals(valStr)) { + return (T) e; + } + } + } + + // collection + if (ClassUtil.isTypeOf(destinationType, Collection.class)) { + // component type is unknown because of Java's type-erasure + final CollectionConverter collectionConverter = + new CollectionConverter(this, destinationType, Object.class); + + return (T) collectionConverter.convert(value); + } + + // fail + throw new TypeConversionException("Conversion failed of input type: " + value.getClass() + " into: " + destinationType.getName()); } /** - * Special conversion to collections, when component type is known. + * Special case of {@link #convertType(Object, Class)} when target is collection and + * when component type is known. */ - public static T convertToCollection(Object value, Class destinationType, Class componentType) { - return (T) TYPE_CONVERTER_MANAGER_BEAN.convertToCollection(value, destinationType, componentType); + @SuppressWarnings("unchecked") + public , T> C convertToCollection(final Object value, final Class destinationType, final Class componentType) { + if (value == null) { + return null; + } + + // check same instances + if (ClassUtil.isInstanceOf(value, destinationType)) { + return (C) value; + } + + final CollectionConverter collectionConverter; + + if (componentType == null) { + collectionConverter = new CollectionConverter(destinationType, Object.class); + } + else { + collectionConverter = new CollectionConverter(destinationType, componentType); + } + + return (C) collectionConverter.convert(value); } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverterManagerBean.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverterManagerBean.java deleted file mode 100644 index 6bd4d51c0..000000000 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/TypeConverterManagerBean.java +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright (c) 2003-present, Jodd Team (http://jodd.org) -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package com.fr.third.jodd.typeconverter; - -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.mutable.MutableByte; -import com.fr.third.jodd.mutable.MutableDouble; -import com.fr.third.jodd.mutable.MutableFloat; -import com.fr.third.jodd.mutable.MutableInteger; -import com.fr.third.jodd.mutable.MutableLong; -import com.fr.third.jodd.mutable.MutableShort; -import com.fr.third.jodd.typeconverter.impl.ArrayConverter; -import com.fr.third.jodd.typeconverter.impl.BigDecimalConverter; -import com.fr.third.jodd.typeconverter.impl.BigIntegerConverter; -import com.fr.third.jodd.typeconverter.impl.BooleanArrayConverter; -import com.fr.third.jodd.typeconverter.impl.BooleanConverter; -import com.fr.third.jodd.typeconverter.impl.ByteArrayConverter; -import com.fr.third.jodd.typeconverter.impl.ByteConverter; -import com.fr.third.jodd.typeconverter.impl.CalendarConverter; -import com.fr.third.jodd.typeconverter.impl.CharacterArrayConverter; -import com.fr.third.jodd.typeconverter.impl.CharacterConverter; -import com.fr.third.jodd.typeconverter.impl.ClassArrayConverter; -import com.fr.third.jodd.typeconverter.impl.ClassConverter; -import com.fr.third.jodd.typeconverter.impl.CollectionConverter; -import com.fr.third.jodd.typeconverter.impl.DateConverter; -import com.fr.third.jodd.typeconverter.impl.DoubleArrayConverter; -import com.fr.third.jodd.typeconverter.impl.DoubleConverter; -import com.fr.third.jodd.typeconverter.impl.FileConverter; -import com.fr.third.jodd.typeconverter.impl.FloatArrayConverter; -import com.fr.third.jodd.typeconverter.impl.FloatConverter; -import com.fr.third.jodd.typeconverter.impl.IntegerArrayConverter; -import com.fr.third.jodd.typeconverter.impl.IntegerConverter; -import com.fr.third.jodd.typeconverter.impl.JDateTimeConverter; -import com.fr.third.jodd.typeconverter.impl.LocaleConverter; -import com.fr.third.jodd.typeconverter.impl.LongArrayConverter; -import com.fr.third.jodd.typeconverter.impl.LongConverter; -import com.fr.third.jodd.typeconverter.impl.MutableByteConverter; -import com.fr.third.jodd.typeconverter.impl.MutableDoubleConverter; -import com.fr.third.jodd.typeconverter.impl.MutableFloatConverter; -import com.fr.third.jodd.typeconverter.impl.MutableIntegerConverter; -import com.fr.third.jodd.typeconverter.impl.MutableLongConverter; -import com.fr.third.jodd.typeconverter.impl.MutableShortConverter; -import com.fr.third.jodd.typeconverter.impl.ShortArrayConverter; -import com.fr.third.jodd.typeconverter.impl.ShortConverter; -import com.fr.third.jodd.typeconverter.impl.SqlDateConverter; -import com.fr.third.jodd.typeconverter.impl.SqlTimeConverter; -import com.fr.third.jodd.typeconverter.impl.SqlTimestampConverter; -import com.fr.third.jodd.typeconverter.impl.StringArrayConverter; -import com.fr.third.jodd.typeconverter.impl.StringConverter; -import com.fr.third.jodd.typeconverter.impl.TimeZoneConverter; -import com.fr.third.jodd.typeconverter.impl.URIConverter; -import com.fr.third.jodd.typeconverter.impl.URLConverter; -import com.fr.third.jodd.util.ReflectUtil; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.mutable.MutableByte; -import com.fr.third.jodd.mutable.MutableDouble; -import com.fr.third.jodd.mutable.MutableFloat; -import com.fr.third.jodd.mutable.MutableInteger; -import com.fr.third.jodd.mutable.MutableLong; -import com.fr.third.jodd.mutable.MutableShort; -import com.fr.third.jodd.typeconverter.impl.ArrayConverter; -import com.fr.third.jodd.typeconverter.impl.BigDecimalConverter; -import com.fr.third.jodd.typeconverter.impl.BigIntegerConverter; -import com.fr.third.jodd.typeconverter.impl.BooleanArrayConverter; -import com.fr.third.jodd.typeconverter.impl.BooleanConverter; -import com.fr.third.jodd.typeconverter.impl.ByteArrayConverter; -import com.fr.third.jodd.typeconverter.impl.ByteConverter; -import com.fr.third.jodd.typeconverter.impl.CalendarConverter; -import com.fr.third.jodd.typeconverter.impl.CharacterArrayConverter; -import com.fr.third.jodd.typeconverter.impl.CharacterConverter; -import com.fr.third.jodd.typeconverter.impl.ClassArrayConverter; -import com.fr.third.jodd.typeconverter.impl.ClassConverter; -import com.fr.third.jodd.typeconverter.impl.CollectionConverter; -import com.fr.third.jodd.typeconverter.impl.DateConverter; -import com.fr.third.jodd.typeconverter.impl.DoubleArrayConverter; -import com.fr.third.jodd.typeconverter.impl.DoubleConverter; -import com.fr.third.jodd.typeconverter.impl.FileConverter; -import com.fr.third.jodd.typeconverter.impl.FloatArrayConverter; -import com.fr.third.jodd.typeconverter.impl.FloatConverter; -import com.fr.third.jodd.typeconverter.impl.IntegerArrayConverter; -import com.fr.third.jodd.typeconverter.impl.IntegerConverter; -import com.fr.third.jodd.typeconverter.impl.JDateTimeConverter; -import com.fr.third.jodd.typeconverter.impl.LocaleConverter; -import com.fr.third.jodd.typeconverter.impl.LongArrayConverter; -import com.fr.third.jodd.typeconverter.impl.LongConverter; -import com.fr.third.jodd.typeconverter.impl.MutableByteConverter; -import com.fr.third.jodd.typeconverter.impl.MutableDoubleConverter; -import com.fr.third.jodd.typeconverter.impl.MutableFloatConverter; -import com.fr.third.jodd.typeconverter.impl.MutableIntegerConverter; -import com.fr.third.jodd.typeconverter.impl.MutableLongConverter; -import com.fr.third.jodd.typeconverter.impl.MutableShortConverter; -import com.fr.third.jodd.typeconverter.impl.ShortArrayConverter; -import com.fr.third.jodd.typeconverter.impl.ShortConverter; -import com.fr.third.jodd.typeconverter.impl.SqlDateConverter; -import com.fr.third.jodd.typeconverter.impl.SqlTimeConverter; -import com.fr.third.jodd.typeconverter.impl.SqlTimestampConverter; -import com.fr.third.jodd.typeconverter.impl.StringArrayConverter; -import com.fr.third.jodd.typeconverter.impl.StringConverter; -import com.fr.third.jodd.typeconverter.impl.TimeZoneConverter; -import com.fr.third.jodd.typeconverter.impl.URIConverter; -import com.fr.third.jodd.typeconverter.impl.URLConverter; -import com.fr.third.jodd.util.ReflectUtil; - -import java.io.File; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.net.URI; -import java.net.URL; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Calendar; -import java.util.Collection; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Provides dynamic object conversion to a type. - * Contains a map of registered converters. User may add new converters. - * Instantiable version of {@link TypeConverterManager}. - */ -public class TypeConverterManagerBean { - - private final HashMap converters = new HashMap(70); - - // ---------------------------------------------------------------- converter - - protected ConvertBean convertBean = new ConvertBean(); - - /** - * Returns {@link ConvertBean}. - */ - public ConvertBean getConvertBean() { - return convertBean; - } - - // ---------------------------------------------------------------- methods - - public TypeConverterManagerBean() { - registerDefaults(); - } - - /** - * Registers default set of converters. - */ - @SuppressWarnings( {"UnnecessaryFullyQualifiedName"}) - public void registerDefaults() { - register(String.class, new StringConverter()); - register(String[].class, new StringArrayConverter(this)); - - IntegerConverter integerConverter = new IntegerConverter(); - register(Integer.class, integerConverter); - register(int.class, integerConverter); - register(MutableInteger.class, new MutableIntegerConverter(this)); - - ShortConverter shortConverter = new ShortConverter(); - register(Short.class, shortConverter); - register(short.class, shortConverter); - register(MutableShort.class, new MutableShortConverter(this)); - - LongConverter longConverter = new LongConverter(); - register(Long.class, longConverter); - register(long.class, longConverter); - register(MutableLong.class, new MutableLongConverter(this)); - - ByteConverter byteConverter = new ByteConverter(); - register(Byte.class, byteConverter); - register(byte.class, byteConverter); - register(MutableByte.class, new MutableByteConverter(this)); - - FloatConverter floatConverter = new FloatConverter(); - register(Float.class, floatConverter); - register(float.class, floatConverter); - register(MutableFloat.class, new MutableFloatConverter(this)); - - DoubleConverter doubleConverter = new DoubleConverter(); - register(Double.class, doubleConverter); - register(double.class, doubleConverter); - register(MutableDouble.class, new MutableDoubleConverter(this)); - - BooleanConverter booleanConverter = new BooleanConverter(); - register(Boolean.class, booleanConverter); - register(boolean.class, booleanConverter); - - CharacterConverter characterConverter = new CharacterConverter(); - register(Character.class, characterConverter); - register(char.class, characterConverter); - - register(byte[].class, new ByteArrayConverter(this)); - register(short[].class, new ShortArrayConverter(this)); - register(int[].class, new IntegerArrayConverter(this)); - register(long[].class, new LongArrayConverter(this)); - register(float[].class, new FloatArrayConverter(this)); - register(double[].class, new DoubleArrayConverter(this)); - register(boolean[].class, new BooleanArrayConverter(this)); - register(char[].class, new CharacterArrayConverter(this)); - - // we don't really need these, but converters will be cached and not created every time - register(Integer[].class, new ArrayConverter(this, Integer.class) { - @Override - protected Integer[] createArray(int length) { - return new Integer[length]; - } - }); - register(Long[].class, new ArrayConverter(this, Long.class) { - @Override - protected Long[] createArray(int length) { - return new Long[length]; - } - }); - register(Byte[].class, new ArrayConverter(this, Byte.class) { - @Override - protected Byte[] createArray(int length) { - return new Byte[length]; - } - }); - register(Short[].class, new ArrayConverter(this, Short.class) { - @Override - protected Short[] createArray(int length) { - return new Short[length]; - } - }); - register(Float[].class, new ArrayConverter(this, Float.class) { - @Override - protected Float[] createArray(int length) { - return new Float[length]; - } - }); - register(Double[].class, new ArrayConverter(this, Double.class) { - @Override - protected Double[] createArray(int length) { - return new Double[length]; - } - }); - register(Boolean[].class, new ArrayConverter(this, Boolean.class) { - @Override - protected Boolean[] createArray(int length) { - return new Boolean[length]; - } - }); - register(Character[].class, new ArrayConverter(this, Character.class) { - @Override - protected Character[] createArray(int length) { - return new Character[length]; - } - }); - - register(MutableInteger[].class, new ArrayConverter(this, MutableInteger.class)); - register(MutableLong[].class, new ArrayConverter(this, MutableLong.class)); - register(MutableByte[].class, new ArrayConverter(this, MutableByte.class)); - register(MutableShort[].class, new ArrayConverter(this, MutableShort.class)); - register(MutableFloat[].class, new ArrayConverter(this, MutableFloat.class)); - register(MutableDouble[].class, new ArrayConverter(this, MutableDouble.class)); - - register(BigDecimal.class, new BigDecimalConverter()); - register(BigInteger.class, new BigIntegerConverter()); - register(BigDecimal[].class, new ArrayConverter(this, BigDecimal.class)); - register(BigInteger[].class, new ArrayConverter(this, BigInteger.class)); - - register(java.util.Date.class, new DateConverter()); - register(java.sql.Date.class, new SqlDateConverter()); - register(Time.class, new SqlTimeConverter()); - register(Timestamp.class, new SqlTimestampConverter()); - register(Calendar.class, new CalendarConverter()); - register(GregorianCalendar.class, new CalendarConverter()); - register(JDateTime.class, new JDateTimeConverter()); - - register(File.class, new FileConverter()); - - register(Class.class, new ClassConverter()); - register(Class[].class, new ClassArrayConverter(this)); - - register(URI.class, new URIConverter()); - register(URL.class, new URLConverter()); - - register(Locale.class, new LocaleConverter()); - register(TimeZone.class, new TimeZoneConverter()); - } - - /** - * Registers a converter for specified type. - * User must register converter for all super-classes as well. - * - * @param type class that converter is for - * @param typeConverter converter for provided class - */ - public void register(Class type, TypeConverter typeConverter) { - convertBean.register(type, typeConverter); - converters.put(type, typeConverter); - } - - /** - * Un-registers converter for given type. - */ - public void unregister(Class type) { - convertBean.register(type, null); - converters.remove(type); - } - - // ---------------------------------------------------------------- lookup - - /** - * Retrieves converter for provided type. Only registered types are matched, - * therefore subclasses must be also registered. - * - * @return founded converter or null - */ - public TypeConverter lookup(Class type) { - return converters.get(type); - } - - // ---------------------------------------------------------------- convert - - /** - * Converts an object to destination type. If type is registered, it's - * {@link TypeConverter} will be used. If not, it scans of destination is - * an array or enum, as those two cases are handled in a special way. - *

    - * If destination type is one of common types, consider using {@link com.fr.third.jodd.typeconverter.Convert} - * instead for somewhat faster approach (no lookup). - */ - @SuppressWarnings({"unchecked"}) - public T convertType(Object value, Class destinationType) { - if (destinationType == Object.class) { - // no conversion :) - return (T) value; - } - TypeConverter converter = lookup(destinationType); - - if (converter != null) { - return (T) converter.convert(value); - } - - // no converter - - if (value == null) { - return null; - } - - // handle destination arrays - if (destinationType.isArray()) { - ArrayConverter arrayConverter = new ArrayConverter(this, destinationType.getComponentType()); - - return (T) arrayConverter.convert(value); - } - - // handle enums - if (destinationType.isEnum()) { - Object[] enums = destinationType.getEnumConstants(); - String valStr = value.toString(); - for (Object e : enums) { - if (e.toString().equals(valStr)) { - return (T) e; - } - } - } - - // check same instances - if (ReflectUtil.isInstanceOf(value, destinationType)) { - return (T) value; - } - - // collection - if (ReflectUtil.isTypeOf(destinationType, Collection.class)) { - // component type is unknown because of Java's type-erasure - CollectionConverter collectionConverter = - new CollectionConverter(this, destinationType, Object.class); - - return (T) collectionConverter.convert(value); - } - - // fail - throw new TypeConversionException("Conversion failed: " + destinationType.getName()); - } - - /** - * Special case of {@link #convertType(Object, Class)} when target is collection and - * when component type is known. - */ - @SuppressWarnings("unchecked") - public Collection convertToCollection(Object value, Class destinationType, Class componentType) { - if (value == null) { - return null; - } - - // check same instances - if (ReflectUtil.isInstanceOf(value, destinationType)) { - return (Collection) value; - } - - if (componentType == null) { - componentType = Object.class; - } - - CollectionConverter collectionConverter = new CollectionConverter(destinationType, componentType); - - return collectionConverter.convert(value); - } - -} \ No newline at end of file diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ArrayConverter.java index 9c420d530..c48ec6794 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ArrayConverter.java @@ -25,9 +25,8 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.CsvUtil; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.CsvUtil; import java.lang.reflect.Array; @@ -51,15 +50,16 @@ public class ArrayConverter implements TypeConverter { public static final char[] NUMBER_DELIMITERS = new char[] {',', ';', '\n'}; - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; protected final Class targetComponentType; - public ArrayConverter(TypeConverterManagerBean typeConverterManagerBean, Class targetComponentType) { - this.typeConverterManagerBean = typeConverterManagerBean; + public ArrayConverter(final TypeConverterManager typeConverterManager, final Class targetComponentType) { + this.typeConverterManager = typeConverterManager; this.targetComponentType = targetComponentType; } - public T[] convert(Object value) { + @Override + public T[] convert(final Object value) { if (value == null) { return null; } @@ -68,7 +68,7 @@ public class ArrayConverter implements TypeConverter { if (!valueClass.isArray()) { // source is not an array - return convertValueToArray(value); + return convertValueToArray(value); } // source is an array @@ -78,8 +78,8 @@ public class ArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected T convertType(Object value) { - return typeConverterManagerBean.convertType(value, targetComponentType); + protected T convertType(final Object value) { + return typeConverterManager.convertType(value, targetComponentType); } /** @@ -87,14 +87,14 @@ public class ArrayConverter implements TypeConverter { * Default implementation uses reflection to create * an array of target type. Override it for better performances. */ - protected T[] createArray(int length) { + protected T[] createArray(final int length) { return (T[]) Array.newInstance(targetComponentType, length); } /** * Creates an array with single element. */ - protected T[] convertToSingleElementArray(Object value) { + protected T[] convertToSingleElementArray(final Object value) { T[] singleElementArray = createArray(1); singleElementArray[0] = convertType(value); @@ -106,20 +106,8 @@ public class ArrayConverter implements TypeConverter { * Converts non-array value to array. Detects various * collection types and iterates them to make conversion * and to create target array. - */ - protected T[] convertValueToArray(Object value) { - if (value instanceof List) { - List list = (List) value; - T[] target = createArray(list.size()); - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - - return target; - } - + */ + protected T[] convertValueToArray(final Object value) { if (value instanceof Collection) { Collection collection = (Collection) value; T[] target = createArray(collection.size()); @@ -135,7 +123,7 @@ public class ArrayConverter implements TypeConverter { if (value instanceof Iterable) { Iterable iterable = (Iterable) value; - List list = new ArrayList(); + List list = new ArrayList<>(); for (Object element : iterable) { list.add(convertType(element)); @@ -156,16 +144,16 @@ public class ArrayConverter implements TypeConverter { /** * Converts string to array, for the {@link #convertValueToArray(Object)} method. - * By default, the string is converted into an array using {@link CsvUtil}. + * By default, the string is converted into an array using {@link com.fr.third.jodd.util.CsvUtil}. */ - protected String[] convertStringToArray(String value) { + protected String[] convertStringToArray(final String value) { return CsvUtil.toStringArray(value); } /** * Converts array value to array. */ - protected T[] convertArrayToArray(Object value) { + protected T[] convertArrayToArray(final Object value) { Class valueComponentType = value.getClass().getComponentType(); if (valueComponentType == targetComponentType) { @@ -195,7 +183,7 @@ public class ArrayConverter implements TypeConverter { * Converts primitive array to target array. */ @SuppressWarnings("AutoBoxing") - protected T[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected T[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { T[] result = null; if (primitiveComponentType == int.class) { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BigDecimalConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BigDecimalConverter.java index 7d2af1c2e..d26c2575c 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BigDecimalConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BigDecimalConverter.java @@ -41,11 +41,12 @@ import java.math.BigDecimal; */ public class BigDecimalConverter implements TypeConverter { - public BigDecimal convert(Object value) { + @Override + public BigDecimal convert(final Object value) { if (value == null) { return null; } - + if (value instanceof BigDecimal) { return (BigDecimal) value; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BigIntegerConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BigIntegerConverter.java index f81fc5a32..ce8edaad8 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BigIntegerConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BigIntegerConverter.java @@ -41,7 +41,7 @@ import java.math.BigInteger; */ public class BigIntegerConverter implements TypeConverter { - public BigInteger convert(Object value) { + public BigInteger convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BooleanArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BooleanArrayConverter.java index 467d6df90..a084110f5 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BooleanArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BooleanArrayConverter.java @@ -25,32 +25,31 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.StringUtil; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * Converts given object to boolean[]. */ public class BooleanArrayConverter implements TypeConverter { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; - public BooleanArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - this.typeConverterManagerBean = typeConverterManagerBean; + public BooleanArrayConverter(final TypeConverterManager typeConverterManager) { + this.typeConverterManager = typeConverterManager; } - public boolean[] convert(Object value) { + @Override + public boolean[] convert(final Object value) { if (value == null) { return null; } - Class valueClass = value.getClass(); + final Class valueClass = value.getClass(); if (!valueClass.isArray()) { // source is not an array @@ -64,14 +63,14 @@ public class BooleanArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected boolean convertType(Object value) { - return typeConverterManagerBean.convertType(value, boolean.class).booleanValue(); + protected boolean convertType(final Object value) { + return typeConverterManager.convertType(value, boolean.class).booleanValue(); } /** * Creates an array with single element. */ - protected boolean[] convertToSingleElementArray(Object value) { + protected boolean[] convertToSingleElementArray(final Object value) { return new boolean[] {convertType(value)}; } @@ -79,25 +78,14 @@ public class BooleanArrayConverter implements TypeConverter { * Converts non-array value to array. Detects various * collection types and iterates them to make conversion * and to create target array. - */ - protected boolean[] convertValueToArray(Object value) { - if (value instanceof List) { - List list = (List) value; - boolean[] target = new boolean[list.size()]; - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - return target; - } - + */ + protected boolean[] convertValueToArray(final Object value) { if (value instanceof Collection) { - Collection collection = (Collection) value; - boolean[] target = new boolean[collection.size()]; + final Collection collection = (Collection) value; + final boolean[] target = new boolean[collection.size()]; int i = 0; - for (Object element : collection) { + for (final Object element : collection) { target[i] = convertType(element); i++; } @@ -105,27 +93,27 @@ public class BooleanArrayConverter implements TypeConverter { } if (value instanceof Iterable) { - Iterable iterable = (Iterable) value; + final Iterable iterable = (Iterable) value; - ArrayList booleanArrayList = new ArrayList(); + final ArrayList booleanArrayList = new ArrayList<>(); - for (Object element : iterable) { - boolean convertedValue = convertType(element); + for (final Object element : iterable) { + final boolean convertedValue = convertType(element); booleanArrayList.add(Boolean.valueOf(convertedValue)); - } - - boolean[] array = new boolean[booleanArrayList.size()]; + } + + final boolean[] array = new boolean[booleanArrayList.size()]; for (int i = 0; i < booleanArrayList.size(); i++) { - Boolean b = booleanArrayList.get(i); + final Boolean b = booleanArrayList.get(i); array[i] = b.booleanValue(); } - + return array; } if (value instanceof CharSequence) { - String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); + final String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); return convertArrayToArray(strings); } @@ -136,22 +124,16 @@ public class BooleanArrayConverter implements TypeConverter { /** * Converts array value to array. */ - protected boolean[] convertArrayToArray(Object value) { - Class valueComponentType = value.getClass().getComponentType(); - - if (valueComponentType == boolean.class) { - // equal types, no conversion needed - return (boolean[]) value; - } + protected boolean[] convertArrayToArray(final Object value) { + final Class valueComponentType = value.getClass().getComponentType(); - boolean[] result; + final boolean[] result; if (valueComponentType.isPrimitive()) { - // convert primitive array to target array result = convertPrimitiveArrayToArray(value, valueComponentType); } else { // convert object array to target array - Object[] array = (Object[]) value; + final Object[] array = (Object[]) value; result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { @@ -166,57 +148,57 @@ public class BooleanArrayConverter implements TypeConverter { /** * Converts primitive array to target array. */ - protected boolean[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected boolean[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { boolean[] result = null; - if (primitiveComponentType == boolean[].class) { + if (primitiveComponentType == boolean.class) { return (boolean[]) value; } if (primitiveComponentType == int.class) { - int[] array = (int[]) value; + final int[] array = (int[]) value; result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] != 0; } } else if (primitiveComponentType == long.class) { - long[] array = (long[]) value; + final long[] array = (long[]) value; result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] != 0; } } else if (primitiveComponentType == float.class) { - float[] array = (float[]) value; + final float[] array = (float[]) value; result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] != 0; } } else if (primitiveComponentType == double.class) { - double[] array = (double[]) value; + final double[] array = (double[]) value; result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] != 0; } } else if (primitiveComponentType == short.class) { - short[] array = (short[]) value; + final short[] array = (short[]) value; result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] != 0; } } else if (primitiveComponentType == byte.class) { - byte[] array = (byte[]) value; + final byte[] array = (byte[]) value; result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] != 0; } } else if (primitiveComponentType == char.class) { - char[] array = (char[]) value; + final char[] array = (char[]) value; result = new boolean[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] != 0; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BooleanConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BooleanConverter.java index 596574f51..79e400916 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BooleanConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/BooleanConverter.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringPool; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; @@ -44,7 +43,7 @@ import static com.fr.third.jodd.util.StringPool.*; */ public class BooleanConverter implements TypeConverter { - public Boolean convert(Object value) { + public Boolean convert(final Object value) { if (value == null) { return null; } @@ -53,23 +52,28 @@ public class BooleanConverter implements TypeConverter { return (Boolean) value; } - String stringValue = value.toString().trim().toLowerCase(); - if (stringValue.equals(StringPool.YES) || - stringValue.equals(StringPool.Y) || - stringValue.equals(StringPool.TRUE) || - stringValue.equals(StringPool.ON) || - stringValue.equals(StringPool.ONE)) { + String stringValue = value.toString(); + if (stringValue.isEmpty()) { + return Boolean.FALSE; + } + + stringValue = stringValue.trim().toLowerCase(); + if (stringValue.equals(YES) || + stringValue.equals(Y) || + stringValue.equals(TRUE) || + stringValue.equals(ON) || + stringValue.equals(ONE)) { return Boolean.TRUE; } - if (stringValue.equals(StringPool.NO) || - stringValue.equals(StringPool.N) || - stringValue.equals(StringPool.FALSE) || - stringValue.equals(StringPool.OFF) || - stringValue.equals(StringPool.ZERO)) { + if (stringValue.equals(NO) || + stringValue.equals(N) || + stringValue.equals(FALSE) || + stringValue.equals(OFF) || + stringValue.equals(ZERO)) { return Boolean.FALSE; } throw new TypeConversionException(value); } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ByteArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ByteArrayConverter.java index 14adf2982..e2263517f 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ByteArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ByteArrayConverter.java @@ -25,12 +25,10 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.io.FileUtil; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.io.FileUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.StringUtil; import java.io.File; @@ -39,25 +37,25 @@ import java.sql.Blob; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * Converts given object to byte[]. */ public class ByteArrayConverter implements TypeConverter { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; - public ByteArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - this.typeConverterManagerBean = typeConverterManagerBean; + public ByteArrayConverter(final TypeConverterManager typeConverterManager) { + this.typeConverterManager = typeConverterManager; } - public byte[] convert(Object value) { + @Override + public byte[] convert(final Object value) { if (value == null) { return null; } - Class valueClass = value.getClass(); + final Class valueClass = value.getClass(); if (!valueClass.isArray()) { // source is not an array @@ -71,14 +69,14 @@ public class ByteArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected byte convertType(Object value) { - return typeConverterManagerBean.convertType(value, byte.class).byteValue(); + protected byte convertType(final Object value) { + return typeConverterManager.convertType(value, byte.class).byteValue(); } /** * Creates an array with single element. */ - protected byte[] convertToSingleElementArray(Object value) { + protected byte[] convertToSingleElementArray(final Object value) { return new byte[] {convertType(value)}; } @@ -86,12 +84,12 @@ public class ByteArrayConverter implements TypeConverter { * Converts non-array value to array. Detects various * types and collections, iterates them to make conversion * and to create target array. - */ - protected byte[] convertValueToArray(Object value) { + */ + protected byte[] convertValueToArray(final Object value) { if (value instanceof Blob) { - Blob blob = (Blob) value; + final Blob blob = (Blob) value; try { - long length = blob.length(); + final long length = blob.length(); if (length > Integer.MAX_VALUE) { throw new TypeConversionException("Blob is too big."); } @@ -109,24 +107,12 @@ public class ByteArrayConverter implements TypeConverter { } } - if (value instanceof List) { - List list = (List) value; - byte[] target = new byte[list.size()]; - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - - return target; - } - if (value instanceof Collection) { - Collection collection = (Collection) value; - byte[] target = new byte[collection.size()]; + final Collection collection = (Collection) value; + final byte[] target = new byte[collection.size()]; int i = 0; - for (Object element : collection) { + for (final Object element : collection) { target[i] = convertType(element); i++; } @@ -135,19 +121,19 @@ public class ByteArrayConverter implements TypeConverter { } if (value instanceof Iterable) { - Iterable iterable = (Iterable) value; + final Iterable iterable = (Iterable) value; - ArrayList byteArrayList = new ArrayList(); + final ArrayList byteArrayList = new ArrayList<>(); - for (Object element : iterable) { - byte convertedValue = convertType(element); + for (final Object element : iterable) { + final byte convertedValue = convertType(element); byteArrayList.add(Byte.valueOf(convertedValue)); } - byte[] array = new byte[byteArrayList.size()]; + final byte[] array = new byte[byteArrayList.size()]; for (int i = 0; i < byteArrayList.size(); i++) { - Byte b = byteArrayList.get(i); + final Byte b = byteArrayList.get(i); array[i] = b.byteValue(); } @@ -155,7 +141,7 @@ public class ByteArrayConverter implements TypeConverter { } if (value instanceof CharSequence) { - String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); + final String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); return convertArrayToArray(strings); } @@ -166,22 +152,16 @@ public class ByteArrayConverter implements TypeConverter { /** * Converts array value to array. */ - protected byte[] convertArrayToArray(Object value) { - Class valueComponentType = value.getClass().getComponentType(); - - if (valueComponentType == byte.class) { - // equal types, no conversion needed - return (byte[]) value; - } + protected byte[] convertArrayToArray(final Object value) { + final Class valueComponentType = value.getClass().getComponentType(); - byte[] result; + final byte[] result; if (valueComponentType.isPrimitive()) { - // convert primitive array to target array result = convertPrimitiveArrayToArray(value, valueComponentType); } else { // convert object array to target array - Object[] array = (Object[]) value; + final Object[] array = (Object[]) value; result = new byte[array.length]; for (int i = 0; i < array.length; i++) { @@ -196,57 +176,57 @@ public class ByteArrayConverter implements TypeConverter { /** * Converts primitive array to target array. */ - protected byte[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected byte[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { byte[] result = null; - if (primitiveComponentType == byte[].class) { + if (primitiveComponentType == byte.class) { return (byte[]) value; } if (primitiveComponentType == int.class) { - int[] array = (int[]) value; + final int[] array = (int[]) value; result = new byte[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (byte) array[i]; } } else if (primitiveComponentType == long.class) { - long[] array = (long[]) value; + final long[] array = (long[]) value; result = new byte[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (byte) array[i]; } } else if (primitiveComponentType == float.class) { - float[] array = (float[]) value; + final float[] array = (float[]) value; result = new byte[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (byte) array[i]; } } else if (primitiveComponentType == double.class) { - double[] array = (double[]) value; + final double[] array = (double[]) value; result = new byte[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (byte) array[i]; } } else if (primitiveComponentType == short.class) { - short[] array = (short[]) value; + final short[] array = (short[]) value; result = new byte[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (byte) array[i]; } } else if (primitiveComponentType == char.class) { - char[] array = (char[]) value; + final char[] array = (char[]) value; result = new byte[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (byte) array[i]; } } else if (primitiveComponentType == boolean.class) { - boolean[] array = (boolean[]) value; + final boolean[] array = (boolean[]) value; result = new byte[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (byte) (array[i] ? 1 : 0); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ByteConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ByteConverter.java index 842a76ac6..e58d40bff 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ByteConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ByteConverter.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; @@ -42,7 +41,8 @@ import com.fr.third.jodd.util.StringUtil; */ public class ByteConverter implements TypeConverter { - public Byte convert(Object value) { + @Override + public Byte convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CalendarConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CalendarConverter.java index fa79f84f8..cb7d925d5 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CalendarConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CalendarConverter.java @@ -25,13 +25,14 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.datetime.JDateTime; +import com.fr.third.jodd.time.JulianDate; +import com.fr.third.jodd.time.TimeUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Calendar; import java.util.Date; @@ -42,15 +43,17 @@ import java.util.Date; *

  • null value is returned as null
  • *
  • object of destination type is simply casted
  • *
  • Date object is converted
  • - *
  • JDateTime object is converted
  • + *
  • JulianDate object is converted
  • + *
  • LocalDateTime object is converted
  • + *
  • LocalDate object is converted
  • *
  • Number is used as number of milliseconds
  • - *
  • finally, if string value contains only numbers it is parsed as milliseconds; - * otherwise as JDateTime pattern
  • + *
  • finally, if string value contains only numbers it is parsed as milliseconds
  • * */ public class CalendarConverter implements TypeConverter { - public Calendar convert(Object value) { + @Override + public Calendar convert(final Object value) { if (value == null) { return null; } @@ -63,8 +66,14 @@ public class CalendarConverter implements TypeConverter { calendar.setTime((Date) value); return calendar; } - if (value instanceof JDateTime) { - return ((JDateTime)value).convertToCalendar(); + if (value instanceof JulianDate) { + return TimeUtil.toCalendar(((JulianDate)value).toLocalDateTime()); + } + if (value instanceof LocalDateTime) { + return TimeUtil.toCalendar((LocalDateTime)value); + } + if (value instanceof LocalDate) { + return TimeUtil.toCalendar((LocalDate)value); } if (value instanceof Number) { @@ -77,8 +86,7 @@ public class CalendarConverter implements TypeConverter { if (!StringUtil.containsOnlyDigits(stringValue)) { // try to parse default string format - JDateTime jdt = new JDateTime(stringValue, JDateTime.DEFAULT_FORMAT); - return jdt.convertToCalendar(); + return TimeUtil.toCalendar(LocalDateTime.parse(stringValue)); } try { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CharacterArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CharacterArrayConverter.java index d30dbeed0..299a1b1a3 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CharacterArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CharacterArrayConverter.java @@ -27,29 +27,29 @@ package com.fr.third.jodd.typeconverter.impl; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * Converts given object to char[]. */ public class CharacterArrayConverter implements TypeConverter { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; - public CharacterArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - this.typeConverterManagerBean = typeConverterManagerBean; + public CharacterArrayConverter(final TypeConverterManager typeConverterManager) { + this.typeConverterManager = typeConverterManager; } - public char[] convert(Object value) { + @Override + public char[] convert(final Object value) { if (value == null) { return null; } - Class valueClass = value.getClass(); + final Class valueClass = value.getClass(); if (!valueClass.isArray()) { // source is not an array @@ -63,14 +63,14 @@ public class CharacterArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected char convertType(Object value) { - return typeConverterManagerBean.convertType(value, char.class).charValue(); + protected char convertType(final Object value) { + return typeConverterManager.convertType(value, char.class).charValue(); } /** * Creates an array with single element. */ - protected char[] convertToSingleElementArray(Object value) { + protected char[] convertToSingleElementArray(final Object value) { return new char[] {convertType(value)}; } @@ -78,26 +78,14 @@ public class CharacterArrayConverter implements TypeConverter { * Converts non-array value to array. Detects various * collection types and iterates them to make conversion * and to create target array. - */ - protected char[] convertValueToArray(Object value) { - if (value instanceof List) { - List list = (List) value; - char[] target = new char[list.size()]; - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - - return target; - } - + */ + protected char[] convertValueToArray(final Object value) { if (value instanceof Collection) { - Collection collection = (Collection) value; - char[] target = new char[collection.size()]; + final Collection collection = (Collection) value; + final char[] target = new char[collection.size()]; int i = 0; - for (Object element : collection) { + for (final Object element : collection) { target[i] = convertType(element); i++; } @@ -106,19 +94,19 @@ public class CharacterArrayConverter implements TypeConverter { } if (value instanceof Iterable) { - Iterable iterable = (Iterable) value; + final Iterable iterable = (Iterable) value; - ArrayList charArrayList = new ArrayList(); + final ArrayList charArrayList = new ArrayList<>(); - for (Object element : iterable) { - char convertedValue = convertType(element); + for (final Object element : iterable) { + final char convertedValue = convertType(element); charArrayList.add(Character.valueOf(convertedValue)); } - char[] array = new char[charArrayList.size()]; + final char[] array = new char[charArrayList.size()]; for (int i = 0; i < charArrayList.size(); i++) { - Character c = charArrayList.get(i); + final Character c = charArrayList.get(i); array[i] = c.charValue(); } @@ -127,9 +115,9 @@ public class CharacterArrayConverter implements TypeConverter { } if (value instanceof CharSequence) { - CharSequence charSequence = (CharSequence) value; + final CharSequence charSequence = (CharSequence) value; - char[] result = new char[charSequence.length()]; + final char[] result = new char[charSequence.length()]; for (int i = 0; i < result.length; i++) { result[i] = charSequence.charAt(i); @@ -145,22 +133,16 @@ public class CharacterArrayConverter implements TypeConverter { /** * Converts array value to array. */ - protected char[] convertArrayToArray(Object value) { - Class valueComponentType = value.getClass().getComponentType(); - - if (valueComponentType == char.class) { - // equal types, no conversion needed - return (char[]) value; - } + protected char[] convertArrayToArray(final Object value) { + final Class valueComponentType = value.getClass().getComponentType(); - char[] result; + final char[] result; if (valueComponentType.isPrimitive()) { - // convert primitive array to target array result = convertPrimitiveArrayToArray(value, valueComponentType); } else { // convert object array to target array - Object[] array = (Object[]) value; + final Object[] array = (Object[]) value; result = new char[array.length]; for (int i = 0; i < array.length; i++) { @@ -175,57 +157,57 @@ public class CharacterArrayConverter implements TypeConverter { /** * Converts primitive array to target array. */ - protected char[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected char[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { char[] result = null; - if (primitiveComponentType == char[].class) { + if (primitiveComponentType == char.class) { return (char[]) value; } if (primitiveComponentType == int.class) { - int[] array = (int[]) value; + final int[] array = (int[]) value; result = new char[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (char) array[i]; } } else if (primitiveComponentType == long.class) { - long[] array = (long[]) value; + final long[] array = (long[]) value; result = new char[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (char) array[i]; } } else if (primitiveComponentType == float.class) { - float[] array = (float[]) value; + final float[] array = (float[]) value; result = new char[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (char) array[i]; } } else if (primitiveComponentType == double.class) { - double[] array = (double[]) value; + final double[] array = (double[]) value; result = new char[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (char) array[i]; } } else if (primitiveComponentType == short.class) { - short[] array = (short[]) value; + final short[] array = (short[]) value; result = new char[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (char) array[i]; } } else if (primitiveComponentType == byte.class) { - byte[] array = (byte[]) value; + final byte[] array = (byte[]) value; result = new char[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (char) array[i]; } } else if (primitiveComponentType == boolean.class) { - boolean[] array = (boolean[]) value; + final boolean[] array = (boolean[]) value; result = new char[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (char) (array[i] ? 1 : 0); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CharacterConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CharacterConverter.java index 26daba776..55c08463e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CharacterConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CharacterConverter.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; @@ -43,7 +42,7 @@ import com.fr.third.jodd.util.StringUtil; */ public class CharacterConverter implements TypeConverter { - public Character convert(Object value) { + public Character convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ClassArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ClassArrayConverter.java index e8a5f8036..367b33fc1 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ClassArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ClassArrayConverter.java @@ -25,10 +25,7 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.ArraysUtil; -import com.fr.third.jodd.util.StringPool; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.ArraysUtil; import com.fr.third.jodd.util.StringPool; import com.fr.third.jodd.util.StringUtil; @@ -53,17 +50,17 @@ import com.fr.third.jodd.util.StringUtil; */ public class ClassArrayConverter extends ArrayConverter { - public ClassArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - super(typeConverterManagerBean, Class.class); + public ClassArrayConverter(final TypeConverterManager typeConverterManager) { + super(typeConverterManager, Class.class); } @Override - protected Class[] createArray(int length) { + protected Class[] createArray(final int length) { return new Class[length]; } @Override - protected String[] convertStringToArray(String value) { + protected String[] convertStringToArray(final String value) { String[] strings = StringUtil.splitc(value, NUMBER_DELIMITERS); int count = 0; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ClassConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ClassConverter.java index 755f65708..6f4c5969a 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ClassConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ClassConverter.java @@ -25,8 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.ClassLoaderUtil; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.ClassLoaderUtil; @@ -43,7 +41,8 @@ import com.fr.third.jodd.util.StringUtil; */ public class ClassConverter implements TypeConverter { - public Class convert(Object value) { + @Override + public Class convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CollectionConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CollectionConverter.java index 301ca7018..af84fde4e 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CollectionConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/CollectionConverter.java @@ -25,11 +25,9 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.CsvUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.typeconverter.TypeConverterManager; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; import com.fr.third.jodd.util.CsvUtil; import java.lang.reflect.Constructor; @@ -44,34 +42,35 @@ import java.util.Set; */ public class CollectionConverter implements TypeConverter> { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; protected final Class collectionType; protected final Class targetComponentType; public CollectionConverter( - Class collectionType, - Class targetComponentType) { - this(TypeConverterManager.getDefaultTypeConverterManager(), collectionType, targetComponentType); + final Class collectionType, + final Class targetComponentType) { + this(TypeConverterManager.get(), collectionType, targetComponentType); } public CollectionConverter( - TypeConverterManagerBean typeConverterManagerBean, - Class collectionType, - Class targetComponentType) { + final TypeConverterManager typeConverterManager, + final Class collectionType, + final Class targetComponentType) { - this.typeConverterManagerBean = typeConverterManagerBean; + this.typeConverterManager = typeConverterManager; this.collectionType = collectionType; this.targetComponentType = targetComponentType; } - public Collection convert(Object value) { + @Override + public Collection convert(final Object value) { if (value == null) { return null; } if (!(value instanceof Collection)) { // source is not an array - return convertValueToCollection(value); + return convertValueToCollection(value); } // source is a collection @@ -81,8 +80,8 @@ public class CollectionConverter implements TypeConverter> { /** * Converts type using type converter manager. */ - protected T convertType(Object value) { - return typeConverterManagerBean.convertType(value, targetComponentType); + protected T convertType(final Object value) { + return typeConverterManager.convertType(value, targetComponentType); } /** @@ -91,21 +90,21 @@ public class CollectionConverter implements TypeConverter> { * an collection of target type. Override it for better performances. */ @SuppressWarnings("unchecked") - protected Collection createCollection(int length) { + protected Collection createCollection(final int length) { if (collectionType.isInterface()) { if (collectionType == List.class) { if (length > 0) { - return new ArrayList(length); + return new ArrayList<>(length); } else { - return new ArrayList(); + return new ArrayList<>(); } } if (collectionType == Set.class) { if (length > 0) { - return new HashSet(length); + return new HashSet<>(length); } else { - return new HashSet(); + return new HashSet<>(); } } @@ -121,7 +120,7 @@ public class CollectionConverter implements TypeConverter> { } try { - return collectionType.newInstance(); + return collectionType.getDeclaredConstructor().newInstance(); } catch (Exception ex) { throw new TypeConversionException(ex); } @@ -130,9 +129,10 @@ public class CollectionConverter implements TypeConverter> { /** * Creates a collection with single element. */ - protected Collection convertToSingleElementCollection(Object value) { + protected Collection convertToSingleElementCollection(final Object value) { Collection collection = createCollection(0); + //noinspection unchecked collection.add((T) value); return collection; @@ -140,7 +140,7 @@ public class CollectionConverter implements TypeConverter> { /** * Converts non-collection value to collection. - */ + */ protected Collection convertValueToCollection(Object value) { if (value instanceof Iterable) { Iterable iterable = (Iterable) value; @@ -182,7 +182,7 @@ public class CollectionConverter implements TypeConverter> { * Converts collection value to target collection. * Each element is converted to target component type. */ - protected Collection convertCollectionToCollection(Collection value) { + protected Collection convertCollectionToCollection(final Collection value) { Collection collection = createCollection(value.size()); for (Object v : value) { @@ -196,7 +196,7 @@ public class CollectionConverter implements TypeConverter> { * Converts primitive array to target collection. */ @SuppressWarnings("AutoBoxing") - protected Collection convertPrimitiveArrayToCollection(Object value, Class primitiveComponentType) { + protected Collection convertPrimitiveArrayToCollection(final Object value, final Class primitiveComponentType) { Collection result = null; if (primitiveComponentType == int.class) { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DateConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DateConverter.java index 49618ac2e..ffe33c9f9 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DateConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DateConverter.java @@ -25,13 +25,14 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.datetime.JDateTime; +import com.fr.third.jodd.time.JulianDate; +import com.fr.third.jodd.time.TimeUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Calendar; import java.util.Date; @@ -42,15 +43,17 @@ import java.util.Date; *
  • null value is returned as null
  • *
  • object of destination type is simply casted
  • *
  • Calendar object is converted
  • - *
  • JDateTime object is converted
  • + *
  • JulianDate object is converted
  • + *
  • LocalDateTime object is converted
  • + *
  • LocalDate object is converted
  • *
  • Number is used as number of milliseconds
  • - *
  • finally, if string value contains only numbers it is parsed as milliseconds; - * otherwise as JDateTime pattern
  • + *
  • finally, if string value contains only numbers it is parsed as milliseconds
  • * */ public class DateConverter implements TypeConverter { - public Date convert(Object value) { + @Override + public Date convert(final Object value) { if (value == null) { return null; } @@ -61,19 +64,24 @@ public class DateConverter implements TypeConverter { if (value instanceof Calendar) { return new Date(((Calendar)value).getTimeInMillis()); } - if (value instanceof JDateTime) { - return ((JDateTime) value).convertToDate(); + if (value instanceof JulianDate) { + return new Date(((JulianDate) value).toMilliseconds()); + } + if (value instanceof LocalDateTime) { + return TimeUtil.toDate((LocalDateTime)value); + } + if (value instanceof LocalDate) { + return TimeUtil.toDate((LocalDate)value); } if (value instanceof Number) { return new Date(((Number) value).longValue()); } - String stringValue = value.toString().trim(); + final String stringValue = value.toString().trim(); if (!StringUtil.containsOnlyDigits(stringValue)) { // try to parse default string format - JDateTime jdt = new JDateTime(stringValue, JDateTime.DEFAULT_FORMAT); - return jdt.convertToDate(); + return TimeUtil.toDate(LocalDateTime.parse(stringValue)); } try { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DoubleArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DoubleArrayConverter.java index 8e4523a88..17b107447 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DoubleArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DoubleArrayConverter.java @@ -25,32 +25,31 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.StringUtil; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * Converts given object to double[]. */ public class DoubleArrayConverter implements TypeConverter { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; - public DoubleArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - this.typeConverterManagerBean = typeConverterManagerBean; + public DoubleArrayConverter(final TypeConverterManager typeConverterManager) { + this.typeConverterManager = typeConverterManager; } - public double[] convert(Object value) { + @Override + public double[] convert(final Object value) { if (value == null) { return null; } - Class valueClass = value.getClass(); + final Class valueClass = value.getClass(); if (!valueClass.isArray()) { // source is not an array @@ -64,14 +63,14 @@ public class DoubleArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected double convertType(Object value) { - return typeConverterManagerBean.convertType(value, double.class).doubleValue(); + protected double convertType(final Object value) { + return typeConverterManager.convertType(value, double.class).doubleValue(); } /** * Creates an array with single element. */ - protected double[] convertToSingleElementArray(Object value) { + protected double[] convertToSingleElementArray(final Object value) { return new double[] {convertType(value)}; } @@ -79,26 +78,14 @@ public class DoubleArrayConverter implements TypeConverter { * Converts non-array value to array. Detects various * collection types and iterates them to make conversion * and to create target array. - */ - protected double[] convertValueToArray(Object value) { - if (value instanceof List) { - List list = (List) value; - double[] target = new double[list.size()]; - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - - return target; - } - + */ + protected double[] convertValueToArray(final Object value) { if (value instanceof Collection) { - Collection collection = (Collection) value; - double[] target = new double[collection.size()]; + final Collection collection = (Collection) value; + final double[] target = new double[collection.size()]; int i = 0; - for (Object element : collection) { + for (final Object element : collection) { target[i] = convertType(element); i++; } @@ -107,19 +94,19 @@ public class DoubleArrayConverter implements TypeConverter { } if (value instanceof Iterable) { - Iterable iterable = (Iterable) value; + final Iterable iterable = (Iterable) value; - ArrayList doubleArrayList = new ArrayList(); + final ArrayList doubleArrayList = new ArrayList<>(); - for (Object element : iterable) { - double convertedValue = convertType(element); + for (final Object element : iterable) { + final double convertedValue = convertType(element); doubleArrayList.add(Double.valueOf(convertedValue)); } - double[] array = new double[doubleArrayList.size()]; + final double[] array = new double[doubleArrayList.size()]; for (int i = 0; i < doubleArrayList.size(); i++) { - Double d = doubleArrayList.get(i); + final Double d = doubleArrayList.get(i); array[i] = d.doubleValue(); } @@ -127,7 +114,7 @@ public class DoubleArrayConverter implements TypeConverter { } if (value instanceof CharSequence) { - String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); + final String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); return convertArrayToArray(strings); } @@ -138,22 +125,16 @@ public class DoubleArrayConverter implements TypeConverter { /** * Converts array value to array. */ - protected double[] convertArrayToArray(Object value) { - Class valueComponentType = value.getClass().getComponentType(); - - if (valueComponentType == double.class) { - // equal types, no conversion needed - return (double[]) value; - } + protected double[] convertArrayToArray(final Object value) { + final Class valueComponentType = value.getClass().getComponentType(); - double[] result; + final double[] result; if (valueComponentType.isPrimitive()) { - // convert primitive array to target array result = convertPrimitiveArrayToArray(value, valueComponentType); } else { // convert object array to target array - Object[] array = (Object[]) value; + final Object[] array = (Object[]) value; result = new double[array.length]; for (int i = 0; i < array.length; i++) { @@ -168,57 +149,57 @@ public class DoubleArrayConverter implements TypeConverter { /** * Converts primitive array to target array. */ - protected double[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected double[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { double[] result = null; - if (primitiveComponentType == double[].class) { + if (primitiveComponentType == double.class) { return (double[]) value; } if (primitiveComponentType == int.class) { - int[] array = (int[]) value; + final int[] array = (int[]) value; result = new double[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == long.class) { - long[] array = (long[]) value; + final long[] array = (long[]) value; result = new double[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == float.class) { - float[] array = (float[]) value; + final float[] array = (float[]) value; result = new double[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == short.class) { - short[] array = (short[]) value; + final short[] array = (short[]) value; result = new double[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == byte.class) { - byte[] array = (byte[]) value; + final byte[] array = (byte[]) value; result = new double[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == char.class) { - char[] array = (char[]) value; + final char[] array = (char[]) value; result = new double[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == boolean.class) { - boolean[] array = (boolean[]) value; + final boolean[] array = (boolean[]) value; result = new double[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] ? 1 : 0; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DoubleConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DoubleConverter.java index ebcc86e5e..b983155eb 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DoubleConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/DoubleConverter.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; @@ -42,7 +41,7 @@ import com.fr.third.jodd.util.StringUtil; */ public class DoubleConverter implements TypeConverter { - public Double convert(Object value) { + public Double convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FileConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FileConverter.java index 2456f72b0..5ee9a299d 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FileConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FileConverter.java @@ -26,14 +26,14 @@ package com.fr.third.jodd.typeconverter.impl; import com.fr.third.jodd.io.FileUtil; -import com.fr.third.jodd.util.ArraysUtil; -import com.fr.third.jodd.io.FileUtil; +import com.fr.third.jodd.io.StreamUtil; +import com.fr.third.jodd.io.upload.FileUpload; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.util.ArraysUtil; import java.io.File; import java.io.IOException; +import java.io.InputStream; /** * Converts given object into the File. @@ -51,18 +51,8 @@ import java.io.IOException; */ public class FileConverter implements TypeConverter { - protected TypeConverter[] addonFileConverters; - - @SuppressWarnings("unchecked") - public void registerAddonConverter(TypeConverter fileTypeConverter) { - if (addonFileConverters == null) { - addonFileConverters = new TypeConverter[0]; - } - - addonFileConverters = ArraysUtil.append(addonFileConverters, fileTypeConverter); - } - - public File convert(Object value) { + @Override + public File convert(final Object value) { if (value == null) { return null; } @@ -71,17 +61,6 @@ public class FileConverter implements TypeConverter { return (File) value; } - if (addonFileConverters != null) { - for (TypeConverter addonFileConverter : addonFileConverters) { - File file = addonFileConverter.convert(value); - - if (file != null) { - return file; - } - } - } - -/* if (value instanceof FileUpload) { FileUpload fileUpload = (FileUpload) value; @@ -97,7 +76,6 @@ public class FileConverter implements TypeConverter { StreamUtil.close(in); } } -*/ Class type = value.getClass(); if (type == byte[].class) { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/bean/JoddBean.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FileUploadConverter.java similarity index 66% rename from fine-jodd/src/main/java/com/fr/third/jodd/bean/JoddBean.java rename to fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FileUploadConverter.java index f80e0ba4b..ea85b84a1 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/bean/JoddBean.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FileUploadConverter.java @@ -23,36 +23,31 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -package com.fr.third.jodd.bean; +package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.Jodd; -import com.fr.third.jodd.introspector.Introspector; -import com.fr.third.jodd.introspector.JoddIntrospector; -import com.fr.third.jodd.Jodd; -import com.fr.third.jodd.introspector.Introspector; -import com.fr.third.jodd.introspector.JoddIntrospector; +import com.fr.third.jodd.io.upload.FileUpload; +import com.fr.third.jodd.typeconverter.TypeConversionException; +import com.fr.third.jodd.typeconverter.TypeConverter; /** - * Jodd BEAN module. + * Converts given object to {@link FileUpload}. + * Conversion rules: + *
      + *
    • null value is returned as null
    • + *
    • object of destination type is simply casted
    • + *
    */ -public class JoddBean { - - /** - * Default {@link Introspector} implementation. - */ - public static Introspector introspector; - - - // ---------------------------------------------------------------- module - - static { - init(); - } - - public static void init() { - //JoddIntrospector.init(); - Jodd.init(JoddBean.class); - introspector = JoddIntrospector.introspector; +public class FileUploadConverter implements TypeConverter { + + @Override + public FileUpload convert(final Object value) { + if (value == null) { + return null; + } + if (value instanceof FileUpload) { + return (FileUpload) value; + } + throw new TypeConversionException(value); } -} \ No newline at end of file +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FloatArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FloatArrayConverter.java index 211dd9172..b4fbcb178 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FloatArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FloatArrayConverter.java @@ -25,32 +25,31 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.StringUtil; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * Converts given object to float[]. */ public class FloatArrayConverter implements TypeConverter { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; - public FloatArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - this.typeConverterManagerBean = typeConverterManagerBean; + public FloatArrayConverter(final TypeConverterManager typeConverterManager) { + this.typeConverterManager = typeConverterManager; } - public float[] convert(Object value) { + @Override + public float[] convert(final Object value) { if (value == null) { return null; } - Class valueClass = value.getClass(); + final Class valueClass = value.getClass(); if (!valueClass.isArray()) { // source is not an array @@ -64,14 +63,14 @@ public class FloatArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected float convertType(Object value) { - return typeConverterManagerBean.convertType(value, float.class).floatValue(); + protected float convertType(final Object value) { + return typeConverterManager.convertType(value, float.class).floatValue(); } /** * Creates an array with single element. */ - protected float[] convertToSingleElementArray(Object value) { + protected float[] convertToSingleElementArray(final Object value) { return new float[] {convertType(value)}; } @@ -79,26 +78,14 @@ public class FloatArrayConverter implements TypeConverter { * Converts non-array value to array. Detects various * collection types and iterates them to make conversion * and to create target array. - */ - protected float[] convertValueToArray(Object value) { - if (value instanceof List) { - List list = (List) value; - float[] target = new float[list.size()]; - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - - return target; - } - + */ + protected float[] convertValueToArray(final Object value) { if (value instanceof Collection) { - Collection collection = (Collection) value; - float[] target = new float[collection.size()]; + final Collection collection = (Collection) value; + final float[] target = new float[collection.size()]; int i = 0; - for (Object element : collection) { + for (final Object element : collection) { target[i] = convertType(element); i++; } @@ -107,19 +94,19 @@ public class FloatArrayConverter implements TypeConverter { } if (value instanceof Iterable) { - Iterable iterable = (Iterable) value; + final Iterable iterable = (Iterable) value; - ArrayList floatArrayList = new ArrayList(); + final ArrayList floatArrayList = new ArrayList<>(); - for (Object element : iterable) { - float convertedValue = convertType(element); + for (final Object element : iterable) { + final float convertedValue = convertType(element); floatArrayList.add(Float.valueOf(convertedValue)); } - float[] array = new float[floatArrayList.size()]; + final float[] array = new float[floatArrayList.size()]; for (int i = 0; i < floatArrayList.size(); i++) { - Float f = floatArrayList.get(i); + final Float f = floatArrayList.get(i); array[i] = f.floatValue(); } @@ -127,7 +114,7 @@ public class FloatArrayConverter implements TypeConverter { } if (value instanceof CharSequence) { - String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); + final String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); return convertArrayToArray(strings); } @@ -138,22 +125,16 @@ public class FloatArrayConverter implements TypeConverter { /** * Converts array value to array. */ - protected float[] convertArrayToArray(Object value) { - Class valueComponentType = value.getClass().getComponentType(); - - if (valueComponentType == float.class) { - // equal types, no conversion needed - return (float[]) value; - } + protected float[] convertArrayToArray(final Object value) { + final Class valueComponentType = value.getClass().getComponentType(); - float[] result; + final float[] result; if (valueComponentType.isPrimitive()) { - // convert primitive array to target array result = convertPrimitiveArrayToArray(value, valueComponentType); } else { // convert object array to target array - Object[] array = (Object[]) value; + final Object[] array = (Object[]) value; result = new float[array.length]; for (int i = 0; i < array.length; i++) { @@ -168,57 +149,57 @@ public class FloatArrayConverter implements TypeConverter { /** * Converts primitive array to target array. */ - protected float[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected float[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { float[] result = null; - if (primitiveComponentType == float[].class) { + if (primitiveComponentType == float.class) { return (float[]) value; } if (primitiveComponentType == int.class) { - int[] array = (int[]) value; + final int[] array = (int[]) value; result = new float[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == long.class) { - long[] array = (long[]) value; + final long[] array = (long[]) value; result = new float[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == double.class) { - double[] array = (double[]) value; + final double[] array = (double[]) value; result = new float[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (float) array[i]; } } else if (primitiveComponentType == short.class) { - short[] array = (short[]) value; + final short[] array = (short[]) value; result = new float[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == byte.class) { - byte[] array = (byte[]) value; + final byte[] array = (byte[]) value; result = new float[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == char.class) { - char[] array = (char[]) value; + final char[] array = (char[]) value; result = new float[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == boolean.class) { - boolean[] array = (boolean[]) value; + final boolean[] array = (boolean[]) value; result = new float[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] ? 1 : 0; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FloatConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FloatConverter.java index 129698054..fb9155175 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FloatConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/FloatConverter.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; @@ -42,7 +41,7 @@ import com.fr.third.jodd.util.StringUtil; */ public class FloatConverter implements TypeConverter { - public Float convert(Object value) { + public Float convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/IntegerArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/IntegerArrayConverter.java index c70cd4cea..c2ec6b475 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/IntegerArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/IntegerArrayConverter.java @@ -25,33 +25,31 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.util.collection.IntArrayList; +import com.fr.third.jodd.buffer.FastIntBuffer; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.util.collection.IntArrayList; import java.util.Collection; -import java.util.List; /** * Converts given object to int[]. */ public class IntegerArrayConverter implements TypeConverter { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; - public IntegerArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - this.typeConverterManagerBean = typeConverterManagerBean; + public IntegerArrayConverter(final TypeConverterManager typeConverterManager) { + this.typeConverterManager = typeConverterManager; } - public int[] convert(Object value) { + @Override + public int[] convert(final Object value) { if (value == null) { return null; } - Class valueClass = value.getClass(); + final Class valueClass = value.getClass(); if (!valueClass.isArray()) { // source is not an array @@ -65,14 +63,14 @@ public class IntegerArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected int convertType(Object value) { - return typeConverterManagerBean.convertType(value, int.class).intValue(); + protected int convertType(final Object value) { + return typeConverterManager.convertType(value, int.class).intValue(); } /** * Creates an array with single element. */ - protected int[] convertToSingleElementArray(Object value) { + protected int[] convertToSingleElementArray(final Object value) { return new int[] {convertType(value)}; } @@ -81,24 +79,13 @@ public class IntegerArrayConverter implements TypeConverter { * collection types and iterates them to make conversion * and to create target array. */ - protected int[] convertValueToArray(Object value) { - if (value instanceof List) { - List list = (List) value; - int[] target = new int[list.size()]; - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - return target; - } - + protected int[] convertValueToArray(final Object value) { if (value instanceof Collection) { - Collection collection = (Collection) value; - int[] target = new int[collection.size()]; + final Collection collection = (Collection) value; + final int[] target = new int[collection.size()]; int i = 0; - for (Object element : collection) { + for (final Object element : collection) { target[i] = convertType(element); i++; } @@ -107,20 +94,20 @@ public class IntegerArrayConverter implements TypeConverter { } if (value instanceof Iterable) { - Iterable iterable = (Iterable) value; + final Iterable iterable = (Iterable) value; - IntArrayList intArrayList = new IntArrayList(); + final FastIntBuffer fastIntBuffer = new FastIntBuffer(); - for (Object element : iterable) { - int convertedValue = convertType(element); - intArrayList.add(convertedValue); + for (final Object element : iterable) { + final int convertedValue = convertType(element); + fastIntBuffer.append(convertedValue); } - return intArrayList.toArray(); + return fastIntBuffer.toArray(); } if (value instanceof CharSequence) { - String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); + final String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); return convertArrayToArray(strings); } @@ -131,22 +118,16 @@ public class IntegerArrayConverter implements TypeConverter { /** * Converts array value to array. */ - protected int[] convertArrayToArray(Object value) { - Class valueComponentType = value.getClass().getComponentType(); - - if (valueComponentType == int.class) { - // equal types, no conversion needed - return (int[]) value; - } + protected int[] convertArrayToArray(final Object value) { + final Class valueComponentType = value.getClass().getComponentType(); - int[] result; + final int[] result; if (valueComponentType.isPrimitive()) { - // convert primitive array to target array result = convertPrimitiveArrayToArray(value, valueComponentType); } else { // convert object array to target array - Object[] array = (Object[]) value; + final Object[] array = (Object[]) value; result = new int[array.length]; for (int i = 0; i < array.length; i++) { @@ -161,57 +142,57 @@ public class IntegerArrayConverter implements TypeConverter { /** * Converts primitive array to target array. */ - protected int[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected int[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { int[] result = null; - if (primitiveComponentType == int[].class) { + if (primitiveComponentType == int.class) { return (int[]) value; } if (primitiveComponentType == long.class) { - long[] array = (long[]) value; + final long[] array = (long[]) value; result = new int[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (int) array[i]; } } else if (primitiveComponentType == float.class) { - float[] array = (float[]) value; + final float[] array = (float[]) value; result = new int[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (int) array[i]; } } else if (primitiveComponentType == double.class) { - double[] array = (double[]) value; + final double[] array = (double[]) value; result = new int[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (int) array[i]; } } else if (primitiveComponentType == short.class) { - short[] array = (short[]) value; + final short[] array = (short[]) value; result = new int[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (int) array[i]; } } else if (primitiveComponentType == byte.class) { - byte[] array = (byte[]) value; + final byte[] array = (byte[]) value; result = new int[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (int) array[i]; } } else if (primitiveComponentType == char.class) { - char[] array = (char[]) value; + final char[] array = (char[]) value; result = new int[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (int) array[i]; } } else if (primitiveComponentType == boolean.class) { - boolean[] array = (boolean[]) value; + final boolean[] array = (boolean[]) value; result = new int[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] ? 1 : 0; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/IntegerConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/IntegerConverter.java index bffac1abe..d0f05b4a2 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/IntegerConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/IntegerConverter.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; @@ -42,7 +41,7 @@ import com.fr.third.jodd.util.StringUtil; */ public class IntegerConverter implements TypeConverter { - public Integer convert(Object value) { + public Integer convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalDateConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalDateConverter.java new file mode 100644 index 000000000..15fa6736a --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalDateConverter.java @@ -0,0 +1,80 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.typeconverter.impl; + +import com.fr.third.jodd.time.TimeUtil; +import com.fr.third.jodd.typeconverter.TypeConversionException; +import com.fr.third.jodd.typeconverter.TypeConverter; +import com.fr.third.jodd.util.StringUtil; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Calendar; +import java.util.Date; + +public class LocalDateConverter implements TypeConverter { + @Override + public LocalDate convert(Object value) { + if (value == null) { + return null; + } + + if (value instanceof LocalDateTime) { + return ((LocalDateTime)value).toLocalDate(); + } + if (value instanceof Calendar) { + return TimeUtil.fromCalendar((Calendar) value).toLocalDate(); + } + if (value instanceof Timestamp) { + return TimeUtil.fromMilliseconds(((Timestamp)value).getTime()).toLocalDate(); + } + if (value instanceof Date) { + return TimeUtil.fromDate((Date) value).toLocalDate(); + } + if (value instanceof Number) { + return TimeUtil.fromMilliseconds(((Number)value).longValue()).toLocalDate(); + } + if (value instanceof LocalTime) { + throw new TypeConversionException("Can't convert to date just from time: " + value); + } + + String stringValue = value.toString().trim(); + + if (!StringUtil.containsOnlyDigits(stringValue)) { + // try to parse default string format + return LocalDate.parse(stringValue); + } + + try { + return TimeUtil.fromMilliseconds(Long.parseLong(stringValue)).toLocalDate(); + } catch (NumberFormatException nfex) { + throw new TypeConversionException(value, nfex); + } + + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalDateTimeConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalDateTimeConverter.java new file mode 100644 index 000000000..d26dfd183 --- /dev/null +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalDateTimeConverter.java @@ -0,0 +1,80 @@ +// Copyright (c) 2003-present, Jodd Team (http://jodd.org) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package com.fr.third.jodd.typeconverter.impl; + +import com.fr.third.jodd.time.TimeUtil; +import com.fr.third.jodd.typeconverter.TypeConversionException; +import com.fr.third.jodd.typeconverter.TypeConverter; +import com.fr.third.jodd.util.StringUtil; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Calendar; +import java.util.Date; + +public class LocalDateTimeConverter implements TypeConverter { + @Override + public LocalDateTime convert(Object value) { + if (value == null) { + return null; + } + + if (value instanceof LocalDate) { + return LocalDateTime.of(((LocalDate) value), LocalTime.MIDNIGHT); + } + if (value instanceof Calendar) { + return TimeUtil.fromCalendar((Calendar) value); + } + if (value instanceof Timestamp) { + return TimeUtil.fromMilliseconds(((Timestamp)value).getTime()); + } + if (value instanceof Date) { + return TimeUtil.fromDate((Date) value); + } + if (value instanceof Number) { + return TimeUtil.fromMilliseconds(((Number)value).longValue()); + } + if (value instanceof LocalTime) { + throw new TypeConversionException("Can't convert to date just from time: " + value); + } + + String stringValue = value.toString().trim(); + + if (!StringUtil.containsOnlyDigits(stringValue)) { + // try to parse default string format + return LocalDateTime.parse(stringValue); + } + + try { + return TimeUtil.fromMilliseconds(Long.parseLong(stringValue)); + } catch (NumberFormatException nfex) { + throw new TypeConversionException(value, nfex); + } + + } +} diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/JDateTimeConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalTimeConverter.java similarity index 57% rename from fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/JDateTimeConverter.java rename to fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalTimeConverter.java index 1d153c475..42982c1fe 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/JDateTimeConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocalTimeConverter.java @@ -25,72 +25,56 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.datetime.DateTimeStamp; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.datetime.JulianDateStamp; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.datetime.DateTimeStamp; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.datetime.JulianDateStamp; +import com.fr.third.jodd.time.TimeUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.Calendar; import java.util.Date; -/** - * Converts object to {@link JDateTime}. - * Conversion rules: - *
      - *
    • null value is returned as null
    • - *
    • object of destination type is simply casted
    • - *
    • Calendar object is converted
    • - *
    • Date object is converted
    • - *
    • Number is used as number of milliseconds
    • - *
    • finally, if string value contains only numbers it is parsed as milliseconds; - * otherwise as JDateTime pattern
    • - *
    - */ -public class JDateTimeConverter implements TypeConverter { - - public JDateTime convert(Object value) { +public class LocalTimeConverter implements TypeConverter { + @Override + public LocalTime convert(Object value) { if (value == null) { return null; } - if (value instanceof JDateTime) { - return (JDateTime) value; + if (value instanceof LocalDateTime) { + return ((LocalDateTime)value).toLocalTime(); } if (value instanceof Calendar) { - return new JDateTime((Calendar)value); + return TimeUtil.fromCalendar((Calendar) value).toLocalTime(); + } + if (value instanceof Timestamp) { + return TimeUtil.fromMilliseconds(((Timestamp)value).getTime()).toLocalTime(); } if (value instanceof Date) { - return new JDateTime((Date) value); + return TimeUtil.fromDate((Date) value).toLocalTime(); } - if (value instanceof Number) { - return new JDateTime(((Number) value).longValue()); + return TimeUtil.fromMilliseconds(((Number)value).longValue()).toLocalTime(); } - - if (value instanceof JulianDateStamp) { - return new JDateTime((JulianDateStamp) value); - } - if (value instanceof DateTimeStamp) { - return new JDateTime((DateTimeStamp) value); + if (value instanceof LocalDate) { + throw new TypeConversionException("Can't convert to time just from date: " + value); } String stringValue = value.toString().trim(); if (!StringUtil.containsOnlyDigits(stringValue)) { - return new JDateTime(stringValue, JDateTime.DEFAULT_FORMAT); + // try to parse default string format + return LocalTime.parse(stringValue); } + try { - long milliseconds = Long.parseLong(stringValue); - return new JDateTime(milliseconds); + return TimeUtil.fromMilliseconds(Long.parseLong(stringValue)).toLocalTime(); } catch (NumberFormatException nfex) { throw new TypeConversionException(value, nfex); } - } + } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocaleConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocaleConverter.java index 3a60d6651..5ceb40b61 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocaleConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LocaleConverter.java @@ -25,9 +25,7 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.LocaleUtil; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.util.LocaleUtil; import java.util.Locale; @@ -41,7 +39,8 @@ import java.util.Locale; */ public class LocaleConverter implements TypeConverter { - public Locale convert(Object value) { + @Override + public Locale convert(final Object value) { if (value == null) { return null; } @@ -50,7 +49,7 @@ public class LocaleConverter implements TypeConverter { return (Locale) value; } - return LocaleUtil.getLocale(value.toString()); + return Locale.forLanguageTag(value.toString()); } } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LongArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LongArrayConverter.java index 1e7cf22b2..94cf2be74 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LongArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LongArrayConverter.java @@ -25,32 +25,31 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.StringUtil; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * Converts given object to long[]. */ public class LongArrayConverter implements TypeConverter { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; - public LongArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - this.typeConverterManagerBean = typeConverterManagerBean; + public LongArrayConverter(final TypeConverterManager typeConverterManager) { + this.typeConverterManager = typeConverterManager; } - public long[] convert(Object value) { + @Override + public long[] convert(final Object value) { if (value == null) { return null; } - Class valueClass = value.getClass(); + final Class valueClass = value.getClass(); if (!valueClass.isArray()) { // source is not an array @@ -64,14 +63,14 @@ public class LongArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected long convertType(Object value) { - return typeConverterManagerBean.convertType(value, long.class).longValue(); + protected long convertType(final Object value) { + return typeConverterManager.convertType(value, long.class).longValue(); } /** * Creates an array with single element. */ - protected long[] convertToSingleElementArray(Object value) { + protected long[] convertToSingleElementArray(final Object value) { return new long[] {convertType(value)}; } @@ -79,26 +78,14 @@ public class LongArrayConverter implements TypeConverter { * Converts non-array value to array. Detects various * collection types and iterates them to make conversion * and to create target array. - */ - protected long[] convertValueToArray(Object value) { - if (value instanceof List) { - List list = (List) value; - long[] target = new long[list.size()]; - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - - return target; - } - + */ + protected long[] convertValueToArray(final Object value) { if (value instanceof Collection) { - Collection collection = (Collection) value; - long[] target = new long[collection.size()]; + final Collection collection = (Collection) value; + final long[] target = new long[collection.size()]; int i = 0; - for (Object element : collection) { + for (final Object element : collection) { target[i] = convertType(element); i++; } @@ -107,19 +94,19 @@ public class LongArrayConverter implements TypeConverter { } if (value instanceof Iterable) { - Iterable iterable = (Iterable) value; + final Iterable iterable = (Iterable) value; - ArrayList longArrayList = new ArrayList(); + final ArrayList longArrayList = new ArrayList<>(); - for (Object element : iterable) { - long convertedValue = convertType(element); + for (final Object element : iterable) { + final long convertedValue = convertType(element); longArrayList.add(Long.valueOf(convertedValue)); } - long[] array = new long[longArrayList.size()]; + final long[] array = new long[longArrayList.size()]; for (int i = 0; i < longArrayList.size(); i++) { - Long l = longArrayList.get(i); + final Long l = longArrayList.get(i); array[i] = l.longValue(); } @@ -127,7 +114,7 @@ public class LongArrayConverter implements TypeConverter { } if (value instanceof CharSequence) { - String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); + final String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); return convertArrayToArray(strings); } @@ -138,22 +125,16 @@ public class LongArrayConverter implements TypeConverter { /** * Converts array value to array. */ - protected long[] convertArrayToArray(Object value) { - Class valueComponentType = value.getClass().getComponentType(); - - if (valueComponentType == long.class) { - // equal types, no conversion needed - return (long[]) value; - } + protected long[] convertArrayToArray(final Object value) { + final Class valueComponentType = value.getClass().getComponentType(); - long[] result; + final long[] result; if (valueComponentType.isPrimitive()) { - // convert primitive array to target array result = convertPrimitiveArrayToArray(value, valueComponentType); } else { // convert object array to target array - Object[] array = (Object[]) value; + final Object[] array = (Object[]) value; result = new long[array.length]; for (int i = 0; i < array.length; i++) { @@ -168,57 +149,57 @@ public class LongArrayConverter implements TypeConverter { /** * Converts primitive array to target array. */ - protected long[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected long[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { long[] result = null; - if (primitiveComponentType == long[].class) { + if (primitiveComponentType == long.class) { return (long[]) value; } if (primitiveComponentType == int.class) { - int[] array = (int[]) value; + final int[] array = (int[]) value; result = new long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == float.class) { - float[] array = (float[]) value; + final float[] array = (float[]) value; result = new long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (long) array[i]; } } else if (primitiveComponentType == double.class) { - double[] array = (double[]) value; + final double[] array = (double[]) value; result = new long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (long) array[i]; } } else if (primitiveComponentType == short.class) { - short[] array = (short[]) value; + final short[] array = (short[]) value; result = new long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == byte.class) { - byte[] array = (byte[]) value; + final byte[] array = (byte[]) value; result = new long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == char.class) { - char[] array = (char[]) value; + final char[] array = (char[]) value; result = new long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == boolean.class) { - boolean[] array = (boolean[]) value; + final boolean[] array = (boolean[]) value; result = new long[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i] ? 1 : 0; diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LongConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LongConverter.java index 1306752b6..c4043e7a6 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LongConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/LongConverter.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; @@ -42,7 +41,7 @@ import com.fr.third.jodd.util.StringUtil; */ public class LongConverter implements TypeConverter { - public Long convert(Object value) { + public Long convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableByteConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableByteConverter.java index 25b04abd5..54a6762f4 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableByteConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableByteConverter.java @@ -25,10 +25,9 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.mutable.MutableByte; import com.fr.third.jodd.mutable.MutableByte; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; /** * Converts given object to {@link MutableByte}. @@ -38,11 +37,12 @@ public class MutableByteConverter implements TypeConverter { protected final TypeConverter typeConverter; @SuppressWarnings("unchecked") - public MutableByteConverter(TypeConverterManagerBean typeConverterManagerBean) { - typeConverter = typeConverterManagerBean.lookup(Byte.class); + public MutableByteConverter(final TypeConverterManager typeConverterManager) { + typeConverter = typeConverterManager.lookup(Byte.class); } - public MutableByte convert(Object value) { + @Override + public MutableByte convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableDoubleConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableDoubleConverter.java index 94b15d035..7866f14b4 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableDoubleConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableDoubleConverter.java @@ -25,10 +25,9 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.mutable.MutableDouble; import com.fr.third.jodd.mutable.MutableDouble; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; /** * Converts given object to {@link MutableDouble}. @@ -38,11 +37,12 @@ public class MutableDoubleConverter implements TypeConverter { protected final TypeConverter typeConverter; @SuppressWarnings("unchecked") - public MutableDoubleConverter(TypeConverterManagerBean typeConverterManagerBean) { - typeConverter = typeConverterManagerBean.lookup(Double.class); + public MutableDoubleConverter(final TypeConverterManager typeConverterManager) { + typeConverter = typeConverterManager.lookup(Double.class); } - public MutableDouble convert(Object value) { + @Override + public MutableDouble convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableFloatConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableFloatConverter.java index f6cf037a4..0b804f25f 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableFloatConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableFloatConverter.java @@ -25,10 +25,9 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.mutable.MutableFloat; import com.fr.third.jodd.mutable.MutableFloat; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; /** * Converts given object to {@link MutableFloat}. @@ -38,15 +37,16 @@ public class MutableFloatConverter implements TypeConverter { protected final TypeConverter typeConverter; @SuppressWarnings("unchecked") - public MutableFloatConverter(TypeConverterManagerBean typeConverterManagerBean) { - typeConverter = typeConverterManagerBean.lookup(Float.class); + public MutableFloatConverter(final TypeConverterManager typeConverterManager) { + typeConverter = typeConverterManager.lookup(Float.class); } - public MutableFloat convert(Object value) { + @Override + public MutableFloat convert(final Object value) { if (value == null) { return null; } - + if (value.getClass() == MutableFloat.class) { return (MutableFloat) value; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableIntegerConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableIntegerConverter.java index 8ec1bef09..85940507b 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableIntegerConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableIntegerConverter.java @@ -25,10 +25,9 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.mutable.MutableInteger; import com.fr.third.jodd.mutable.MutableInteger; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; /** * Converts given object to an {@link MutableInteger}. @@ -38,11 +37,12 @@ public class MutableIntegerConverter implements TypeConverter { protected final TypeConverter typeConverter; @SuppressWarnings("unchecked") - public MutableIntegerConverter(TypeConverterManagerBean typeConverterManagerBean) { - typeConverter = typeConverterManagerBean.lookup(Integer.class); + public MutableIntegerConverter(final TypeConverterManager typeConverterManager) { + typeConverter = typeConverterManager.lookup(Integer.class); } - public MutableInteger convert(Object value) { + @Override + public MutableInteger convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableLongConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableLongConverter.java index ac303dcd2..075f00959 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableLongConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableLongConverter.java @@ -25,10 +25,9 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.mutable.MutableLong; import com.fr.third.jodd.mutable.MutableLong; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; /** * Converts given object to a {@link MutableLong}. @@ -38,11 +37,12 @@ public class MutableLongConverter implements TypeConverter { protected final TypeConverter typeConverter; @SuppressWarnings("unchecked") - public MutableLongConverter(TypeConverterManagerBean typeConverterManagerBean) { - typeConverter = typeConverterManagerBean.lookup(Long.class); + public MutableLongConverter(final TypeConverterManager typeConverterManager) { + typeConverter = typeConverterManager.lookup(Long.class); } - public MutableLong convert(Object value) { + @Override + public MutableLong convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableShortConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableShortConverter.java index 7c46b0c2d..14f828ebb 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableShortConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/MutableShortConverter.java @@ -25,10 +25,9 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.mutable.MutableShort; import com.fr.third.jodd.mutable.MutableShort; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; /** * Converts given object to {@link MutableShort}. @@ -38,11 +37,12 @@ public class MutableShortConverter implements TypeConverter { protected final TypeConverter typeConverter; @SuppressWarnings("unchecked") - public MutableShortConverter(TypeConverterManagerBean typeConverterManagerBean) { - typeConverter = typeConverterManagerBean.lookup(Short.class); + public MutableShortConverter(final TypeConverterManager typeConverterManager) { + typeConverter = typeConverterManager.lookup(Short.class); } - public MutableShort convert(Object value) { + @Override + public MutableShort convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ShortArrayConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ShortArrayConverter.java index 6979a3e34..076789c6a 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ShortArrayConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ShortArrayConverter.java @@ -25,32 +25,31 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConverter; -import com.fr.third.jodd.typeconverter.TypeConverterManagerBean; +import com.fr.third.jodd.typeconverter.TypeConverterManager; import com.fr.third.jodd.util.StringUtil; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * Converts given object to short[]. */ public class ShortArrayConverter implements TypeConverter { - protected final TypeConverterManagerBean typeConverterManagerBean; + protected final TypeConverterManager typeConverterManager; - public ShortArrayConverter(TypeConverterManagerBean typeConverterManagerBean) { - this.typeConverterManagerBean = typeConverterManagerBean; + public ShortArrayConverter(final TypeConverterManager typeConverterManager) { + this.typeConverterManager = typeConverterManager; } - public short[] convert(Object value) { + @Override + public short[] convert(final Object value) { if (value == null) { return null; } - Class valueClass = value.getClass(); + final Class valueClass = value.getClass(); if (!valueClass.isArray()) { // source is not an array @@ -64,14 +63,14 @@ public class ShortArrayConverter implements TypeConverter { /** * Converts type using type converter manager. */ - protected short convertType(Object value) { - return typeConverterManagerBean.convertType(value, short.class).shortValue(); + protected short convertType(final Object value) { + return typeConverterManager.convertType(value, short.class).shortValue(); } /** * Creates an array with single element. */ - protected short[] convertToSingleElementArray(Object value) { + protected short[] convertToSingleElementArray(final Object value) { return new short[] {convertType(value)}; } @@ -79,26 +78,14 @@ public class ShortArrayConverter implements TypeConverter { * Converts non-array value to array. Detects various * collection types and iterates them to make conversion * and to create target array. - */ - protected short[] convertValueToArray(Object value) { - if (value instanceof List) { - List list = (List) value; - short[] target = new short[list.size()]; - - for (int i = 0; i < list.size(); i++) { - Object element = list.get(i); - target[i] = convertType(element); - } - - return target; - } - + */ + protected short[] convertValueToArray(final Object value) { if (value instanceof Collection) { - Collection collection = (Collection) value; - short[] target = new short[collection.size()]; + final Collection collection = (Collection) value; + final short[] target = new short[collection.size()]; int i = 0; - for (Object element : collection) { + for (final Object element : collection) { target[i] = convertType(element); i++; } @@ -107,19 +94,19 @@ public class ShortArrayConverter implements TypeConverter { } if (value instanceof Iterable) { - Iterable iterable = (Iterable) value; + final Iterable iterable = (Iterable) value; - ArrayList shortArrayList = new ArrayList(); + final ArrayList shortArrayList = new ArrayList<>(); - for (Object element : iterable) { - short convertedValue = convertType(element); + for (final Object element : iterable) { + final short convertedValue = convertType(element); shortArrayList.add(Short.valueOf(convertedValue)); } - short[] array = new short[shortArrayList.size()]; + final short[] array = new short[shortArrayList.size()]; for (int i = 0; i < shortArrayList.size(); i++) { - Short s = shortArrayList.get(i); + final Short s = shortArrayList.get(i); array[i] = s.shortValue(); } @@ -127,7 +114,7 @@ public class ShortArrayConverter implements TypeConverter { } if (value instanceof CharSequence) { - String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); + final String[] strings = StringUtil.splitc(value.toString(), ArrayConverter.NUMBER_DELIMITERS); return convertArrayToArray(strings); } @@ -138,22 +125,16 @@ public class ShortArrayConverter implements TypeConverter { /** * Converts array value to array. */ - protected short[] convertArrayToArray(Object value) { - Class valueComponentType = value.getClass().getComponentType(); - - if (valueComponentType == short.class) { - // equal types, no conversion needed - return (short[]) value; - } + protected short[] convertArrayToArray(final Object value) { + final Class valueComponentType = value.getClass().getComponentType(); - short[] result; + final short[] result; if (valueComponentType.isPrimitive()) { - // convert primitive array to target array result = convertPrimitiveArrayToArray(value, valueComponentType); } else { // convert object array to target array - Object[] array = (Object[]) value; + final Object[] array = (Object[]) value; result = new short[array.length]; for (int i = 0; i < array.length; i++) { @@ -168,57 +149,57 @@ public class ShortArrayConverter implements TypeConverter { /** * Converts primitive array to target array. */ - protected short[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) { + protected short[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) { short[] result = null; - if (primitiveComponentType == short[].class) { + if (primitiveComponentType == short.class) { return (short[]) value; } if (primitiveComponentType == int.class) { - int[] array = (int[]) value; + final int[] array = (int[]) value; result = new short[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (short) array[i]; } } else if (primitiveComponentType == long.class) { - long[] array = (long[]) value; + final long[] array = (long[]) value; result = new short[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (short) array[i]; } } else if (primitiveComponentType == float.class) { - float[] array = (float[]) value; + final float[] array = (float[]) value; result = new short[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (short) array[i]; } } else if (primitiveComponentType == double.class) { - double[] array = (double[]) value; + final double[] array = (double[]) value; result = new short[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (short) array[i]; } } else if (primitiveComponentType == byte.class) { - byte[] array = (byte[]) value; + final byte[] array = (byte[]) value; result = new short[array.length]; for (int i = 0; i < array.length; i++) { result[i] = array[i]; } } else if (primitiveComponentType == char.class) { - char[] array = (char[]) value; + final char[] array = (char[]) value; result = new short[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (short) array[i]; } } else if (primitiveComponentType == boolean.class) { - boolean[] array = (boolean[]) value; + final boolean[] array = (boolean[]) value; result = new short[array.length]; for (int i = 0; i < array.length; i++) { result[i] = (short) (array[i] ? 1 : 0); diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ShortConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ShortConverter.java index 6b888cf8b..834d413e4 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ShortConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/ShortConverter.java @@ -25,7 +25,6 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.util.StringUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; @@ -42,7 +41,8 @@ import com.fr.third.jodd.util.StringUtil; */ public class ShortConverter implements TypeConverter { - public Short convert(Object value) { + @Override + public Short convert(final Object value) { if (value == null) { return null; } diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/SqlDateConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/SqlDateConverter.java index 6dcc84df5..7932e47c0 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/SqlDateConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/SqlDateConverter.java @@ -25,14 +25,15 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.datetime.JDateTime; +import com.fr.third.jodd.time.JulianDate; +import com.fr.third.jodd.time.TimeUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; import java.sql.Date; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Calendar; @@ -44,19 +45,21 @@ import java.util.Calendar; *
  • object of destination type is simply casted
  • *
  • Calendar object is converted
  • *
  • Date object is converted
  • - *
  • JDateTime object is converted
  • + *
  • JulianDate object is converted
  • + *
  • LocalDateTime object is converted
  • + *
  • LocalDate object is converted
  • *
  • Number is used as number of milliseconds
  • - *
  • finally, if string value contains only numbers it is parsed as milliseconds; - * otherwise as JDateTime pattern
  • + *
  • finally, if string value contains only numbers it is parsed as milliseconds
  • * */ public class SqlDateConverter implements TypeConverter { - public Date convert(Object value) { + @Override + public Date convert(final Object value) { if (value == null) { return null; } - + if (value instanceof Date) { return (Date) value; } @@ -66,15 +69,20 @@ public class SqlDateConverter implements TypeConverter { if (value instanceof java.util.Date) { return new Date(((java.util.Date)value).getTime()); } - if (value instanceof JDateTime) { - return ((JDateTime) value).convertToSqlDate(); + if (value instanceof JulianDate) { + return new Date(((JulianDate) value).toMilliseconds()); + } + if (value instanceof LocalDateTime) { + return new Date(TimeUtil.toMilliseconds((LocalDateTime)value)); + } + if (value instanceof LocalDate) { + return new Date(TimeUtil.toMilliseconds((LocalDate)value)); } - if (value instanceof Number) { return new Date(((Number) value).longValue()); } - String stringValue = value.toString().trim(); + final String stringValue = value.toString().trim(); // try yyyy-mm-dd for valueOf if (!StringUtil.containsOnlyDigits(stringValue)) { diff --git a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/SqlTimeConverter.java b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/SqlTimeConverter.java index c10abeb7f..e1bae488d 100644 --- a/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/SqlTimeConverter.java +++ b/fine-jodd/src/main/java/com/fr/third/jodd/typeconverter/impl/SqlTimeConverter.java @@ -25,14 +25,15 @@ package com.fr.third.jodd.typeconverter.impl; -import com.fr.third.jodd.datetime.JDateTime; -import com.fr.third.jodd.util.StringUtil; -import com.fr.third.jodd.datetime.JDateTime; +import com.fr.third.jodd.time.JulianDate; +import com.fr.third.jodd.time.TimeUtil; import com.fr.third.jodd.typeconverter.TypeConversionException; import com.fr.third.jodd.typeconverter.TypeConverter; import com.fr.third.jodd.util.StringUtil; import java.sql.Time; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Calendar; import java.util.Date; @@ -44,19 +45,21 @@ import java.util.Date; *
  • object of destination type is simply casted
  • *
  • Calendar object is converted
  • *
  • Date object is converted
  • - *
  • JDateTime object is converted
  • + *
  • JulianDate object is converted
  • + *
  • LocalDateTime object is converted
  • + *
  • LocalDate object is converted
  • *
  • Number is used as number of milliseconds
  • - *
  • finally, if string value contains only numbers it is parsed as milliseconds; - * otherwise as JDateTime pattern
  • + *
  • finally, if string value contains only numbers it is parsed as milliseconds
  • * */ public class SqlTimeConverter implements TypeConverter