|
|
@@ -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()) {
|