Initial Java Trie

This commit is contained in:
Tommi Reiman 2019-01-08 10:03:06 +02:00
parent 35ff62a1da
commit 2a1fea2ccb
2 changed files with 160 additions and 5 deletions

View file

@ -0,0 +1,148 @@
package reitit;
import clojure.lang.Keyword;
import java.util.*;
import static java.util.Arrays.asList;
public class Trie {
public static class Match {
public Map<Keyword, String> params = new HashMap<>();
public Object data;
@Override
public String toString() {
Map<Object, Object> m = new HashMap<>();
m.put(Keyword.intern("data"), data);
m.put(Keyword.intern("params"), params);
return m.toString();
}
}
private Map<String, Trie> childs = new HashMap<>();
private Map<Keyword, Trie> wilds = new HashMap<>();
private Map<Keyword, Trie> catchAll = new HashMap<>();
private Object data;
@Override
public String toString() {
Map<Object, Object> m = new HashMap<>();
m.put(Keyword.intern("childs"), childs);
m.put(Keyword.intern("wilds"), wilds);
m.put(Keyword.intern("catchAll"), catchAll);
m.put(Keyword.intern("data"), data);
return m.toString();
}
public static Match lookup(Trie root, String path) {
return lookup(root, new Match(), split(path));
}
private static Match lookup(Trie root, Match match, List<String> parts) {
Trie childTrie = null;
if (parts.isEmpty()) {
return match;
} else {
Trie trie = root;
int i = 0;
for (final String part : parts) {
i++;
childTrie = trie.childs.get(part);
if (childTrie != null) {
trie = childTrie;
} else {
for (final Map.Entry<Keyword, Trie> e : trie.wilds.entrySet()) {
childTrie = e.getValue();
match.data = childTrie.data;
Match m = lookup(childTrie, match, parts.subList(i, parts.size()));
if (m != null) {
match.params.put(e.getKey(), part);
return m;
}
}
for (Map.Entry<Keyword, Trie> e : trie.catchAll.entrySet()) {
childTrie = e.getValue();
match.params.put(e.getKey(), String.join("/", parts.subList(i - 1, parts.size())));
match.data = childTrie.data;
return match;
}
break;
}
}
}
if (childTrie != null) {
match.data = childTrie.data;
return match;
}
return null;
}
public Trie add(String path, Object data) {
List<String> paths = split(path);
Trie pointer = this;
for (String p : paths) {
if (p.startsWith(":")) {
Keyword k = Keyword.intern(p.substring(1));
Trie s = pointer.wilds.get(k);
if (s == null) {
s = new Trie();
pointer.wilds.put(k, s);
}
pointer = s;
} else if (p.startsWith("*")) {
Keyword k = Keyword.intern(p.substring(1));
Trie s = pointer.catchAll.get(k);
if (s == null) {
s = new Trie();
pointer.catchAll.put(k, s);
}
break;
} else {
Trie s = pointer.childs.get(p);
if (s == null) {
s = new Trie();
pointer.childs.put(p, s);
}
pointer = s;
}
}
pointer.data = data;
return this;
}
public static List<String> split(String path) {
ArrayList<String> strings = new ArrayList<>(asList(path.split("/")));
strings.remove(0);
return strings;
}
public static void main(String[] args) {
Trie trie =
new Trie()
.add("/kikka", 1)
.add("/kakka", 2)
.add("/api/ping", 3)
.add("/api/pong", 4)
.add("/api/ipa/ping", 5)
.add("/api/ipa/pong", 6)
.add("/users/:user-id", 7)
.add("/users/:user-id/orders", 8)
.add("/users/:user-id/price", 9)
.add("/orders/:id/price", 10)
.add("/orders/:super", 11)
.add("/orders/:super/hyper/:giga", 12);
//System.out.println(lookup(trie, split("/kikka")));
System.out.println(lookup(trie, "/orders/mies/hyper/peikko"));
System.out.println(lookup(
new Trie().add("/user/:id/profile/:type/", 1),
"/user/1/profile/compat"));
System.out.println(lookup(
new Trie().add("/user/*path", 1),
"/user/1/profile/compat"));
}
}

View file

@ -1,7 +1,8 @@
(ns reitit.segment (ns reitit.segment
(:refer-clojure :exclude [-lookup]) (:refer-clojure :exclude [-lookup])
(:require [reitit.impl :as impl] (:require [reitit.impl :as impl]
[clojure.string :as str])) [clojure.string :as str])
#?(:clj (:import (reitit Trie Trie$Match))))
(defrecord Match [data path-params]) (defrecord Match [data path-params])
@ -44,13 +45,19 @@
(if catch-all (-catch-all children' catch-all path-params p ps))))))))) (if catch-all (-catch-all children' catch-all path-params p ps)))))))))
(defn insert [root path data] (defn insert [root path data]
(-insert (or root (segment)) (impl/segments path) (map->Match {:data data}))) #?(:cljs (-insert (or root (segment)) (impl/segments path) (map->Match {:data data}))
:clj (.add (or ^Trie root ^Trie (Trie.)) ^String path data)))
(defn create [paths] (defn create [paths]
(reduce (reduce
(fn [segment [p data]] (fn [segment [p data]]
(insert segment p data)) #?(:cljs (insert segment p data)
nil paths)) :clj (.add ^Trie segment ^String p data)))
#?(:cljs nil
:clj (Trie.))
paths))
(defn lookup [segment path] (defn lookup [segment path]
(-lookup segment (impl/segments path) {})) #?(:cljs (-lookup segment (impl/segments path) {})
:clj (if-let [match ^Trie$Match (Trie/lookup segment path)]
(->Match (.data match) (clojure.lang.PersistentHashMap/create (.params match))))))