2424import java .util .ArrayList ;
2525import java .util .BitSet ;
2626import java .util .Properties ;
27+ import java .util .Set ;
2728
2829/**
2930 * This Class provides some text related utilities
@@ -41,6 +42,8 @@ private Text() {
4142 */
4243 public static final char [] hexTable = "0123456789abcdef" .toCharArray ();
4344
45+ private static final Set <Character > INVALID_JCR_LOCAL_NAME_CHARS = Set .of ( '/' , ':' , '[' , ']' , '*' , '|' );
46+
4447 /**
4548 * Calculate an MD5 hash of the string given.
4649 *
@@ -466,6 +469,10 @@ public static String unescape(String string) {
466469 * char ::= nonspace | ' '
467470 * nonspace ::= (* Any Unicode character except: '/', ':', '[', ']', '*', '|' or any whitespace character *)
468471 * </pre>
472+ * <p>
473+ * Note that just using this method does not necessarily return a string which is a
474+ * <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.2%20Local%20Names">valid local name</a>.
475+ * You still have to take care of invalid <a href="https://www.w3.org/TR/xml/#NT-Char">XML characters</a>.
469476 *
470477 * @param name the name to escape
471478 * @return the escaped name
@@ -567,6 +574,31 @@ public static String unescapeIllegalJcrChars(String name) {
567574 return buffer .toString ();
568575 }
569576
577+ /**
578+ * Checks if the given name is a valid JCR local name.
579+ * <p>
580+ * Note that the return value of {@link #escapeIllegalJcrChars(String)} is not necessarily a valid local name.
581+ * You still have to take care of invalid <a href="https://www.w3.org/TR/xml/#NT-Char">XML characters</a>.
582+ *
583+ * @param localName the string value to check
584+ * @return <code>true</code> if the name is valid, <code>false</code> otherwise.
585+ * @see <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.2%20Local%20Names">JCR 2.0 Spec, §3.2.2 Local Names</a>
586+ * @see #escapeIllegalJcrChars(String)
587+ * @since 2.6.0 (Apache Jackrabbit 2.24.0)
588+ */
589+ public static boolean isValidJcrLocalName (String localName ) {
590+ if (localName == null || localName .isEmpty ()) {
591+ return false ;
592+ }
593+ // self or parent are invalid
594+ if (localName .equals ("." ) || localName .equals (".." )) {
595+ return false ;
596+ }
597+ return localName .chars ().noneMatch (c ->
598+ INVALID_JCR_LOCAL_NAME_CHARS .contains ((char ) c ) || !XMLChar .isValid (c )
599+ );
600+ }
601+
570602 /**
571603 * Returns the name part of the path. If the given path is already a name
572604 * (i.e. contains no slashes) it is returned.
0 commit comments