--- /dev/null
+package org.simantics.graph.store;\r
+\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.util.regex.Pattern;\r
+\r
+import org.simantics.graph.query.Path;\r
+import org.simantics.graph.query.PathChild;\r
+import org.simantics.graph.query.Paths;\r
+\r
+public class PathPattern {\r
+ \r
+ private static PathPattern EMPTY_PATTERN = new PathPattern(null, null);\r
+ \r
+ Path prefix;\r
+ Pattern suffix;\r
+ \r
+ private PathPattern(Path prefix, Pattern suffix) {\r
+ this.prefix = prefix;\r
+ this.suffix = suffix;\r
+ }\r
+\r
+ public static PathPattern compile(String pattern) {\r
+ pattern = stripPatternPrefix(pattern);\r
+ if(pattern == null)\r
+ return EMPTY_PATTERN;\r
+ String[] parts = pattern.split("/");\r
+ \r
+ Path path = Paths.Root;\r
+ for(int i=0;i<parts.length;++i) {\r
+ String part = parts[i];\r
+ if(containsWildcards(part)) {\r
+ StringBuilder b = new StringBuilder(pattern.length());\r
+ for(;i<parts.length;++i) {\r
+ b.append('/');\r
+ b.append(parts[i]);\r
+ }\r
+ return new PathPattern(path, compileGlobPattern(b.toString()));\r
+ }\r
+ else\r
+ path = new PathChild(part, path);\r
+ }\r
+ \r
+ return new PathPattern(path, null);\r
+ }\r
+ \r
+ private static String patternStart = "http://";\r
+ private static String stripPatternPrefix(String pattern) {\r
+ for(int i=0;i<patternStart.length();++i) {\r
+ if(pattern.length() <= i)\r
+ return null;\r
+ char c = pattern.charAt(i);\r
+ if(c == '*')\r
+ return pattern.substring(i);\r
+ if(c != patternStart.charAt(i) && c != '?')\r
+ return null;\r
+ }\r
+ return pattern.substring(patternStart.length());\r
+ }\r
+ \r
+ private static boolean containsWildcards(String pattern) {\r
+ return pattern.contains("*") || pattern.contains("?");\r
+ }\r
+ \r
+ private static Pattern compileGlobPattern(String pattern) {\r
+ int length = pattern.length();\r
+ StringBuilder b = new StringBuilder(2*length);\r
+ b.append("\\Q");\r
+ for(int i=0;i<length;++i) {\r
+ char c = pattern.charAt(i);\r
+ switch(c) {\r
+ case '*':\r
+ b.append("\\E.*\\Q");\r
+ break;\r
+ case '?':\r
+ b.append("\\E.\\Q");\r
+ break;\r
+ case '\\':\r
+ ++i;\r
+ if(i < length) {\r
+ c = pattern.charAt(i);\r
+ if(c == '\\' && i+1 < length && pattern.charAt(i+1) == 'E') {\r
+ ++i;\r
+ b.append("\\E\\\\E\\Q");\r
+ }\r
+ else if(c == '*' || c == '?')\r
+ b.append(c);\r
+ else {\r
+ b.append('\\');\r
+ b.append(c);\r
+ }\r
+ }\r
+ break;\r
+ default:\r
+ b.append(c);\r
+ }\r
+ }\r
+ b.append("\\E");\r
+ return Pattern.compile(b.toString());\r
+ }\r
+ \r
+ @Override\r
+ public String toString() {\r
+ return "(" + prefix + ", " + suffix + ")";\r
+ }\r
+ \r
+ public void search(IdentityStore store, THashSet<Path> result) {\r
+ int id = store.pathToId(prefix);\r
+ if(id == -1)\r
+ return;\r
+ store.findChildren(id, prefix, "", suffix, result);\r
+ }\r
+ \r
+ public static void main(String[] args) {\r
+ System.out.println(compile("http://www.simantics.org/*/foo"));\r
+ }\r
+}\r