|
|
@@ -0,0 +1,169 @@
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
+ */
|
|
|
+package net.ranides.assira.text;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.Serializable;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+import net.ranides.assira.generic.CompareUtils;
|
|
|
+import net.ranides.assira.generic.SerializationUtils;
|
|
|
+import net.ranides.assira.generic.ValueUtils;
|
|
|
+import net.ranides.assira.reflection.IClass;
|
|
|
+import net.ranides.assira.reflection.Resolver;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+public class ResolvePattern implements Serializable {
|
|
|
+
|
|
|
+ private static final long serialVersionUID = 1L;
|
|
|
+
|
|
|
+ private static final Pattern RE_COMPILE = Pattern.compile("\\{(.+?)\\}");
|
|
|
+
|
|
|
+ private final String pattern;
|
|
|
+
|
|
|
+ private transient final Pattern regex;
|
|
|
+
|
|
|
+ private transient final Resolver[] resolvers;
|
|
|
+
|
|
|
+ private transient final Token[] tokens;
|
|
|
+
|
|
|
+ private ResolvePattern(String pattern) {
|
|
|
+
|
|
|
+ final List<Resolver> iresolvers = new ArrayList<>();
|
|
|
+ String cpattern = StringUtils.replace(pattern, RE_COMPILE, m -> {
|
|
|
+ iresolvers.add( Resolver.compile(m.group(1)) );
|
|
|
+ return "(.+)";
|
|
|
+ });
|
|
|
+ this.pattern = pattern;
|
|
|
+ this.regex = Pattern.compile(cpattern);
|
|
|
+ this.resolvers = iresolvers.toArray(new Resolver[iresolvers.size()]);
|
|
|
+
|
|
|
+ List<Token> itokens = new ArrayList<>();
|
|
|
+ Matcher hit = RE_COMPILE.matcher(pattern);
|
|
|
+ int last = 0;
|
|
|
+ while(hit.find()) {
|
|
|
+ if(hit.start()-last > 0) {
|
|
|
+ itokens.add(new SToken(pattern.substring(last,hit.start())));
|
|
|
+ }
|
|
|
+ itokens.add(new RToken(hit.group(1)));
|
|
|
+ last = hit.end();
|
|
|
+ }
|
|
|
+ if(last<pattern.length()) {
|
|
|
+ itokens.add(new SToken(pattern.substring(last)));
|
|
|
+ }
|
|
|
+ this.tokens = itokens.toArray(new Token[itokens.size()]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static ResolvePattern compile(String pattern) {
|
|
|
+ return new ResolvePattern(pattern);
|
|
|
+ }
|
|
|
+
|
|
|
+ public String pattern() {
|
|
|
+ return pattern;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object writeReplace() {
|
|
|
+ return SerializationUtils.proxy(this, pattern);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int hashCode() {
|
|
|
+ return pattern.hashCode();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean equals(Object object) {
|
|
|
+ if(object instanceof ResolvePattern) {
|
|
|
+ ResolvePattern that = (ResolvePattern)object;
|
|
|
+ return CompareUtils.equals(this.pattern, that.pattern);
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public String format(Object context) {
|
|
|
+ try {
|
|
|
+ return format(new StrBuilder(), context).toString();
|
|
|
+ } catch(IOException cause) {
|
|
|
+ throw new AssertionError("unreachable code", cause);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String format(String pattern, Object context) {
|
|
|
+ return compile(pattern).format(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T extends Appendable> T format(T output, Object context) throws IOException {
|
|
|
+ for(Token token : tokens) {
|
|
|
+ token.append(output, context);
|
|
|
+ }
|
|
|
+ return output;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static <T extends Appendable> T format(String pattern, T output, Object context) throws IOException {
|
|
|
+ return compile(pattern).format(output, context);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean scan(CharSequence input, Object context) {
|
|
|
+ Matcher hit = regex.matcher(input);
|
|
|
+ if(!hit.matches()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for(int i=0; i<resolvers.length; i++) {
|
|
|
+ IClass type = resolvers[i].type(context);
|
|
|
+ Object value = LexicalCast.cast(hit.group(i+1), type);
|
|
|
+ resolvers[i].set(context, value);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static boolean scan(String pattern, CharSequence input, Object context) {
|
|
|
+ return compile(pattern).scan(input, context);
|
|
|
+ }
|
|
|
+
|
|
|
+ private interface Token {
|
|
|
+
|
|
|
+ void append(Appendable target, Object context) throws IOException;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SToken implements Token {
|
|
|
+
|
|
|
+ private final String value;
|
|
|
+
|
|
|
+ public SToken(String value) {
|
|
|
+ this.value = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void append(Appendable target, Object context) throws IOException {
|
|
|
+ target.append(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class RToken implements Token {
|
|
|
+
|
|
|
+ private final Resolver resolver;
|
|
|
+
|
|
|
+ public RToken(String expression) {
|
|
|
+ this.resolver = Resolver.compile(expression);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void append(Appendable target, Object context) throws IOException {
|
|
|
+ target.append(String.valueOf( resolver.get(context)));
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|