Browse Source

Merge pull request #3979 in CORE/base-third from final/10.0 to persist/10.0

* commit '4d7a5eb8a541b67a9f9d3fc6742be4f62997086f':
  KERNEL-9224	refactor: 升级Jodd到5.0.4以上版本
persist/10.0 10.0.19.2021.11.11
superman 3 years ago
parent
commit
99096599f9
  1. 205
      fine-jodd/src/main/java/com/fr/third/jodd/Jodd.java
  2. 35
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanCopy.java
  3. 11
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanException.java
  4. 37
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanProperty.java
  5. 22
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanTemplateParser.java
  6. 9
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtil.java
  7. 64
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilBean.java
  8. 83
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanUtilUtil.java
  9. 57
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitor.java
  10. 47
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanVisitorImplBase.java
  11. 12
      fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanWalker.java
  12. 166
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastBooleanBuffer.java
  13. 166
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastByteBuffer.java
  14. 211
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastCharBuffer.java
  15. 166
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastDoubleBuffer.java
  16. 165
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastFloatBuffer.java
  17. 166
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastIntBuffer.java
  18. 166
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastLongBuffer.java
  19. 165
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/FastShortBuffer.java
  20. 4
      fine-jodd/src/main/java/com/fr/third/jodd/buffer/package-info.java
  21. 137
      fine-jodd/src/main/java/com/fr/third/jodd/cache/AbstractCacheMap.java
  22. 29
      fine-jodd/src/main/java/com/fr/third/jodd/cache/Cache.java
  23. 10
      fine-jodd/src/main/java/com/fr/third/jodd/cache/FIFOCache.java
  24. 153
      fine-jodd/src/main/java/com/fr/third/jodd/cache/FileCache.java
  25. 114
      fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLFUCache.java
  26. 74
      fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLRUCache.java
  27. 13
      fine-jodd/src/main/java/com/fr/third/jodd/cache/LFUCache.java
  28. 9
      fine-jodd/src/main/java/com/fr/third/jodd/cache/LRUCache.java
  29. 38
      fine-jodd/src/main/java/com/fr/third/jodd/cache/NoCache.java
  30. 6
      fine-jodd/src/main/java/com/fr/third/jodd/cache/TimedCache.java
  31. 205
      fine-jodd/src/main/java/com/fr/third/jodd/cache/TypeCache.java
  32. 298
      fine-jodd/src/main/java/com/fr/third/jodd/chalk/Chalk.java
  33. 123
      fine-jodd/src/main/java/com/fr/third/jodd/chalk/Chalk256.java
  34. 6
      fine-jodd/src/main/java/com/fr/third/jodd/chalk/package-info.java
  35. 233
      fine-jodd/src/main/java/com/fr/third/jodd/cli/Cli.java
  36. 17
      fine-jodd/src/main/java/com/fr/third/jodd/cli/CliException.java
  37. 96
      fine-jodd/src/main/java/com/fr/third/jodd/cli/Option.java
  38. 84
      fine-jodd/src/main/java/com/fr/third/jodd/cli/Param.java
  39. 13
      fine-jodd/src/main/java/com/fr/third/jodd/cli/package-info.java
  40. 47
      fine-jodd/src/main/java/com/fr/third/jodd/core/JoddCore.java
  41. 2
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/DateTimeStamp.java
  42. 2
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/JDateTime.java
  43. 4
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/JDateTimeDefault.java
  44. 3
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/JStopWatch.java
  45. 2
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/JulianDateStamp.java
  46. 3
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/Period.java
  47. 3
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeUtil.java
  48. 3
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/TimeZoneUtil.java
  49. 5
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/AbstractFormatter.java
  50. 3
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/Iso8601JdtFormatter.java
  51. 5
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormat.java
  52. 4
      fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/JdtFormatter.java
  53. 132
      fine-jodd/src/main/java/com/fr/third/jodd/exception/ExceptionUtil.java
  54. 64
      fine-jodd/src/main/java/com/fr/third/jodd/exception/UncheckedException.java
  55. 113
      fine-jodd/src/main/java/com/fr/third/jodd/format/Printf.java
  56. 766
      fine-jodd/src/main/java/com/fr/third/jodd/format/PrintfFormat.java
  57. 25
      fine-jodd/src/main/java/com/fr/third/jodd/inex/InExRuleMatcher.java
  58. 54
      fine-jodd/src/main/java/com/fr/third/jodd/inex/InExRules.java
  59. 4
      fine-jodd/src/main/java/com/fr/third/jodd/inex/package-info.java
  60. 51
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/CachingIntrospector.java
  61. 83
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassDescriptor.java
  62. 32
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/ClassIntrospector.java
  63. 9
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/CtorDescriptor.java
  64. 6
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/Ctors.java
  65. 4
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/Descriptor.java
  66. 63
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/FieldDescriptor.java
  67. 44
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/Fields.java
  68. 51
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/Getter.java
  69. 33
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/Mapper.java
  70. 37
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/MapperFunction.java
  71. 57
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/MapperFunctionInstances.java
  72. 133
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodDescriptor.java
  73. 38
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/MethodParamDescriptor.java
  74. 47
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/Methods.java
  75. 32
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/Properties.java
  76. 35
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/PropertyDescriptor.java
  77. 49
      fine-jodd/src/main/java/com/fr/third/jodd/introspector/Setter.java
  78. 121
      fine-jodd/src/main/java/com/fr/third/jodd/io/AppendableWriter.java
  79. 9
      fine-jodd/src/main/java/com/fr/third/jodd/io/CharBufferReader.java
  80. 60
      fine-jodd/src/main/java/com/fr/third/jodd/io/FastByteArrayOutputStream.java
  81. 43
      fine-jodd/src/main/java/com/fr/third/jodd/io/FastCharArrayWriter.java
  82. 79
      fine-jodd/src/main/java/com/fr/third/jodd/io/FileNameUtil.java
  83. 1694
      fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtil.java
  84. 100
      fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtilParams.java
  85. 105
      fine-jodd/src/main/java/com/fr/third/jodd/io/NetUtil.java
  86. 21
      fine-jodd/src/main/java/com/fr/third/jodd/io/PathUtil.java
  87. 8
      fine-jodd/src/main/java/com/fr/third/jodd/io/StreamGobbler.java
  88. 623
      fine-jodd/src/main/java/com/fr/third/jodd/io/StreamUtil.java
  89. 4
      fine-jodd/src/main/java/com/fr/third/jodd/io/UnicodeInputStream.java
  90. 36
      fine-jodd/src/main/java/com/fr/third/jodd/io/ZipBuilder.java
  91. 66
      fine-jodd/src/main/java/com/fr/third/jodd/io/ZipUtil.java
  92. 584
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassFinder.java
  93. 601
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassScanner.java
  94. 58
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileExtensionComparator.java
  95. 54
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileLastModifiedTimeComparator.java
  96. 57
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FileNameComparator.java
  97. 435
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFile.java
  98. 9
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FindFileException.java
  99. 53
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/FolderFirstComparator.java
  100. 24
      fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/RegExpFindFile.java
  101. Some files were not shown because too many files have changed in this diff Show More

205
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.
* <p>
* 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 <code>init()</code>
* that register the module here. This method should be used when modules
* can not be found by classloader.
* <p>
* Important: static block and init methods <b>must</b> be declared <b>last</b>
* in the module class! Also, if module class contains some default instance
* (as part of the module's configuration), this instance must <b>not</b>
* 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 <code>true</code> if module is loaded.
*/
public static boolean isModuleLoaded(int moduleNdx) {
return MODULES[moduleNdx] != null;
}
/**
* Returns module instance if module is loaded. It may return:
* <ul>
* <li>null - when module is not registered</li>
* <li>class - when module is registered, but not yet loaded</li>
* <li>object - when module is registered and loaded</li>
* </ul>
* 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";
}

35
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<BeanCopy> {
* Creates new BeanCopy process between the source and the destination.
* Both source and destination can be a POJO object or a <code>Map</code>.
*/
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<BeanCopy> {
* Simple static factory for <code>BeanCopy</code>.
* @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 <copy>BeanCopy</copy> with given POJO bean as a source.
* Creates <code>BeanCopy</code> 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 <copy>BeanCopy</copy> with given <code>Map</code> as a source.
* Creates <code>BeanCopy</code> with given <code>Map</code> 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<BeanCopy> {
/**
* 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<BeanCopy> {
/**
* 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<BeanCopy> {
/**
* 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<BeanCopy> {
/**
* 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<BeanCopy> {
* Defines if all properties should be copied (when set to <code>true</code>)
* or only public (when set to <code>false</code>, 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<BeanCopy> {
/**
* 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;
}
@ -176,9 +175,9 @@ public class BeanCopy extends BeanVisitorImplBase<BeanCopy> {
* 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);

11
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);
}

37
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;
}

22
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 <code>MacroResolver</code>.
*/
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 <code>MacroResolver</code>.
*/
public static MacroResolver createBeanMacroResolver(final Object context) {
return new MacroResolver() {
public String resolve(String 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();
}
};
});
}
}

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

64
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, <code>null</code> 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> T getSimpleProperty(Object bean, String property) {
public <T> 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> T getIndexProperty(Object bean, String property, int index) {
public <T> 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> T getProperty(Object bean, String name) {
public <T> 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;

83
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,52 +49,38 @@ 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 <code>TypeConversionException</code>
* 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);
}
@ -107,11 +90,18 @@ abstract class BeanUtilUtil implements BeanUtil {
/**
* 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 <code>null</code>, 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;
@ -160,7 +151,7 @@ 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.
*/
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 <code>-1</code> 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 <code>null</code>.
*/
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 <code>Object.class</code>
* 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 <b>Map</b> 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) {

57
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<String, String> {
/**
* Include/exclude rules.
*/
protected InExRules<String, String> rules = new InExRules<String, String>(this);
protected InExRules<String, String, String> rules = new InExRules<>(this);
/**
* Flag for enabling declared properties, or just public ones.
*/
@ -71,6 +65,10 @@ public abstract class BeanVisitor implements InExRuleMatcher<String, String> {
* 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<String, String> {
/**
* 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<String> names = new ArrayList<String>(propertyDescriptors.length);
final ArrayList<String> 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<String, String> {
}
}
return names.toArray(new String[names.size()]);
return names.toArray(new String[0]);
}
/**
* Returns an array of bean properties. If bean is a <code>Map</code>,
* 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<String, String> {
* 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<String, String> {
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<String, String> {
continue;
}
if (ignoreEmptyString && value instanceof String && StringUtil.isEmpty((String) value)) {
continue;
}
visitProperty(name, value);
}
}
@ -185,7 +187,8 @@ public abstract class BeanVisitor implements InExRuleMatcher<String, String> {
/**
* 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);
}
}

47
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<T> 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 <code>null</code> 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 <code>empty string</code> should be ignored.
*/
public T ignoreEmptyString(final boolean ignoreEmptyString) {
this.ignoreEmptyString = ignoreEmptyString;
return _this();
}
/**
* Defines if all properties should be copied (when set to <code>true</code>)
* or only public (when set to <code>false</code>, 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();
}
}

12
fine-jodd/src/main/java/com/fr/third/jodd/bean/BeanWalker.java

@ -41,18 +41,18 @@ public class BeanWalker extends BeanVisitorImplBase<BeanWalker> {
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<BeanWalker> {
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<BeanWalker> {
}
@Override
protected boolean visitProperty(String name, Object value) {
protected boolean visitProperty(final String name, final Object value) {
callback.visitProperty(name, value);
return true;
}

166
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];
}
}

166
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];
}
}

211
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());
}
}

166
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];
}
}

165
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];
}
}

166
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];
}
}

166
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];
}
}

165
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];
}
}

4
fine-jodd/src/main/java/com/fr/third/jodd/format/package-info.java → 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;
package com.fr.third.jodd.buffer;

137
fine-jodd/src/main/java/com/fr/third/jodd/cache/AbstractCacheMap.java vendored

@ -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<K,V> implements Cache<K,V> {
class CacheObject<K2,V2> {
CacheObject(K2 key, V2 object, long ttl) {
static class CacheObject<K2,V2> {
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<K,V> implements Cache<K,V> {
accessCount++;
return cachedObject;
}
V2 peekObject() {
return cachedObject;
}
}
protected Map<K,CacheObject<K,V>> 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<K,V> implements Cache<K,V> {
/**
* {@inheritDoc}
*/
public int getCacheSize() {
@Override
public int limit() {
return cacheSize;
}
@ -94,7 +94,8 @@ public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {
* Returns default cache timeout or <code>0</code> 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<K,V> implements Cache<K,V> {
/**
* {@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<K,V> implements Cache<K,V> {
/**
* {@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<K,V> co = new CacheObject<K,V>(key, object, timeout);
final CacheObject<K,V> co = createCacheObject(key, object, timeout);
if (timeout != 0) {
existCustomTimeout = true;
}
@ -141,10 +146,13 @@ public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {
cacheMap.put(key, co);
}
finally {
writeLock.unlock();
lock.unlockWrite(stamp);
}
}
protected CacheObject<K, V> createCacheObject(K key, V object, long timeout) {
return new CacheObject<>(key, object, timeout);
}
// ---------------------------------------------------------------- get
@ -168,18 +176,33 @@ public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {
/**
* {@inheritDoc}
*/
public V get(K key) {
readLock.lock();
@Override
public V get(final K key) {
long stamp = lock.readLock();
try {
CacheObject<K,V> co = cacheMap.get(key);
final CacheObject<K,V> 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<K,V> removedCo = cacheMap.remove(key);
if (removedCo != null) {
onRemove(removedCo.key, removedCo.cachedObject);
}
missCount++;
return null;
@ -189,17 +212,10 @@ public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {
return co.getObject();
}
finally {
readLock.unlock();
lock.unlock(stamp);
}
}
/**
* {@inheritDoc}
*/
public Iterator<V> iterator() {
return new CacheValuesIterator<V>(this);
}
// ---------------------------------------------------------------- prune
/**
@ -210,13 +226,14 @@ public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {
/**
* {@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<K,V> implements Cache<K,V> {
/**
* {@inheritDoc}
*/
@Override
public boolean isFull() {
if (cacheSize == 0) {
return false;
@ -232,7 +250,7 @@ public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {
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<K,V> implements Cache<K,V> {
/**
* {@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<K,V> 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<K,V> implements Cache<K,V> {
/**
* {@inheritDoc}
*/
@Override
public boolean isEmpty() {
return size() == 0;
}
/**
* {@inheritDoc}
*/
@Override
public Map<K, V> snapshot(final boolean peek) {
final long stamp = lock.writeLock();
try {
final Map<K, V> 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) {
}
}

29
fine-jodd/src/main/java/com/fr/third/jodd/cache/Cache.java vendored

@ -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<K, V> {
/**
* Returns cache size or <code>0</code> if there is no size limit.
*/
int getCacheSize();
int limit();
/**
* Returns default timeout or <code>0</code> 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<K, V> {
/**
* 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<K, V> {
*/
V get(K key);
/**
* Returns iterator over non-expired values.
*/
Iterator<V> 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<K, V> {
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<K, V> {
* Returns <code>true</code> 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<K, V> snapshot(boolean peek);
default Map<K, V> snapshot() {
return this.snapshot(false);
}
}

10
fine-jodd/src/main/java/com/fr/third/jodd/cache/FIFOCache.java vendored

@ -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<K, V> extends AbstractCacheMap<K, V> {
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<K, CacheObject<K, V>>(cacheSize + 1, 1.0f, false);
cacheMap = new LinkedHashMap<>(cacheSize + 1, 1.0f, false);
}
@ -70,6 +70,7 @@ public class FIFOCache<K, V> extends AbstractCacheMap<K, V> {
CacheObject<K,V> co = values.next();
if (co.isExpired()) {
values.remove();
onRemove(co.key, co.cachedObject);
count++;
}
if (first == null) {
@ -79,6 +80,7 @@ public class FIFOCache<K, V> extends AbstractCacheMap<K, V> {
if (isFull()) {
if (first != null) {
cacheMap.remove(first.key);
onRemove(first.key, first.cachedObject);
count++;
}
}

153
fine-jodd/src/main/java/com/fr/third/jodd/cache/FileCache.java vendored

@ -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<File, byte[]> 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<File, byte[]> createCache();
/**
* Creates CacheObject that updates last access time based on files last modification.
*/
protected AbstractCacheMap.CacheObject<File, byte[]> createFileCacheObject(File fileKey, byte[] object, long timeout) {
return new AbstractCacheMap.CacheObject<File, byte[]>(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;
}
}

114
fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLFUCache.java vendored

@ -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<File, byte[]> 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<File, byte[]>(0, timeout) {
public FileLFUCache(final int maxSize, final int maxFileSize, final long timeout) {
super(maxSize, maxFileSize, timeout);
}
@Override
protected Cache<File, byte[]> createCache() {
return new LFUCache<File, byte[]>(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;
}
};
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;
@Override
protected CacheObject<File, byte[]> createCacheObject(File key, byte[] object, long timeout) {
return createFileCacheObject(key, object, timeout);
}
usedSize += bytes.length;
// put file into cache
// if used size > total, purge() will be invoked
cache.put(file, bytes);
return bytes;
};
}
}

74
fine-jodd/src/main/java/com/fr/third/jodd/cache/CacheValuesIterator.java → fine-jodd/src/main/java/com/fr/third/jodd/cache/FileLRUCache.java vendored

@ -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<V> implements Iterator<V> {
private final Iterator<? extends AbstractCacheMap<?, V>.CacheObject<?, V>> iterator;
public class FileLRUCache extends FileCache {
private AbstractCacheMap<?,V>.CacheObject<?,V> nextValue;
/**
* Creates file LRU cache with specified size. Sets
* {@link #maxFileSize max available file size} to half of this value.
*/
public FileLRUCache(final int maxSize) {
this(maxSize, maxSize / 2, 0);
}
CacheValuesIterator(AbstractCacheMap<?,V> abstractCacheMap) {
iterator = abstractCacheMap.cacheMap.values().iterator();
nextValue();
public FileLRUCache(final int maxSize, final int maxFileSize) {
this(maxSize, maxFileSize, 0);
}
/**
* Resolves next value. If next value doesn't exist, next value will be <code>null</code>.
* 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
*/
private void nextValue() {
while (iterator.hasNext()) {
nextValue = iterator.next();
if (!nextValue.isExpired()) {
return;
}
public FileLRUCache(final int maxSize, final int maxFileSize, final long timeout) {
super(maxSize, maxFileSize, timeout);
}
nextValue = null;
@Override
protected Cache<File, byte[]> createCache() {
return new LRUCache<File, byte[]>(0, timeout) {
@Override
public boolean isFull() {
return usedSize > FileLRUCache.this.maxSize;
}
/**
* Returns <code>true</code> if there are more elements in the cache.
*/
public boolean hasNext() {
return nextValue != null;
@Override
protected boolean isReallyFull(final File file) {
return isFull();
}
/**
* Returns next non-expired element from the cache.
*/
public V next() {
if (!hasNext()) {
throw new NoSuchElementException();
@Override
protected void onRemove(final File key, final byte[] cachedObject) {
usedSize -= cachedObject.length;
}
V cachedObject = nextValue.cachedObject;
nextValue();
return cachedObject;
@Override
protected CacheObject<File, byte[]> createCacheObject(File key, byte[] object, long timeout) {
return createFileCacheObject(key, object, timeout);
}
/**
* Removes current non-expired element from the cache.
*/
public void remove() {
iterator.remove();
};
}
}

13
fine-jodd/src/main/java/com/fr/third/jodd/cache/LFUCache.java vendored

@ -44,14 +44,14 @@ import java.util.Iterator;
*/
public class LFUCache<K,V> extends AbstractCacheMap<K,V> {
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<K, CacheObject<K, V>>(maxSize + 1);
cacheMap = new HashMap<>(maxSize + 1);
}
// ---------------------------------------------------------------- prune
@ -108,11 +108,4 @@ public class LFUCache<K,V> extends AbstractCacheMap<K,V> {
return count;
}
/**
* Callback method invoked on cached object removal.
* By default does nothing.
*/
protected void onRemove(K key, V cachedObject) {
}
}

9
fine-jodd/src/main/java/com/fr/third/jodd/cache/LRUCache.java vendored

@ -50,19 +50,19 @@ import java.util.Iterator;
*/
public class LRUCache<K, V> extends AbstractCacheMap<K, V> {
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<K, CacheObject<K,V>>(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<K, V> extends AbstractCacheMap<K, V> {
/**
* 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;
}
@ -94,6 +94,7 @@ public class LRUCache<K, V> extends AbstractCacheMap<K, V> {
CacheObject<K,V> co = values.next();
if (co.isExpired()) {
values.remove();
onRemove(co.key, co.cachedObject);
count++;
}
}

38
fine-jodd/src/main/java/com/fr/third/jodd/cache/NoCache.java vendored

@ -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<K, V> implements Cache<K, V> {
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<V> 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<K, V> snapshot(final boolean peek) {
return Collections.emptyMap();
}
}

6
fine-jodd/src/main/java/com/fr/third/jodd/cache/TimedCache.java vendored

@ -37,10 +37,10 @@ import java.util.TimerTask;
*/
public class TimedCache<K, V> extends AbstractCacheMap<K, V> {
public TimedCache(long timeout) {
public TimedCache(final long timeout) {
this.cacheSize = 0;
this.timeout = timeout;
cacheMap = new HashMap<K, CacheObject<K, V>>();
cacheMap = new HashMap<>();
}
// ---------------------------------------------------------------- prune
@ -70,7 +70,7 @@ public class TimedCache<K, V> extends AbstractCacheMap<K, V> {
/**
* Schedules prune.
*/
public void schedulePrune(long delay) {
public void schedulePrune(final long delay) {
if (pruneTimer != null) {
pruneTimer.cancel();
}

205
fine-jodd/src/main/java/com/fr/third/jodd/cache/TypeCache.java vendored

@ -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:
* <ul>
* <li>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</li>
* <li>weak - if your key classes are replaced during the runtime, you should use weak map, in order to automatically
* remove obsolete keys.</li>
* </ul>
*/
public class TypeCache<T> {
// ---------------------------------------------------------------- builder
/**
* Creates a type cache by using a builder.
*/
public static <A> Builder<A> create() {
return new Builder<>();
}
/**
* Creates default implementation of the type cache.
*/
@SuppressWarnings("unchecked")
public static <A> TypeCache<A> createDefault() {
return TypeCache.<A>create().get();
}
public static class Builder<A> {
private boolean threadsafe;
private boolean weak;
private boolean none;
/**
* No cache will be used.
* Setting other properties will not have any affect.
*/
public Builder<A> noCache() {
none = true;
return this;
}
/**
* Cache keys will be weak.
*/
public Builder<A> weak(final boolean weak) {
this.weak = weak;
return this;
}
/**
* Cache will be thread-safe.
*/
public Builder<A> threadsafe(final boolean threadsafe) {
this.threadsafe = threadsafe;
return this;
}
/**
* Builds a type cache.
*/
public TypeCache<A> get() {
final Map<Class<?>, A> map;
if (none) {
map = new AbstractMap<Class<?>, 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<Entry<Class<?>, 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<Class<?>, T> map;
private TypeCache(final Map<Class<?>, 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 <K> T get(final Class<K> key, final Function<Class<K>, ? extends T> mappingFunction) {
return map.computeIfAbsent(key, aClass -> mappingFunction.apply((Class<K>) 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<? super T> valueConsumer) {
map.values().forEach(valueConsumer);
}
}

298
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<T extends Chalk<T>> {
/**
* 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));
}
}

123
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<Chalk256> {
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;
}
}

6
fine-jodd/src/main/java/com/fr/third/jodd/util/buffer/package-info.java → 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;
package com.fr.third.jodd.chalk;

233
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<String[]> {
private final List<Option> options = new ArrayList<>();
private final List<Param> params = new ArrayList<>();
public Option option() {
final Option option = new Option();
options.add(option);
return option;
}
public Param param() {
final Param param = new Param();
params.add(param);
return param;
}
private boolean consumeOptionWithLongName(final String input, final String valueToConsume) {
for (final Option option : options) {
if (input.equals(option.longName)) {
if (option.hasArg && valueToConsume == null) {
throw new CliException("Option value not provided for: " + input);
}
option.consumer.accept(option.hasArg ? valueToConsume : input);
return option.hasArg;
}
if (option.longName != null && input.startsWith(option.longName + "=")) {
option.consumer.accept(input.substring(option.longName.length() + 1));
return false;
}
}
throw new CliException("Unknown option: " + input);
}
private boolean consumeOptionWithShortName(final String input, final String valueToConsume) {
for (final Option option : options) {
if (input.equals(option.shortName)) {
if (option.hasArg) {
if (valueToConsume == null) {
throw new CliException("Option value not provided for: " + input);
}
option.consumer.accept(valueToConsume);
return true;
}
else {
option.consumer.accept(input);
return false;
}
}
if (option.shortName != null && input.startsWith(option.shortName + "=")) {
option.consumer.accept(input.substring(option.shortName.length() + 1));
return false;
}
}
throw new CliException("Unknown option: " + input);
}
private void consumeOptionWithShortNameAndNoArguments(final String shortName) {
for (final Option option : options) {
if (shortName.equals(option.shortName) && !option.hasArg) {
option.consumer.accept(shortName);
return;
}
}
throw new CliException("Unknown option: " + shortName);
}
@Override
public void accept(final String... args) {
assertConfigurationIsValid();
boolean dontParseOptionsAnyMore = false;
int i;
int paramsIndex = 0;
for (i = 0; i < args.length; i++) {
final String arg = args[i];
if (arg.isEmpty()) {
continue;
}
final String value = (i + 1 < args.length) ? args[i + 1] : null;
if (arg.equals("--")) {
dontParseOptionsAnyMore = true;
continue;
}
if (!dontParseOptionsAnyMore) {
// long names
if (arg.startsWith("--")) {
final String argLongName = arg.substring(2);
consumeOptionWithLongName(argLongName, value);
args[i] = null;
continue;
}
// short names
if (arg.startsWith("-")) {
final String argShortName = arg.substring(1);
if (argShortName.length() > 1 && argShortName.charAt(1) != '=') {
// compressed options
final char[] allShortNames = argShortName.toCharArray();
for (final char c : allShortNames) {
final String argName = String.valueOf(c);
consumeOptionWithShortNameAndNoArguments(argName);
}
args[i] = null;
continue;
}
final boolean valueConsumed = consumeOptionWithShortName(argShortName, value);
// mark argument as consumed
args[i] = null;
if (valueConsumed) {
// mark value as consumed, too
i++;
args[i] = null;
}
continue;
}
}
// params
if (paramsIndex == params.size()) {
// we are done here
break;
}
final Param param = params.get(paramsIndex++);
final List<String> paramArguments = new ArrayList<>();
int from = 0;
final int to = param.required + param.optional;
for (; from < to; from++, i++) {
final String paramValue = (i < args.length) ? args[i] : null;
if (paramValue == null) {
break;
}
paramArguments.add(paramValue);
}
i--;
if (paramArguments.size() < param.required) {
throw new CliException("Parameter required: " + param.label);
}
if (paramArguments.isEmpty()) {
// parameter not found
continue;
}
param.consumer.accept(paramArguments.toArray(new String[0]));
}
// must check if remaining parameters are not satisfied
while (paramsIndex < params.size()) {
final Param param = params.get(paramsIndex++);
if (param.required > 0) {
throw new CliException("Parameter required: " + param.label);
}
}
}
private void assertConfigurationIsValid() {
for (final Option option : options) {
if (option.consumer == null) {
throw new CliException("Option has no registered consumer: " + option);
}
}
}
/**
* Prints the usage line.
*/
public void printUsage(final String commandName) {
final StringBuilder usage = new StringBuilder(commandName);
for (final Option option : options) {
if (option.shortName != null) {
usage.append(" [-").append(option.shortName).append("]");
} else if (option.longName != null) {
usage.append(" [--").append(option.longName).append("]");
}
}
for (final Param param : params) {
usage.append(" ").append(param.label);
}
System.out.println(usage);
}
}

17
fine-jodd/src/main/java/com/fr/third/jodd/mutable/ValueProvider.java → fine-jodd/src/main/java/com/fr/third/jodd/cli/CliException.java

@ -23,16 +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.mutable;
package com.fr.third.jodd.cli;
/**
* Lazy immutable {@link jodd.mutable.ValueHolder}.
* @param <T> value type
*/
public interface ValueProvider<T> {
import com.fr.third.jodd.exception.UncheckedException;
/**
* Returns value.
/**
* CLI exception.
*/
T value();
public class CliException extends UncheckedException {
public CliException(final String message) {
super(message);
}
}

96
fine-jodd/src/main/java/com/fr/third/jodd/cli/Option.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.cli;
import java.util.function.Consumer;
public class Option {
String label;
String shortName;
String longName;
String description;
boolean hasArg;
String argLabel;
Consumer<String> consumer;
public Option shortName(final String shortName) {
this.shortName = shortName;
return this;
}
public Option longName(final String longName) {
this.longName = longName;
return this;
}
public Option names(final String shortName, final String longName) {
this.shortName = shortName;
this.longName = longName;
return this;
}
public Option description(final String description) {
this.description = description;
return this;
}
public Option hasArg() {
this.hasArg = true;
return this;
}
public Option hasArg(final String argLabel) {
this.hasArg = true;
this.argLabel = argLabel;
return this;
}
public Option label(final String label) {
this.label = label;
return this;
}
public Option with(final Consumer<String> consumer) {
this.consumer = consumer;
return this;
}
@Override
public String toString() {
String out = "";
if (shortName != null) {
out += "-" + shortName;
}
if (longName != null) {
if (!out.isEmpty()) {
out += " | ";
}
out += "--" + longName;
}
return out;
}
}

84
fine-jodd/src/main/java/com/fr/third/jodd/cli/Param.java

@ -0,0 +1,84 @@
// 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.function.Consumer;
public class Param {
String label;
int required = 0;
int optional = 1;
String description;
Consumer<String[]> consumer;
public Param required(final int required) {
this.required = required;
return this;
}
public Param optional(final int optional) {
this.optional = optional;
return this;
}
public Param required() {
this.required = 1;
this.optional = 0;
return this;
}
public Param optional() {
this.required = 0;
this.optional = 1;
return this;
}
public Param all() {
this.optional = 1_000_000; // magic number indicating *ALL* parameters
return this;
}
public Param range(final int required, final int max) {
this.required = required;
this.optional = max - required;
return this;
}
public Param label(final String label) {
this.label = label;
return this;
}
public Param description(final String description) {
this.description = description;
return this;
}
public Param with(final Consumer<String[]> consumer) {
this.consumer = consumer;
return this;
}
}

13
fine-jodd/src/main/java/com/fr/third/jodd/JoddModule.java → fine-jodd/src/main/java/com/fr/third/jodd/cli/package-info.java

@ -23,16 +23,7 @@
// 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;
/**
* Optional interface for Jodd modules.
*/
public interface JoddModule {
/**
* Invoked once when module has been created.
* Small parser for command line arguments.
*/
public void start();
}
package com.fr.third.jodd.cli;

47
fine-jodd/src/main/java/com/fr/third/jodd/core/JoddCore.java

@ -26,53 +26,40 @@
package com.fr.third.jodd.core;
import com.fr.third.jodd.util.StringPool;
import com.fr.third.jodd.util.cl.ClassLoaderStrategy;
import com.fr.third.jodd.util.cl.DefaultClassLoaderStrategy;
import com.fr.third.jodd.Jodd;
import com.fr.third.jodd.io.FileUtilParams;
import com.fr.third.jodd.util.StringPool;
import com.fr.third.jodd.util.cl.ClassLoaderStrategy;
import com.fr.third.jodd.util.cl.DefaultClassLoaderStrategy;
import java.security.Security;
/**
* Jodd CORE module.
* Contains some global defaults.
* Jodd library-wide properties.
*/
public class JoddCore {
static {
// Starting from Java8 u151, the `Unlimited Strength Jurisdiction Policy Files`
// are included with Java, but has to be enabled.
// They are enabled on Java9 by default.
Security.setProperty("crypto.policy", "unlimited");
}
// ---------------------------------------------------------------- settings
/**
* Default temp file prefix.
* Default prefix for temporary files.
*/
public static String tempFilePrefix = "jodd-";
/**
* Default file encoding (UTF8).
* The encoding used across the Jodd classes, "UTF-8" by default.
*/
public static String encoding = StringPool.UTF_8;
/**
* Default IO buffer size (16 KB).
* Buffer size for various I/O operations.
*/
public static int ioBufferSize = 16384;
/**
* Default parameters used in {@link com.fr.third.jodd.io.FileUtil} operations.
*/
public static FileUtilParams fileUtilParams = new FileUtilParams();
/**
* Default class loader strategy.
* Flag that controls the {@code Unsafe} usage (if system detects it). Enabled by default.
*/
public static ClassLoaderStrategy classLoaderStrategy = new DefaultClassLoaderStrategy();
// ---------------------------------------------------------------- module
static {
init();
}
public static void init() {
Jodd.init(JoddCore.class);
}
public static boolean unsafeUsageEnabled = true;
}

2
fine-jodd/src/main/java/com/fr/third/jodd/datetime/DateTimeStamp.java

@ -37,7 +37,9 @@ import java.io.Serializable;
*
* @see JDateTime
* @see JulianDateStamp
* @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类
*/
@Deprecated
public class DateTimeStamp implements Comparable, Serializable, Cloneable {
/**

2
fine-jodd/src/main/java/com/fr/third/jodd/datetime/JDateTime.java

@ -128,7 +128,9 @@ import static com.fr.third.jodd.util.HashCode.hash;
*
* <p>
* More info: <a href="http://en.wikipedia.org/wiki/Julian_Date">Julian Date on Wikipedia</a>
* @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";

4
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 {

3
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 {
/**

2
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 {
/**

3
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;

3
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;

3
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 {
/**

5
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;
* <p>
*
* It is not necessary to have parsers for all patterns.
*
* @deprecated jodd目前版本为5.1.6, 此版本已移除此类, 兼容问题暂不删除此类
*/
@Deprecated
public abstract class AbstractFormatter implements JdtFormatter {
/**

3
fine-jodd/src/main/java/com/fr/third/jodd/datetime/format/Iso8601JdtFormatter.java

@ -67,7 +67,10 @@ import java.util.TimeZone;
* <p>
* 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() {

5
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;

4
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 {
/**

132
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<StackTraceElement> result = new ArrayList<StackTraceElement>(st.length);
ArrayList<StackTraceElement> 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<StackTraceElement[]> result = new ArrayList<StackTraceElement[]>();
public static StackTraceElement[][] getStackTraceChain(Throwable t, final String[] allow, final String[] deny) {
ArrayList<StackTraceElement[]> 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<Throwable> list = new ArrayList<Throwable>();
ArrayList<Throwable> 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;
}
return cause;
t = cause;
}
return throwable;
}
/**
@ -202,7 +213,7 @@ public class ExceptionUtil {
* Otherwise, returns <code>null</code>.
*/
@SuppressWarnings({"unchecked"})
public static <T extends Throwable> T findCause(Throwable throwable, Class<T> cause) {
public static <T extends Throwable> T findCause(Throwable throwable, final Class<T> cause) {
while (throwable != null) {
if (throwable.getClass().equals(cause)) {
return (T) throwable;
@ -220,7 +231,7 @@ public class ExceptionUtil {
* and making it a child of the previous using the <code>setNextException</code>
* method of SQLException.
*/
public static SQLException rollupSqlExceptions(Collection<SQLException> exceptions) {
public static SQLException rollupSqlExceptions(final Collection<SQLException> exceptions) {
SQLException parent = null;
for (SQLException exception : exceptions) {
if (parent != null) {
@ -234,74 +245,57 @@ public class ExceptionUtil {
// ---------------------------------------------------------------- misc
/**
* Throws target of <code>InvocationTargetException</code> 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 <code>non-null</code> 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();
return new RuntimeException(throwable);
}
} catch (InstantiationException iex) {
throw new RuntimeException(iex);
} catch (IllegalAccessException iex) {
throw new RuntimeException(iex);
} finally {
ThrowableThrower.throwable = null;
public static Exception wrapToException(final Throwable throwable) {
if (throwable instanceof Exception) {
return (Exception) throwable;
}
return new RuntimeException(throwable);
}
/**
* Returns <code>non-null</code> 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();
}
return message;
else if (unwrapped instanceof UndeclaredThrowableException) {
unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();
}
private static class ThrowableThrower {
private static Throwable throwable;
ThrowableThrower() throws Throwable {
throw throwable;
else {
return unwrapped;
}
}
}
}

64
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 <code>UncheckedException</code>.
* Unchecked exceptions are not wrapped.
*/
public static RuntimeException wrapChecked(Throwable t) {
if (t instanceof RuntimeException) {
return (RuntimeException) t;
public static <V> V callAndWrapException(final Callable<V> 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 <code>UncheckedException</code>.
* 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 <code>UncheckedException</code>
*/
public static RuntimeException wrap(Throwable t) {
public static RuntimeException wrap(final Throwable t) {
return new UncheckedException(t);
}
/**
* Wraps all exceptions in a <code>UncheckedException</code>
*/
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;
}
}

113
fine-jodd/src/main/java/com/fr/third/jodd/format/Printf.java

@ -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;
}
}

766
fine-jodd/src/main/java/com/fr/third/jodd/format/PrintfFormat.java

@ -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
* <ul>
* <li> a <b>%</b> (required)
*
* <li> a modifier (optional)
* <dl>
* <dt> + <dd> forces display of + for positive numbers
* <dt> ~ <dd> do not count leading + or - in length
* <dt> 0 <dd> show leading zeroes
* <dt> - <dd> align left in the field
* <dt> space <dd> prepend a space in front of positive numbers
* <dt> # <dd> 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.
* <dt> , <dd> groups decimal values by thousands (for 'diuxXb' formats)
* </dl>
*
* <li> an integer denoting field width (optional)
*
* <li> a period (<b>.</b>) followed by an integer denoting precision (optional)
*
* <li> a format descriptor (required)
* <dl>
* <dt>f <dd> floating point number in fixed format,
* <dt>e, E <dd> 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,
* <dt>g, G <dd> 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,.
* <dt>d, i <dd> signed long and integer in decimal,
* <dt>u <dd> unsigned long or integer in decimal,
* <dt>x <dd> unsigned long or integer in hexadecimal,
* <dt>o <dd> unsigned long or integer in octal,
* <dt>b <dd> unsigned long or integer in binary,
* <dt>s <dd> string (actually, <code>toString()</code> value of an object),
* <dt>c <dd> character,
* <dt>l, L <dd> boolean in lower or upper case (for booleans and int/longs),
* <dt>p <dd> identity hash code of an object (pointer :).
* </dl>
* </ul>
*/
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 <code>byte</code> and <code>short</code> 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 <code>IllegalArgumentException</code> with message.
*/
protected IllegalArgumentException newIllegalArgumentException(String allowedFormats) {
return new IllegalArgumentException("Invalid format: '" + fmt + "' is not one of '" + allowedFormats + "'");
}
}

25
fine-jodd/src/main/java/com/fr/third/jodd/util/InExRuleMatcher.java → 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<T, R> {
/**
* {@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<String, String> WILDCARD_RULE_MATCHER = new InExRuleMatcher<String, String>() {
public boolean accept(String value, String rule, boolean include) {
return Wildcard.match(value, rule);
}
};
InExRuleMatcher<String, String> 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<String, String> WILDCARD_PATH_RULE_MATCHER = new InExRuleMatcher<String, String>() {
public boolean accept(String value, String rule, boolean include) {
return Wildcard.matchPath(value, rule);
}
};
InExRuleMatcher<String, String> 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);

54
fine-jodd/src/main/java/com/fr/third/jodd/util/InExRules.java → 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.
* <p>
@ -50,11 +50,18 @@ import java.util.List;
* <p>
* All Jodd classes that filters something uses this class to unify the
* behavior across the Jodd library.
* <p>
* 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<T, R> implements InExRuleMatcher<T, R> {
public class InExRules<V, D, R> implements InExRuleMatcher<V, R> {
public InExRules<String, String, String> create() {
return new InExRules<>();
}
protected List<Rule<R>> rules;
protected final InExRuleMatcher<T, R> inExRuleMatcher;
protected final InExRuleMatcher<V, R> inExRuleMatcher;
protected int includesCount;
protected int excludesCount;
protected boolean blacklist = true;
@ -69,7 +76,7 @@ public class InExRules<T, R> implements InExRuleMatcher<T, R> {
/**
* Creates instance that uses provided matcher.
*/
public InExRules(InExRuleMatcher<T, R> inExRuleMatcher) {
public InExRules(final InExRuleMatcher<V, R> inExRuleMatcher) {
this.inExRuleMatcher = inExRuleMatcher;
}
@ -114,7 +121,7 @@ public class InExRules<T, R> implements InExRuleMatcher<T, R> {
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<T, R> implements InExRuleMatcher<T, R> {
}
@Override
public boolean equals(Object o) {
public boolean equals(final Object o) {
if (this == o) {
return true;
}
@ -156,7 +163,7 @@ public class InExRules<T, R> implements InExRuleMatcher<T, R> {
/**
* 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<T, R> implements InExRuleMatcher<T, R> {
* </ul>
* Should be called <b>after</b> 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<T, R> implements InExRuleMatcher<T, R> {
/**
* 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<Rule<R>>();
rules = new ArrayList<>();
}
if (include) {
@ -248,7 +255,7 @@ public class InExRules<T, R> implements InExRuleMatcher<T, R> {
excludesCount++;
}
Rule<R> newRule = new Rule<R>(rule, include);
Rule<R> newRule = new Rule<>(makeRule(ruleDefinition), include);
if (rules.contains(newRule)) {
return;
@ -257,16 +264,20 @@ public class InExRules<T, R> implements InExRuleMatcher<T, R> {
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<T, R> implements InExRuleMatcher<T, R> {
* 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<T, R> implements InExRuleMatcher<T, R> {
* 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<T, R> implements InExRuleMatcher<T, R> {
/**
* 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<R> rule : rules) {
@ -340,7 +351,7 @@ public class InExRules<T, R> implements InExRuleMatcher<T, R> {
/**
* 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<R> rule : rules) {
@ -362,7 +373,8 @@ public class InExRules<T, R> implements InExRuleMatcher<T, R> {
* Matches value against single rule. By default performs <code>equals</code> 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);
}

4
fine-jodd/src/main/java/com/fr/third/jodd/io/filter/package-info.java → 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;
package com.fr.third.jodd.inex;

51
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 <b>accessible</b> or <b>supported</b> fields/methods/constructors.
* <p>
* It simply caches <b>all</b> class descriptors.
*/
public class CachingIntrospector implements Introspector {
public class CachingIntrospector implements ClassIntrospector {
protected final Map<Class, ClassDescriptor> cache;
protected final TypeCache<ClassDescriptor> 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
* <b>accessible</b> or <b>supported</b> fields, methods or
* constructors.
*/
public CachingIntrospector(boolean scanAccessible, boolean enhancedProperties, boolean includeFieldsAsProperties, String[] propertyFieldPrefix) {
this.cache = new HashMap<Class, ClassDescriptor>();
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,37 +64,21 @@ 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();
}

83
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 <code>true</code> if type is a supplier.
*/
public boolean isSupplier() {
return isSupplier;
}
private boolean isSystemClass;
/**
* Returns <code>true</code> 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 <code>null</code> 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 <code>null</code> 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)) {

32
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();
/**
* Returns class descriptor for specified type.
* Sets default implementation.
*/
public static ClassDescriptor lookup(Class type) {
return JoddIntrospector.introspector.lookup(type);
public static void set(final ClassIntrospector classIntrospector) {
Objects.requireNonNull(classIntrospector);
Implementation.classIntrospector = classIntrospector;
}
}
/**
* Registers new type.
* Returns default implementation.
*/
public static ClassDescriptor register(Class type) {
return JoddIntrospector.introspector.register(type);
static ClassIntrospector get() {
return Implementation.classIntrospector;
}
/**
* Returns class descriptor for specified type.
*/
ClassDescriptor lookup(Class type);
/**
* Clears all cached data.
*/
public static void reset() {
JoddIntrospector.introspector.reset();
}
void reset();
}

9
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);
}
/**

6
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();

4
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 <code>true</code> if descriptor content matches required declared flag.
*/
public boolean matchDeclared(boolean declared) {
public boolean matchDeclared(final boolean declared) {
if (!declared) {
return isPublic;
}

63
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

44
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<String, FieldDescriptor> fieldsMap;
protected final Map<String, FieldDescriptor> 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<String, FieldDescriptor> inspectFields() {
boolean scanAccessible = classDescriptor.isScanAccessible();
Class type = classDescriptor.getType();
private Map<String, FieldDescriptor> 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<String, FieldDescriptor> map = new HashMap<String, FieldDescriptor>(fields.length);
final HashMap<String, FieldDescriptor> 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<String, FieldDescriptor> 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 <code>null</code> 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<FieldDescriptor>() {
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;
}

51
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();

33
fine-jodd/src/main/java/com/fr/third/jodd/introspector/JoddIntrospector.java → 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.
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
*/
public static Introspector introspector = new CachingIntrospector();
// ---------------------------------------------------------------- module
static {
init();
}
public static void init() {
Jodd.init(JoddIntrospector.class);
}
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {
Class<? extends MapperFunction> value();
}

37
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<IN, OUT> extends Function<IN, OUT> {
}

57
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<MapperFunction> typeCache = TypeCache.createDefault();
public MapperFunction lookup(final Class<? extends MapperFunction> mapperFunctionClass) {
return typeCache.get(mapperFunctionClass, (c) -> {
try {
return ClassUtil.newInstance(mapperFunctionClass);
} catch (final Exception ex) {
throw new IllegalArgumentException("Invalid mapper class " + c, ex);
}
});
}
}

133
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
Type[] params = method.getGenericParameterTypes();
Type[] genericParams = method.getGenericParameterTypes();
ClassUtil.forceAccess(method);
rawParameterTypes = new Class[params.length];
rawParameterComponentTypes = genericParams.length == 0 ? null : new Class[params.length];
// mapper
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);
final Mapper mapper = method.getAnnotation(Mapper.class);
if (mapper != null) {
mapperFunction = MapperFunctionInstances.get().lookup(mapper.value());
} else {
mapperFunction = null;
}
// parameters
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 <code>null</code>
* 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

38
fine-jodd/src/main/java/com/fr/third/jodd/introspector/Introspector.java → 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
*/
public interface Introspector {
/**
* Returns the {@link ClassDescriptor} object for specified class.
* Method parameter descriptor.
*/
ClassDescriptor lookup(Class type);
public class MethodParamDescriptor {
protected final Class type;
protected final Class rawType;
protected final Class rawComponentType;
/**
* Registers new class type. If type already registered, it will be
* reset and registered again with new class descriptor.
*/
ClassDescriptor register(Class type);
public MethodParamDescriptor(final Class parameterType, final Class rawParameterType, final Class rawParameterComponentType) {
this.type = parameterType;
this.rawType = rawParameterType;
this.rawComponentType = rawParameterComponentType;
}
/**
* Resets current cache.
*/
void reset();
public Class getType() {
return type;
}
public Class getRawType() {
return rawType;
}
public Class getRawComponentType() {
return rawComponentType;
}
}

47
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<String, MethodDescriptor[]> 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();
HashMap<String, MethodDescriptor[]> map = new HashMap<String, MethodDescriptor[]>(methods.length);
final Method[] methods = scanAccessible ? ClassUtil.getAccessibleMethods(type) : ClassUtil.getSupportedMethods(type);
for (Method method : methods) {
String methodName = method.getName();
final HashMap<String, MethodDescriptor[]> map = new HashMap<>(methods.length);
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 <code>null</code> 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 <code>null</code> 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 <code>null</code> 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<MethodDescriptor> allMethodsList = new ArrayList<MethodDescriptor>();
final List<MethodDescriptor> 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<MethodDescriptor>() {
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;
}

32
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<String, PropertyDescriptor> map = new HashMap<String, PropertyDescriptor>();
HashMap<String, PropertyDescriptor> 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<String, PropertyDescriptor> map, String name, MethodDescriptor methodDescriptor, boolean isSetter) {
protected void addProperty(final HashMap<String, PropertyDescriptor> 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,14 +206,14 @@ 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) {
protected PropertyDescriptor createPropertyDescriptor(final String name, final FieldDescriptor fieldDescriptor) {
return new PropertyDescriptor(classDescriptor, name, fieldDescriptor);
}
@ -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<PropertyDescriptor>() {
public int compare(PropertyDescriptor pd1, PropertyDescriptor pd2) {
@Override
public int compare(final PropertyDescriptor pd1, final PropertyDescriptor pd2) {
return pd1.getName().compareTo(pd2.getName());
}
});

35
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 <code>null</code>
* 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 <code>null</code>
* 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);

49
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();
}

121
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;
}
}
}

9
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) {
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() {
}
}

60
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.
* <p>
* The data can be retrieved using <code>toByteArray()</code> and
* <code>toString()</code>.
* The data can be retrieved using {@link #toByteArray()} and {@link #toString}.
* <p>
* Closing a <code>FastByteArrayOutputStream</code> 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 <code>IOException</code>.
* generating an {@link IOException}.
* <p>
* 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 <code>FastByteArrayOutputStream</code> 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 <code>IOException</code>.
* 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);
}
}

43
fine-jodd/src/main/java/com/fr/third/jodd/io/FastCharArrayWriter.java

@ -25,14 +25,14 @@
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 {
@ -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 <code>FastCharArrayWriter</code> 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 <code>IOException</code>.
* generating an {@link IOException}.
*/
@Override
public void close() {
@ -96,7 +96,7 @@ public class FastCharArrayWriter extends Writer {
}
/**
* Flushing a <code>FastCharArrayWriter</code> 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() {

79
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);
}
@ -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 <code>true</code> 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;
}
@ -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 <code>true</code> 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;
}
@ -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;
}
@ -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 <code>~</code> 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();
}
}

1694
fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtil.java

File diff suppressed because it is too large Load Diff

100
fine-jodd/src/main/java/com/fr/third/jodd/io/FileUtilParams.java

@ -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();
}
}

105
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 <tt>true</tt> if param has a valid ip v4 format <tt>false</tt> otherwise
* @see <a href="https://en.wikipedia.org/wiki/IP_address#IPv4_addresses">ip address v4</a>
*/
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();
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();
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();
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 {
public static void downloadFile(final String url, final File file) throws IOException {
try (
InputStream inputStream = new URL(url).openStream();
ReadableByteChannel rbc = Channels.newChannel(inputStream);
FileOutputStream fos = new FileOutputStream(file);
fos.getChannel().transferFrom(rbc, 0, 1 << 24);
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();
}
}
}
}

21
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();
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<Path>() {
@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;
}

8
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();
}
}

623
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 <code>FLushable</code>, 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,245 +75,447 @@ 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) {
break;
while ((read = input.read(buffer, ZERO, numToRead)) >= ZERO) {
output.write(buffer, ZERO, read);
totalRead = totalRead + read;
}
output.write(buffer, 0, read);
count += read;
output.flush();
return totalRead;
}
return count;
/**
* 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 bytes from input stream to output stream using buffer.
* 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(InputStream input, OutputStream output, int byteCount) throws IOException {
int bufferSize = (byteCount > JoddCore.ioBufferSize) ? JoddCore.ioBufferSize : byteCount;
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];
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);
while (numToRead > ZERO) {
read = input.read(buffer, ZERO, bufferSize(numToRead));
if (read == NEGATIVE_ONE) {
break;
}
output.write(buffer, ZERO, read);
numToRead = numToRead - read;
totalRead = totalRead + read;
}
output.flush();
return totalRead;
}
if (read == -1) {
/**
* 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(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];
int totalRead = ZERO;
int read;
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 extends OutputStream> 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 extends OutputStream> 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 extends OutputStream> 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 extends OutputStream> 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());
}
output.flush();
return count;
/**
* @see #copyToOutputStream(InputStream, int)
*/
public static FastByteArrayOutputStream copyToOutputStream(final InputStream input) throws IOException {
return copyToOutputStream(input, ALL);
}
/**
* Copies specified number of characters from reader to writer using buffer.
* Copies {@link InputStream} to a new {@link FastByteArrayOutputStream} using buffer and specified encoding.
*
* @see #copy(InputStream, OutputStream, int)
*/
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 InputStream input, final int count) throws IOException {
try (FastByteArrayOutputStream output = createFastByteArrayOutputStream()) {
copy(input, output, count);
return output;
}
}
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);
/**
* @see #copyToOutputStream(Reader, String)
*/
public static FastByteArrayOutputStream copyToOutputStream(final Reader input) throws IOException {
return copyToOutputStream(input, encoding());
}
if (read == -1) {
break;
/**
* @see #copyToOutputStream(Reader, String, int)
*/
public static FastByteArrayOutputStream copyToOutputStream(final Reader input, final String encoding) throws IOException {
return copyToOutputStream(input, encoding, ALL);
}
charCount -= read;
count += read;
output.write(buffer, 0, read);
/**
* @see #copyToOutputStream(Reader, String, int)
*/
public static FastByteArrayOutputStream copyToOutputStream(final Reader input, final int count) throws IOException {
return copyToOutputStream(input, encoding(), count);
}
return 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;
}
}
// ---------------------------------------------------------------- copy to Writer
/**
* @see #copy(InputStream, Writer, String)
*/
public static <T extends Writer> T copy(final InputStream input, final T output) throws IOException {
return copy(input, output, encoding());
}
/**
* @see #copy(InputStream, Writer, String, int)
*/
public static <T extends Writer> T copy(final InputStream input, final T output, final int count) throws IOException {
return copy(input, output, encoding(), count);
}
/**
* @see #copy(InputStream, Writer, String, int)
*/
public static <T extends Writer> 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.
* Copies {@link InputStream} to {@link Writer} using buffer and specified encoding.
*
* @see #copy(Reader, Writer, int)
*/
public static void copy(Reader input, OutputStream output) throws IOException {
copy(input, output, JoddCore.encoding);
public static <T extends Writer> 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.
* @see #copy(InputStream, String)
*/
public static void copy(Reader input, OutputStream output, int charCount) throws IOException {
copy(input, output, JoddCore.encoding, charCount);
public static FastCharArrayWriter copy(final InputStream input) throws IOException {
return copy(input, encoding());
}
/**
* Copies reader to output stream using buffer and specified encoding.
* @see #copy(InputStream, String, 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 FastCharArrayWriter copy(final InputStream input, final int count) throws IOException {
return copy(input, encoding(), count);
}
/**
* Copies specified number of characters from reader to output stream using buffer and specified encoding.
* @see #copy(InputStream, String, int)
*/
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, final String encoding) throws IOException {
return copy(input, encoding, ALL);
}
/**
* Copies {@link InputStream} to a new {@link FastCharArrayWriter} using buffer and specified encoding.
*
* @see #copy(InputStream, Writer, String, int)
*/
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;
}
}
// ---------------------------------------------------------------- read bytes
/**
* @see #copy(Reader, int)
*/
public static FastCharArrayWriter copy(final Reader input) throws IOException {
return copy(input, ALL);
}
/**
* Reads all available bytes from InputStream as a byte array.
* Uses <code>in.available()</code> 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 <code>available()</code>.
* Buffered internally.
* Copies {@link Reader} to a new {@link FastCharArrayWriter} using buffer and specified encoding.
*
* @see #copy(Reader, Writer, 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 Reader input, final int count) throws IOException {
try (FastCharArrayWriter output = createFastCharArrayWriter()) {
copy(input, output, count);
return output;
}
if (i < l) {
throw new IOException("Failed to completely read input stream");
}
return byteArray;
/**
* 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());
}
public static byte[] readBytes(InputStream input) throws IOException {
FastByteArrayOutputStream output = new FastByteArrayOutputStream();
copy(input, output);
return output.toByteArray();
// ---------------------------------------------------------------- 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();
}
public static char[] readChars(InputStream input, int charCount) throws IOException {
FastCharArrayWriter output = new FastCharArrayWriter();
copy(input, output, charCount);
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, String encoding) throws IOException {
FastCharArrayWriter output = new FastCharArrayWriter();
copy(input, output, encoding);
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, 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 <code>true</code> 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)) {
@ -320,7 +525,7 @@ public class StreamUtil {
input2 = new BufferedInputStream(input2);
}
int ch = input1.read();
while (ch != -1) {
while (ch != NEGATIVE_ONE) {
int ch2 = input2.read();
if (ch != ch2) {
return false;
@ -328,13 +533,14 @@ public class StreamUtil {
ch = input1.read();
}
int ch2 = input2.read();
return (ch2 == -1);
return (ch2 == NEGATIVE_ONE);
}
/**
* Compares the content of two character streams.
* Compares the content of two character streams ({@link Reader}s).
*
* @return <code>true</code> 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)) {
@ -345,7 +551,7 @@ public class StreamUtil {
}
int ch = input1.read();
while (ch != -1) {
while (ch != NEGATIVE_ONE) {
int ch2 = input2.read();
if (ch != ch2) {
return false;
@ -353,7 +559,98 @@ public class StreamUtil {
ch = input1.read();
}
int ch2 = input2.read();
return (ch2 == -1);
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);
}
}

4
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 <code>null</code> 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() {

36
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;
}

66
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");
}
@ -102,14 +102,14 @@ public class ZipUtil {
/**
* 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,14 +161,14 @@ 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)
@ -178,11 +178,30 @@ public class ZipUtil {
// ---------------------------------------------------------------- unzip
/**
* Lists zip content.
*/
public static List<String> listZip(final File zipFile) throws IOException {
List<String> 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 <code>null</code>
*/
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 <code>true</code> 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();

584
fine-jodd/src/main/java/com/fr/third/jodd/io/findfile/ClassFinder.java

@ -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 <code>URL</code>s for classes.
* Its purpose is to help scanning class paths for some classes.
* Content of Jar files is also examined.
* <p>
* Scanning starts in included all mode (blacklist mode) for both jars and lists.
* User can set explicit excludes. Of course, mode can be changed.
* <p>
* 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<String, String> rulesJars = createJarRules();
/**
* Creates JAR rules. By default, excludes all system jars.
*/
protected InExRules<String, String> createJarRules() {
InExRules<String, String> rulesJars = new InExRules<String, String>(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<String, String> rulesEntries = createEntriesRules();
protected InExRules<String, String> createEntriesRules() {
return new InExRules<String, String>(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 <code>true</code> all files will be scanned and not only classes.
*/
protected boolean includeResources;
/**
* If set to <code>true</code> 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 <code>true</code> 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 <code>true</code> 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.
* <ul>
* <li>Class name is java-alike class name (pk1.pk2.class) that may be immediately used
* for dynamic loading.</li>
* <li>Resource name starts with '\' and represents either jar path (\pk1/pk2/res) or relative file path (\pk1\pk2\res).</li>
* </ul>
*
* <code>InputStream</code> 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 <code>true</code> 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 <code>null</code> 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 <code>true</code> if archive.
*/
public boolean isArchive() {
return zipFile != null;
}
/**
* Returns archive name or <code>null</code> 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 + '\'' +'}';
}
}
}

601
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 <code>scan()</code> methods that can be directly used.
* Convenient class path scanner.
*/
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<String, String, String> 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<String, String, String> 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 <code>true</code> all files will be scanned and not only classes.
*/
protected boolean includeResources;
/**
* If set to <code>true</code> 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
/**
* Returns <code>true</code> if some JAR file has to be accepted.
*/
protected boolean acceptJar(final File jarFile) {
String path = jarFile.getAbsolutePath();
path = FileNameUtil.separatorsToUnix(path);
return rulesJars.match(path);
}
// ---------------------------------------------------------------- internal
/**
* Scans classes inside single JAR archive. Archive is scanned as a zip file.
* @see #onEntry(ClassPathEntry)
*/
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 <code>true</code> if some entry name has to be accepted.
* @see #prepareEntryName(String, boolean)
* @see #scanEntry(ClassPathEntry)
*/
public abstract class ClassScanner extends ClassFinder {
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<ClassPathEntry> entryDataConsumers = Consumers.empty();
/**
* Scans provided classpath.
* Registers a {@link ClassPathEntry class path entry} consumer.
* It will be called on each loaded entry.
*/
public void scan(URL... urls) {
scanUrls(urls);
public ClassScanner registerEntryConsumer(final Consumer<ClassPathEntry> entryDataConsumer) {
entryDataConsumers.add(entryDataConsumer);
return this;
}
/**
* Scans {@link ClassLoaderUtil#getDefaultClasspath() default class path}.
* Called during classpath scanning when class or resource is found.
* <ul>
* <li>Class name is java-alike class name (pk1.pk2.class) that may be immediately used
* for dynamic loading.</li>
* <li>Resource name starts with '\' and represents either jar path (\pk1/pk2/res) or relative file path (\pk1\pk2\res).</li>
* </ul>
*
* <code>InputStream</code> is provided by InputStreamProvider and opened lazy.
* Once opened, input stream doesn't have to be closed - this is done by this class anyway.
*/
public void scanDefaultClasspath() {
scan(ClassLoaderUtil.getDefaultClasspath());
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 <code>true</code> if archive.
*/
public boolean isArchive() {
return zipFile != null;
}
/**
* Returns archive name or <code>null</code> if entry is not inside archived file.
*/
public String archiveName() {
if (zipFile != null) {
return zipFile.getName();
}
return null;
}
/**
* Returns <code>true</code> 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 <code>null</code> 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<File> 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);
}
});
}
}

58
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<File>, 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;
}
}

54
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<File>, 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;
}
}

57
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<File>, Serializable {
protected final int order;
protected NaturalOrderComparator<String> 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;
}
}

435
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<T extends FindFile> {
public class FindFile implements Iterable<File> {
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<T extends 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<T extends 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<File> consumers;
/**
* Registers file consumer
*/
public FindFile onFile(final Consumer<File> fileConsumer) {
if (consumers == null) {
consumers = Consumers.of(fileConsumer);
}
else {
consumers.add(fileConsumer);
}
return this;
}
// ---------------------------------------------------------------- search path
@ -163,19 +177,19 @@ public class FindFile<T extends 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;
}
/**
@ -183,75 +197,75 @@ public class FindFile<T extends FindFile> {
* {@link File#pathSeparator} than string will be tokenized
* 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)
*/
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<T extends 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<File>(sortComparators));
Arrays.sort(this.files, new MultiComparator<>(sortComparators));
}
this.fileNames = null;
@ -282,14 +296,14 @@ public class FindFile<T extends 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<T extends 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<T extends 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<T extends 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<T extends FindFile> {
// ---------------------------------------------------------------- matching
protected final InExRules<String, String> rules = createRulesEngine();
protected final InExRules<String, String, ?> rules = createRulesEngine();
/**
* Creates rule engine.
*/
protected InExRules createRulesEngine() {
return new InExRules();
protected InExRules<String, String, ?> 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<T extends 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<T extends FindFile> {
// ---------------------------------------------------------------- next file
protected JoddArrayList<File> pathList;
protected JoddArrayList<File> pathListOriginal;
protected JoddArrayList<File> todoFolders;
protected JoddArrayList<FilesIterator> todoFiles;
protected LinkedList<File> pathList;
protected LinkedList<File> pathListOriginal;
protected LinkedList<File> todoFolders;
protected LinkedList<FilesIterator> todoFiles;
protected File lastFile;
protected File rootFile;
@ -501,12 +521,12 @@ public class FindFile<T extends 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<File>();
pathList = new LinkedList<>();
}
pathList.add(path);
@ -538,8 +558,8 @@ public class FindFile<T extends 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<T extends FindFile> {
// process folders
File folder;
final File folder;
boolean initialDir = false;
if (todoFolders.isEmpty()) {
@ -603,53 +623,41 @@ public class FindFile<T extends 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<File> findAll() {
final List<File> 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<FilesIterator>();
todoFolders = new JoddArrayList<File>();
todoFiles = new LinkedList<>();
todoFolders = new LinkedList<>();
if (pathList == null) {
pathList = new JoddArrayList<File>();
pathList = new LinkedList<>();
return;
}
if (pathListOriginal == null) {
pathListOriginal = (JoddArrayList<File>) pathList.clone();
pathListOriginal = (LinkedList<File>) pathList.clone();
}
String[] files = new String[pathList.size()];
final String[] files = new String[pathList.size()];
int index = 0;
Iterator<File> iterator = pathList.iterator();
final Iterator<File> 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<T extends 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<T extends FindFile> {
/**
* Returns file walking iterator.
*/
@Override
public Iterator<File> iterator() {
return new Iterator<File>() {
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<T extends FindFile> {
return nextFile;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@ -693,9 +705,9 @@ public class FindFile<T extends FindFile> {
protected List<Comparator<File>> sortComparators;
protected void addComparator(Comparator<File> comparator) {
protected void addComparator(final Comparator<File> comparator) {
if (sortComparators == null) {
sortComparators = new ArrayList<Comparator<File>>(4);
sortComparators = new ArrayList<>(4);
}
sortComparators.add(comparator);
}
@ -703,184 +715,81 @@ public class FindFile<T extends 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<File> fileComparator) {
public FindFile sortWith(final Comparator<File> 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 <b>natural</b> sort.
*/
public T sortByName() {
public FindFile sortByName() {
addComparator(new FileNameComparator(true));
return (T) this;
return this;
}
/**
* Sorts files by file names descending, using <b>natural</b> 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<File>, 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;
}
}
public static class FileNameComparator implements Comparator<File>, Serializable {
private static final long serialVersionUID = 1;
protected final int order;
protected NaturalOrderComparator<String> naturalOrderComparator = new NaturalOrderComparator<String>(true);
public FileNameComparator(boolean ascending) {
if (ascending) {
order = 1;
} else {
order = -1;
}
return this;
}
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<File>, 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<File>, 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;
}
}
}

9
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);
}
}

53
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<File>, 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;
}
}

24
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<RegExpFindFile> {
public class RegExpFindFile extends FindFile {
public static RegExpFindFile create() {
return new RegExpFindFile();
}
@Override
protected InExRules createRulesEngine() {
return new InExRules<String, Object>() {
protected InExRules<String, String, Pattern> createRulesEngine() {
return new InExRules<String, String, Pattern>() {
@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();
}
};
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save