Browse Source

resolve format: parse

Ranides Atterwim 10 years ago
parent
commit
3ffeb7696a

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

@@ -211,5 +211,5 @@ public final class FormatNumber {
         }
         
     }
-
+    
 }

+ 130 - 57
assira/src/main/java/net/ranides/assira/text/ResolveDialect.java

@@ -20,10 +20,8 @@ import java.time.Instant;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.Date;
-import java.util.IllegalFormatConversionException;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Objects;
 import java.util.function.BiFunction;
 import java.util.function.Function;
 import net.ranides.assira.collection.maps.Cache;
@@ -34,6 +32,7 @@ import net.ranides.assira.generic.ValueUtils;
 import net.ranides.assira.reflection.IAttribute;
 import net.ranides.assira.reflection.IClass;
 import net.ranides.assira.reflection.Resolver;
+import static net.ranides.assira.text.FormatNumber.MP;
 import net.ranides.assira.text.FormatNumber.UnitPrefix;
 
 /**
@@ -68,7 +67,7 @@ public final class ResolveDialect {
         Locale locale = new Locale(name);
         DecimalFormatSymbols us = DecimalFormatSymbols.getInstance(Locale.US);
         
-        put("", "", r -> new FGeneric(r));
+        putFormat("", "", r -> new FGeneric(r));
         
         // number       : 2 + 3      5
         // date         : 2 + 8     10
@@ -77,58 +76,60 @@ public final class ResolveDialect {
         // metric       : 1 + 4      5
         // binary       : 1 + 6      7
         //              :           40 x language
-        
-        put("number",          (r,p) -> new FNumber(r, new DecimalFormat(p,us)));
-        put("number", "",          r -> new FNumber(r, DecimalFormat.getInstance(locale)));
-        put("number", "integer",   r -> new FNumber(r, DecimalFormat.getIntegerInstance(locale)));
-        put("number", "currency",  r -> new FNumber(r, getCurrencyFormat(locale)));
-        put("number", "percent",   r -> new FNumber(r, DecimalFormat.getPercentInstance(locale)));
-        
-        put("date",            (r,p) -> new FDateTime(r, new SimpleDateFormat(p,locale)));
-        put("date", "",            r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.DEFAULT,locale)));
-        put("date", "short",       r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.SHORT,locale)));
-        put("date", "medium",      r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.MEDIUM,locale)));
-        put("date", "long",        r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.LONG,locale)));
-        put("date", "full",        r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.FULL,locale)));
-        put("date", "iso",         r -> new FDateTime8(r, "yyyy-MM-dd'T'HH:mmX"));
-        put("date", "gnu",         r -> new FDateTime(r, new SimpleDateFormat("yyyyMMddHHmm",Locale.ROOT)));
-        put("date", "trace",       r -> new FDateTime(r, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS",Locale.ROOT)));
-        put("date", "local",       r -> new FDateTime(r, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.ROOT)));
-        
-        put("time",            (r,p) -> new FDateTime(r, new SimpleDateFormat(p,locale)));
-        put("time", "",            r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.DEFAULT,locale)));
-        put("time", "short",       r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.SHORT,locale)));
-        put("time", "medium",      r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.MEDIUM,locale)));
-        put("time", "long",        r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.LONG,locale)));
-        put("time", "full",        r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.FULL,locale)));
-        put("time", "iso",         r -> new FDateTime8(r, "HH:mm:ssX"));
-        put("time", "gnu",         r -> new FDateTime(r, new SimpleDateFormat("HHmmss",Locale.ROOT)));
-        put("time", "trace",       r -> new FDateTime(r, new SimpleDateFormat("HH:mm:ss.SSS",Locale.ROOT)));
-        put("time", "local",       r -> new FDateTime(r, new SimpleDateFormat("HH:mm:ss",Locale.ROOT)));
-        
-        put("hex", "",             r -> new FHexArray(r));
-        put("hex", "array",        r -> new FHexArray(r));
-        put("hex", "stream",       r -> new FHexStream(r));
-        
-        put("metric",          (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.MP));
-        put("metric/si",       (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.MP));
-        put("metric/e",        (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.ME));
-        put("metric", "",          r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.MP));
-        put("metric/si", "",       r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.MP));
-        put("metric/e", "",        r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.ME));
-        
-        put("binary",          (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.BP));
-        put("binary/iec",      (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.BP));
-        put("binary/e",        (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.BE));
-        put("binary/knuth",    (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.BK));
-        put("binary", "",          r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.BP));
-        put("binary/iec", "",      r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.BP));
-        put("binary/e", "",        r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.BE));
-        put("binary/knuth", "",    r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.BK));
-
-        put("choice",          (r,p) -> new FChoice(r, new ChoiceFormat(p)));
-    }
 
+        putFormat("number",          (r,p) -> new FNumber(r, new DecimalFormat(p,us)));
+        putFormat("number", "",          r -> new FNumber(r, DecimalFormat.getInstance(locale)));
+        putFormat("number", "integer",   r -> new FNumber(r, DecimalFormat.getIntegerInstance(locale)));
+        putFormat("number", "c++",       r -> new FNumber(r, getDecimalFormat(Locale.US, '.','\'')));
+        putFormat("number", "java",      r -> new FNumber(r, getDecimalFormat(Locale.US, '.','_')));
+        putFormat("number", "currency",  r -> new FNumber(r, getCurrencyFormat(locale)));
+        putFormat("number", "percent",   r -> new FNumber(r, DecimalFormat.getPercentInstance(locale)));
+        
+        putFormat("date",            (r,p) -> new FDateTime(r, new SimpleDateFormat(p,locale)));
+        putFormat("date", "",            r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.DEFAULT,locale)));
+        putFormat("date", "short",       r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.SHORT,locale)));
+        putFormat("date", "medium",      r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.MEDIUM,locale)));
+        putFormat("date", "long",        r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.LONG,locale)));
+        putFormat("date", "full",        r -> new FDateTime(r, DateFormat.getDateInstance(DateFormat.FULL,locale)));
+        putFormat("date", "iso",         r -> new FDateTime8(r, "yyyy-MM-dd'T'HH:mmX"));
+        putFormat("date", "gnu",         r -> new FDateTime(r, new SimpleDateFormat("yyyyMMddHHmm",Locale.ROOT)));
+        putFormat("date", "trace",       r -> new FDateTime(r, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS",Locale.ROOT)));
+        putFormat("date", "local",       r -> new FDateTime(r, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.ROOT)));
+        
+        putFormat("time",            (r,p) -> new FDateTime(r, new SimpleDateFormat(p,locale)));
+        putFormat("time", "",            r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.DEFAULT,locale)));
+        putFormat("time", "short",       r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.SHORT,locale)));
+        putFormat("time", "medium",      r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.MEDIUM,locale)));
+        putFormat("time", "long",        r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.LONG,locale)));
+        putFormat("time", "full",        r -> new FDateTime(r, DateFormat.getTimeInstance(DateFormat.FULL,locale)));
+        putFormat("time", "iso",         r -> new FDateTime8(r, "HH:mm:ssX"));
+        putFormat("time", "gnu",         r -> new FDateTime(r, new SimpleDateFormat("HHmmss",Locale.ROOT)));
+        putFormat("time", "trace",       r -> new FDateTime(r, new SimpleDateFormat("HH:mm:ss.SSS",Locale.ROOT)));
+        putFormat("time", "local",       r -> new FDateTime(r, new SimpleDateFormat("HH:mm:ss",Locale.ROOT)));
+        
+        putFormat("hex", "",             r -> new FHexArray(r));
+        putFormat("hex", "array",        r -> new FHexArray(r));
+        putFormat("hex", "stream",       r -> new FHexStream(r));
+        
+        putFormat("metric",          (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.MP));
+        putFormat("metric/si",       (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.MP));
+        putFormat("metric/e",        (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.ME));
+        putFormat("metric", "",          r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.MP));
+        putFormat("metric/si", "",       r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.MP));
+        putFormat("metric/e", "",        r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.ME));
+        
+        putFormat("binary",          (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.BP));
+        putFormat("binary/iec",      (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.BP));
+        putFormat("binary/e",        (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.BE));
+        putFormat("binary/knuth",    (r,p) -> new FEngineer(r, new DecimalFormat(p,us), FormatNumber.BK));
+        putFormat("binary", "",          r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.BP));
+        putFormat("binary/iec", "",      r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.BP));
+        putFormat("binary/e", "",        r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.BE));
+        putFormat("binary/knuth", "",    r -> new FEngineer(r, DecimalFormat.getInstance(Locale.US), FormatNumber.BK));
+
+        putFormat("choice",          (r,p) -> new FChoice(r, new ChoiceFormat(p)));
+    }
+    
     public static ResolveDialect getDefault() {
         return Li.DEFAULT;
     }
@@ -166,12 +167,11 @@ public final class ResolveDialect {
     }
     
     
-    
-    void put(String format, String pattern, Function<Resolver,ResolveFormat.Token> token) {
+    private void putFormat(String format, String pattern, Function<Resolver,ResolveFormat.Token> token) {
         mDefined.put(new TokenKey(format, pattern), token);
     }
     
-    void put(String format, BiFunction<Resolver,String,ResolveFormat.Token> token) {
+    private void putFormat(String format, BiFunction<Resolver,String,ResolveFormat.Token> token) {
         mPattern.put(format, token);
     }
     
@@ -198,6 +198,16 @@ public final class ResolveDialect {
         return nf;
     }
     
+    private DecimalFormat getDecimalFormat(Locale locale, char dseparator, char gseparator) {
+        DecimalFormat fmt = (DecimalFormat)DecimalFormat.getInstance(locale);
+        DecimalFormatSymbols dfs = fmt.getDecimalFormatSymbols();
+        dfs.setDecimalSeparator(dseparator);
+        dfs.setGroupingSeparator(gseparator);
+        fmt.setGroupingUsed(true);
+        fmt.setDecimalFormatSymbols(dfs);
+        return fmt;
+    }
+    
     private static StrBuffer asHex(StrBuffer target, byte value) {
         int ivalue = value & 0xFF;
         target.append( HC_TABLE[ivalue >>> 4] );
@@ -268,6 +278,34 @@ public final class ResolveDialect {
             this.format = nf;
         }
 
+        @Override
+        public String regex() {
+            if(format instanceof DecimalFormat) {
+                DecimalFormatSymbols dfs = ((DecimalFormat)format).getDecimalFormatSymbols();
+                StrBuilder s = new StrBuilder();
+                s.append("(");
+                digits(s, dfs);
+                s.append("(?:")
+                    .append(dfs.getExponentSeparator())
+                    .append("|")
+                    .append(dfs.getExponentSeparator().toLowerCase(Locale.ROOT))
+                    .append(")?");
+                digits(s, dfs);
+                s.append(")");
+                return s.toString();
+            } else {
+                return "((?:0x)?[0-9.,'A-Fa-f]+)";
+            }
+        }
+
+        private StrBuilder digits(StrBuilder s, DecimalFormatSymbols dfs) {
+            s.append("[0-9");
+            s.append(dfs.getDecimalSeparator());
+            s.append(dfs.getGroupingSeparator());
+            s.append("]+");
+            return s;
+        }
+
         @Override
         public void append(Appendable target, Object context) throws IOException {
             target.append(format.format(get(context)));
@@ -289,6 +327,11 @@ public final class ResolveDialect {
             this.format = nf;
         }
 
+        @Override
+        public String regex() {
+            return "(.+)";
+        }
+
         @Override
         public void append(Appendable target, Object context) throws IOException {
             target.append(format.format(get(context,Date.class)));
@@ -309,6 +352,11 @@ public final class ResolveDialect {
             super(resolver);
             this.format = DateTimeFormatter.ofPattern(pattern).withZone(ZoneOffset.UTC);
         }
+        
+        @Override
+        public String regex() {
+            return "(.+)";
+        }
 
         @Override
         public void append(Appendable target, Object context) throws IOException {
@@ -332,6 +380,11 @@ public final class ResolveDialect {
             super(resolver);
             this.format = nf;
         }
+        
+        @Override
+        public String regex() {
+            return "(.+)";
+        }
 
         @Override
         public void append(Appendable target, Object context) throws IOException {
@@ -350,6 +403,11 @@ public final class ResolveDialect {
         public FHexArray(Resolver resolver) {
             super(resolver);
         }
+        
+        @Override
+        public String regex() {
+            return "(\\[[0-9]+\\]\\{(?:0x[0-9]{2}?)?(?:,0x[0-9]{2})*\\})";
+        }
 
         @Override
         public void append(Appendable target, Object context) throws IOException {
@@ -386,6 +444,11 @@ public final class ResolveDialect {
         public FHexStream(Resolver resolver) {
             super(resolver);
         }
+        
+        @Override
+        public String regex() {
+            return "((?:[0-9A-F]{2})?(?: [0-9A-F]{2})*)";
+        }
 
         @Override
         public void append(Appendable target, Object context) throws IOException {
@@ -421,6 +484,11 @@ public final class ResolveDialect {
             this.units = units;
         }
 
+        @Override
+        public String regex() {
+            return "([0-9,]+(?:\\.[0-9,]+)?(?:" + StringUtils.join(MP, "|", b -> b.text()) + "))";
+        }
+        
         @Override
         public void append(Appendable target, Object context) throws IOException {
             Object object = get(context);
@@ -509,6 +577,11 @@ public final class ResolveDialect {
             this.resolver = resolver;
         }
 
+        @Override
+        public String regex() {
+            return "(.+)";
+        }
+        
         @Override
         public void append(Appendable target, Object context) throws IOException {
             target.append(String.valueOf(resolver.get(context)));

+ 33 - 16
assira/src/main/java/net/ranides/assira/text/ResolveFormat.java

@@ -65,11 +65,16 @@ public class ResolveFormat implements Serializable {
     
     private final Token[] tokens;
     
+    private final Token[] parsers;
+    
+    private final Pattern scanre;
+    
     ResolveFormat(ResolveDialect dialect, String pattern) {
         this.dialect = dialect;
         this.pattern = pattern;
 
         List<Token> itokens = new ArrayList<>();
+        List<Token> iparsers = new ArrayList<>();
         Matcher hit = RE_COMPILE.matcher(pattern);
         int last = 0;
         while(hit.find()) {
@@ -77,7 +82,9 @@ public class ResolveFormat implements Serializable {
                 String text = pattern.substring(last,hit.start());
                 itokens.add(new FString(text));
             }
-            itokens.add(dialect.get(hit.group(1), hit.group(2), hit.group(3)));
+            Token token = dialect.get(hit.group(1), hit.group(2), hit.group(3));
+            itokens.add(token);
+            iparsers.add(token);
             
             last = hit.end();
         }
@@ -86,6 +93,12 @@ public class ResolveFormat implements Serializable {
             itokens.add(new FString(text));
         }
         this.tokens = itokens.toArray(new Token[itokens.size()]);
+        this.parsers = iparsers.toArray(new Token[iparsers.size()]);
+        
+        int[] index = new int[]{0};
+        this.scanre = Pattern.compile(StringUtils.replace(pattern, RE_COMPILE, h -> {
+            return parsers[index[0]++].regex();
+        }));
     }
     
     public static ResolveFormat compile(String pattern) {
@@ -112,7 +125,7 @@ public class ResolveFormat implements Serializable {
         }
     }
     
-    public String format(Object... arguments) {
+    public String vformat(Object... arguments) {
         return format((Object)arguments);
     }
     
@@ -123,22 +136,22 @@ public class ResolveFormat implements Serializable {
         return output;
     }
     
-    public <T extends Appendable> T format(T output, Object... arguments) throws IOException {
+    public <T extends Appendable> T vformat(T output, Object... arguments) throws IOException {
         return format(output, (Object)arguments);
     }
     
     public void parse(String input, Object context) {
-        ParsePosition position = new ParsePosition(0);
-        for(Token token : tokens) {
-            token.parse(input, position, context);
+        Matcher hit = scanre.matcher(input);
+        if(!hit.matches()) {
+            throw new IllegalArgumentException("Expression does not match pattern: \"" + pattern + "\" expression: \"" + input + "\"nregex: " + scanre.pattern());
+        }
+        int i=0;
+        for(Token p : parsers) {
+            p.parse(hit.group(++i), new ParsePosition(0), context);
         }
-        // @todo (assira #0) ResolvePattern#parse
-        //      jeśli next token jest FString'iem to robimy:
-        //      int end = next.indexOf(position)
-        //      token.parse(StringUtils.limit(input, end), position)
     }
     
-    public void parse(String input, Object... arguments) {
+    public void vparse(String input, Object... arguments) {
         parse(input, (Object)arguments);
     }
     
@@ -172,6 +185,8 @@ public class ResolveFormat implements Serializable {
     
     interface Token {
 
+        String regex();
+        
         void append(Appendable target, Object context) throws IOException;
 
         void parse(String source, ParsePosition position, Object context);
@@ -190,13 +205,15 @@ public class ResolveFormat implements Serializable {
         public void append(Appendable target, Object context) throws IOException {
             target.append(value);
         }
-
+        
+        @Override
+        public String regex() {
+            throw new UnsupportedOperationException("Unreachable code.");
+        }
+            
         @Override
         public void parse(String source, ParsePosition position, Object context) {
-            if( !source.substring(position.getIndex()).startsWith(value) ) {
-                throw new RuntimeException("");
-            }
-            position.setIndex(position.getIndex()+value.length());
+            throw new UnsupportedOperationException("Unreachable code.");
         }
                 
     }

+ 23 - 3
assira/src/test/java/net/ranides/assira/text/ResolveFormatTest.java

@@ -7,6 +7,8 @@
 package net.ranides.assira.text;
 
 import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.Instant;
@@ -69,7 +71,7 @@ public class ResolveFormatTest {
     }
 
     @Test
-    public void testFormat_Number() {
+    public void testFormat_Number() throws UnsupportedEncodingException {
         assertEquals("3.1415",      ResolveFormat.compile("{value1}").format(ITEM) );
         
         assertEquals("3,141",       ResolveFormat.compile("{value1,number}").format(ITEM) );
@@ -83,7 +85,27 @@ public class ResolveFormatTest {
         assertEquals(" 3.14",       ResolveFormat.compile("US","{value1,number,currency}").format(ITEM) );
         assertEquals("- 150.50",    ResolveFormat.compile("US","{value2,number,currency}").format(ITEM) );
         assertEquals("314%",        ResolveFormat.compile("US","{value1,number,percent}").format(ITEM) );
+        
+        assertEquals("-150_456.5",    ResolveFormat.compile("{fvalue,number,java}").format(ITEM) );
+        assertEquals("-150'456.5",    ResolveFormat.compile("{fvalue,number,c++}").format(ITEM) );
+    }
+    
+        @Test
+    public void testParse_Number_int() {
+        Integer[] out= new Integer[3];
+        ResolveFormat rf = ResolveFormat.compile("{0,number} / {1,number,java} / {2,number,java}");
+        rf.parse("12 / 33 / 55_100", out);
+        assertArrayEquals(new Integer[]{12,33,55100}, out);
+        
+        Double[] out2= new Double[3];
+        ResolveFormat.compile("{0,number} / {1,number,java} / {2,number,java}").parse("14,3 / 35 / 55", out2);
+        assertArrayEquals(new Double[]{14.3,35.0,55.0}, out2);
+        
+        Double[] out3= new Double[3];
+        ResolveFormat.compile("US","{0,number} / {1,number,java} / {2,number,java}").parse("14.3 / 35 / 55", out3);
+        assertArrayEquals(new Double[]{14.3,35.0,55.0}, out3);
     }
+
     
     @Test
     public void testFormat_Date() {
@@ -353,8 +375,6 @@ public class ResolveFormatTest {
     // @todo (assira #2) test: ResolveFormat# parse
     
     // @todo (assira #3) test: ResolveFormat# choice pattern
-    // @todo (assira #3) test: ResolveFormat# metric BigDecimal - big values
-    // @todo (assira #3) test: ResolveFormat# binary BigDecimal - big values
     
     private static final Object ITEM = new Object(){