Bladeren bron

resolve format

Ranides Atterwim 10 jaren geleden
bovenliggende
commit
6c003b38e4

+ 15 - 0
assira/src/main/java/net/ranides/assira/text/LexicalCast.java

@@ -11,8 +11,14 @@ import java.io.IOException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.type.TypeFactory;
+import java.time.DateTimeException;
 import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoField;
 import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
 import java.util.Date;
 import net.ranides.assira.reflection.IClass;
 
@@ -141,6 +147,15 @@ public final class LexicalCast {
                 return new Date(((Number)value).longValue());
             }
         }
+        if(value instanceof LocalDateTime) {
+            LocalDateTime dt = (LocalDateTime)value;
+            if( raw.equals(Date.class)) {
+                return Date.from(dt.atZone(ZoneOffset.systemDefault()).toInstant());
+            }
+            if( raw.equals(java.sql.Date.class)) {
+                return java.sql.Date.from(dt.atZone(ZoneOffset.systemDefault()).toInstant());
+            }
+        }
         if(value instanceof TemporalAccessor) {
             if( raw.equals(Date.class)) {
                 return Date.from(Instant.from((TemporalAccessor)value));

+ 23 - 20
assira/src/main/java/net/ranides/assira/text/ResolveDialect.java

@@ -14,9 +14,11 @@ import java.text.DateFormat;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.text.NumberFormat;
+import java.text.ParseException;
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.time.Instant;
+import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.Date;
@@ -262,11 +264,11 @@ public final class ResolveDialect {
         }
 
         @Override
-        public final void parse(String source, ParsePosition position, Object context) {
-            set(context, parse(source, position));
+        public final void parse(String source, Object context) throws ParseException {
+            set(context, parse(source));
         }
         
-        public abstract Object parse(String source, ParsePosition position);
+        public abstract Object parse(String source) throws ParseException;
     }
     
     private static final class FNumber extends RToken {
@@ -284,13 +286,17 @@ public final class ResolveDialect {
                 DecimalFormatSymbols dfs = ((DecimalFormat)format).getDecimalFormatSymbols();
                 StrBuilder s = new StrBuilder();
                 s.append("(");
+                s.append(dfs.getMinusSign()).append("?");
                 digits(s, dfs);
+                s.append("+");
                 s.append("(?:")
                     .append(dfs.getExponentSeparator())
                     .append("|")
                     .append(dfs.getExponentSeparator().toLowerCase(Locale.ROOT))
                     .append(")?");
                 digits(s, dfs);
+                s.append("*");
+                s.append("%?");
                 s.append(")");
                 return s.toString();
             } else {
@@ -302,7 +308,7 @@ public final class ResolveDialect {
             s.append("[0-9");
             s.append(dfs.getDecimalSeparator());
             s.append(dfs.getGroupingSeparator());
-            s.append("]+");
+            s.append("]");
             return s;
         }
 
@@ -312,8 +318,8 @@ public final class ResolveDialect {
         }
 
         @Override
-        public Object parse(String source, ParsePosition position) {
-            return format.parse(source, position);
+        public Object parse(String source) throws ParseException {
+            return format.parse(source);
         }
         
     }
@@ -338,8 +344,8 @@ public final class ResolveDialect {
         }
 
         @Override
-        public Object parse(String source, ParsePosition position) {
-            return format.parse(source, position);
+        public Object parse(String source) throws ParseException {
+            return format.parse(source);
         }
         
     }
@@ -364,8 +370,8 @@ public final class ResolveDialect {
         }
 
         @Override
-        public Object parse(String source, ParsePosition position) {
-            return format.parse(source, position);
+        public Object parse(String source) {
+            return LocalDateTime.parse(source, format);
         }
 
     }
@@ -392,8 +398,8 @@ public final class ResolveDialect {
         }
 
         @Override
-        public Object parse(String source, ParsePosition position) {
-            return format.parse(source, position);
+        public Object parse(String source) throws ParseException {
+            return format.parse(source);
         }
         
     }
@@ -433,7 +439,7 @@ public final class ResolveDialect {
         }
 
         @Override
-        public Object parse(String source, ParsePosition position) {
+        public Object parse(String source) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
 
@@ -467,7 +473,7 @@ public final class ResolveDialect {
         }
 
         @Override
-        public Object parse(String source, ParsePosition position) {
+        public Object parse(String source) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
         
@@ -518,7 +524,7 @@ public final class ResolveDialect {
         }
 
         @Override
-        public Object parse(String source, ParsePosition position) {
+        public Object parse(String source) {
             throw new UnsupportedOperationException("Not supported yet.");
         }
         
@@ -588,11 +594,8 @@ public final class ResolveDialect {
         }
         
         @Override
-        public void parse(String source, ParsePosition position, Object context) {
-            IClass type = resolver.type(context);
-            Object value = null;
-            resolver.set(context, value);
-            throw new UnsupportedOperationException("Not supported yet.");
+        public void parse(String source, Object context) {
+            resolver.set(context, LexicalCast.cast(source, resolver.type(context)));
         }
         
     }

+ 23 - 11
assira/src/main/java/net/ranides/assira/text/ResolveFormat.java

@@ -9,6 +9,7 @@ package net.ranides.assira.text;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.IOException;
 import java.io.Serializable;
+import java.text.ParseException;
 import java.text.ParsePosition;
 import java.util.ArrayList;
 import java.util.List;
@@ -20,7 +21,6 @@ import net.ranides.assira.generic.HashUtils;
 
 /**
  *
- * @todo (assira #0) ResolvePattern: formatting
  * Składnia wzorców jest identyczna jak składnia z MessageFormat.
  * Podajemy kolejno:
  *      { ArgumentIndex, Format, Pattern  }
@@ -57,6 +57,8 @@ public class ResolveFormat implements Serializable {
     
     private static final long serialVersionUID = 1L;
     
+    private static final Pattern  REGEX_SPECIAL = Pattern.compile("([\\\\*+\\[\\]()\\$.?\\^|])");
+    
     private static final Pattern RE_COMPILE = Pattern.compile("\\{(.+?)(?:,(.+?))?(?:,(.+?))?\\}");
     
     private final ResolveDialect dialect;
@@ -96,7 +98,9 @@ public class ResolveFormat implements Serializable {
         this.parsers = iparsers.toArray(new Token[iparsers.size()]);
         
         int[] index = new int[]{0};
-        this.scanre = Pattern.compile(StringUtils.replace(pattern, RE_COMPILE, h -> {
+        
+        String p = REGEX_SPECIAL.matcher(pattern).replaceAll("\\\\$1");
+        this.scanre = Pattern.compile(StringUtils.replace(p, RE_COMPILE, h -> {
             return parsers[index[0]++].regex();
         }));
     }
@@ -145,16 +149,16 @@ public class ResolveFormat implements Serializable {
         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);
+        try {
+            int i=0;
+            for(Token p : parsers) {
+                p.parse(hit.group(++i), context);
+            }
+        } catch(ParseException cause) {
+            throw new ResolveException(cause);
         }
     }
     
-    public void vparse(String input, Object... arguments) {
-        parse(input, (Object)arguments);
-    }
-    
     private Object writeReplace() {
         return new SResolveFormat(dialect.name(), pattern);
     }
@@ -189,7 +193,7 @@ public class ResolveFormat implements Serializable {
         
         void append(Appendable target, Object context) throws IOException;
 
-        void parse(String source, ParsePosition position, Object context);
+        void parse(String source, Object context) throws ParseException;
 
     }
     
@@ -212,7 +216,7 @@ public class ResolveFormat implements Serializable {
         }
             
         @Override
-        public void parse(String source, ParsePosition position, Object context) {
+        public void parse(String source, Object context) {
             throw new UnsupportedOperationException("Unreachable code.");
         }
                 
@@ -237,4 +241,12 @@ public class ResolveFormat implements Serializable {
 
     }
     
+    public static class ResolveException extends IllegalArgumentException {
+
+        public ResolveException(Throwable cause) {
+            super(cause);
+        }
+
+    }
+    
 }

+ 0 - 4
assira/src/main/java/net/ranides/assira/text/ResolveUtils.java

@@ -38,8 +38,4 @@ public final class ResolveUtils {
         ResolveFormat.compile(pattern).parse(input, context);
     }
     
-    public static void vparse(String pattern, String input, Object... arguments) {
-        ResolveFormat.compile(pattern).parse(input, arguments);
-    }
-    
 }

+ 79 - 8
assira/src/test/java/net/ranides/assira/text/ResolveFormatTest.java

@@ -7,11 +7,12 @@
 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.text.ChoiceFormat;
 import java.time.Instant;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.regex.Matcher;
@@ -90,8 +91,22 @@ public class ResolveFormatTest {
         assertEquals("-150'456.5",    ResolveFormat.compile("{fvalue,number,c++}").format(ITEM) );
     }
     
-        @Test
-    public void testParse_Number_int() {
+    @Test
+    public void testParse_Errors() {
+        Integer[] out= new Integer[3];
+        ResolveFormat rf = ResolveFormat.compile("{0,number} / {1,number,java} / {2,number,java}");
+        assertThrows(IllegalArgumentException.class, ()->{
+            rf.parse("12z / 33 / 55_100", out);
+        }); 
+        assertThrows(IllegalArgumentException.class, ()->{
+            rf.parse("12 33 / 55_100", out);
+        }); 
+        rf.parse("12 / 33 / 55_100", out);
+        rf.parse("12,4 / 33 / 55_100", out);
+    }
+    
+    @Test
+    public void testParse_Number() {
         Integer[] out= new Integer[3];
         ResolveFormat rf = ResolveFormat.compile("{0,number} / {1,number,java} / {2,number,java}");
         rf.parse("12 / 33 / 55_100", out);
@@ -102,8 +117,8 @@ public class ResolveFormatTest {
         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);
+        ResolveFormat.compile("US","{0,number} / {1,number,percent} / {2,number,java}").parse("14.3 / 35% / 55", out3);
+        assertArrayEquals(new Double[]{14.3, 0.35, 55.0}, out3);
     }
 
     
@@ -133,6 +148,37 @@ public class ResolveFormatTest {
         
     }
     
+    @Test
+    public void testParse_Date() {
+        Date[] out1 = new Date[5];
+        ResolveFormat
+                .compile("PL","{0,date} / {1,date,short} / {2,date,medium} / {3,date,long} / {4,date,full}")
+                .parse("2014-06-11 / 14-06-12 / 2014-06-13 / 14 czerwca 2014 / niedziela, 15 czerwca 2014", out1);
+        assertEquals(
+            "[Wed Jun 11 00:00:00 CEST 2014, Thu Jun 12 00:00:00 CEST 2014, Fri Jun 13 00:00:00 CEST 2014, Sat Jun 14 00:00:00 CEST 2014, Sun Jun 15 00:00:00 CEST 2014]",
+            Arrays.toString(out1)
+        );
+        
+        Date[] out2 = new Date[4];
+        ResolveFormat
+                .compile("PL","{0,date,iso} / {1,date,gnu} / {2,date,trace} / {3,date,local}")
+                .parse("2014-06-11T11:25Z / 201406121325 / 2014-06-13 14:25:10.000 / 2014-06-14 18:25:10", out2);
+        assertEquals(
+            "[Wed Jun 11 11:25:00 CEST 2014, Thu Jun 12 13:25:00 CEST 2014, Fri Jun 13 14:25:10 CEST 2014, Sat Jun 14 18:25:10 CEST 2014]",
+            Arrays.toString(out2)
+        );
+        
+        
+        Date[] out3 = new Date[5];
+        ResolveFormat
+            .compile("US","{0,date} | {1,date,short} | {2,date,medium} | {3,date,long} | {4,date,full}")
+            .parse("Jun 11, 2014 | 6/12/14 | Jun 13, 2014 | June 14, 2014 | Sunday, June 15, 2014", out3);
+        assertEquals(
+            "[Wed Jun 11 00:00:00 CEST 2014, Thu Jun 12 00:00:00 CEST 2014, Fri Jun 13 00:00:00 CEST 2014, Sat Jun 14 00:00:00 CEST 2014, Sun Jun 15 00:00:00 CEST 2014]",
+            Arrays.toString(out3)
+        );
+    }
+    
     @Test
     public void testFormat_Instant() {
         assertEquals("2014-06-15T11:25:10.777Z", ResolveFormat.compile("{date2}").format(ITEM) );
@@ -322,6 +368,30 @@ public class ResolveFormatTest {
         assertEquals("Hello 77 t world ?", r2);
     }
     
+    @Test
+    public void testFormat_Choice() {
+        ResolveFormat rf = ResolveFormat.compile("value = {0,number} {1,choice,-1#negative|0#zero|1#one|2#two|2<positive}");
+
+        assertEquals("value = -3 negative", rf.vformat(-3,-3));
+        assertEquals("value = -1 negative", rf.vformat(-1,-1));
+        assertEquals("value = 0 zero", rf.vformat(0,0));
+        assertEquals("value = 1,5 one", rf.vformat(1.5,1.5));
+        assertEquals("value = 3 positive", rf.vformat(3.0,3.0));
+        
+        Double[] out1 = new Double[2];
+        rf.parse("value = 3 negative", out1);
+        assertEquals("[3.0, -1.0]", Arrays.toString(out1));
+        
+        Double[] out2 = new Double[2];
+        rf.parse("value = 77 one", out2);
+        assertEquals("[77.0, 1.0]", Arrays.toString(out2));
+        
+        assertThrows(ResolveFormat.ResolveException.class, ()->{
+            rf.parse("value = 77 four", out2);
+        });
+        
+    }
+    
     private static final class Wrapper {
         public Object[] items;
 
@@ -372,9 +442,10 @@ public class ResolveFormatTest {
     }
     
     
-    // @todo (assira #2) test: ResolveFormat# parse
-    
-    // @todo (assira #3) test: ResolveFormat# choice pattern
+    // @todo (assira #0) test: ResolveFormat# parse FHexArray 
+    // @todo (assira #0) test: ResolveFormat# parse FHexStream  
+    // @todo (assira #0) test: ResolveFormat# parse FEngineer   
+    // @todo (assira #0) test: ResolveFormat# parse FGeneric    
     
     private static final Object ITEM = new Object(){
     

+ 22 - 2
assira/src/test/java/net/ranides/assira/text/ResolveUtilsTest.java

@@ -17,7 +17,7 @@ import static org.junit.Assert.*;
 public class ResolveUtilsTest {
     
     @Test
-    public void testFormat_Pattern_Array() {
+    public void testFormat_Array() {
         String r1 = ResolveUtils.format("Hello {1} world {2}{3}!", new int[]{17, 19, 47, 33});
         String r2 = ResolveUtils.vformat("Hello {1} world {2}{3}!", 17, 19, 47, 33);
         String r3 = ResolveUtils.vformat("Hello {0} world!", 17);
@@ -28,7 +28,7 @@ public class ResolveUtilsTest {
     }
     
     @Test
-    public void testFormat_Pattern_Array_Append() throws IOException {
+    public void testFormat_Append() throws IOException {
         StringBuilder r1 = new StringBuilder().append("r1 = ");
         StringBuilder r2 = new StringBuilder().append("r2 = ");
         StringBuilder r3 = new StringBuilder().append("r3 = ");
@@ -41,4 +41,24 @@ public class ResolveUtilsTest {
         assertEquals("r3 = Hello 17 world!", r3.toString());
     }
     
+    @Test
+    public void testCompileFormat_Array() {
+        String r2 = ResolveFormat.compile("Hello {1} world {2}{3}!").vformat(17, 19, 47, 33);
+        String r3 = ResolveFormat.compile("Hello {0} world!").vformat(17);
+        
+        assertEquals("Hello 19 world 4733!", r2);
+        assertEquals("Hello 17 world!", r3);
+    }
+    
+    @Test
+    public void testCompileFormat_Append() throws IOException {
+        StringBuilder r2 = new StringBuilder().append("r2 = ");
+        StringBuilder r3 = new StringBuilder().append("r3 = ");
+        ResolveFormat.compile("Hello {1} world {2}{3}!").vformat(r2, 17, 19, 47, 33);
+        ResolveFormat.compile("Hello {0} world!").vformat(r3, 17);
+        
+        assertEquals("r2 = Hello 19 world 4733!", r2.toString());
+        assertEquals("r3 = Hello 17 world!", r3.toString());
+    }
+    
 }