Jelajahi Sumber

fix: ResolvePattern: error on non-existent map keys
fix: ResolveDialect.SHELL - it is usable now
new: RuntimeUtils#expand

Mariusz Sieroń 6 tahun lalu
induk
melakukan
c1ce4320ce

+ 4 - 1
assira/src/main/java/net/ranides/assira/reflection/ResolvePattern.java

@@ -257,7 +257,10 @@ public class ResolvePattern implements Serializable {
                 return that!=IClass.NULL && RCompareUtils.isRawSuper(IClass.typeinfo(Map.class), that);
             }
             @Override
-            public Object get(ResolveStatus _s, Object that, String name) {
+            public Object get(ResolveStatus status, Object that, String name) {
+                if(!((Map<String,Object>)that).containsKey(name)) {
+                    status.error = true;
+                }
                 return ((Map<?,?>)that).get(name);
             }
             @Override

+ 10 - 3
assira/src/main/java/net/ranides/assira/system/RuntimeUtils.java

@@ -7,14 +7,17 @@
 package net.ranides.assira.system;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import net.ranides.assira.generic.ValueUtils;
+import net.ranides.assira.text.FormatNumber;
+import net.ranides.assira.text.ResolveDialect;
+import net.ranides.assira.text.ResolveFormat;
+import net.ranides.assira.trace.LoggerUtils;
+
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.LinkedList;
 import java.util.List;
-import net.ranides.assira.generic.ValueUtils;
-import net.ranides.assira.text.FormatNumber;
-import net.ranides.assira.trace.LoggerUtils;
 
 /**
  *
@@ -101,4 +104,8 @@ public final class RuntimeUtils {
         return Double.parseDouble(getProperty("java.specification.version", "0"));
     }
 
+    public static String expand(String value) {
+        return ResolveFormat.compile(ResolveDialect.SHELL, value).format(System.getenv());
+    }
+
 }

+ 18 - 5
assira/src/main/java/net/ranides/assira/text/ResolveDialect.java

@@ -8,20 +8,33 @@ public enum ResolveDialect {
 
     DEFAULT(ResolveStrategy.FORMAT, "(\\{)(.+?)(?:,(.+?))?(?:,(.+?))?(\\})"),
     HANDLEBARS(ResolveStrategy.HANDLEBARS, "(\\{{2,3})(.+?)(?:/(.+?))?(?:/(.+?))?(\\}{2,3})"),
-    SHELL(ResolveStrategy.THROW, "\\$(\\{)(.+?)(?:,(.+?))?(?:,(.+?))?(\\})"),
+    SHELL(ResolveStrategy.THROW, "\\$(\\{)(.+?)(?:,(.+?))?(?:,(.+?))?(\\})", "\\\\\\$(\\{)(.+?)(?:,(.+?))?(?:,(.+?))?(\\})"),
     ;
 
-    private final Pattern pattern;
+    private final Pattern opattern;
+
+    private final Pattern ipattern;
 
     private final ResolveStrategy strategy;
 
     ResolveDialect(ResolveStrategy strategy, String pattern) {
         this.strategy = strategy;
-        this.pattern = Pattern.compile(pattern);
+        this.opattern = Pattern.compile(pattern);
+        this.ipattern = Pattern.compile(pattern);
+    }
+
+    ResolveDialect(ResolveStrategy strategy, String opattern, String ipattern) {
+        this.strategy = strategy;
+        this.opattern = Pattern.compile(opattern);
+        this.ipattern = Pattern.compile(ipattern);
+    }
+
+    public Pattern opattern() {
+        return opattern;
     }
 
-    public Pattern pattern() {
-        return pattern;
+    public Pattern ipattern() {
+        return ipattern;
     }
 
     public ResolveStrategy strategy() {

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

@@ -77,7 +77,7 @@ public class ResolveFormat implements Serializable {
 
         List<Token> itokens = new ArrayList<>();
         List<Token> iparsers = new ArrayList<>();
-        Matcher hit = dialect.pattern().matcher(pattern);
+        Matcher hit = dialect.opattern().matcher(pattern);
         int last = 0;
         while(hit.find()) {
             if(hit.group(1).length() != hit.group(5).length()) {
@@ -103,7 +103,7 @@ public class ResolveFormat implements Serializable {
         int[] index = new int[]{0};
         
         String p = REGEX_SPECIAL.matcher(pattern).replaceAll("\\\\$1");
-        String p2 = StringUtils.replace(p, dialect.pattern(), h -> parsers[index[0]++].regex());
+        String p2 = StringUtils.replace(p, dialect.ipattern(), h -> parsers[index[0]++].regex());
 
         if(dialect != ResolveDialect.DEFAULT) {
             p2 = p2.replaceAll("([{}])", "\\\\$1");

+ 9 - 2
assira/src/test/java/net/ranides/assira/reflection/ResolvePatternTest.java

@@ -101,6 +101,9 @@ public class ResolvePatternTest {
         assertEquals((Integer)801, ResolveUtils.get(context, "content.item.hello"));
         assertEquals((Long)7L, ResolveUtils.get(context, "list[1].numbers.seven"));
         assertNull(ResolveUtils.get(context, "list[1].numbers.eight"));
+        assertThrows(ResolveException.class, () -> {
+            ResolveUtils.get(context, "list[1].numbers.nine");
+        });
         assertEquals("B", ResolveUtils.get(context, "list[1].name"));
         assertEquals("b", ResolveUtils.get(context, "array[1].name"));
         
@@ -113,6 +116,7 @@ public class ResolvePatternTest {
         Map<String,Integer> newmap1 = new HashMap<>();
         Map<String,Integer> newmap2 = new HashMap<>();
         newmap1.put("eight", 8);
+        newmap1.put("nine", null);
         ResolveUtils.set(context, "list[1].numbers", newmap1);
         assertSame(newmap1, ResolveUtils.replace(context, "list[1].numbers", newmap2));
         assertSame(newmap2, context.list.get(1).numbers);
@@ -155,8 +159,11 @@ public class ResolvePatternTest {
 
         // get: (edge case) key handling if context is a map
         assertEquals(8, ResolveUtils.get(newmap1, "eight"));
-        assertNull(ResolveUtils.get(newmap1, "unknown"));
-        
+        assertNull(ResolveUtils.get(newmap1, "nine"));
+        assertThrows(ResolveException.class, () -> {
+            ResolveUtils.get(newmap1, "unknown");
+        });
+
         // set: (edge case) property handling
         ResolveUtils.set(context, "x", 71);
         ResolveUtils.set(context, ".y", 72);

+ 1 - 0
assira/src/test/java/net/ranides/assira/reflection/mockup/ForResolver.java

@@ -57,6 +57,7 @@ public final class ForResolver {
             this.numbers.put("zero", 0);
             this.numbers.put("two", 2);
             this.numbers.put("seven", 7L);
+            this.numbers.put("eight", null);
         }
         
     }

+ 23 - 0
assira/src/test/java/net/ranides/assira/system/RuntimeUtilsTest.java

@@ -0,0 +1,23 @@
+package net.ranides.assira.system;
+
+import net.ranides.assira.junit.NewAssert;
+import net.ranides.assira.reflection.ResolveException;
+import org.junit.Assume;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class RuntimeUtilsTest {
+
+    @Test
+    public void expand() {
+        Assume.assumeNotNull(System.getenv("USERPROFILE"));
+
+        String expected = System.getenv("USERPROFILE") + "/dir/file.txt";
+        assertEquals(expected, RuntimeUtils.expand("${USERPROFILE}/dir/file.txt"));
+
+        NewAssert.assertThrows(ResolveException.class, ()->{
+            System.out.println(RuntimeUtils.expand("${USERPRO2FILE}/ssh/id_rsa"));
+        });
+    }
+}