Add Name classes that deal with various flavors of Java names, and some
uses in GWT code.

This was split out from the IHM patch.

Patch by: jat
Review by: scottb.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5859 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
index 8a8a661..ba50d5b 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType;
 import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.util.Name;
 import com.google.gwt.dev.util.collect.HashMap;
 import com.google.gwt.dev.util.collect.IdentityHashMap;
 
@@ -253,6 +254,7 @@
    * @return <code>null</code> if the type is not found
    */
   public JClassType findType(String name) {
+    assert Name.isSourceName(name);
     return allTypes.get(name);
   }
 
@@ -264,6 +266,7 @@
    * @return <code>null</code> if the type is not found
    */
   public JClassType findType(String pkgName, String typeName) {
+    assert Name.isSourceName(typeName);
     JPackage pkg = findPackage(pkgName);
     if (pkg != null) {
       JClassType type = pkg.findType(typeName);
@@ -471,6 +474,7 @@
    * @return the specified type
    */
   public JClassType getType(String name) throws NotFoundException {
+    assert Name.isSourceName(name);
     JClassType type = findType(name);
     if (type == null) {
       throw new NotFoundException(name);
@@ -487,6 +491,7 @@
    */
   public JClassType getType(String pkgName, String topLevelTypeSimpleName)
       throws NotFoundException {
+    assert Name.isSourceName(topLevelTypeSimpleName);
     JClassType type = findType(pkgName, topLevelTypeSimpleName);
     if (type == null) {
       throw new NotFoundException(pkgName + "." + topLevelTypeSimpleName);
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
index 900f3c3..0815fad 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -36,6 +36,8 @@
 import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
 import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodOracle;
 import com.google.gwt.dev.util.JsniRef;
+import com.google.gwt.dev.util.Name.SourceOrBinaryName;
+import com.google.gwt.dev.util.Name.InternalName;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.util.tools.Utility;
 
@@ -251,12 +253,12 @@
      */
     private Class<?> getClassFromBinaryOrSourceName(String className) {
       // Try the type oracle first
-      JClassType type = typeOracle.findType(className.replace('$', '.'));
+      JClassType type = typeOracle.findType(SourceOrBinaryName.toSourceName(className));
       if (type != null) {
         // Use the type oracle to compute the exact binary name
         String jniSig = type.getJNISignature();
         jniSig = jniSig.substring(1, jniSig.length() - 1);
-        className = jniSig.replace('/', '.');
+        className = InternalName.toBinaryName(jniSig);
       }
       return getClassFromBinaryName(className);
     }
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
index 8720a1b..01426ad 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
@@ -17,6 +17,8 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.util.Name;
+import com.google.gwt.dev.util.Name.BinaryName;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -396,13 +398,14 @@
   @SuppressWarnings("unchecked")
   public <T> T rebindAndCreate(String requestedClassName)
       throws UnableToCompleteException {
+    assert Name.isBinaryName(requestedClassName);
     Throwable caught = null;
     String msg = null;
     String resultName = null;
     try {
       // Rebind operates on source-level names.
       //
-      String sourceName = requestedClassName.replace('$', '.');
+      String sourceName = BinaryName.toSourceName(requestedClassName);
       resultName = rebind(sourceName);
       Class<?> resolvedClass = loadClassFromSourceName(resultName);
       if (Modifier.isAbstract(resolvedClass.getModifiers())) {
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java
index 31de8ff..aed8145 100644
--- a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteJsniMethods.java
@@ -23,6 +23,7 @@
 import com.google.gwt.dev.asm.commons.GeneratorAdapter;
 import com.google.gwt.dev.asm.commons.Method;
 import com.google.gwt.dev.shell.JavaScriptHost;
+import com.google.gwt.dev.util.Name.InternalName;
 
 import java.lang.reflect.Modifier;
 import java.util.Map;
@@ -234,6 +235,7 @@
      *     new Class[] {int.class,}, new Object[] {x,});
      * </pre>
      */
+    @Override
     public void visitCode() {
       super.visitCode();
 
@@ -319,7 +321,7 @@
   private static final Type VOID_TYPE = Type.getObjectType("java/lang/Void");
 
   /**
-   * The name of the class we're operating on.
+   * The internal name of the class we're operating on.
    */
   private String classDesc;
   private Map<String, String> anonymousClassMap;
@@ -370,11 +372,12 @@
         + descriptor;
     String argsDescriptor = descriptor.substring(argsIndexBegin,
         argsIndexEnd + 1);
-    String classDescriptor = classDesc.replace('/', '.');
+    String classDescriptor = InternalName.toBinaryName(classDesc);
     String newDescriptor = anonymousClassMap.get(classDesc);
     if (newDescriptor != null) {
-      classDescriptor = newDescriptor.replace('/', '.');
+      classDescriptor = InternalName.toBinaryName(newDescriptor);
     }
+    // Always use binary names for JSNI method names
     return "@" + classDescriptor + "::" + name + argsDescriptor;
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/util/Name.java b/dev/core/src/com/google/gwt/dev/util/Name.java
new file mode 100644
index 0000000..99b43f8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/Name.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2009 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util;
+
+/**
+ * Utility methods for dealing with the various types of Java names.
+ */
+public class Name {
+
+  /**
+   * Represents a Java class name in binary form, for example:
+   * {@code org.example.Foo$Bar}.
+   * 
+   * See {@link "http://java.sun.com/docs/books/jls/third_edition/html/binaryComp.html#59892"}
+   */
+  public static class BinaryName {
+
+    public static String getClassName(String binaryName) {
+      assert isBinaryName(binaryName);
+      int lastDot = binaryName.lastIndexOf('.');
+      if (lastDot < 0) {
+        return binaryName;
+      }
+      return binaryName.substring(lastDot + 1);
+    }
+
+    /**
+     * Construct the fully qualified name of an inner class.
+     * 
+     * @param outerClassBinaryName binary name of outer class, ie
+     *     {@code org.test.Foo}
+     * @param innerClassShortName short name of inner class, ie {@code Bar}
+     * @return fully qualified binary name of the inner class
+     */
+    public static String getInnerClassName(String outerClassBinaryName,
+        String innerClassShortName) {
+      assert isBinaryName(outerClassBinaryName);
+      return outerClassBinaryName + '$' + innerClassShortName;
+    }
+
+    public static String getOuterClassName(String binaryName) {
+      assert isBinaryName(binaryName);
+      int lastDollar = binaryName.lastIndexOf('$');
+      if (lastDollar < 0) {
+        return null;
+      }
+      return binaryName.substring(0, lastDollar);
+    }
+    
+    public static String getPackageName(String binaryName) {
+      assert isBinaryName(binaryName);
+      int lastDot = binaryName.lastIndexOf('.');
+      if (lastDot < 0) {
+        return "";
+      }
+      return binaryName.substring(0, lastDot);
+    }
+
+    public static String getShortClassName(String binaryName) {
+      assert isBinaryName(binaryName);
+      String className = getClassName(binaryName);
+      int lastDollar = className.lastIndexOf('$', className.length() - 2);
+      if (lastDollar < 0) {
+        return className;
+      }
+      return className.substring(lastDollar + 1);
+    }
+
+    public static String toInternalName(String binaryName) {
+      assert isBinaryName(binaryName);
+      return binaryName.replace('.', '/');
+    }
+
+    public static String toSourceName(String binaryName) {
+      assert isBinaryName(binaryName);
+      // don't change a trailing $ to a .
+      return binaryName.replaceAll("[$](\\w)", ".$1");
+    }
+    
+    private BinaryName() {
+    }
+  }
+
+  /**
+     * Represents a Java class name in internal form, for example:
+     * {@code org/example/Foo$Bar}.
+     * 
+     * See {@link "http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#14757"}
+     */
+    public static class InternalName {
+  
+      public static String getClassName(String name) {
+        assert isInternalName(name);
+        int lastSlash = name.lastIndexOf('/');
+        if (lastSlash < 0) {
+          return name;
+        }
+        return name.substring(lastSlash + 1);
+      }
+  
+      /**
+       * Construct the fully qualified name of an inner class.
+       * 
+       * @param outerClassInternalName internal name of outer class,
+       *     ie {@code org.test.Foo}
+       * @param innerClassShortName short name of inner class, ie {@code Bar}
+       * @return fully qualified internal name of the inner class
+       */
+      public static String getInnerClassName(String outerClassInternalName,
+          String innerClassShortName) {
+        assert isInternalName(outerClassInternalName);
+        return outerClassInternalName + '$' + innerClassShortName;
+      }
+  
+      /**
+       * Return the outer class name of an inner class, or null if this is not
+       * an inner class.
+       * 
+       * @param name internal name which might be an inner class
+       * @return an internal name of the enclosing class or null if none
+       */
+      public static String getOuterClassName(String name) {
+        int lastDollar = name.lastIndexOf('$');
+        if (lastDollar < 0) {
+          return null;
+        }
+        return name.substring(0, lastDollar);
+      }
+  
+      public static String getPackageName(String name) {
+        assert isInternalName(name);
+        int lastSlash = name.lastIndexOf('/');
+        if (lastSlash < 0) {
+          return "";
+        }
+        return name.substring(0, lastSlash);
+      }
+      
+      public static String getShortClassName(String internalName) {
+        assert isInternalName(internalName);
+        String className = getClassName(internalName);
+        int lastDollar = className.lastIndexOf('$', className.length() - 2);
+        if (lastDollar < 0) {
+          return className;
+        }
+        return className.substring(lastDollar + 1);
+      }
+  
+      public static String toBinaryName(String internalName) {
+        assert isInternalName(internalName);
+        return internalName.replace('/', '.');
+      }
+      
+      public static String toSourceName(String internalName) {
+        assert isInternalName(internalName);
+        // don't change a trailing $ or slash to a .
+        return internalName.replaceAll("[$/](\\w)", ".$1");
+      }
+  
+      private InternalName() {
+      }
+    }
+
+ /**
+ * Represents a Java class name in source form, for example:
+ * {@code org.example.Foo.Bar}.
+ * 
+ * See {@link "http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc.html#20207"}
+ */
+public static class SourceName {
+
+  /**
+   * Construct the fully qualified name of an inner class.
+   * 
+   * @param outerClassSourceName source name of outer class, ie 
+   *     {@code org.test.Foo}
+   * @param innerClassShortName short name of inner class, ie {@code Bar}
+   * @return fully qualified source name of the inner class
+   */
+  public static String getInnerClassName(String outerClassSourceName,
+      String innerClassShortName) {
+    assert isSourceName(outerClassSourceName);
+    return outerClassSourceName + '.' + innerClassShortName;
+  }
+
+  public static String getShortClassName(String sourceName) {
+    assert isSourceName(sourceName);
+    int lastDollar = sourceName.lastIndexOf('.');
+    if (lastDollar < 0) {
+      return sourceName;
+    }
+    return sourceName.substring(lastDollar + 1);
+  }
+  
+  private SourceName() {
+  }
+}
+
+  /**
+   * Represents a Java class name in either source or binary form, for example:
+   * {@code org.example.Foo.Bar or org.example.Foo$Bar}.
+   * 
+   * See {@link "http://java.sun.com/docs/books/jls/third_edition/html/binaryComp.html#59892"}
+   */
+  public static class SourceOrBinaryName {
+
+    public static String toSourceName(String dottedName) {
+      // don't change a trailing $ to a .
+      return dottedName.replaceAll("[$](\\w)", ".$1");
+    }
+  }
+
+  /**
+   * Get the binary name for a Java class.
+   * 
+   * @param clazz class literal
+   * @return binary name for the class
+   */
+  public static String getBinaryNameForClass(Class<?> clazz) {
+    return clazz.getName();
+  }
+
+  /**
+   * Get the internal name for a Java class.
+   * 
+   * @param clazz class literal
+   * @return internal name for the class
+   */
+  public static String getInternalNameForClass(Class<?> clazz) {
+    return BinaryName.toInternalName(getBinaryNameForClass(clazz));
+  }
+
+  /**
+   * Get the source name for a Java class.
+   * 
+   * @param clazz class literal
+   * @return source name for the class
+   */
+  public static String getSourceNameForClass(Class<?> clazz) {
+    return clazz.getCanonicalName();
+  }
+
+  /**
+   * @return true if name could be a valid binary name.
+   * 
+   * Note that many invalid names might pass this test -- in particular, source
+   * names cannot be verified to know they are not valid binary names without
+   * being able to tell the package name part of the name.
+   * 
+   * @param name class name to test
+   */
+  public static boolean isBinaryName(String name) {
+    return name == null || !name.contains("/");
+  }
+
+  /**
+   * @return true if name could be a valid internal name.
+   * 
+   * Note that many invalid names might pass this test.
+   * 
+   * @param name class name to test
+   */
+  public static boolean isInternalName(String name) {
+    return name == null || !name.contains(".");
+  }
+
+  /**
+   * @return true if name could be a valid source name.
+   * 
+   * Note that many invalid names might pass this test.
+   * 
+   * @param name class name to test
+   */
+  public static boolean isSourceName(String name) {
+    if (name == null) {
+      return true;
+    }
+    int dollar = name.indexOf('$');
+    return !name.contains("/") && (dollar < 0 || dollar == name.length() - 1);
+  }
+
+  /**
+   * @return true if name could be a valid source or binary name.
+   * 
+   * Note that many invalid names might pass this test.
+   * 
+   * @param name class name to test
+   */
+  public static boolean isSourceOrBinaryName(String name) {
+    return name == null || !name.contains("/");
+  }
+  
+  private Name() {
+  }
+}
diff --git a/dev/core/test/com/google/gwt/dev/util/NameTest.java b/dev/core/test/com/google/gwt/dev/util/NameTest.java
new file mode 100644
index 0000000..c0b062f
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/util/NameTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2009 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util;
+
+import com.google.gwt.dev.util.Name.BinaryName;
+import com.google.gwt.dev.util.Name.SourceOrBinaryName;
+import com.google.gwt.dev.util.Name.InternalName;
+import com.google.gwt.dev.util.Name.SourceName;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link Name}.
+ */
+public class NameTest extends TestCase {
+
+  /**
+   * Used to test getting names from a Class instance.
+   */
+  private static class Inner {   
+  }
+
+  public void testBinaryName() {
+    assertEquals("org.test.Foo", BinaryName.toSourceName("org.test.Foo"));
+    assertEquals("org.test.Foo.Bar",
+        BinaryName.toSourceName("org.test.Foo$Bar"));
+    assertEquals("org.test.Foo.Bar.Baz",
+        BinaryName.toSourceName("org.test.Foo$Bar$Baz"));
+    assertEquals("org.test.Foo.Bar.Baz$",
+        BinaryName.toSourceName("org.test.Foo$Bar$Baz$"));
+    assertEquals("org/test/Foo", BinaryName.toInternalName("org.test.Foo"));
+    assertEquals("org/test/Foo$Bar",
+        BinaryName.toInternalName("org.test.Foo$Bar"));
+    assertEquals("org/test/Foo$Bar$Baz",
+        BinaryName.toInternalName("org.test.Foo$Bar$Baz"));
+    assertEquals("org/test/Foo$Bar$Baz$",
+        BinaryName.toInternalName("org.test.Foo$Bar$Baz$"));
+    assertEquals("org/test/Foo$Bar$Baz$1",
+        BinaryName.toInternalName("org.test.Foo$Bar$Baz$1"));
+    assertEquals("org.test.Foo$Bar",
+        BinaryName.getInnerClassName("org.test.Foo", "Bar"));
+    assertEquals("org.test.Foo",
+        BinaryName.getOuterClassName("org.test.Foo$Bar"));
+    assertEquals("org.test",
+        BinaryName.getPackageName("org.test.Foo$Bar"));
+    assertEquals("Foo$Bar", BinaryName.getClassName("org.test.Foo$Bar"));
+    assertEquals("Bar", BinaryName.getShortClassName("org.test.Foo$Bar"));
+  }
+
+  public void testSourceOrBinaryName() {
+    assertEquals("org.test.Foo.Bar",
+        SourceOrBinaryName.toSourceName("org.test.Foo.Bar"));
+    assertEquals("org.test.Foo.Bar",
+        SourceOrBinaryName.toSourceName("org.test.Foo$Bar"));
+    assertEquals("org.test.Foo.Bar$",
+        SourceOrBinaryName.toSourceName("org.test.Foo.Bar$"));
+    assertEquals("org.test.Foo.Bar$",
+        SourceOrBinaryName.toSourceName("org.test.Foo$Bar$"));
+  }
+
+  public void testGetBinaryNameForClass() {
+    assertEquals("com.google.gwt.dev.util.NameTest$Inner",
+        Name.getBinaryNameForClass(Inner.class));
+  }
+
+  public void testGetInternalNameForClass() {
+    assertEquals("com/google/gwt/dev/util/NameTest$Inner",
+        Name.getInternalNameForClass(Inner.class));
+  }
+
+  public void testGetSourceNameForClass() {
+    assertEquals("com.google.gwt.dev.util.NameTest.Inner",
+        Name.getSourceNameForClass(Inner.class));
+  }
+
+  public void testInternalName() {
+    assertEquals("org.test.Foo", InternalName.toSourceName("org/test/Foo"));
+    assertEquals("org.test.Foo.Bar",
+        InternalName.toSourceName("org/test/Foo$Bar"));
+    assertEquals("org.test.Foo.Bar.Baz",
+        InternalName.toSourceName("org/test/Foo$Bar$Baz"));
+    assertEquals("org.test.Foo.Bar.Baz$",
+        InternalName.toSourceName("org/test/Foo$Bar$Baz$"));
+    assertEquals("org.test.Foo", InternalName.toBinaryName("org/test/Foo"));
+    assertEquals("org.test.Foo$Bar",
+        InternalName.toBinaryName("org/test/Foo$Bar"));
+    assertEquals("org.test.Foo$Bar$Baz",
+        InternalName.toBinaryName("org/test/Foo$Bar$Baz"));
+    assertEquals("org.test.Foo$Bar$Baz$",
+        InternalName.toBinaryName("org/test/Foo$Bar$Baz$"));
+    assertEquals("org.test.Foo$Bar$Baz$1",
+        InternalName.toBinaryName("org/test/Foo$Bar$Baz$1"));
+    assertEquals("org/test/Foo$Bar",
+        InternalName.getInnerClassName("org/test/Foo", "Bar"));
+    assertEquals("org/test/Foo",
+        InternalName.getOuterClassName("org/test/Foo$Bar"));
+    assertEquals("org/test",
+        InternalName.getPackageName("org/test/Foo$Bar"));
+    assertEquals("Foo$Bar", InternalName.getClassName("org/test/Foo$Bar"));
+    assertEquals("Bar", InternalName.getShortClassName("org/test/Foo$Bar"));
+  }
+
+  public void testIsBinaryName() {
+    assertTrue(Name.isBinaryName("org.test.Foo"));
+    assertTrue(Name.isBinaryName("org.test.Foo$Bar"));
+    assertTrue(Name.isBinaryName("org.test.Foo$Bar$Baz"));
+    assertTrue(Name.isBinaryName("org.test.Foo$Bar$Baz$"));
+    assertTrue(Name.isBinaryName("org.test.Foo$Bar$Baz$1"));
+    assertFalse(Name.isBinaryName("org/test/Foo"));
+    assertFalse(Name.isBinaryName("org/test/Foo$Bar"));
+    assertFalse(Name.isBinaryName("org/test/Foo$Bar$Baz"));
+    assertFalse(Name.isBinaryName("org/test/Foo$Bar$Baz$"));
+    assertFalse(Name.isBinaryName("org/test/Foo$Bar$Baz$1"));
+    assertTrue(Name.isBinaryName("org.test.Foo.Bar"));
+    // We can't tell these aren't binary names without being able to tell
+    // what the name of the top-level class is, but don't want to encode
+    // bad behavior in the test.
+    // assertTrue(Name.isBinaryName("org.test.Foo.Bar.Baz"));
+    // assertTrue(Name.isBinaryName("org.test.Foo.Bar.Baz$"));
+  }
+
+  public void testIsInternalName() {
+    assertFalse(Name.isInternalName("org.test.Foo"));
+    assertFalse(Name.isInternalName("org.test.Foo$Bar"));
+    assertFalse(Name.isInternalName("org.test.Foo$Bar$Baz"));
+    assertFalse(Name.isInternalName("org.test.Foo$Bar$Baz$"));
+    assertFalse(Name.isInternalName("org.test.Foo$Bar$Baz$1"));
+    assertTrue(Name.isInternalName("org/test/Foo"));
+    assertTrue(Name.isInternalName("org/test/Foo$Bar"));
+    assertTrue(Name.isInternalName("org/test/Foo$Bar$Baz"));
+    assertTrue(Name.isInternalName("org/test/Foo$Bar$Baz$"));
+    assertTrue(Name.isInternalName("org/test/Foo$Bar$Baz$1"));
+    assertFalse(Name.isInternalName("org.test.Foo.Bar"));
+    assertFalse(Name.isInternalName("org.test.Foo.Bar.Baz"));
+    assertFalse(Name.isInternalName("org.test.Foo.Bar.Baz$"));
+  }
+
+  public void testIsSourceName() {
+    assertTrue(Name.isSourceName("org.test.Foo"));
+    assertFalse(Name.isSourceName("org.test.Foo$Bar"));
+    assertFalse(Name.isSourceName("org.test.Foo$Bar$Baz"));
+    assertFalse(Name.isSourceName("org.test.Foo$Bar$Baz$"));
+    assertFalse(Name.isSourceName("org.test.Foo$Bar$Baz$1"));
+    assertFalse(Name.isSourceName("org/test/Foo"));
+    assertFalse(Name.isSourceName("org/test/Foo$Bar"));
+    assertFalse(Name.isSourceName("org/test/Foo$Bar$Baz"));
+    assertFalse(Name.isSourceName("org/test/Foo$Bar$Baz$"));
+    assertFalse(Name.isSourceName("org/test/Foo$Bar$Baz$1"));
+    assertTrue(Name.isSourceName("org.test.Foo.Bar"));
+    assertTrue(Name.isSourceName("org.test.Foo.Bar.Baz"));
+    assertTrue(Name.isSourceName("org.test.Foo.Bar.Baz$"));
+  }
+
+  public void testIsSourceOrBinaryName() {
+    assertTrue(Name.isSourceOrBinaryName("org.test.Foo"));
+    assertTrue(Name.isSourceOrBinaryName("org.test.Foo$Bar"));
+    assertTrue(Name.isSourceOrBinaryName("org.test.Foo$Bar$Baz"));
+    assertTrue(Name.isSourceOrBinaryName("org.test.Foo$Bar$Baz$"));
+    assertTrue(Name.isSourceOrBinaryName("org.test.Foo$Bar$Baz$1"));
+    assertFalse(Name.isSourceOrBinaryName("org/test/Foo"));
+    assertFalse(Name.isSourceOrBinaryName("org/test/Foo$Bar"));
+    assertFalse(Name.isSourceOrBinaryName("org/test/Foo$Bar$Baz"));
+    assertFalse(Name.isSourceOrBinaryName("org/test/Foo$Bar$Baz$"));
+    assertFalse(Name.isSourceOrBinaryName("org/test/Foo$Bar$Baz$1"));
+    assertTrue(Name.isSourceOrBinaryName("org.test.Foo.Bar"));
+    assertTrue(Name.isSourceOrBinaryName("org.test.Foo.Bar.Baz"));
+    assertTrue(Name.isSourceOrBinaryName("org.test.Foo.Bar.Baz$"));
+  }
+
+  public void testSourceName() {
+    assertEquals("org.test.Foo.Bar",
+        SourceName.getInnerClassName("org.test.Foo", "Bar"));
+    assertEquals("Bar", SourceName.getShortClassName("org.test.Foo.Bar"));
+  }
+}