Skip to content

Commit 503cada

Browse files
committed
validate created identifiers
1 parent e5513d7 commit 503cada

File tree

3 files changed

+140
-9
lines changed

3 files changed

+140
-9
lines changed

libraries/core/src/main/java/net/ornithemc/osl/core/api/util/NamespacedIdentifier.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,12 @@
44

55
public final class NamespacedIdentifier {
66

7-
public static NamespacedIdentifier fromMinecraft(String identifier) {
8-
return new NamespacedIdentifier("minecraft", identifier);
9-
}
10-
11-
public static NamespacedIdentifier from(String namespace, String identifier) {
12-
return new NamespacedIdentifier(namespace, identifier);
13-
}
7+
public static final char SEPARATOR = ':';
148

159
private final String namespace;
1610
private final String identifier;
1711

18-
private NamespacedIdentifier(String namespace, String identifier) {
12+
NamespacedIdentifier(String namespace, String identifier) {
1913
this.namespace = namespace;
2014
this.identifier = identifier;
2115
}
@@ -39,7 +33,7 @@ public int hashCode() {
3933

4034
@Override
4135
public String toString() {
42-
return namespace + ":" + identifier;
36+
return namespace + SEPARATOR + identifier;
4337
}
4438

4539
public String getNamespace() {
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package net.ornithemc.osl.core.api.util;
2+
3+
import net.ornithemc.osl.core.impl.util.NamespacedIdentifierException;
4+
5+
/**
6+
* Utility methods for creating and validating {@link NamespacedIdentifier}s.
7+
*/
8+
public final class NamespacedIdentifiers {
9+
10+
/**
11+
* The {@code minecraft} namespace is used for Vanilla resources and ids.
12+
*/
13+
public static final String MINECRAFT_NAMESPACE = "minecraft";
14+
/**
15+
* The default namespace of {@code NamespacedIdentifier}s.
16+
* It is recommended to use a custom namespace for your own identifiers.
17+
*/
18+
public static final String DEFAULT_NAMESPACE = MINECRAFT_NAMESPACE;
19+
20+
/**
21+
* The maximum length of a {@code NamespacedIdentifier}'s namespace string.
22+
*/
23+
public static final int MAX_LENGTH_NAMESPACE = Integer.MAX_VALUE;
24+
/**
25+
* The maximum length of a {@code NamespacedIdentifier} identifier string.
26+
*/
27+
public static final int MAX_LENGTH_IDENTIFIER = Integer.MAX_VALUE;
28+
29+
/**
30+
* Construct and validate a {@code NamespacedIdentifier} with the default namespace and the given identifier.
31+
*/
32+
public static NamespacedIdentifier from(String identifier) {
33+
return from(DEFAULT_NAMESPACE, identifier);
34+
}
35+
36+
/**
37+
* Construct and validate a {@code NamespacedIdentifier} from the given namespace and identifier.
38+
*/
39+
public static NamespacedIdentifier from(String namespace, String identifier) {
40+
return new NamespacedIdentifier(
41+
validateNamespace(namespace),
42+
validateIdentifier(identifier)
43+
);
44+
}
45+
46+
/**
47+
* Parse and validate a {@code NamespacedIdentifier} from the given {@code String}.
48+
*/
49+
public static NamespacedIdentifier parse(String s) {
50+
int i = s.indexOf(NamespacedIdentifier.SEPARATOR);
51+
52+
if (i > 0) {
53+
return from(s.substring(0, i), s.substring(i + 1));
54+
} else {
55+
return from(s.substring(i + 1));
56+
}
57+
}
58+
59+
/**
60+
* Check whether the given {@code NamespacedIdentifier} is valid, or throw an exception.
61+
*/
62+
public static NamespacedIdentifier validate(NamespacedIdentifier id) {
63+
try {
64+
validateNamespace(id.getNamespace());
65+
validateIdentifier(id.getIdentifier());
66+
67+
return id;
68+
} catch (NamespacedIdentifierException e) {
69+
throw NamespacedIdentifierException.invalid(id, e);
70+
}
71+
}
72+
73+
/**
74+
* Check that the given namespace is valid for a {@code NamespacedIdentifier}.
75+
*/
76+
public static String validateNamespace(String namespace) {
77+
if (namespace == null || namespace.isEmpty()) {
78+
throw NamespacedIdentifierException.invalidNamespace(namespace, "null or empty");
79+
}
80+
if (namespace.length() > MAX_LENGTH_NAMESPACE) {
81+
throw NamespacedIdentifierException.invalidNamespace(namespace, "length " + namespace.length() + " is greater than maximum allowed " + MAX_LENGTH_NAMESPACE);
82+
}
83+
if (!namespace.chars().allMatch(chr -> chr == '-' || chr == '.' || chr == '_' || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9'))) {
84+
throw NamespacedIdentifierException.invalidNamespace(namespace, "contains illegal characters - only [a-zA-Z0-9-._] are allowed");
85+
}
86+
87+
return namespace;
88+
}
89+
90+
/**
91+
* Check that the given identifier is valid for a {@code NamespacedIdentifier}.
92+
*/
93+
public static String validateIdentifier(String identifier) {
94+
if (identifier == null || identifier.isEmpty()) {
95+
throw NamespacedIdentifierException.invalidIdentifier(identifier, "null or empty");
96+
}
97+
if (identifier.length() > MAX_LENGTH_IDENTIFIER) {
98+
throw NamespacedIdentifierException.invalidIdentifier(identifier, "length " + identifier.length() + " is greater than maximum allowed " + MAX_LENGTH_IDENTIFIER);
99+
}
100+
if (!identifier.chars().allMatch(chr -> chr == '-' || chr == '.' || chr == '_' || chr == '/' || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9'))) {
101+
throw NamespacedIdentifierException.invalidIdentifier(identifier, "contains illegal characters - only [a-zA-Z0-9-._/] are allowed");
102+
}
103+
104+
return identifier;
105+
}
106+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package net.ornithemc.osl.core.impl.util;
2+
3+
import net.ornithemc.osl.core.api.util.NamespacedIdentifier;
4+
5+
@SuppressWarnings("serial")
6+
public class NamespacedIdentifierException extends RuntimeException {
7+
8+
private NamespacedIdentifierException(String message) {
9+
super(message);
10+
}
11+
12+
private NamespacedIdentifierException(String message, Throwable cause) {
13+
super(message, cause);
14+
}
15+
16+
public static NamespacedIdentifierException invalid(NamespacedIdentifier id, Throwable cause) {
17+
return new NamespacedIdentifierException("\'" + id + "\' is not a valid namespaced identifier", cause);
18+
}
19+
20+
public static NamespacedIdentifierException invalid(NamespacedIdentifier id, String reason) {
21+
return new NamespacedIdentifierException("\'" + id + "\' is not a valid namespaced identifier: " + reason);
22+
}
23+
24+
public static NamespacedIdentifierException invalidNamespace(String namespace, String reason) {
25+
return new NamespacedIdentifierException("\'" + namespace + "\' is not a valid namespace: " + reason);
26+
}
27+
28+
public static NamespacedIdentifierException invalidIdentifier(String identifier, String reason) {
29+
return new NamespacedIdentifierException("\'" + identifier + "\' is not a valid identifier: " + reason);
30+
}
31+
}

0 commit comments

Comments
 (0)