diff --git a/src/de/steamwar/sql/SchemElo.java b/src/de/steamwar/sql/SchemElo.java index 3aedaba..83d78a6 100644 --- a/src/de/steamwar/sql/SchemElo.java +++ b/src/de/steamwar/sql/SchemElo.java @@ -22,6 +22,7 @@ package de.steamwar.sql; import de.steamwar.sql.internal.Field; import de.steamwar.sql.internal.SelectStatement; import de.steamwar.sql.internal.Table; +import de.steamwar.sql.v2.SchematicNode; import lombok.AllArgsConstructor; @AllArgsConstructor diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index cc62acc..e249817 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -19,134 +19,43 @@ package de.steamwar.sql; -import de.steamwar.sql.internal.*; -import lombok.AccessLevel; -import lombok.Setter; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; import java.sql.Timestamp; -import java.time.Instant; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Collectors; +@Deprecated public class SchematicNode { - static { - new SqlTypeMapper<>(SchematicNode.class, null, (rs, identifier) -> { throw new SecurityException("SchematicNode cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.nodeId)); + private static int nullableInt(Integer i) { + return i == null ? 0 : i; } - - private static final Map>> TAB_CACHE = new HashMap<>(); - public static void clear() { - TAB_CACHE.clear(); - } - - private static final String[] fields = {"NodeId", "NodeOwner", "NodeName", "ParentNode", "LastUpdate", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay", "NodeFormat"}; - private static String nodeSelectCreator(String itemPrefix) { - return "SELECT " + Arrays.stream(fields).map(s -> itemPrefix + s).collect(Collectors.joining(", ")) + " FROM SchematicNode "; - } - - private static final Table table = new Table<>(SchematicNode.class); - private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType"); - private static final Statement update = table.insertAll(); - private static final Statement delete = table.delete(Table.PRIMARY); - - private static final SelectStatement byId = table.select(Table.PRIMARY); - private static final SelectStatement byOwnerNameParent = table.select("OwnerNameParent"); - private static final SelectStatement byOwnerNameParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode is NULL"); - private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? ORDER BY NodeName"); - private static final SelectStatement byParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL ORDER BY NodeName"); - private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? AND NodeType is NULL ORDER BY NodeName"); - private static final SelectStatement dirsByParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL AND NodeType is NULL ORDER BY NodeName"); - private static final SelectStatement byParentName = table.selectFields("NodeName", "ParentNode"); - private static final SelectStatement byParentName_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeName = ? AND ParentNode is NULL"); - private static final SelectStatement accessibleByUserTypeParent = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode = ? ORDER BY NodeName"); - private static final SelectStatement accessibleByUserTypeParent_Null = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode is null ORDER BY NodeName"); - private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? ORDER BY NodeName"); - private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); - private static final SelectStatement byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName"); - private static final SelectStatement accessibleByUser = new SelectStatement<>(table, nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) AND ((s.NodeOwner = ? AND s.ParentNode IS NULL) OR NOT s.NodeOwner = ?) GROUP BY s.NodeId ORDER BY s.NodeName"); - private static final Statement schematicAccessibleForUser = new Statement("WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT COUNT(RSN.NodeId) AS `Accessible` FROM RSN LEFT Join NodeMember NM On NM.NodeId = RSN.NodeId WHERE NodeOwner = ? OR UserId = ? LIMIT 1"); - private static final SelectStatement allAccessibleByUser = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); - private static final SelectStatement allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); - - @Field(keys = {Table.PRIMARY}, autoincrement = true) - private final int nodeId; - @Field(keys = {"OwnerNameParent"}) - private final int nodeOwner; - @Field(keys = {"OwnerNameParent"}) - private String nodeName; - @Field(keys = {"OwnerNameParent"}, nullable = true) - private Integer parentNode; - @Field(def = "CURRENT_TIMESTAMP") - private Timestamp lastUpdate; - @Field(def = "''") - private String nodeItem; - @Field(def = "'normal'", nullable = true) - private SchematicType nodeType; - @Field(def = "0") - private int nodeRank; - @Field(def = "1") - private boolean replaceColor; - @Field(def = "1") - private boolean allowReplay; - @Setter(AccessLevel.PACKAGE) - @Field(def = "1") - private boolean nodeFormat; + private final de.steamwar.sql.v2.SchematicNode node; private final Map brCache = new HashMap<>(); - public SchematicNode( - int nodeId, - int nodeOwner, - String nodeName, - Integer parentNode, - Timestamp lastUpdate, - String nodeItem, - SchematicType nodeType, - int nodeRank, - boolean replaceColor, - boolean allowReplay, - boolean nodeFormat - ) { - this.nodeId = nodeId; - this.nodeOwner = nodeOwner; - this.nodeName = nodeName; - this.parentNode = parentNode; - this.nodeItem = nodeItem; - this.nodeType = nodeType; - this.lastUpdate = lastUpdate; - this.nodeRank = nodeRank; - this.replaceColor = replaceColor; - this.allowReplay = allowReplay; - this.nodeFormat = nodeFormat; + public SchematicNode(de.steamwar.sql.v2.SchematicNode node) { + this.node = node; } public static SchematicNode createSchematic(int owner, String name, Integer parent) { - return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), ""); + return new SchematicNode(de.steamwar.sql.v2.SchematicNode.createSchematic(SteamwarUser.get(owner), name, nullableInt(parent))); } public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) { - return createSchematicNode(owner, name, parent, null, ""); + return new SchematicNode(de.steamwar.sql.v2.SchematicNode.createSchematicNode(SteamwarUser.get(owner), name, nullableInt(parent), null, "")); } public static SchematicNode createSchematicNode(int owner, String name, Integer parent, String type, String item) { - if (parent != null && parent == 0) - parent = null; - int nodeId = create.insertGetKey(owner, name, parent, type, item); - return getSchematicNode(nodeId); + return new SchematicNode(de.steamwar.sql.v2.SchematicNode.createSchematicNode(SteamwarUser.get(owner), name, nullableInt(parent), type, item)); } public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) { - return getSchematicNode(owner, name, parent.getId()); + return de.steamwar.sql.v2.SchematicNode.getSchematicNode(SteamwarUser.get(owner), name, parent.node).map(SchematicNode::new).orElse(null); } public static SchematicNode getSchematicNode(int owner, String name, Integer parent) { - if (parent == null || parent == 0) - return byOwnerNameParent_null.select(owner, name); - return byOwnerNameParent.select(owner, name, parent); + return de.steamwar.sql.v2.SchematicNode.getSchematicNode(SteamwarUser.get(owner), name, de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).map(SchematicNode::new).orElse(null); } public static List getSchematicNodeInNode(SchematicNode parent) { @@ -154,20 +63,11 @@ public class SchematicNode { } public static List getSchematicNodeInNode(Integer parent) { - if(parent == null || parent == 0) { - rootWarning(); - return byParent_null.listSelect(); - } - - return byParent.listSelect(parent); + return de.steamwar.sql.v2.SchematicNode.getSchematicNodeInNode(de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static List getSchematicDirectoryInNode(Integer parent) { - if(parent == null || parent == 0) { - rootWarning(); - return dirsByParent_null.listSelect(); - } - return dirsByParent.listSelect(parent); + return de.steamwar.sql.v2.SchematicNode.getSchematicDirectoryInNode(de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).stream().map(SchematicNode::new).collect(Collectors.toList()); } @Deprecated @@ -181,69 +81,44 @@ public class SchematicNode { } public static SchematicNode getSchematicNode(String name, Integer parent) { - if(parent == null || parent == 0) { - rootWarning(); - return byParentName_null.select(name); - } - return byParentName.select(name, parent); + return de.steamwar.sql.v2.SchematicNode.getSchematicNode(name, de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).map(SchematicNode::new).orElse(null); } public static SchematicNode getSchematicNode(int id) { - return byId.select(id); + return new SchematicNode(de.steamwar.sql.v2.SchematicNode.getSchematicNode(id)); } public static List getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) { - if(parent == null || parent == 0) - return accessibleByUserTypeParent_Null.listSelect(owner, owner, schemType); - return accessibleByUserTypeParent.listSelect(owner, owner, schemType, parent); + return de.steamwar.sql.v2.SchematicNode.getAccessibleSchematicsOfTypeInParent(SteamwarUser.get(owner), schemType, de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static List getAllAccessibleSchematicsOfType(int user, String schemType) { - return accessibleByUserType.listSelect(user, user, schemType); + return de.steamwar.sql.v2.SchematicNode.getAllAccessibleSchematicsOfType(SteamwarUser.get(user), SchematicType.fromDB(schemType)).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static List getAllSchematicsOfType(int owner, String schemType) { - return byOwnerType.listSelect(owner, schemType); + return de.steamwar.sql.v2.SchematicNode.getAllSchematicsOfType(SteamwarUser.get(owner), SchematicType.fromDB(schemType)).stream().map(SchematicNode::new).collect(Collectors.toList()); } @Deprecated public static List getAllSchematicsOfType(String schemType) { - return byType.listSelect(schemType); + return de.steamwar.sql.v2.SchematicNode.getAllSchematicsOfType(SchematicType.fromDB(schemType)).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static List getAllSchematicsOfType(SchematicType schemType) { - return byType.listSelect(schemType); + return de.steamwar.sql.v2.SchematicNode.getAllSchematicsOfType(schemType).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static List deepGet(Integer parent, Predicate filter) { - List finalList = new ArrayList<>(); - List nodes = SchematicNode.getSchematicNodeInNode(parent); - nodes.forEach(node -> { - if (node.isDir()) { - finalList.addAll(deepGet(node.getId(), filter)); - } else { - if (filter.test(node)) - finalList.add(node); - } - }); - return finalList; + return de.steamwar.sql.v2.SchematicNode.deepGet(de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent)), schematicNode -> filter.test(new SchematicNode(schematicNode))).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static List getSchematicsAccessibleByUser(int user, Integer parent) { - if (parent == null || parent == 0) - return accessibleByUser.listSelect(user, user, user, user); - - if(schematicAccessibleForUser.select(rs -> { - rs.next(); - return rs.getInt("Accessible") > 0; - }, parent, user, user)) - return getSchematicNodeInNode(parent); - - return Collections.emptyList(); + return de.steamwar.sql.v2.SchematicNode.getSchematicsAccessibleByUser(SteamwarUser.get(user), de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static List getAllSchematicsAccessibleByUser(int user) { - return allAccessibleByUser.listSelect(user, user); + return de.steamwar.sql.v2.SchematicNode.getAllSchematicsAccessibleByUser(SteamwarUser.get(user)).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static List getAllParentsOfNode(SchematicNode node) { @@ -251,55 +126,15 @@ public class SchematicNode { } public static List getAllParentsOfNode(int node) { - return allParentsOfNode.listSelect(node); + return de.steamwar.sql.v2.SchematicNode.getAllParentsOfNode(de.steamwar.sql.v2.SchematicNode.getSchematicNode(node)).stream().map(SchematicNode::new).collect(Collectors.toList()); } public static SchematicNode getNodeFromPath(SteamwarUser user, String s) { - if (s.startsWith("/")) { - s = s.substring(1); - } - if (s.isEmpty()) { - return null; - } - if (s.contains("/")) { - String[] layers = s.split("/"); - SchematicNode currentNode = null; - for (int i = 0; i < layers.length; i++) { - int finalI = i; - Optional node; - if (currentNode == null) { - node = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(layers[finalI])).findAny(); - } else { - node = Optional.ofNullable(SchematicNode.getSchematicNode(layers[i], currentNode.getId())); - } - if (!node.isPresent()) { - return null; - } else { - currentNode = node.get(); - if (!currentNode.isDir() && i != layers.length - 1) { - return null; - } - } - } - return currentNode; - } else { - String finalS = s; - return SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(finalS)).findAny().orElse(null); - } + return de.steamwar.sql.v2.SchematicNode.getNodeFromPath(user, s).map(SchematicNode::new).orElse(null); } public static List filterSchems(int user, Predicate filter) { - List finalList = new ArrayList<>(); - List nodes = getSchematicsAccessibleByUser(user, null); - nodes.forEach(node -> { - if (node.isDir()) { - finalList.addAll(deepGet(node.getId(), filter)); - } else { - if (filter.test(node)) - finalList.add(node); - } - }); - return finalList; + return de.steamwar.sql.v2.SchematicNode.filterSchems(SteamwarUser.get(user), schematicNode -> filter.test(new SchematicNode(schematicNode))).stream().map(SchematicNode::new).collect(Collectors.toList()); } @Deprecated @@ -308,236 +143,139 @@ public class SchematicNode { } public int getId() { - return nodeId; + return node.getId(); } public int getOwner() { - return nodeOwner; + return node.getOwner().getId(); } public String getName() { - return nodeName; + return node.getName(); } public void setName(String name) { - this.nodeName = name; - updateDB(); + node.setName(name); } public Integer getParent() { - return parentNode; + return node.getParent() == null ? null : node.getParent().getId(); } public void setParent(Integer parent) { - this.parentNode = parent; - updateDB(); + node.setParent(de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))); } public String getItem() { - if (nodeItem.isEmpty()) { - return isDir() ? "CHEST" : "CAULDRON_ITEM"; - } - return nodeItem; + return node.getItem(); } public void setItem(String item) { - this.nodeItem = item; - updateDB(); + node.setItem(item); } @Deprecated public String getType() { - return nodeType.name(); + return node.getSchemtype().toDB(); } @Deprecated public void setType(String type) { - if(isDir()) - throw new SecurityException("Node is Directory"); - this.nodeType = SchematicType.fromDB(type); - updateDB(); + node.setSchemtype(SchematicType.fromDB(type)); } public boolean isDir() { - return nodeType == null; + return node.isDir(); } public boolean getSchemFormat() { - if(isDir()) - throw new SecurityException("Node is Directory"); - return nodeFormat; + return node.getSchemFormat(); } public int getRank() { - if(isDir()) - throw new SecurityException("Node is Directory"); - return nodeRank; + return node.getRank(); } @Deprecated public int getRankUnsafe() { - return nodeRank; + return node.getRank(); } public void setRank(int rank) { - if(isDir()) - throw new SecurityException("Node is Directory"); - this.nodeRank = rank; + node.setRank(rank); } public SchematicType getSchemtype() { - if(isDir()) - throw new SecurityException("Is Directory"); - return nodeType; + return node.getSchemtype(); } public void setSchemtype(SchematicType type) { - if(isDir()) - throw new SecurityException("Is Directory"); - this.nodeType = type; - updateDB(); + node.setSchemtype(type); } public boolean replaceColor() { - return replaceColor; + return node.replaceColor(); } public void setReplaceColor(boolean replaceColor) { - if(isDir()) - throw new SecurityException("Is Directory"); - this.replaceColor = replaceColor; - updateDB(); + node.setReplaceColor(replaceColor); } public boolean allowReplay() { - return allowReplay; + return node.allowReplay(); } public void setAllowReplay(boolean allowReplay) { - if(isDir()) - throw new SecurityException("Is Directory"); - this.allowReplay = allowReplay; - updateDB(); + node.setAllowReplay(allowReplay); } public SchematicNode getParentNode() { - if(parentNode == null) return null; - return SchematicNode.getSchematicNode(parentNode); + return node.getParent() == de.steamwar.sql.v2.SchematicNode.ROOT ? null : new SchematicNode(node.getParent()); } public int getElo(int season) { - return SchemElo.getElo(this, season); + return node.getElo(season); } public boolean accessibleByUser(int user) { - return NodeMember.getNodeMember(nodeId, user) != null; + return node.accessibleByUser(user); } public Set getMembers() { - return NodeMember.getNodeMembers(nodeId); + return node.getMembers(); } public Timestamp getLastUpdate() { - return lastUpdate; - } - - private void updateDB() { - this.lastUpdate = Timestamp.from(Instant.now()); - update.update(nodeId, nodeOwner, nodeName, parentNode, nodeItem, nodeType, lastUpdate, nodeRank, replaceColor, allowReplay, nodeFormat); - this.brCache.clear(); - TAB_CACHE.clear(); + return node.getLastUpdate(); } public void delete() { - delete.update(nodeId); + node.delete(); } @Override public int hashCode() { - return nodeId; + return node.hashCode(); } @Override public boolean equals(Object obj) { - if (!(obj instanceof SchematicNode)) - return false; - - return ((SchematicNode) obj).getId() == nodeId; + return node.equals(obj); } public String generateBreadcrumbs(SteamwarUser user) { - return brCache.computeIfAbsent(user.getId(), integer -> generateBreadcrumbs("/", user)); + return node.generateBreadcrumbs(user); } public String generateBreadcrumbs(String split, SteamwarUser user) { - StringBuilder builder = new StringBuilder(getName()); - SchematicNode currentNode = this; - if (currentNode.isDir()) builder.append("/"); - final Set nodeMembers = NodeMember.getSchematics(user.getId()); - AtomicInteger i = new AtomicInteger(); - i.set(currentNode.getId()); - while (currentNode.getParentNode() != null && nodeMembers.stream().noneMatch(nodeMember -> nodeMember.getNode() == i.get())) { - currentNode = currentNode.getParentNode(); - i.set(currentNode.getId()); - builder.insert(0, split) - .insert(0, currentNode.getName()); - } - return builder.toString(); + return node.generateBreadcrumbs(split, user); } - private static final List FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public")); public static boolean invalidSchemName(String[] layers) { - for (String layer : layers) { - if (layer.isEmpty()) { - return true; - } - if (layer.contains("/") || - layer.contains("\\") || - layer.contains("<") || - layer.contains(">") || - layer.contains("^") || - layer.contains("°") || - layer.contains("'") || - layer.contains("\"") || - layer.contains(" ")) { - return true; - } - if(FORBIDDEN_NAMES.contains(layer.toLowerCase())) { - return true; - } - } - return false; + return de.steamwar.sql.v2.SchematicNode.invalidSchemName(layers); } public static List getNodeTabcomplete(SteamwarUser user, String s) { - boolean sws = s.startsWith("/"); - if (sws) { - s = s.substring(1); - } - int index = s.lastIndexOf("/"); - String cacheKey = index == -1 ? "" : s.substring(0, index); - if(TAB_CACHE.containsKey(user.getId()) && TAB_CACHE.get(user.getId()).containsKey(cacheKey)) { - return new ArrayList<>(TAB_CACHE.get(user.getId()).get(cacheKey)); - } - List list = new ArrayList<>(); - if (s.contains("/")) { - String preTab = s.substring(0, s.lastIndexOf("/") + 1); - SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab); - if (pa == null) return Collections.emptyList(); - List nodes = SchematicNode.getSchematicNodeInNode(pa); - nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs(user))); - } else { - List nodes = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0); - nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : ""))); - } - list.remove("//copy"); - TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list); - return list; - } - - private static void rootWarning() { - ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream(); - new Throwable().printStackTrace(new PrintStream(stacktraceOutput)); - SWException.log("PERFORMANCE!!! Getting all/weird subset of schematic nodes with parent NULL", stacktraceOutput.toString()); + return de.steamwar.sql.v2.SchematicNode.getNodeTabcomplete(user, s); } } diff --git a/src/de/steamwar/sql/v2/SchematicNode.java b/src/de/steamwar/sql/v2/SchematicNode.java new file mode 100644 index 0000000..c05ea9c --- /dev/null +++ b/src/de/steamwar/sql/v2/SchematicNode.java @@ -0,0 +1,482 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.sql.v2; + +import de.steamwar.sql.*; +import de.steamwar.sql.internal.*; +import lombok.AccessLevel; +import lombok.Setter; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class SchematicNode { + + static { + new SqlTypeMapper<>(SchematicNode.class, null, (rs, identifier) -> { throw new SecurityException("SchematicNode cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.nodeId)); + } + + private static final Map>> TAB_CACHE = new HashMap<>(); + public static void clear() { + TAB_CACHE.clear(); + } + + private static final String[] fields = {"NodeId", "NodeOwner", "NodeName", "ParentNode", "LastUpdate", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay", "NodeFormat"}; + private static String nodeSelectCreator(String itemPrefix) { + return "SELECT " + Arrays.stream(fields).map(s -> itemPrefix + s).collect(Collectors.joining(", ")) + " FROM SchematicNode "; + } + + public static final SchematicNode ROOT = new SchematicNode(0, 0, "ROOT", 0, Timestamp.from(Instant.now()), "", SchematicType.Normal, 0, false, false, false); + + private static final Table table = new Table<>(SchematicNode.class); + private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType"); + private static final Statement update = table.insertAll(); + private static final Statement delete = table.delete(Table.PRIMARY); + + private static final SelectStatement byId = table.select(Table.PRIMARY); + private static final SelectStatement byOwnerNameParent = table.select("OwnerNameParent"); + private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? ORDER BY NodeName"); + private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? AND NodeType is NULL ORDER BY NodeName"); + private static final SelectStatement byParentName = table.selectFields("NodeName", "ParentNode"); + private static final SelectStatement accessibleByUserTypeParent = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode = ? ORDER BY NodeName"); + private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? ORDER BY NodeName"); + private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); + private static final SelectStatement byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName"); + private static final SelectStatement accessibleByUser = new SelectStatement<>(table, nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) AND ((s.NodeOwner = ? AND s.ParentNode IS NULL) OR NOT s.NodeOwner = ?) GROUP BY s.NodeId ORDER BY s.NodeName"); + private static final Statement schematicAccessibleForUser = new Statement("WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT COUNT(RSN.NodeId) AS `Accessible` FROM RSN LEFT Join NodeMember NM On NM.NodeId = RSN.NodeId WHERE NodeOwner = ? OR UserId = ? LIMIT 1"); + private static final SelectStatement allAccessibleByUser = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); + private static final SelectStatement allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); + + @Field(keys = {Table.PRIMARY}, autoincrement = true) + private final int nodeId; + @Field(keys = {"OwnerNameParent"}) + private final int nodeOwner; + @Field(keys = {"OwnerNameParent"}) + private String nodeName; + @Field(keys = {"OwnerNameParent"}) + private int parentNode; + @Field(def = "CURRENT_TIMESTAMP") + private Timestamp lastUpdate; + @Field(def = "''") + private String nodeItem; + @Field(def = "'normal'", nullable = true) + private SchematicType nodeType; + @Field(def = "0") + private int nodeRank; + @Field(def = "1") + private boolean replaceColor; + @Field(def = "1") + private boolean allowReplay; + @Setter(AccessLevel.PACKAGE) + @Field(def = "1") + private boolean nodeFormat; + + private final Map brCache = new HashMap<>(); + + public SchematicNode( + int nodeId, + int nodeOwner, + String nodeName, + int parentNode, + Timestamp lastUpdate, + String nodeItem, + SchematicType nodeType, + int nodeRank, + boolean replaceColor, + boolean allowReplay, + boolean nodeFormat + ) { + this.nodeId = nodeId; + this.nodeOwner = nodeOwner; + this.nodeName = nodeName; + this.parentNode = parentNode; + this.nodeItem = nodeItem; + this.nodeType = nodeType; + this.lastUpdate = lastUpdate; + this.nodeRank = nodeRank; + this.replaceColor = replaceColor; + this.allowReplay = allowReplay; + this.nodeFormat = nodeFormat; + } + + public static SchematicNode createSchematic(SteamwarUser owner, String name, int parent) { + return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), ""); + } + + public static SchematicNode createSchematicDirectory(SteamwarUser owner, String name, int parent) { + return createSchematicNode(owner, name, parent, null, ""); + } + + public static SchematicNode createSchematicNode(SteamwarUser owner, String name, int parent, String type, String item) { + int nodeId = create.insertGetKey(owner, name, parent, type, item); + return getSchematicNode(nodeId); + } + + public static Optional getSchematicNode(SteamwarUser owner, String name, SchematicNode parent) { + return getSchematicNode(owner, name, parent.getId()); + } + + public static Optional getSchematicNode(SteamwarUser owner, String name, int parent) { + return Optional.ofNullable(byOwnerNameParent.select(owner, name, parent)); + } + + public static List getSchematicNodeInNode(SchematicNode parent) { + if(parent.equals(ROOT)) { + rootWarning(); + } + + return byParent.listSelect(parent); + } + + public static List getSchematicDirectoryInNode(SchematicNode parent) { + if(parent.equals(ROOT)) { + rootWarning(); + } + return dirsByParent.listSelect(parent); + } + + public static Optional getSchematicNode(String name, SchematicNode parent) { + if(parent.equals(ROOT)) { + rootWarning(); + } + return Optional.ofNullable(byParentName.select(name, parent)); + } + + public static SchematicNode getSchematicNode(int id) { + if(id == 0) return ROOT; + return Optional.ofNullable(byId.select(id)).orElseThrow(() -> new IllegalStateException("Schematic node " + id + " does not exist")); + } + + public static List getAccessibleSchematicsOfTypeInParent(SteamwarUser owner, String schemType, SchematicNode parent) { + return accessibleByUserTypeParent.listSelect(owner, owner, schemType, parent); + } + + public static List getAllAccessibleSchematicsOfType(SteamwarUser user, SchematicType schemType) { + return accessibleByUserType.listSelect(user, user, schemType); + } + + public static List getAllSchematicsOfType(SteamwarUser owner, SchematicType schemType) { + return byOwnerType.listSelect(owner, schemType); + } + + public static List getAllSchematicsOfType(SchematicType schemType) { + return byType.listSelect(schemType); + } + + public static List deepGet(SchematicNode parent, Predicate filter) { + List finalList = new ArrayList<>(); + List nodes = SchematicNode.getSchematicNodeInNode(parent); + nodes.forEach(node -> { + if (node.isDir()) { + finalList.addAll(deepGet(node, filter)); + } else { + if (filter.test(node)) + finalList.add(node); + } + }); + return finalList; + } + + public static List getSchematicsAccessibleByUser(SteamwarUser user, SchematicNode parent) { + if (parent.equals(ROOT)) { + return accessibleByUser.listSelect(user, user, user, user); + } + + if(Boolean.TRUE.equals(schematicAccessibleForUser.select(rs -> { + rs.next(); + return rs.getBoolean("Accessible"); + }, parent, user, user))) + return getSchematicNodeInNode(parent); + + return Collections.emptyList(); + } + + public static List getAllSchematicsAccessibleByUser(SteamwarUser user) { + return allAccessibleByUser.listSelect(user, user); + } + + public static List getAllParentsOfNode(SchematicNode node) { + return allParentsOfNode.listSelect(node); + } + + public static Optional getNodeFromPath(SteamwarUser user, String s) { + if (s.startsWith("/")) { + s = s.substring(1); + } + if (s.isEmpty()) { + return Optional.empty(); + } + if (s.contains("/")) { + String[] layers = s.split("/"); + SchematicNode currentNode = null; + for (int i = 0; i < layers.length; i++) { + int finalI = i; + Optional node; + if (currentNode == null) { + node = SchematicNode.getSchematicsAccessibleByUser(user, ROOT).stream().filter(node1 -> node1.getName().equals(layers[finalI])).findAny(); + } else { + node = SchematicNode.getSchematicNode(layers[i], currentNode); + } + if (!node.isPresent()) { + return Optional.empty(); + } else { + currentNode = node.get(); + if (!currentNode.isDir() && i != layers.length - 1) { + return Optional.empty(); + } + } + } + return Optional.ofNullable(currentNode); + } else { + String finalS = s; + return SchematicNode.getSchematicsAccessibleByUser(user, ROOT).stream().filter(node1 -> node1.getName().equals(finalS)).findAny(); + } + } + + public static List filterSchems(SteamwarUser user, Predicate filter) { + List finalList = new ArrayList<>(); + List nodes = getSchematicsAccessibleByUser(user, ROOT); + nodes.forEach(node -> { + if (node.isDir()) { + finalList.addAll(deepGet(node, filter)); + } else { + if (filter.test(node)) + finalList.add(node); + } + }); + return finalList; + } + + public int getId() { + return nodeId; + } + + public SteamwarUser getOwner() { + return SteamwarUser.get(nodeOwner); + } + + public String getName() { + return nodeName; + } + + public void setName(String name) { + this.nodeName = name; + updateDB(); + } + + public SchematicNode getParent() { + return SchematicNode.getSchematicNode(parentNode); + } + + public void setParent(SchematicNode parent) { + this.parentNode = parent.nodeId; + updateDB(); + } + + public String getItem() { + if (nodeItem.isEmpty()) { + return isDir() ? "CHEST" : "CAULDRON_ITEM"; + } + return nodeItem; + } + + public void setItem(String item) { + this.nodeItem = item; + updateDB(); + } + + public boolean isDir() { + return nodeType == null; + } + + public boolean getSchemFormat() { + throwIfDir(); + return nodeFormat; + } + + public int getRank() { + throwIfDir(); + return nodeRank; + } + + public void setRank(int rank) { + throwIfDir(); + this.nodeRank = rank; + } + + public SchematicType getSchemtype() { + throwIfDir(); + return nodeType; + } + + public void setSchemtype(SchematicType type) { + throwIfDir(); + this.nodeType = type; + updateDB(); + } + + public boolean replaceColor() { + return replaceColor; + } + + public void setReplaceColor(boolean replaceColor) { + throwIfDir(); + this.replaceColor = replaceColor; + updateDB(); + } + + public boolean allowReplay() { + return allowReplay; + } + + public void setAllowReplay(boolean allowReplay) { + throwIfDir(); + this.allowReplay = allowReplay; + updateDB(); + } + + public int getElo(int season) { + return SchemElo.getElo(this, season); + } + + public boolean accessibleByUser(int user) { + return NodeMember.getNodeMember(nodeId, user) != null; + } + + public Set getMembers() { + return NodeMember.getNodeMembers(nodeId); + } + + public Timestamp getLastUpdate() { + return lastUpdate; + } + + private void updateDB() { + this.lastUpdate = Timestamp.from(Instant.now()); + update.update(nodeId, nodeOwner, nodeName, parentNode, nodeItem, nodeType, lastUpdate, nodeRank, replaceColor, allowReplay, nodeFormat); + this.brCache.clear(); + TAB_CACHE.clear(); + } + + public void delete() { + delete.update(nodeId); + } + + @Override + public int hashCode() { + return nodeId; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SchematicNode)) + return false; + + return ((SchematicNode) obj).getId() == nodeId; + } + + public String generateBreadcrumbs(SteamwarUser user) { + return brCache.computeIfAbsent(user.getId(), integer -> generateBreadcrumbs("/", user)); + } + + public String generateBreadcrumbs(String split, SteamwarUser user) { + StringBuilder builder = new StringBuilder(getName()); + SchematicNode currentNode = this; + if (currentNode.isDir()) builder.append("/"); + final Set nodeMembers = NodeMember.getSchematics(user.getId()); + AtomicInteger i = new AtomicInteger(); + i.set(currentNode.getId()); + while (!currentNode.getParent().equals(ROOT) && nodeMembers.stream().noneMatch(nodeMember -> nodeMember.getNode() == i.get())) { + currentNode = currentNode.getParent(); + i.set(currentNode.getId()); + builder.insert(0, split) + .insert(0, currentNode.getName()); + } + return builder.toString(); + } + + private static final List FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public")); + public static boolean invalidSchemName(String[] layers) { + for (String layer : layers) { + if (layer.isEmpty()) { + return true; + } + if (layer.contains("/") || + layer.contains("\\") || + layer.contains("<") || + layer.contains(">") || + layer.contains("^") || + layer.contains("°") || + layer.contains("'") || + layer.contains("\"") || + layer.contains(" ")) { + return true; + } + if(FORBIDDEN_NAMES.contains(layer.toLowerCase())) { + return true; + } + } + return false; + } + + public static List getNodeTabcomplete(SteamwarUser user, String s) { + boolean sws = s.startsWith("/"); + if (sws) { + s = s.substring(1); + } + int index = s.lastIndexOf("/"); + String cacheKey = index == -1 ? "" : s.substring(0, index); + if(TAB_CACHE.containsKey(user.getId()) && TAB_CACHE.get(user.getId()).containsKey(cacheKey)) { + return new ArrayList<>(TAB_CACHE.get(user.getId()).get(cacheKey)); + } + List list = new ArrayList<>(); + if (s.contains("/")) { + String preTab = s.substring(0, s.lastIndexOf("/") + 1); + Optional pa = SchematicNode.getNodeFromPath(user, preTab); + if (!pa.isPresent()) return Collections.emptyList(); + List nodes = pa.map(SchematicNode::getSchematicNodeInNode).orElseGet(Collections::emptyList); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs(user))); + } else { + List nodes = SchematicNode.getSchematicsAccessibleByUser(user, ROOT); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : ""))); + } + list.remove("//copy"); + TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list); + return list; + } + + private static void rootWarning() { + ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream(); + new Throwable().printStackTrace(new PrintStream(stacktraceOutput)); + SWException.log("PERFORMANCE!!! Getting all/weird subset of schematic nodes with parent NULL", stacktraceOutput.toString()); + } + + private void throwIfDir() { + if (isDir()) { + throw new SecurityException("Is Directory"); + } + } +}