diff --git a/src/main/java/pro/komaru/tridot/core/struct/Structs.java b/src/main/java/pro/komaru/tridot/core/struct/Structs.java
index d21879d..2ce7b7c 100644
--- a/src/main/java/pro/komaru/tridot/core/struct/Structs.java
+++ b/src/main/java/pro/komaru/tridot/core/struct/Structs.java
@@ -1,6 +1,7 @@
package pro.komaru.tridot.core.struct;
+import pro.komaru.tridot.core.struct.data.Seq;
import pro.komaru.tridot.core.struct.func.Cons;
import pro.komaru.tridot.core.struct.func.Func;
import pro.komaru.tridot.core.struct.func.Prov;
@@ -14,6 +15,9 @@ public static Prov nil(){
return () -> null;
}
+ public static A cast(B obj) {
+ return (A) obj;
+ }
public static T or(T a, T b) {
if(a == null) return b;
return a;
@@ -54,6 +58,11 @@ public static HashMap map(Object... objs) {
}
return map;
}
+ public static A[] pop(A[] def) {
+ var n = Seq.with(def);
+ n.slice();
+ return n.toArray();
+ }
public static HashMap revMap(HashMap mapOrig) {
HashMap map = new HashMap<>();
mapOrig.forEach((k,v) -> map.put(v,k));
diff --git a/src/main/java/pro/komaru/tridot/core/struct/data/Seq.java b/src/main/java/pro/komaru/tridot/core/struct/data/Seq.java
index d83efa7..751b173 100644
--- a/src/main/java/pro/komaru/tridot/core/struct/data/Seq.java
+++ b/src/main/java/pro/komaru/tridot/core/struct/data/Seq.java
@@ -108,6 +108,11 @@ public static Seq withArrays(Object... arrays){
return result;
}
+ /** @see #Seq(Object[]) */
+ public static Seq with(){
+ return new Seq<>();
+ }
+
/** @see #Seq(Object[]) */
public static Seq with(T... array){
return new Seq<>(array);
diff --git a/src/main/java/pro/komaru/tridot/core/struct/data/tree/ContainerTree.java b/src/main/java/pro/komaru/tridot/core/struct/data/tree/ContainerTree.java
new file mode 100644
index 0000000..344a070
--- /dev/null
+++ b/src/main/java/pro/komaru/tridot/core/struct/data/tree/ContainerTree.java
@@ -0,0 +1,198 @@
+package pro.komaru.tridot.core.struct.data.tree;
+
+import pro.komaru.tridot.core.struct.data.Seq;
+import pro.komaru.tridot.core.struct.func.Boolf;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+@SuppressWarnings("rawtypes,unchecked")
+public class ContainerTree implements ITree {
+ private final Seq children;
+ private final Seq> containers;
+
+ public ContainerTree parent = null;
+
+ public ContainerTree() {
+ children = Seq.with();
+ containers = Seq.with();
+ }
+
+
+ /**
+ * Adds all elements as children of this container tree
+ * @param all elements to add
+ */
+ @SafeVarargs
+ public final ContainerTree add(A... all) {
+ children.add(all);
+
+ return this;
+ }
+ /**
+ * Adds element as a child of this container tree
+ * @param child element to add
+ */
+ public ContainerTree add(A child) {
+ children.add(child);
+ return this;
+ }
+
+ /**
+ * Adds element as a subcontainer of this container tree
+ * @param child element to add
+ */
+ public ContainerTree add(ContainerTree child) {
+ containers.add(child);
+ child.parent = this;
+ return this;
+ }
+
+ /**
+ * Adds all elements as children of this container tree if they don't exist yet
+ * @param all elements to add
+ */
+ @SafeVarargs
+ public final ContainerTree addUnique(A... all) {
+ for (A a : all) {
+ addUnique(a);
+ }
+ return this;
+ }
+ /**
+ * Adds element as a child of this container tree if it doesn't exist yet
+ * @param child element to add
+ */
+ public ContainerTree addUnique(A child) {
+ children.addUnique(child);
+ return this;
+ }
+
+ /**
+ * Finds a child of this container using a filter function
+ * @param filter filter function
+ * @return child object
+ */
+ @Override
+ public A child(Boolf filter) {
+ return children().find(filter);
+ }
+ /**
+ * Finds a child in all containers of this tree using a filter function
+ * @param filter filter function
+ * @return child object
+ */
+ @Override
+ public A childDeep(Boolf filter,boolean depthSearch) {
+ A child = children().find(filter);
+ if(depthSearch) {
+ if (child == null) {
+ for (ITree container : containers) {
+ A other = container.childDeep(filter);
+ if (other != null) {
+ return other;
+ }
+ }
+ }
+ } else {
+ Queue> queue = new LinkedList<>();
+ queue.add(this);
+
+ while (!queue.isEmpty()) {
+ ContainerTree current = queue.poll();
+ A other = current.children().find(filter);
+ if (other != null) {
+ return other;
+ }
+ queue.addAll(current.containers.list());
+ }
+ }
+ return child;
+ }
+
+ /**
+ * Finds a child in all containers of this tree using a filter function, in a BFS method
+ * @param filter filter function
+ * @return child object
+ */
+ @Override
+ public A childDeep(Boolf filter) {
+ return childDeep(filter,false);
+ }
+
+ /**
+ * Clears all tree
+ */
+ @Override
+ public void clean() {
+ children().clear();
+ containers.each(e -> e.parent = null); //bye-bye!
+ containers().clear();
+ }
+
+
+ /**
+ * @return Children Seq
+ */
+ @Override
+ public Seq children() {
+ return children;
+ }
+
+ /**
+ * @return Children of this tree and subcontainers' children
+ */
+ @Override
+ public Seq childrenDeep() {
+ Seq all = Seq.with();
+ all.addAll(children());
+ for (ITree container : containers())
+ all.addAll(container.childrenDeep());
+ return all;
+ }
+
+ /**
+ * @return Subcontainer Seq
+ */
+ public Seq> containers() {
+ return containers;
+ }
+
+ /**
+ * @return All containers of this tree
+ */
+ public Seq> containersDeep() {
+ Seq> all = Seq.with();
+ all.addAll(containers());
+ for (ContainerTree other : containers())
+ all.addAll(other.containers);
+ return all;
+ }
+
+
+ @Override
+ public String toString() {
+ String name = this instanceof NamedContTree> a ? a.name : "***";
+ StringBuilder sb = new StringBuilder("\u001B[33m[" + name + "]\u001B[0m");
+ sb.append("\n");
+ for (ContainerTree cont : containers) {
+ Seq lines = Seq.with(cont.toString().lines().map(e -> " " + e).toList());
+ if(!lines.isEmpty()) {
+ lines.set(0,lines.get(0).replaceFirst(" ",""));
+ }
+ sb.append("\u001B[33m-\u001B[0m");
+ sb.append(String.join("\n", lines));
+ sb.append("\n");
+ }
+ for (A child : children) {
+ Seq lines = Seq.with(child.toString().lines().map(e -> " " + e).toList());
+ if(!lines.isEmpty()) {
+ lines.set(0,lines.get(0).replaceFirst(" ",""));
+ }
+ sb.append("\u001B[37m*\u001B[0m");
+ sb.append(String.join("\n", lines));
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/pro/komaru/tridot/core/struct/data/tree/INamedTree.java b/src/main/java/pro/komaru/tridot/core/struct/data/tree/INamedTree.java
new file mode 100644
index 0000000..3decc86
--- /dev/null
+++ b/src/main/java/pro/komaru/tridot/core/struct/data/tree/INamedTree.java
@@ -0,0 +1,5 @@
+package pro.komaru.tridot.core.struct.data.tree;
+
+public interface INamedTree {
+ String name();
+}
diff --git a/src/main/java/pro/komaru/tridot/core/struct/data/tree/ITree.java b/src/main/java/pro/komaru/tridot/core/struct/data/tree/ITree.java
new file mode 100644
index 0000000..c9213b0
--- /dev/null
+++ b/src/main/java/pro/komaru/tridot/core/struct/data/tree/ITree.java
@@ -0,0 +1,35 @@
+package pro.komaru.tridot.core.struct.data.tree;
+
+import pro.komaru.tridot.core.struct.data.Seq;
+import pro.komaru.tridot.core.struct.func.Boolf;
+
+public interface ITree {
+ Seq children();
+ Seq childrenDeep();
+
+ A child(Boolf filter);
+
+
+ /**
+ * Deepfinding a child using a filter in either BFS or DFS
+ * @param filter filter function
+ * @param depthSearch if true, then it uses DFS instead of BFS
+ * @return child object
+ */
+ A childDeep(Boolf filter, boolean depthSearch);
+
+ default A childDeep(Boolf filter) {
+ return childDeep(filter,false);
+ };
+ default A child(Boolf filter, boolean deep, boolean depthSearch) {
+ return deep ? childDeep(filter,depthSearch) : child(filter);
+ }
+ default A child(Boolf filter, boolean deep) {
+ return child(filter,deep,false);
+ }
+
+ /**
+ * Clears all tree
+ */
+ void clean();
+}
diff --git a/src/main/java/pro/komaru/tridot/core/struct/data/tree/NamedContTree.java b/src/main/java/pro/komaru/tridot/core/struct/data/tree/NamedContTree.java
new file mode 100644
index 0000000..97b38a9
--- /dev/null
+++ b/src/main/java/pro/komaru/tridot/core/struct/data/tree/NamedContTree.java
@@ -0,0 +1,80 @@
+package pro.komaru.tridot.core.struct.data.tree;
+
+import pro.komaru.tridot.core.struct.Structs;
+import pro.komaru.tridot.core.struct.data.Seq;
+
+import java.util.HashMap;
+
+public class NamedContTree extends ContainerTree implements INamedTree {
+ public final String name;
+
+ private final HashMap PATH_CACHE =
+ new HashMap<>();
+
+ public NamedContTree(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public NamedContTree add(A child) {
+ return (NamedContTree) super.add(child);
+ }
+
+ @Override
+ public NamedContTree add(ContainerTree child) {
+ return (NamedContTree) super.add(child);
+ }
+
+ @Override
+ public NamedContTree addUnique(A child) {
+ return (NamedContTree) super.addUnique(child);
+ }
+
+ public NamedContTree cd(boolean autoCreate, String... path) {
+ if(path == null || path.length == 0) return this;
+ if(path[0].equals("..")) return ((NamedContTree) parent).cd(autoCreate,Structs.pop(path));
+ for (NamedContTree cont : namedContainers()) {
+ if(cont.name.equals(path[0]))
+ return cont.cd(autoCreate,Structs.pop(path));
+ }
+ if(autoCreate) {
+ NamedContTree newTree = new NamedContTree<>(path[0]);
+ containers().add(newTree);
+ newTree.parent = this;
+ return newTree.cd(true,Structs.pop(path));
+ }
+ return null;
+ }
+ public NamedContTree cd(boolean autoCreate,String path) {
+ return cd(autoCreate,path.split("/"));
+ }
+ public NamedContTree cd(String... path) {
+ return cd(true,path);
+ }
+ public NamedContTree cd(String path) {
+ return cd(true,path);
+ }
+
+ public static void main(String[] args) {
+ NamedContTree main = new NamedContTree<>("main");
+ main
+ .cd("a")
+ .add("A section!")
+ .cd("../b")
+ .add("B section!")
+ .cd("../a/c")
+ .add("C section!")
+ .cd("../..")
+ .add("End section!");
+ System.out.println(main);
+ }
+
+ public Seq> namedContainers() {
+ return Structs.cast(containers().select(e -> e instanceof NamedContTree));
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+}