Ranides Atterwim 4 anni fa
parent
commit
0254f60345

+ 31 - 31
assira.core/src/main/java/net/ranides/assira/reflection/IAttribute.java

@@ -12,67 +12,67 @@ package net.ranides.assira.reflection;
  */
 public enum IAttribute {
     
-    PUBLIC          (0x00000001),
+    PUBLIC          (0x0000_0001),
 
-    PRIVATE         (0x00000002),
+    PRIVATE         (0x0000_0002),
 
-    PROTECTED       (0x00000004),
+    PROTECTED       (0x0000_0004),
 
-    STATIC          (0x00000008),
+    STATIC          (0x0000_0008),
 
-    FINAL           (0x00000010),
+    FINAL           (0x0000_0010),
 
-    SYNCHRONIZED    (0x00000020),
+    SYNCHRONIZED    (0x0000_0020),
 
-    VOLATILE        (0x00000040),   // special: must be field (conflict with BRIDGE)
+    VOLATILE        (0x0000_0040),   // special: must be field (conflict with BRIDGE)
 
-    TRANSIENT       (0x00000080),   // special: must be field (conflict with VARARGS)
+    TRANSIENT       (0x0000_0080),   // special: must be field (conflict with VARARGS)
 
-    NATIVE          (0x00000100),
+    NATIVE          (0x0000_0100),
 
-    INTERFACE       (0x00000200),
+    INTERFACE       (0x0000_0200),
 
-    ABSTRACT        (0x00000400),
+    ABSTRACT        (0x0000_0400),
 
-    STRICT          (0x00000800),
+    STRICT          (0x0000_0800),
 
-    BRIDGE          (0x01000000, 0x00000040),   // special: must be method (conflict with VOLATILE)
+    BRIDGE          (0x0100_0000, 0x0000_0040),   // special: must be method (conflict with VOLATILE)
     
-    VARARGS         (0x02000000, 0x00000080),   // special: must be method (conflict with TRANSIENT)
+    VARARGS         (0x0200_0000, 0x0000_0080),   // special: must be method (conflict with TRANSIENT)
     
-    SYNTHETIC       (0x00001000),
+    SYNTHETIC       (0x0000_1000),
     
-    ANNOTATION      (0x00002000),
+    ANNOTATION      (0x0000_2000),
     
-    ENUM            (0x00004000),
+    ENUM            (0x0000_4000),
     
-    MANDATED        (0x00008000),
+    MANDATED        (0x0000_8000),
 
-    PACKAGE         (0x04000000, 0),   // special: !(public | protected | private)
+    PACKAGE         (0x0400_0000, 0),   // special: !(public | protected | private)
     
-    PARAMETERIZED   (0x10000000, 0),   // special: is generic class
+    PARAMETERIZED   (0x1000_0000, 0),   // special: is generic class
     
-    DECLARED        (0x40000000, 0),   // special: used in IClass to disable deep scan
+    DECLARED        (0x4000_0000, 0),   // special: used in IClass to disable deep scan
     
-    LAMBDA          (0x80000000, 0),   // special: used in IMethod
+    LAMBDA          (0x8000_0000, 0),   // special: used in IMethod
     
-    ANY             (0x00200000, 0),   // special: used only by AHints for deep scan of declared fields/methods/classes
+    ANY             (0x0020_0000, 0),   // special: used only by AHints for deep scan of declared fields/methods/classes
 
-    ARRAY           (0x00010000, 0),   // special: traits
+    ARRAY           (0x0001_0000, 0),   // special: traits
 
-    PRIMITIVE       (0x00020000, 0),   // special: traits
+    PRIMITIVE       (0x0002_0000, 0),   // special: traits
     
-    NUMBER          (0x20000000, 0),   // special: traits
+    NUMBER          (0x2000_0000, 0),   // special: traits
     
-    INTEGER         (0x00400000, 0),   // special: traits
+    INTEGER         (0x0040_0000, 0),   // special: traits
 
-    BOXED           (0x00040000, 0),   // special: traits
+    BOXED           (0x0004_0000, 0),   // special: traits
     
-    VOID            (0x00080000, 0),   // special: traits
+    VOID            (0x0008_0000, 0),   // special: traits
     
-    BOOLEAN         (0x00100000, 0),   // special: traits
+    BOOLEAN         (0x0010_0000, 0),   // special: traits
     
-    SYMBOLIC        (0x08000000),
+    SYMBOLIC        (0x0800_0000),
     
     //RESERVED        (0x00800000),
     

+ 5 - 5
assira.core/src/main/java/net/ranides/assira/text/FormatNumber.java

@@ -135,8 +135,8 @@ public final class FormatNumber {
      * </ul>
      *
      * @param format "printf-compatible" number format, e.g {@code %2.5M}
-     * @param value
-     * @return
+     * @param value value
+     * @return String
      */
     public static String asEngineer(String format, double value) {
         return asEngineer(new StringBuilder(), format, value).toString();
@@ -160,10 +160,10 @@ public final class FormatNumber {
      *      <li>K (binary, Donald Knuth prefix)</li>
      *      <li>B (binary, exponent)</li>
      * </ul>
-     * @param target
+     * @param target target
      * @param format format string compatible with String#format. 
-     * @param value
-     * @return
+     * @param value value
+     * @return target
      */
     public static StringBuilder asEngineer(StringBuilder target, String format, double value) {
         if(value < 0) {

+ 51 - 11
assira.core/src/main/java/net/ranides/assira/text/ResolveDialect.java

@@ -4,39 +4,79 @@ import net.ranides.assira.reflection.ResolveStrategy;
 
 import java.util.regex.Pattern;
 
+/**
+ * {@link ResolveFormat} class can use different dialects for putting variable expressions inside text.
+ * For example, variable could be written as {@code {variable}} or by {@code $variable}.
+ *
+ * Additionally, different dialects can define different behaviour if unknown variable is found.
+ * For example, it could return "null" or just ignore expression, or throw exception.
+ */
 public enum ResolveDialect {
 
+    /**
+     * Default dialect uses format: {variable,format,options}
+     * If variable expression is invalid, it is unaffected
+     */
     DEFAULT(ResolveStrategy.FORMAT, "(\\{)(.+?)(?:,(.+?))?(?:,(.+?))?(\\})"),
+
+    /**
+     * Handlebars dialect uses format: {{variable/format/options}}
+     * If variable expression is invalid, it is unaffected
+     */
     HANDLEBARS(ResolveStrategy.HANDLEBARS, "(\\{{2,3})(.+?)(?:/(.+?))?(?:/(.+?))?(\\}{2,3})"),
+
+    /**
+     * Shell dialect uses format: ${variable,format,options}
+     * If variable expression is invalid, exception is thrown
+     */
     SHELL(ResolveStrategy.THROW, "\\$(\\{)(.+?)(?:,(.+?))?(?:,(.+?))?(\\})", "\\\\\\$(\\{)(.+?)(?:,(.+?))?(?:,(.+?))?(\\})"),
     ;
 
-    private final Pattern opattern;
+    private final Pattern pattern;
 
-    private final Pattern ipattern;
+    private final Pattern escaped;
 
     private final ResolveStrategy strategy;
 
     ResolveDialect(ResolveStrategy strategy, String pattern) {
         this.strategy = strategy;
-        this.opattern = Pattern.compile(pattern);
-        this.ipattern = Pattern.compile(pattern);
+        this.pattern = Pattern.compile(pattern);
+        this.escaped = Pattern.compile(pattern);
     }
 
-    ResolveDialect(ResolveStrategy strategy, String opattern, String ipattern) {
+    ResolveDialect(ResolveStrategy strategy, String pattern, String escaped) {
         this.strategy = strategy;
-        this.opattern = Pattern.compile(opattern);
-        this.ipattern = Pattern.compile(ipattern);
+        this.pattern = Pattern.compile(pattern);
+        this.escaped = Pattern.compile(escaped);
     }
 
-    public Pattern opattern() {
-        return opattern;
+    /**
+     * This pattern is used to find "variable expression" inside ordinary string
+     *
+     * @return Pattern
+     */
+    public Pattern pattern() {
+        return pattern;
     }
 
-    public Pattern ipattern() {
-        return ipattern;
+    /**
+     * This pattern is used to find "variable expression" inside "regex escaped" string
+     *
+     * That means:
+     * sometimes you want to escape all special regex characters inside string first, and then find
+     * "variable expression" inside it. To do so, you should use "escaped" pattern.
+     *
+     * @return Pattern
+     */
+    public Pattern escaped() {
+        return escaped;
     }
 
+    /**
+     * Returns ResolveStrategy used by this dialect
+     *
+     * @return ResolveStrategy
+     */
     public ResolveStrategy strategy() {
         return strategy;
     }

+ 162 - 22
assira.core/src/main/java/net/ranides/assira/text/ResolveFormat.java

@@ -24,18 +24,30 @@ import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 /**
- *
- * Składnia wzorców jest identyczna jak składnia z MessageFormat.
- * Podajemy kolejno:
- *      { ArgumentIndex, Format, Pattern  }
+ * ResolveFormat replaces all "variable expressions" inside string by values provided in additional arguments.
+ * This class can use custom {@link ResolveDialect} to define format of variable expressions.
+ * Syntax used by default dialect is identical as used in {@code MessageFormat}:
+ * That means:
+ *      { ArgumentIndex, Format, Options  }
  * 
  * ARGUMENT:
- *      ArgumentIndex jest naszym wyrażeniem
- *      . służy jako separator
- *      [ ] służy jako indeks (transformowane na separator)
+ * ArgumentIndex is variable name
+ *      . is used as separator (when accessing fields)
+ *      [ ] is used as indexing operator (when accessing array elements)
+ *
+ *      Please note, that in practice . and [ ] can be used interchangably
  * 
  * FORMATS:
- *  {number} DecimalFormat | integer | currency | percent
+ * Please note, that different formats support different "Options"
+ *
+ * We use some built-in formats:
+ *      DecimalFormat       is described in {@link java.text.DecimalFormat}
+ *      SimpleDateFormat    is described in {@link java.text.SimpleDateFormat}
+ *      ChoiceFormat        is described in {@link java.text.ChoiceFormat}
+ *
+ * List of formats supported by default (with options):
+ *
+ *  {number} DecimalFormat | integer | currency | percent | java | c++
  * 
  *  {metric} DecimalFormat
  *  {metric/e} DecimalFormat
@@ -46,13 +58,13 @@ import java.util.stream.Collectors;
  *  {binary/iec} DecimalFormat
  *  {binary/knuth} DecimalFormat
  *
- *  {date} SimpleDateFormat | short | medium | long | full | iso | gnu
+ *  {date} SimpleDateFormat | short | medium | long | full | iso | gnu | trace | local
  *
- *  {time} SimpleDateFormat | short | medium | long | full | iso | gnu
+ *  {time} SimpleDateFormat | short | medium | long | full | iso | gnu | trace | local
  *
  *  {choice,ChoiceFormat}
  *
- *  {hex} array | matrix
+ *  {hex} array | stream
  * 
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
@@ -74,7 +86,16 @@ public class ResolveFormat implements Serializable {
     private final Token[] parsers;
     
     private final Pattern scanre;
-    
+
+    /**
+     * This protected constructor should not be constructed directly.
+     *
+     * This constructor is executed by ResolveLocale only.
+     *
+     * @param locale locale
+     * @param dialect dialect
+     * @param pattern pattern
+     */
     ResolveFormat(ResolveLocale locale, ResolveDialect dialect, String pattern) {
         this.locale = locale;
         this.dialect = dialect;
@@ -82,7 +103,7 @@ public class ResolveFormat implements Serializable {
 
         List<Token> itokens = new ArrayList<>();
         List<Token> iparsers = new ArrayList<>();
-        Matcher hit = dialect.opattern().matcher(pattern);
+        Matcher hit = dialect.pattern().matcher(pattern);
         int last = 0;
         while(hit.find()) {
             if(hit.group(1).length() != hit.group(5).length()) {
@@ -108,42 +129,95 @@ public class ResolveFormat implements Serializable {
         int[] index = new int[]{0};
         
         String p = REGEX_SPECIAL.matcher(pattern).replaceAll("\\\\$1");
-        String p2 = StringUtils.replace(p, dialect.ipattern(), h -> parsers[index[0]++].regex());
+        String p2 = StringUtils.replace(p, dialect.escaped(), h -> parsers[index[0]++].regex());
 
         if(dialect != ResolveDialect.DEFAULT) {
             p2 = p2.replaceAll("([{}])", "\\\\$1");
         }
         this.scanre = Pattern.compile(p2);
     }
-    
+
+    /**
+     * Compiles expression, using default {@link ResolveLocale} and {@link ResolveDialect}
+     *
+     * @param pattern pattern
+     * @return ResolveFormat
+     */
     public static ResolveFormat compile(String pattern) {
         return ResolveLocale.getDefault().compile(pattern);
     }
-    
+
+    /**
+     * Compiles expression, using default {@link ResolveDialect}
+     * Uses {@link ResolveLocale} found by {@link java.util.Locale#forLanguageTag}.
+     *
+     * @param locale locale
+     * @param pattern pattern
+     * @return ResolveFormat
+     */
     public static ResolveFormat compile(String locale, String pattern) {
         return ResolveLocale.getInstance(locale).compile(pattern);
     }
 
+    /**
+     * Compiles expression, using default {@link ResolveLocale}.
+     * Uses provided {@link ResolveDialect} for processing variable expressions.
+     *
+     * @param dialect dialect
+     * @param pattern pattern
+     * @return ResolveFormat
+     */
     public static ResolveFormat compile(ResolveDialect dialect, String pattern) {
         return ResolveLocale.getDefault().compile(dialect, pattern);
     }
 
+    /**
+     * Compiles expression.
+     * Uses provided {@link ResolveDialect} for processing variable expressions.
+     * Uses {@link ResolveLocale} found by {@link java.util.Locale#forLanguageTag}.
+     *
+     * @param dialect dialect
+     * @param locale locale
+     * @param pattern pattern
+     * @return ResolveFormat
+     */
     public static ResolveFormat compile(ResolveDialect dialect, String locale, String pattern) {
         return ResolveLocale.getInstance(locale).compile(dialect, pattern);
     }
-    
+
+    /**
+     * Returns expression describing this compiled object
+     *
+     * @return String
+     */
     public String pattern() {
         return pattern;
     }
-    
+
+    /**
+     * Returns ResolveLocale used for translation.
+     *
+     * @return ResolveLocale
+     */
     public ResolveLocale locale() {
         return locale;
     }
 
+    /**
+     * Returns ResolveDialect used for processing variable expressions.
+     *
+     * @return ResolveDialect
+     */
     public ResolveDialect dialect() {
         return dialect;
     }
 
+    /**
+     * Generates new String replacing all variable expressions by values resolved from "context"
+     *
+     * @param context context
+     * @return String
+     */
     public String format(Object context) {
         try {
             return format(new StrBuilder(), context).toString();
@@ -151,26 +225,80 @@ public class ResolveFormat implements Serializable {
             throw new AssertionError("unreachable code", cause);
         }
     }
-    
+
+    /**
+     * Generates new String replacing all variable expressions by values passed as arguments.
+     *
+     * Arguments are identified by 0-based index.
+     * Please note, that you can write more complex expressions like: {@code {0.field.value.here}}
+     *
+     * @param arguments arguments
+     * @return String
+     */
     public String vformat(Object... arguments) {
         return format(arguments);
     }
 
+    /**
+     * Generates new String replacing all variable expressions by values passed as arguments.
+     *
+     * Arguments is a list of strings representing "key=value" pairs.
+     * Variable expression {@code {key}} will be resolved to "value".
+     *
+     * @param arguments arguments
+     * @return String
+     */
     public String nformat(String... arguments) {
         return format(args2map(arguments));
     }
-    
+
+    /**
+     * Generates new String replacing all variable expressions by values resolved from "context".
+     * Generated result is stored in "output" buffer.
+     *
+     * @param output output
+     * @param context context
+     * @param <T> T
+     * @return output
+     * @throws IOException on buffer error
+     */
     public <T extends Appendable> T format(T output, Object context) throws IOException {
         for(Token token : tokens) {
             token.append(output, context);
         }
         return output;
     }
-    
+
+    /**
+     * Generates new String replacing all variable expressions by values passed as arguments.
+     * Generated result is stored in "output" buffer.
+     *
+     * Arguments are identified by 0-based index.
+     * Please note, that you can write more complex expressions like: {@code {0.field.value.here}}
+     *
+     * @param output output
+     * @param arguments arguments
+     * @param <T> T
+     * @return output
+     * @throws IOException on buffer error
+     */
     public <T extends Appendable> T vformat(T output, Object... arguments) throws IOException {
         return format(output, arguments);
     }
 
+    /**
+     * Generates new String replacing all variable expressions by values passed as arguments.
+     * Generated result is stored in "output" buffer.
+     *
+     * Arguments is a list of strings representing "key=value" pairs.
+     * Variable expression {@code {key}} will be resolved to "value".
+     *
+     * @param output output
+     * @param arguments arguments
+     * @param <T> T
+     * @return output
+     * @throws IOException on buffer error
+     */
     public <T extends Appendable> T nformat(T output, String... arguments) throws IOException {
         return format(output, args2map(arguments));
     }
@@ -180,7 +308,19 @@ public class ResolveFormat implements Serializable {
             .map(arg -> arg.split("\\s*=\\s*", 2))
             .collect(Collectors.toMap(v -> v[0], v -> v[1]));
     }
-    
+
+    /**
+     * Parses provided input text using compiled pattern and stores every extracted value into "context" object.
+     *
+     * This method makes extensive use of variable names: they are used to modify "context" state.
+     * If some variable name is invalid, exception is thrown.
+     *
+     * This method makes extensive use of variable format: they are used to extract data from text.
+     * You should be carefull and write unambiguous patterns.
+     *
+     * @param input input
+     * @param context context
+     */
     public void parse(String input, Object context) {
         Matcher hit = scanre.matcher(input);
         if(!hit.matches()) {

+ 81 - 8
assira.core/src/main/java/net/ranides/assira/text/ResolveFormatUtils.java

@@ -6,42 +6,115 @@
  */
 package net.ranides.assira.text;
 
+import lombok.experimental.UtilityClass;
+
 import java.io.IOException;
 
 /**
+ * Static methods for quick usage of ResolveFormat without explicit compilation.
+ *
+ * All static methods compile expression, using default {@link ResolveLocale} and {@link ResolveDialect}.
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
+@UtilityClass
 public final class ResolveFormatUtils {
-    
-    private ResolveFormatUtils() {
-        /* utility class */
-    }
-    
+
+    /**
+     * Executes {@link ResolveFormat#format(Object)}
+     *
+     * Uses default {@link ResolveLocale} and {@link ResolveDialect}
+     *
+     * @param pattern pattern
+     * @param context context
+     * @return String
+     */
     public static String format(String pattern, Object context) {
         return ResolveFormat.compile(pattern).format(context);
     }
-    
+
+    /**
+     * Executes {@link ResolveFormat#vformat(Object...)}
+     *
+     * Uses default {@link ResolveLocale} and {@link ResolveDialect}
+     *
+     * @param pattern pattern
+     * @param arguments arguments
+     * @return String
+     */
     public static String vformat(String pattern, Object... arguments) {
         return ResolveFormat.compile(pattern).vformat(arguments);
     }
 
+    /**
+     * Executes {@link ResolveFormat#nformat(String...)}
+     *
+     * Uses default {@link ResolveLocale} and {@link ResolveDialect}
+     *
+     * @param pattern pattern
+     * @param arguments arguments
+     * @return String
+     */
     public static String nformat(String pattern, String... arguments) {
         return ResolveFormat.compile(pattern).nformat(arguments);
     }
 
+    /**
+     * Executes {@link ResolveFormat#format(Appendable, Object)}
+     *
+     * Uses default {@link ResolveLocale} and {@link ResolveDialect}
+     *
+     * @param output output
+     * @param pattern pattern
+     * @param context context
+     * @param <T> T
+     * @return output
+     * @throws IOException on buffer error
+     */
     public static <T extends Appendable> T format(T output, String pattern, Object context) throws IOException {
         return ResolveFormat.compile(pattern).format(output, context);
     }
-    
+
+    /**
+     * Executes {@link ResolveFormat#vformat(Appendable, Object...)}
+     *
+     * Uses default {@link ResolveLocale} and {@link ResolveDialect}
+     *
+     * @param output output
+     * @param pattern pattern
+     * @param arguments arguments
+     * @param <T> T
+     * @return output
+     * @throws IOException on buffer error
+     */
     public static <T extends Appendable> T vformat(T output, String pattern, Object... arguments) throws IOException {
         return ResolveFormat.compile(pattern).vformat(output, arguments);
     }
 
+    /**
+     * Executes {@link ResolveFormat#nformat(Appendable, String...)}
+     *
+     * Uses default {@link ResolveLocale} and {@link ResolveDialect}
+     *
+     * @param output output
+     * @param pattern pattern
+     * @param arguments arguments
+     * @param <T> T
+     * @return output
+     * @throws IOException on buffer error
+     */
     public static <T extends Appendable> T nformat(T output, String pattern, String... arguments) throws IOException {
         return ResolveFormat.compile(pattern).nformat(output, arguments);
     }
-    
+
+    /**
+     *
+     * Uses default {@link ResolveLocale} and {@link ResolveDialect}.
+     *
+     * @param pattern pattern
+     * @param input input
+     * @param context context
+     */
     public static void parse(String pattern, String input, Object context) {
         ResolveFormat.compile(pattern).parse(input, context);
     }

+ 46 - 6
assira.core/src/main/java/net/ranides/assira/text/ResolveLocale.java

@@ -36,6 +36,13 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
+ * ResolveLocale is a class which stores all locale specific configuration.
+ *
+ * Because almost every formatting rule uses localization,
+ * almost every tag supported by "ResolveFormat" is configured here.
+ *
+ * In effect, ResolveFormat contains implementation of parse/format algorithm
+ * which works with any kind of tag, but tag implementations are stored here.
  *
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
@@ -47,7 +54,7 @@ public final class ResolveLocale {
     
     private static final class Li { // NOPMD - lazy init idiom
         
-        public static final CacheMap<String, ResolveLocale> DIALECTS = CacheMap.getInstance(ResolveLocale::new);
+        public static final CacheMap<String, ResolveLocale> LOCALES = CacheMap.getInstance(ResolveLocale::new);
         
         public static final ResolveLocale DEFAULT = new ResolveLocale(Locale.getDefault().getCountry());
     
@@ -159,24 +166,57 @@ public final class ResolveLocale {
     private void putChoice() {
         putFormat("choice",          (r,p) -> new FChoice(r, new ChoiceFormat(p)));
     }
-    
 
+
+    /**
+     * Returns ResolveLocale configured for {@link Locale#getDefault()}.
+     *
+     * @return ResolveLocale
+     */
     public static ResolveLocale getDefault() {
         return Li.DEFAULT;
     }
-    
-    public static ResolveLocale getInstance(String dialect) {
-        return Li.DIALECTS.get(dialect);
+
+    /**
+     * Returns ResolveLocale {@link ResolveLocale} configured for {@link java.util.Locale#forLanguageTag}.
+     *
+     * @param locale locale
+     * @return ResolveLocale
+     */
+    public static ResolveLocale getInstance(String locale) {
+        return Li.LOCALES.get(locale);
     }
-    
+
+    /**
+     * Returns name used to create this ResolveLocale
+     *
+     * @return String
+     */
     public String name() {
         return name;
     }
 
+    /**
+     * Returns compiled expression.
+     * Caches results, invokes {@link ResolveFormat} constructor only if necessary.
+     *
+     * Uses default {@link ResolveDialect}.
+     *
+     * @param pattern pattern
+     * @return ResolveFormat
+     */
     public ResolveFormat compile(String pattern) {
         return cache.get(new CompileKey(ResolveDialect.DEFAULT, pattern));
     }
 
+    /**
+     * Returns compiled expression.
+     * Caches results, invokes {@link ResolveFormat} constructor only if necessary.
+     *
+     * @param dialect dialect
+     * @param pattern pattern
+     * @return ResolveFormat
+     */
     public ResolveFormat compile(ResolveDialect dialect, String pattern) {
         return cache.get(new CompileKey(dialect, pattern));
     }