Prechádzať zdrojové kódy

ResolveFormat: text padding

Ranides Atterwim 5 rokov pred
rodič
commit
a1db3830eb

+ 108 - 9
assira/src/main/java/net/ranides/assira/text/ResolveLocale.java

@@ -28,6 +28,7 @@ import java.time.format.DateTimeFormatter;
 import java.util.Date;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.TimeZone;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -67,15 +68,18 @@ public final class ResolveLocale {
         DecimalFormatSymbols us = DecimalFormatSymbols.getInstance(Locale.US);
         
         putFormat("", "", r -> new FGeneric(r));
-        
-        // number       : 2 + 3      5
-        // date         : 2 + 8     10
-        // time         : 2 + 8     10
-        // hex          : 1 + 2      3
-        // metric       : 1 + 4      5
-        // binary       : 1 + 6      7
-        //              :           40 x language
-        
+
+        // number of entries:
+        //  text         : 2 + 0      2
+        //  number       : 2 + 5      7
+        //  date         : 2 + 8     10
+        //  time         : 2 + 8     10
+        //  hex          : 1 + 2      3
+        //  metric       : 1 + 4      5
+        //  binary       : 1 + 6      7
+        //               :           44 x language
+        
+        putText(locale);
         putNumber(us, locale);
         putDate(locale);
         putTime(locale);
@@ -85,6 +89,11 @@ public final class ResolveLocale {
         putChoice();
     }
 
+    private void putText(Locale locale) {
+        putFormat("format", (r,p) -> new FPrintf(r, p));
+        putFormat("text", (r,p) -> new FText(r, p, locale));
+    }
+
     private void putNumber(DecimalFormatSymbols us, Locale locale) {
         putFormat("number",          (r,p) -> new FNumber(r, new DecimalFormat(p,us)));
         putFormat("number", "",          r -> new FNumber(r, DecimalFormat.getInstance(locale)));
@@ -674,5 +683,95 @@ public final class ResolveLocale {
         }
         
     }
+
+    private static final class FPrintf implements ResolveFormat.Token {
+
+        private final ResolvePattern resolver;
+
+        private final String pattern;
+
+        public FPrintf(ResolvePattern resolver, String pattern) {
+            this.resolver = resolver;
+            this.pattern = pattern;
+        }
+
+        @Override
+        public String regex() {
+            return "(.+)";
+        }
+
+        @Override
+        public void append(Appendable target, Object context) throws IOException {
+            target.append(String.format(pattern, resolver.get(context)));
+        }
+
+        @Override
+        public void parse(String source, Object context) {
+            resolver.set(context, LexicalCast.cast(source, resolver.type(context)));
+        }
+
+    }
+
+    private static final class FText implements ResolveFormat.Token {
+
+        private final ResolvePattern resolver;
+        private final Locale locale;
+        private final boolean left;
+        private final int min;
+        private final int max;
+        private final boolean upper;
+        private final boolean lower;
+
+        public FText(ResolvePattern resolver, String options, Locale locale) {
+            this.resolver = resolver;
+            this.locale = locale;
+            Matcher hit = Pattern.compile("(?<padd>[-+])?(?<min>[0-9]+)(?:.(?<max>[0-9]+))?(?<upper>[uU])?(?<lower>[lLsS])?").matcher(options);
+            if(!hit.matches()) {
+                throw new IllegalArgumentException("Invalid text format: " + options);
+            }
+            this.left = Objects.equals("-", hit.group("padd"));
+            this.min = Integer.parseInt(ValueUtils.or(hit.group("min"), "0"));
+            this.max = Integer.parseInt(ValueUtils.or(hit.group("max"), "0"));
+            this.upper = hit.group("upper") != null;
+            this.lower = hit.group("lower") != null;
+        }
+
+        @Override
+        public String regex() {
+            return "(.+)";
+        }
+
+        @Override
+        public void append(Appendable target, Object context) throws IOException {
+            String text = String.valueOf(resolver.get(context));
+            if(upper) {
+                text = text.toUpperCase(locale);
+            }
+            if(lower) {
+                text = text.toLowerCase(locale);
+            }
+            if (max > 0 && text.length() > max) {
+                text = text.substring(0, max);
+            }
+            if (text.length() < min) {
+                CharSequence padding = StringUtils.repeat(min - text.length(), ' ');
+                if (left) {
+                    target.append(text).append(padding);
+                } else {
+                    target.append(padding).append(text);
+                }
+            } else {
+                target.append(text);
+            }
+        }
+
+        @Override
+        public void parse(String source, Object context) {
+            resolver.set(context, LexicalCast.cast(source, resolver.type(context)));
+        }
+
+    }
+
+
     
 }

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

@@ -15,6 +15,7 @@ import java.time.Instant;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.List;
 import java.util.TimeZone;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Matcher;
@@ -543,6 +544,28 @@ public class ResolveFormatTest {
         });
         
     }
+
+    @Test
+    public void testFormat_printf() {
+        ResolveFormat rf = ResolveFormat.compile("'{x,format,%5s}'/'{y,format,%-5s}'/'{z,format,%6.6s}'");
+
+        assertEquals("'   17'/'25   '/'781234'", rf.format(new A(17, 25, 781234567)));
+    }
+
+    @Test
+    public void testFormat_text() {
+        A value1 = new A(17, 25, 781234567);
+        List<String> value2 = Arrays.asList("hE", "woR");
+
+        ResolveFormat rf1 = ResolveFormat.compile("'{x,text,5}'/'{y,text,-5}'/'{z,text,6.6}'");
+        ResolveFormat rf2 = ResolveFormat.compile("'{0,text,5u}'/'{1,text,-5s}'");
+
+        assertEquals("'   17'/'25   '/'781234'", value1);
+        assertEquals("'   HE'/'wor  '", rf2.format(value2));
+
+        assertEquals("  HEL", ResolveFormatUtils.vformat("{0,text,5u}", "Hel"));
+        assertEquals("hel  ", ResolveFormatUtils.vformat("{0,text,-5s}", "Hel"));
+    }
     
     private static final class Wrapper {
         public Object[] items;