Refactor SchematicNode

This commit is contained in:
2025-10-28 19:03:30 +01:00
parent c8ac984ad3
commit 1aff7b30a6
12 changed files with 426 additions and 599 deletions
@@ -30,8 +30,8 @@ import java.time.Instant
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
val time = timestamp("Time")
val server = varchar("ServerName", 255)
val serverOwner = integer("ServerOwner").nullable()
val actor = integer("Actor")
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable()
val actor = reference("Actor", SteamwarUserTable)
val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
val actionText = text("ActionText")
}
@@ -50,8 +50,8 @@ class AuditLog(id: EntityID<Int>): IntEntity(id) {
new {
this.time = Instant.now()
this.server = serverName
this.serverOwner = serverOwner?.id?.value
this.actor = actor.id.value
this.serverOwner = serverOwner?.id
this.actor = actor.id
this.action = actionType
this.actionText = text
}
@@ -87,7 +87,7 @@ public class NodeData {
private Timestamp createdAt;
@Field
private SchematicFormat nodeFormat;
public SchematicFormat nodeFormat;
public InputStream schemData() throws IOException {
return schemData(true);
@@ -132,6 +132,6 @@ public class NodeData {
SPONGE_V2(".schem"),
SPONGE_V3(".schem");
private final String fileEnding;
public final String fileEnding;
}
}
@@ -17,618 +17,421 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
package de.steamwar.sql
import de.steamwar.sql.internal.*;
import lombok.Getter;
import de.steamwar.sql.internal.fromSql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.*
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.javatime.timestamp
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
import java.sql.Timestamp
import java.util.*
import java.util.function.Consumer
import java.sql.Timestamp;
import java.time.Instant;
import java.util.*;
import java.util.function.Predicate;
object SchematicNodeTable : IntIdTable("SchematicNode", "NodeId") {
val owner = reference("NodeOwner", SteamwarUserTable)
val name = varchar("NodeName", 64)
val parent = optReference("ParentNode", SchematicNodeTable)
val lastUpdate = timestamp("LastUpdate")
val item = text("NodeItem")
val type = varchar("NodeType", 16).nullable()
val config = integer("Config")
}
public class SchematicNode {
class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<SchematicNode>(SchematicNodeTable) {
private val fieldIndex: Map<Expression<*>, Int> =
SchematicNodeTable.columns.mapIndexed { index, column -> column to index }.toMap()
static {
SchematicType.Normal.name(); // Ensure SchematicType is loaded.
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 val FORBIDDEN_NAMES = listOf("public")
private val FORBIDDEN_CHARS = listOf('/', '\\', '<', '>', '^', '°', '\'', '"', ' ')
private static final Map<Integer, Map<String, List<String>>> TAB_CACHE = new HashMap<>();
val tabCache = mutableMapOf<Int, MutableMap<String, List<String>>>()
public static void clear() {
TAB_CACHE.clear();
}
@JvmStatic
fun clear() = tabCache.clear()
private static final String nodeSelector = "SELECT NodeId, NodeOwner, NodeOwner AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode ";
private fun List<SchematicNode>.mapToIds(): Map<Int, SchematicNode> = this.associateBy { it.nodeId }
private static final Table<SchematicNode> table = new Table<>(SchematicNode.class);
private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem",
"NodeType");
private static final Statement update = table.update(Table.PRIMARY, "NodeName", "ParentNode", "NodeItem",
"NodeType", "NodeRank", "Config");
private static final Statement delete = table.delete(Table.PRIMARY);
@JvmStatic
fun getAll(user: SteamwarUser) = fromSql(
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ?} UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.* FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId",
listOf(
IntegerColumnType() to user.getId(),
IntegerColumnType() to user.getId()
),
fieldIndex
)
private static final SelectStatement<SchematicNode> byId = new SelectStatement<>(table,
nodeSelector + "WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> byOwnerNameParent = new SelectStatement<>(table,
nodeSelector + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode " + Statement.NULL_SAFE_EQUALS + "?");
private static final SelectStatement<SchematicNode> byParent = new SelectStatement<>(table,
nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> dirsByParent = new SelectStatement<>(table, nodeSelector
+ "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byOwnerType = new SelectStatement<>(table,
nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byType = new SelectStatement<>(table,
nodeSelector + "WHERE NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> all = new SelectStatement<>(table,
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId");
private static final SelectStatement<SchematicNode> list = new SelectStatement<>(table,
"SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId "
+ Statement.NULL_SAFE_EQUALS
+ "? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byParentName = new SelectStatement<>(table,
"SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId "
+ Statement.NULL_SAFE_EQUALS
+ "? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?");
private static final SelectStatement<SchematicNode> schematicAccessibleForUser = new SelectStatement<>(table,
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> accessibleByUserTypeInParent = new SelectStatement<>(table,
"WITH RECURSIVE RSASN AS(WITH RECURSIVE RSAN AS (WITH RSANH AS (WITH RECURSIVE RSA AS (SELECT SN.NodeId, NM.ParentId FROM SchematicNode SN LEFT JOIN NodeMember NM on SN.NodeId = NM.NodeId WHERE NM.UserId = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN INNER JOIN RSA ON RSA.NodeId = SN.ParentNode) SELECT * FROM RSA UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?) SELECT * FROM RSANH UNION SELECT SN.NodeId, SN.ParentNode FROM RSANH JOIN SchematicNode SN ON SN.ParentNode = RSANH.NodeId) SELECT RSAN.NodeId, RSAN.ParentId FROM RSAN JOIN SchematicNode SN ON SN.NodeId = RSAN.NodeId WHERE NodeType = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN JOIN RSASN ON SN.NodeId = RSASN.ParentId) SELECT SN.*, ? as EffectiveOwner, RSASN.ParentId AS ParentNode FROM RSASN JOIN SchematicNode SN ON SN.NodeId = RSASN.NodeId WHERE RSASN.ParentId"
+ Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> accessibleByUserType = new SelectStatement<>(table,
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeType = ?");
private static final SelectStatement<SchematicNode> byIdAndUser = new SelectStatement<>(table,
"SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> allParentsOfNode = new SelectStatement<>(table,
"WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? AND EffectiveOwner = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, ? AS EffectiveOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.Config FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId");
@JvmStatic
fun getAllMap(user: SteamwarUser) = getAll(user).mapToIds()
static {
NodeMember.init();
}
@JvmStatic
fun list(user: SteamwarUser, schematicId: Int?) = fromSql(
"SELECT SchematicNode.NodeId, NodeOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId <=> ? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName",
listOf(
IntegerColumnType() to schematicId,
IntegerColumnType() to user.getId(),
IntegerColumnType() to user.getId(),
IntegerColumnType() to schematicId,
IntegerColumnType() to user.getId(),
IntegerColumnType() to schematicId,
IntegerColumnType() to schematicId,
), fieldIndex
)
@Field(keys = { Table.PRIMARY }, autoincrement = true)
private final int nodeId;
@Field(keys = { "OwnerNameParent" })
private final int nodeOwner;
@Field(def = "0")
@Getter
private final int effectiveOwner;
@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
private int config;
@JvmStatic
fun byParentName(user: SteamwarUser, schematicId: Int?, name: String) = fromSql(
"SELECT SchematicNode.NodeId, NodeOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId <=> ? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, Config FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?",
listOf(
IntegerColumnType() to schematicId,
IntegerColumnType() to user.getId(),
VarCharColumnType() to name,
IntegerColumnType() to user.getId(),
IntegerColumnType() to schematicId,
IntegerColumnType() to user.getId(),
IntegerColumnType() to schematicId,
IntegerColumnType() to schematicId,
VarCharColumnType() to name,
), fieldIndex
).firstOrNull()
private String brCache;
@JvmStatic
fun accessibleByUserType(user: SteamwarUser, type: SchematicType) = fromSql(
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeType = ?",
listOf(
IntegerColumnType() to user.getId(),
IntegerColumnType() to user.getId(),
IntegerColumnType() to user.getId(),
VarCharColumnType() to type.toDB(),
), fieldIndex
)
public SchematicNode(
int nodeId,
int nodeOwner,
int effectiveOwner,
String nodeName,
Integer parentNode,
Timestamp lastUpdate,
String nodeItem,
SchematicType nodeType,
int nodeRank,
int config) {
this.nodeId = nodeId;
this.nodeOwner = nodeOwner;
this.effectiveOwner = effectiveOwner;
this.nodeName = nodeName;
this.parentNode = parentNode;
this.nodeItem = nodeItem;
this.nodeType = nodeType;
this.lastUpdate = lastUpdate;
this.nodeRank = nodeRank;
this.config = config;
}
@JvmStatic
fun accessibleByUserTypeMap(user: SteamwarUser, type: SchematicType) =
accessibleByUserType(user, type).mapToIds()
public static List<SchematicNode> getAll(SteamwarUser user) {
return all.listSelect(user, user, user);
}
@JvmStatic
fun schematicAccessibleForUser(user: SteamwarUser, schematicId: Int?) = fromSql(
"WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeId = ?",
listOf(
IntegerColumnType() to user.getId(),
IntegerColumnType() to user.getId(),
IntegerColumnType() to user.getId(),
IntegerColumnType() to schematicId,
),
fieldIndex
).isNotEmpty()
public static Map<Integer, List<SchematicNode>> getAllMap(SteamwarUser user) {
return map(getAll(user));
}
@JvmStatic
fun accessibleByUserTypeParent(user: SteamwarUser, type: SchematicType, parentId: Int?) = fromSql(
"WITH RECURSIVE RSASN AS(WITH RECURSIVE RSAN AS (WITH RSANH AS (WITH RECURSIVE RSA AS (SELECT SN.NodeId, NM.ParentId FROM SchematicNode SN LEFT JOIN NodeMember NM on SN.NodeId = NM.NodeId WHERE NM.UserId = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN INNER JOIN RSA ON RSA.NodeId = SN.ParentNode) SELECT * FROM RSA UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?) SELECT * FROM RSANH UNION SELECT SN.NodeId, SN.ParentNode FROM RSANH JOIN SchematicNode SN ON SN.ParentNode = RSANH.NodeId) SELECT RSAN.NodeId, RSAN.ParentId FROM RSAN JOIN SchematicNode SN ON SN.NodeId = RSAN.NodeId WHERE NodeType = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN JOIN RSASN ON SN.NodeId = RSASN.ParentId) SELECT SN.*, ? as EffectiveOwner, RSASN.ParentId AS ParentNode FROM RSASN JOIN SchematicNode SN ON SN.NodeId = RSASN.NodeId WHERE RSASN.ParentId <=> ? ORDER BY NodeName",
listOf(
IntegerColumnType() to user.getId(),
IntegerColumnType() to user.getId(),
VarCharColumnType() to type.toDB(),
IntegerColumnType() to user.getId(),
IntegerColumnType() to parentId,
), fieldIndex
)
public static List<SchematicNode> list(SteamwarUser user, Integer schematicId) {
return list.listSelect(user, schematicId, user, user, schematicId, user, schematicId, schematicId);
}
public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) {
return byParentName.select(user, schematicId, user, name, user, schematicId, user, schematicId, schematicId,
name);
}
public static List<SchematicNode> accessibleByUserType(SteamwarUser user, SchematicType type) {
return accessibleByUserType.listSelect(user, user, user, type);
}
public static Map<Integer, List<SchematicNode>> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) {
return map(accessibleByUserType(user, type));
}
public static boolean schematicAccessibleForUser(SteamwarUser user, Integer schematicId) {
return schematicAccessibleForUser.select(user, user, user, schematicId) != null;
}
public static List<SchematicNode> accessibleByUserTypeParent(SteamwarUser user, SchematicType type,
Integer parentId) {
return accessibleByUserTypeInParent.listSelect(user, user, type, user, parentId);
}
public static SchematicNode byIdAndUser(SteamwarUser user, Integer id) {
return byIdAndUser.select(user, id);
}
public static List<SchematicNode> parentsOfNode(SteamwarUser user, Integer id) {
return allParentsOfNode.listSelect(id, user, user, user);
}
private static Map<Integer, List<SchematicNode>> map(List<SchematicNode> in) {
Map<Integer, List<SchematicNode>> map = new HashMap<>();
for (SchematicNode effectiveSchematicNode : in) {
map.computeIfAbsent(effectiveSchematicNode.getOptionalParent().orElse(0), k -> new ArrayList<>())
.add(effectiveSchematicNode);
@JvmStatic
@Deprecated("Use byId")
fun byIdAndUser(ignored: SteamwarUser, id: Int) = useDb {
findById(id)
}
return map;
}
public static SchematicNode createSchematic(int owner, String name, Integer parent) {
return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), "");
}
@JvmStatic
fun parentsOfNode(user: SteamwarUser, id: Int) = fromSql(
"WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.Config FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId",
listOf(
IntegerColumnType() to id,
IntegerColumnType() to user.getId(),
IntegerColumnType() to user.getId(),
), fieldIndex
)
public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) {
return createSchematicNode(owner, name, parent, null, "");
}
@JvmStatic
fun createSchematic(owner: Int, name: String, parent: Int?) = createSchematicNode(
owner, name, parent,
SchematicType.Normal.toDB(), ""
)
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, item, type);
return getSchematicNode(nodeId);
}
@JvmStatic
fun createSchematicDirectory(owner: Int, name: String, parent: Int?) =
createSchematicNode(owner, name, parent, null, "")
public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) {
return getSchematicNode(owner, name, parent.getId());
}
public static SchematicNode getSchematicNode(int owner, String name, Integer parent) {
return byOwnerNameParent.select(owner, name, parent);
}
public static List<SchematicNode> getSchematicNodeInNode(SchematicNode parent) {
return getSchematicNodeInNode(parent.getId());
}
public static List<SchematicNode> getSchematicNodeInNode(Integer parent) {
return byParent.listSelect(parent);
}
public static List<SchematicNode> getSchematicDirectoryInNode(Integer parent) {
return dirsByParent.listSelect(parent);
}
@Deprecated
public static SchematicNode getSchematicDirectory(String name, SchematicNode parent) {
return getSchematicNode(name, parent.getId());
}
@Deprecated
public static SchematicNode getSchematicDirectory(String name, Integer parent) {
return getSchematicNode(name, parent);
}
public static SchematicNode getSchematicNode(String name, Integer parent) {
return byParentName.select(name, parent);
}
public static SchematicNode getSchematicNode(int id) {
return byId.select(id);
}
public static List<SchematicNode> getAccessibleSchematicsOfTypeInParent(int owner, String schemType,
Integer parent) {
return accessibleByUserTypeParent(SteamwarUser.byId(owner), SchematicType.fromDB(schemType), parent);
}
public static List<SchematicNode> getAllAccessibleSchematicsOfType(int user, String schemType) {
return accessibleByUserType(SteamwarUser.byId(user), SchematicType.fromDB(schemType));
}
public static List<SchematicNode> getAllSchematicsOfType(int owner, String schemType) {
return byOwnerType.listSelect(owner, schemType);
}
@Deprecated
public static List<SchematicNode> getAllSchematicsOfType(String schemType) {
return byType.listSelect(schemType);
}
public static List<SchematicNode> getAllSchematicsOfType(SchematicType schemType) {
return byType.listSelect(schemType);
}
public static List<SchematicNode> deepGet(Integer parent, Predicate<SchematicNode> filter) {
List<SchematicNode> finalList = new ArrayList<>();
List<SchematicNode> nodes = SchematicNode.getSchematicNodeInNode(parent);
nodes.forEach(node -> {
if (node.isDir()) {
finalList.addAll(deepGet(node.getId(), filter));
} else {
if (filter.test(node))
finalList.add(node);
@JvmStatic
fun createSchematicNode(owner: Int, name: String, parent: Int?, type: String?, item: String) = useDb {
val id = SchematicNodeTable.insertAndGetId {
it[this.owner] = EntityID(owner, SteamwarUserTable)
it[this.name] = name
it[this.parent] =
parent?.let { p -> if (p == 0) null else p }?.let { p -> EntityID(p, SchematicNodeTable) }
it[this.item] = item
it[this.type] = type
}
});
return finalList;
}
@Deprecated
public static List<SchematicNode> getSchematicsAccessibleByUser(int user, Integer parent) {
return list(SteamwarUser.byId(user), parent);
}
@Deprecated
public static List<SchematicNode> getAllSchematicsAccessibleByUser(int user) {
return getAll(SteamwarUser.byId(user));
}
public static List<SchematicNode> getAllParentsOfNode(SchematicNode node) {
return getAllParentsOfNode(node.getId());
}
public static List<SchematicNode> getAllParentsOfNode(int node) {
return allParentsOfNode.listSelect(node);
}
public static SchematicNode getNodeFromPath(SteamwarUser user, String s) {
if (s.startsWith("/")) {
s = s.substring(1);
return@useDb findById(id) ?: throw IllegalStateException("SchematicNode $id not found")
}
if (s.endsWith("/")) {
s = s.substring(0, s.length() - 1);
@JvmStatic
fun byId(id: Int) = useDb { findById(id) }
@JvmStatic
fun getSchematicNode(owner: Int, name: String, parent: Int?) = useDb {
find { (SchematicNodeTable.owner eq owner) and (SchematicNodeTable.name eq name) and (SchematicNodeTable.parent eq parent) }.firstOrNull()
}
if (s.isEmpty()) {
return null;
@JvmStatic
fun getSchematicNode(owner: Int, name: String, parent: SchematicNode) =
getSchematicNode(owner, name, parent.nodeId)
@JvmStatic
fun getSchematicNodeInNode(parent: Int?) = useDb {
find { (SchematicNodeTable.parent eq parent) }.orderBy(SchematicNodeTable.name to SortOrder.ASC).toList()
}
if (s.contains("/")) {
String[] layers = s.split("/");
Optional<SchematicNode> currentNode = Optional
.ofNullable(SchematicNode.byParentName(user, null, layers[0]));
for (int i = 1; i < layers.length; i++) {
int finalI = i;
Optional<SchematicNode> node = currentNode.map(effectiveSchematicNode -> SchematicNode
.byParentName(user, effectiveSchematicNode.getId(), layers[finalI]));
if (!node.isPresent()) {
return null;
} else {
currentNode = node;
if (!currentNode.map(SchematicNode::isDir).orElse(false) && i != layers.length - 1) {
return null;
@JvmStatic
fun getSchematicNodeInNode(parent: SchematicNode) = getSchematicNodeInNode(parent.nodeId)
@JvmStatic
fun getSchematicNode(name: String, parent: Int?) = useDb {
find { (SchematicNodeTable.name eq name) and (SchematicNodeTable.parent eq parent) }.firstOrNull()
}
@JvmStatic
fun getSchematicNode(id: Int) = byId(id)
@JvmStatic
fun getAllAccessibleSchematicsOfType(user: Int, type: String) =
accessibleByUserType(SteamwarUser.byId(user)!!, SchematicType.fromDB(type))
@JvmStatic
fun getAllSchematicsAccessibleByUser(user: Int) = getAll(SteamwarUser.byId(user)!!)
@JvmStatic
fun getAllSchematicsOfType(owner: Int, type: String) = useDb {
find { (SchematicNodeTable.owner eq owner) and (SchematicNodeTable.type eq type) }.orderBy(
SchematicNodeTable.name to SortOrder.ASC
).toList()
}
@JvmStatic
fun getAllSchematicsOfType(type: SchematicType) = useDb {
find { (SchematicNodeTable.type eq type.toDB()) }.orderBy(SchematicNodeTable.name to SortOrder.ASC).toList()
}
@JvmStatic
fun deepGet(parent: Int?, filter: (node: SchematicNode) -> Boolean): List<SchematicNode> =
getSchematicNodeInNode(parent)
.flatMap {
return@flatMap if (it.isDir()) {
deepGet(it.nodeId, filter)
} else {
if (filter(it)) listOf(it) else listOf()
}
}
}
return currentNode.orElse(null);
} else {
return SchematicNode.byParentName(user, null, s);
}
}
public static List<SchematicNode> filterSchems(int user, Predicate<SchematicNode> filter) {
List<SchematicNode> finalList = new ArrayList<>();
List<SchematicNode> nodes = getSchematicsAccessibleByUser(user, null);
nodes.forEach(node -> {
if (node.isDir()) {
finalList.addAll(deepGet(node.getId(), filter));
@JvmStatic
fun getNodeFromPath(user: SteamwarUser, path: String): SchematicNode? {
var s = path
if (s.startsWith("/")) {
s = s.drop(1)
}
if (s.endsWith("/")) {
s = s.dropLast(1)
}
if (s.isEmpty()) return null
if (s.contains("/")) {
val layers: Array<String?> = s.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
var currentNode = byParentName(user, null, layers[0]!!)
for (i in 1..<layers.size) {
val node = currentNode?.let { n -> byParentName(user, n.getId(), layers[i]!!) }
if (node == null) {
return null
} else {
currentNode = node
if (!currentNode.isDir() && i != layers.size - 1
) {
return null
}
}
}
return currentNode
} else {
if (filter.test(node))
finalList.add(node);
return byParentName(user, null, s)
}
});
return finalList;
}
public int getId() {
return nodeId;
}
public int getOwner() {
return nodeOwner;
}
public String getName() {
return nodeName;
}
public void setName(String name) {
this.nodeName = name;
updateDB();
}
public Integer getParent() {
return parentNode;
}
public Optional<Integer> getOptionalParent() {
return Optional.ofNullable(parentNode);
}
public void setParent(Integer parent) {
this.parentNode = parent;
updateDB();
}
public String getItem() {
if (nodeItem.isEmpty()) {
return isDir() ? "CHEST" : "CAULDRON_ITEM";
}
return nodeItem;
@JvmStatic
fun invalidSchemName(layers: Array<String>) = layers.any {
it.isEmpty() || FORBIDDEN_CHARS.any { c -> c in it } || FORBIDDEN_NAMES.any { n -> n == it.lowercase() }
}
@JvmStatic
fun getNodeTabcomplete(user: SteamwarUser, s: String): List<String> {
var s = s
val sws = s.startsWith("/")
if (sws) {
s = s.substring(1)
}
val index = s.lastIndexOf("/")
val cacheKey = if (index == -1) "" else s.take(index)
tabCache[user.getId()]?.get(cacheKey)?.also { return it }
val list = mutableListOf<String>()
if (s.contains("/")) {
val preTab = s.take(s.lastIndexOf("/") + 1)
val pa = getNodeFromPath(user, preTab) ?: return emptyList()
val nodes: List<SchematicNode> = list(user, pa.getId())
val br = pa.generateBreadcrumbs(user)
nodes.forEach(Consumer { node: SchematicNode? -> list.add((if (sws) "/" else "") + br + node!!.name + (if (node.isDir()) "/" else "")) })
} else {
val nodes: List<SchematicNode?> = list(user, null)
nodes.forEach(Consumer { node: SchematicNode? -> list.add((if (sws) "/" else "") + node!!.name + (if (node.isDir()) "/" else "")) })
}
list.remove("//copy")
tabCache.computeIfAbsent(user.getId()) { i -> mutableMapOf() }.putIfAbsent(cacheKey, list)
return list
}
}
public void setItem(String item) {
this.nodeItem = item;
updateDB();
val nodeId by SchematicNodeTable.id.transform({ EntityID(it, SchematicNodeTable) }, { it.value })
val nodeOwner by SchematicNodeTable.owner
val owner: Int get() = nodeOwner.value
private var nodeName by SchematicNodeTable.name
var name: String
get() = nodeName
set(value) = useDb {
nodeName = value
}
private var parentNodeId by SchematicNodeTable.parent
var parent: Int?
get() = parentNodeId?.value
set(value) = useDb {
parentNodeId = value?.let { EntityID(it, SchematicNodeTable) }
}
val parentNode: SchematicNode?
get() = parent?.let { findById(it) }
val optionalParent: Optional<Int> get() = Optional.ofNullable(parent)
var lastUpdate by SchematicNodeTable.lastUpdate.transform({ it.toInstant() }, { Timestamp.from(it) })
private var nodeItem by SchematicNodeTable.item
var item: String
get() = nodeItem.ifEmpty {
if (isDir()) "CHEST" else "CAULDRON_ITEM"
}
set(value) = useDb {
nodeItem = value
}
private var nodeType by SchematicNodeTable.type
var schemtype: SchematicType
get() = checkDir { SchematicType.fromDB(nodeType) }
set(value) = checkDir { useDb { nodeType = value.toDB() } }
var config by SchematicNodeTable.config
val members: Set<NodeMember> by lazy { NodeMember.getNodeMembers(nodeId) }
private lateinit var breadcrumbs: String
fun getFileEnding(): String = checkDir { NodeData.getLatest(this).nodeFormat.fileEnding }
fun getId() = nodeId
fun isDir() = nodeType == null
private fun <T> checkDir(block: () -> T): T {
if (!isDir()) {
throw IllegalStateException("Node is not a directory")
}
return block()
}
@Deprecated
public String getType() {
return nodeType.name();
}
fun replaceColor() = getConfig(ConfigFlags.REPLACE_COLOR)
fun setReplaceColor(value: Boolean) = setConfig(ConfigFlags.REPLACE_COLOR, value)
@Deprecated
public void setType(String type) {
if (isDir())
throw new SecurityException("Node is Directory");
this.nodeType = SchematicType.fromDB(type);
updateDB();
}
fun allowReplay() = getConfig(ConfigFlags.ALLOW_REPLAY)
fun setAllowReplay(value: Boolean) = setConfig(ConfigFlags.ALLOW_REPLAY, value)
public boolean isDir() {
return nodeType == null;
}
fun isPrepared() = getConfig(ConfigFlags.IS_PREPARED)
fun setPrepared(value: Boolean) = setConfig(ConfigFlags.IS_PREPARED, value)
public String getFileEnding() {
if (isDir())
throw new SecurityException("Node is Directory");
return NodeData.getLatest(this).getNodeFormat().getFileEnding();
}
public int getRank() {
if (isDir())
throw new SecurityException("Node is Directory");
return nodeRank;
}
@Deprecated
public int getRankUnsafe() {
return nodeRank;
}
public void setRank(int rank) {
if (isDir())
throw new SecurityException("Node is Directory");
this.nodeRank = rank;
}
public SchematicType getSchemtype() {
if (isDir())
throw new SecurityException("Is Directory");
return nodeType;
}
public void setSchemtype(SchematicType type) {
if (isDir())
throw new SecurityException("Is Directory");
this.nodeType = type;
updateDB();
}
public boolean replaceColor() {
return getConfig(ConfigFlags.REPLACE_COLOR);
}
public void setReplaceColor(boolean replaceColor) {
if (isDir())
throw new SecurityException("Is Directory");
setConfig(ConfigFlags.REPLACE_COLOR, replaceColor);
}
public boolean allowReplay() {
return getConfig(ConfigFlags.ALLOW_REPLAY);
}
public void setAllowReplay(boolean allowReplay) {
if (isDir())
throw new SecurityException("Is Directory");
setConfig(ConfigFlags.ALLOW_REPLAY, allowReplay);
}
public boolean isPrepared() {
return getConfig(ConfigFlags.IS_PREPARED);
}
public void setPrepared(boolean prepared) {
if (isDir())
throw new SecurityException("Is Directory");
setConfig(ConfigFlags.IS_PREPARED, prepared);
}
public boolean getConfig(ConfigFlags flag) {
return (config & (1 << flag.ordinal())) != 0;
}
public void setConfig(ConfigFlags flag, boolean value) {
if (value) {
config |= (1 << flag.ordinal());
fun getConfig(flag: ConfigFlags) = config and (1 shl flag.ordinal) != 0
fun setConfig(flag: ConfigFlags, value: Boolean) = useDb {
config = if (value) {
config or (1 shl flag.ordinal)
} else {
config &= ~(1 << flag.ordinal());
config and (1 shl flag.ordinal).inv()
}
updateDB();
}
public SchematicNode getParentNode() {
if (parentNode == null)
return null;
return SchematicNode.getSchematicNode(parentNode);
fun getElo(season: Int) = SchemElo.getElo(this, season)
override fun delete() = useDb {
super.delete()
}
public int getElo(int season) {
return SchemElo.getElo(this, season);
override fun hashCode() = nodeId
override fun equals(other: Any?): Boolean {
if (other !is SchematicNode) return false
return nodeId == other.nodeId
}
public boolean accessibleByUser(SteamwarUser user) {
return NodeMember.getNodeMember(nodeId, user) != null;
}
public Set<NodeMember> getMembers() {
return NodeMember.getNodeMembers(nodeId);
}
public Timestamp getLastUpdate() {
return lastUpdate;
}
private void updateDB() {
this.lastUpdate = Timestamp.from(Instant.now());
update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, config, nodeId);
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 byIdAndUser(user, nodeId).generateBreadcrumbs();
}
public String generateBreadcrumbs(String split, SteamwarUser user) {
return byIdAndUser(user, nodeId).generateBreadcrumbs(split);
}
public String generateBreadcrumbs() {
if (brCache == null) {
brCache = generateBreadcrumbs("/");
fun generateBreadcrumbs(user: SteamwarUser): String {
if (!this::breadcrumbs.isInitialized) {
breadcrumbs = generateBreadcrumbs("/", user)
}
return brCache;
return breadcrumbs
}
public String generateBreadcrumbs(String split) {
StringBuilder builder = new StringBuilder(getName());
Optional<SchematicNode> currentNode = Optional.of(this);
if (currentNode.map(SchematicNode::isDir).orElse(false)) {
builder.append(split);
fun generateBreadcrumbs(split: String, user: SteamwarUser): String {
val builder: StringBuilder = StringBuilder(name)
if (isDir()) {
builder.append(split)
}
while (currentNode.isPresent()) {
var currentNode: SchematicNode? = this
while (currentNode != null) {
currentNode = currentNode
.flatMap(schematicNode -> Optional
.ofNullable(NodeMember.getNodeMember(schematicNode.getId(), effectiveOwner))
.map(NodeMember::getParent).orElse(schematicNode.getOptionalParent()))
.map(SchematicNode::getSchematicNode);
currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName()));
.let {
NodeMember.getNodeMember(it.nodeId, user.getId())
?.parent?.orElse(null) ?: it.parent
}
?.let { findById(it) }
?.also {
builder.insert(0, split).insert(0, it.name)
}
}
return builder.toString();
return builder.toString()
}
public List<Map.Entry<String, Integer>> generateBreadcrumbsMap(SteamwarUser user) {
List<Map.Entry<String, Integer>> map = new ArrayList<>();
Optional<SchematicNode> currentNode = Optional.of(this);
if (currentNode.map(SchematicNode::isDir).orElse(false)) {
map.add(new AbstractMap.SimpleEntry<>(getName(), getId()));
}
while (currentNode.isPresent()) {
fun generateBreadcrumbsMap(user: SteamwarUser): List<Pair<String, Int>> {
val map = mutableListOf<Pair<String, Int>>()
var currentNode: SchematicNode? = this
while (currentNode != null) {
currentNode = currentNode
.flatMap(schematicNode -> Optional
.ofNullable(NodeMember.getNodeMember(schematicNode.getId(), effectiveOwner))
.map(NodeMember::getParent).orElse(schematicNode.getOptionalParent()))
.map(SchematicNode::getSchematicNode);
currentNode.ifPresent(node -> map.add(0, new AbstractMap.SimpleEntry<>(node.getName(), node.getId())));
.also {
map.add(0, Pair(it.name, it.nodeId))
}
.let {
NodeMember.getNodeMember(it.nodeId, user.getId())
?.parent?.orElse(null) ?: it.parent
}
?.let { findById(it) }
}
return map;
return map
}
private static final List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public"));
fun accessibleByUser(user: SteamwarUser) = schematicAccessibleForUser(user, nodeId)
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<String> 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<String> list = new ArrayList<>();
if (s.contains("/")) {
String preTab = s.substring(0, s.lastIndexOf("/") + 1);
SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab);
if (pa == null)
return new ArrayList<>();
List<SchematicNode> nodes = SchematicNode.list(user, pa.getId());
String br = pa.generateBreadcrumbs();
nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName() + (node.isDir() ? "/" : "")));
} else {
List<SchematicNode> nodes = SchematicNode.list(user, null);
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;
}
public static enum ConfigFlags {
enum class ConfigFlags {
REPLACE_COLOR,
ALLOW_REPLAY,
IS_PREPARED
}
@Deprecated("Removed")
var rank = 0
}
@@ -19,11 +19,21 @@
package de.steamwar.sql.internal
import org.intellij.lang.annotations.Language
import org.jetbrains.exposed.v1.core.ColumnType
import org.jetbrains.exposed.v1.core.Expression
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.statements.StatementType
import org.jetbrains.exposed.v1.core.statements.api.RowApi
import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.JdbcTransaction
import org.jetbrains.exposed.v1.jdbc.statements.jdbc.JdbcResult
import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import java.io.File
import java.sql.ResultSet
import java.util.Properties
object KotlinDatabase {
@@ -55,4 +65,18 @@ object KotlinDatabase {
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
KotlinDatabase.ensureConnected()
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db, statement)
}
fun <T : IntEntity> IntEntityClass<T>.fromSql(
@Language("MySQL") stmt: String,
args: List<Pair<ColumnType<*>, Any?>>,
fieldIndex: Map<Expression<*>, Int>
): List<T> = useDb {
exec(stmt, explicitStatementType = StatementType.SELECT, args = args) {
val list = mutableListOf<T>()
while (it.next()) {
list.add(wrapRow(ResultRow.create(JdbcResult(it), fieldIndex)))
}
list
} ?: listOf()
}