mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 00:11:11 +00:00
Initial Java Trie
This commit is contained in:
parent
35ff62a1da
commit
2a1fea2ccb
2 changed files with 160 additions and 5 deletions
148
modules/reitit-core/java-src/reitit/Trie.java
Normal file
148
modules/reitit-core/java-src/reitit/Trie.java
Normal 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"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
(ns reitit.segment
|
||||
(:refer-clojure :exclude [-lookup])
|
||||
(:require [reitit.impl :as impl]
|
||||
[clojure.string :as str]))
|
||||
[clojure.string :as str])
|
||||
#?(:clj (:import (reitit Trie Trie$Match))))
|
||||
|
||||
(defrecord Match [data path-params])
|
||||
|
||||
|
|
@ -44,13 +45,19 @@
|
|||
(if catch-all (-catch-all children' catch-all path-params p ps)))))))))
|
||||
|
||||
(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]
|
||||
(reduce
|
||||
(fn [segment [p data]]
|
||||
(insert segment p data))
|
||||
nil paths))
|
||||
#?(:cljs (insert segment p data)
|
||||
:clj (.add ^Trie segment ^String p data)))
|
||||
#?(:cljs nil
|
||||
:clj (Trie.))
|
||||
paths))
|
||||
|
||||
(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))))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue