Issue #369: Move isTypeName and isFeatureName to public API

- Moved the two methods to the public TypeSystemUtil class
- Deprecate them in the old place and mark them for removal in next major version
diff --git a/uimaj-core/src/main/java/org/apache/uima/cas/impl/TypeSystemUtils.java b/uimaj-core/src/main/java/org/apache/uima/cas/impl/TypeSystemUtils.java
index 35070e2..bde4ade 100644
--- a/uimaj-core/src/main/java/org/apache/uima/cas/impl/TypeSystemUtils.java
+++ b/uimaj-core/src/main/java/org/apache/uima/cas/impl/TypeSystemUtils.java
@@ -21,10 +21,10 @@
 import java.util.ArrayDeque;
 import java.util.Deque;
 import java.util.List;
-import java.util.StringTokenizer;
 
 import org.apache.uima.cas.Type;
 import org.apache.uima.cas.TypeSystem;
+import org.apache.uima.util.TypeSystemUtil;
 
 /**
  * Type Utilities - all static, so class is abstract to prevent creation Used by Feature Path
@@ -233,40 +233,43 @@
     public void setErrorPosition(int errorPosition) {
       this.errorPosition = errorPosition;
     }
-
   }
 
+  /**
+   * @deprecated Use {@link TypeSystemUtil#isFeatureName} instead
+   */
+  @SuppressWarnings("javadoc")
+  @Deprecated
   public static boolean isIdentifier(String s) {
-    if (s == null) {
-      return false;
-    }
-    final int len = s.length();
-    if (len == 0) {
-      return false;
-    }
-    int pos = 0;
-    // Check that the first character is a letter.
-    if (!isIdentifierStart(s.charAt(pos))) {
-      return false;
-    }
-    ++pos;
-    while (pos < len) {
-      if (!isIdentifierChar(s.charAt(pos))) {
-        return false;
-      }
-      ++pos;
-    }
-    return true;
+    return TypeSystemUtil.isFeatureName(s);
   }
 
+  /**
+   * @deprecated To be removed without replacement.
+   * @forRemoval 4.0.0
+   */
+  @SuppressWarnings("javadoc")
+  @Deprecated
   static boolean isNonQualifiedName(String s) {
     return isIdentifier(s);
   }
 
+  /**
+   * @deprecated To be removed without replacement.
+   * @forRemoval 4.0.0
+   */
+  @SuppressWarnings("javadoc")
+  @Deprecated
   static boolean isIdentifierStart(char c) {
     return Character.isLetter(c);
   }
 
+  /**
+   * @deprecated To be removed without replacement.
+   * @forRemoval 4.0.0
+   */
+  @SuppressWarnings("javadoc")
+  @Deprecated
   static boolean isIdentifierChar(char c) {
     return (Character.isLetter(c) || Character.isDigit(c) || (c == '_'));
   }
@@ -280,28 +283,12 @@
    * @param name
    *          The name to check.
    * @return <code>true</code> iff <code>name</code> is a possible type name.
+   * @deprecated Use {@link TypeSystemUtil#isTypeName(String)} instead
+   * @forRemoval 4.0.0
    */
+  @Deprecated
   static boolean isTypeName(String name) {
-    // Create a string tokenizer that will split the string at the name
-    // space
-    // boundaries. We need to see the delimiters to make sure there are no
-    // gratuitous delimiters at the beginning or the end.
-    StringTokenizer tok = new StringTokenizer(name, NAMESPACE_SEPARATOR_AS_STRING, true);
-    // Loop over the tokens and check that every item is an identifier.
-    while (tok.hasMoreTokens()) {
-      // Any subsequence must start with an identifier.
-      if (!isIdentifier(tok.nextToken())) {
-        return false;
-      }
-      // If there is a next token, it must be a separator.
-      if (tok.hasMoreTokens()) {
-        // A sequence can not end in a separator.
-        if (!tok.nextToken().equals(NAMESPACE_SEPARATOR_AS_STRING) || !tok.hasMoreTokens()) {
-          return false;
-        }
-      }
-    }
-    return true;
+    return TypeSystemUtil.isTypeName(name);
   }
 
   static boolean isTypeNameSpaceName(String name) {
@@ -357,16 +344,17 @@
     if (path.isEmpty()) {
       return status;
     }
+
     // Pop the next feature name from the stack and check if it's defined for the current type.
-    String featName = path.pop();
-    FeatureImpl fi = type.getFeatureByBaseName(featName);
+    var featName = path.pop();
+    var fi = type.getFeatureByBaseName(featName);
     if (fi != null) {
       // If feature is defined, we can continue directly.
       return isPathValid(fi.getRangeImpl(), path, status);
     }
+
     // If feature is not defined for type, check to see if there are any subtypes for which the
     // path is defined (possible).
-
     return isPathValidInSubtypes(type, featName, path);
 
   }
@@ -399,9 +387,8 @@
     for (TypeImpl subtype : type.getDirectSubtypes()) {
       FeatureImpl fi = subtype.getFeatureByBaseName(fName);
       if (fi != null) {
-        if (PathValid.POSSIBLE == isPathValid(fi.getRangeImpl(), nextPath, PathValid.POSSIBLE)) { // check
-                                                                                                  // subsequent
-                                                                                                  // types.
+        // check subsequent types.
+        if (PathValid.POSSIBLE == isPathValid(fi.getRangeImpl(), nextPath, PathValid.POSSIBLE)) {
           return PathValid.POSSIBLE;
         } else {
           continue; // try another subtype
diff --git a/uimaj-core/src/main/java/org/apache/uima/util/TypeSystemUtil.java b/uimaj-core/src/main/java/org/apache/uima/util/TypeSystemUtil.java
index 59b9d84..5c05044 100644
--- a/uimaj-core/src/main/java/org/apache/uima/util/TypeSystemUtil.java
+++ b/uimaj-core/src/main/java/org/apache/uima/util/TypeSystemUtil.java
@@ -24,6 +24,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.StringTokenizer;
 
 import org.apache.uima.ResourceSpecifierFactory;
 import org.apache.uima.UIMAFramework;
@@ -42,6 +43,8 @@
 
 public class TypeSystemUtil {
 
+  private static final String NAMESPACE_SEPARATOR_AS_STRING = "" + TypeSystem.NAMESPACE_SEPARATOR;
+
   /**
    * Loads type system descriptions and resolves their imports. For example when you place a
    * {@link TypeSystemDescriptionProvider} implementation and place the type system descriptions it
@@ -206,4 +209,74 @@
     return lts.ll_getStringSet(lts.ll_getCodeForType(aType));
   }
 
+  /**
+   * @return if the given {@code name} is a valid feature name. Does not check if the feature
+   *         actually exists!
+   * @param name
+   *          The name to check.
+   */
+  public static boolean isFeatureName(String name) {
+
+    return isIdentifier(name);
+  }
+
+  /**
+   * Check if {@code name} is a possible type name. Does not check if this type actually exists!
+   * 
+   * @param name
+   *          The name to check.
+   * @return <code>true</code> iff <code>name</code> is a possible type name.
+   */
+  public static boolean isTypeName(String name) {
+    // Create a string tokenizer that will split the string at the name space
+    // boundaries. We need to see the delimiters to make sure there are no
+    // gratuitous delimiters at the beginning or the end.
+    var tok = new StringTokenizer(name, NAMESPACE_SEPARATOR_AS_STRING, true);
+    // Loop over the tokens and check that every item is an identifier.
+    while (tok.hasMoreTokens()) {
+      // Any subsequence must start with an identifier.
+      if (!isIdentifier(tok.nextToken())) {
+        return false;
+      }
+      // If there is a next token, it must be a separator.
+      if (tok.hasMoreTokens()) {
+        // A sequence can not end in a separator.
+        if (!tok.nextToken().equals(NAMESPACE_SEPARATOR_AS_STRING) || !tok.hasMoreTokens()) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  private static boolean isIdentifier(String s) {
+    if (s == null) {
+      return false;
+    }
+    final int len = s.length();
+    if (len == 0) {
+      return false;
+    }
+    int pos = 0;
+    // Check that the first character is a letter.
+    if (!isIdentifierStart(s.charAt(pos))) {
+      return false;
+    }
+    ++pos;
+    while (pos < len) {
+      if (!isIdentifierChar(s.charAt(pos))) {
+        return false;
+      }
+      ++pos;
+    }
+    return true;
+  }
+
+  private static boolean isIdentifierStart(char c) {
+    return Character.isLetter(c);
+  }
+
+  private static boolean isIdentifierChar(char c) {
+    return (Character.isLetter(c) || Character.isDigit(c) || (c == '_'));
+  }
 }