Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ private void printNativeEnum() {
print("}");

if (!printPrivateDeclarations()) {
String swiftName = nameTable.getSwiftClassNameFromAnnotation(typeElement, true);
String swiftName = nameTable.getSwiftClassNameFromAnnotation(typeElement);
if (swiftName != null) {
printf(" NS_SWIFT_NAME(%sEnum)", swiftName);
}
Expand Down Expand Up @@ -337,7 +337,7 @@ private void printSwiftName() {
return;
}

String swiftName = nameTable.getSwiftClassNameFromAnnotation(typeElement, true);
String swiftName = nameTable.getSwiftClassNameFromAnnotation(typeElement);
// We need to force NS_SWIFT_NAME in this case; otherwise swift confuses this with some kind
// of legacy name that was not migrated and errors.
if (swiftName == null && typeElement.getKind().isInterface() && needsCompanionClass()) {
Expand Down Expand Up @@ -493,7 +493,7 @@ protected void printCompanionClassDeclaration() {
return;
}
if (hasCompanionSuffix()) {
String baseSwiftName = nameTable.getSwiftClassNameFromAnnotation(typeElement, true);
String baseSwiftName = nameTable.getSwiftClassNameFromAnnotation(typeElement);
String companionSwiftName = baseSwiftName != null ? baseSwiftName : typeName;
printf("\nNS_SWIFT_NAME(%sCompanion)", companionSwiftName);
printf("\n@interface %sCompanion : NSObject", typeName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,31 +634,60 @@ private boolean hasMethodWithSelector(TypeElement clazz, String selector) {
return Iterables.any(ElementUtil.getMethods(clazz), m -> getMethodSelector(m).equals(selector));
}

public @Nullable String getSwiftClassNameFromAnnotation(TypeElement clazz, boolean getParents) {
public @Nullable String getSwiftClassNameFromAnnotation(TypeElement clazz) {
String annotationName = swiftNameFromAnnotation(clazz);
if (annotationName != null) {
return annotationName;
}

NestingKind nesting = clazz.getNestingKind();
if (nesting == NestingKind.MEMBER) {
Element parent = clazz.getEnclosingElement();
if (parent instanceof TypeElement parentElement) {
// Protocols are named by concatenating the interface name and names of the parent type
// hierarchy. Any types declared within an interface are also named by concatenating the
// inner type name to the parent interface name. E.g. FooBar rather than Foo.Bar.
if (clazz.getKind().isInterface() || parentElement.getKind().isInterface()) {
return getFlatSwiftClassName(clazz);
}
String parentName = getSwiftClassNameFromAnnotation(parentElement);
if (parentName != null) {
return (parentName + "." + clazz.getSimpleName()).replace("$", "");
}
}
}

if (!packageHasSwiftNameAnnotation(clazz) && !elementHasSwiftNameAnnotation(clazz)) {
return null;
}

if (getParents) {
NestingKind nesting = clazz.getNestingKind();
if (nesting == NestingKind.MEMBER) {
Element parent = clazz.getEnclosingElement();
if (parent instanceof TypeElement parentElement && !parent.getKind().isInterface()) {
String parentName = getSwiftClassNameFromAnnotation(parentElement, false);
if (parentName != null) {
return (parentName + "." + clazz.getSimpleName()).replace("$", "");
}
}
String className = clazz.getSimpleName().toString();
return className.replace("$", "");
}

private @Nullable String getFlatSwiftClassName(TypeElement clazz) {
List<String> hierarchy = new ArrayList<>();
Element current = clazz;
boolean hasSwiftName = packageHasSwiftNameAnnotation(clazz);
while (current instanceof TypeElement typeElement) {
String annotationName = swiftNameFromAnnotation(typeElement);
if (annotationName != null && !annotationName.isEmpty()) {
hierarchy.add(annotationName);
hasSwiftName = true;
break;
}
hierarchy.add(typeElement.getSimpleName().toString().replace("$", ""));
if (elementHasSwiftNameAnnotation(typeElement)) {
hasSwiftName = true;
}
current = typeElement.getEnclosingElement();
}

String className = clazz.getSimpleName().toString();
return className.replace("$", "");
if (!hasSwiftName) {
return null;
}

return String.join("", hierarchy.reversed());
}

public @Nullable String getSwiftFunctionNameFromAnnotation(FunctionDeclaration function) {
Expand Down Expand Up @@ -686,7 +715,7 @@ private boolean hasMethodWithSelector(TypeElement clazz, String selector) {
return null;
}

String className = getSwiftClassNameFromAnnotation(owner, false);
String className = getSwiftClassNameFromAnnotation(owner);
if (className == null) {
// There isn't nice naming so fallback to the normal ObjC class name
className = getObjCType(owner.asType()).replace(" *", "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1849,9 +1849,128 @@ public void testSwiftNameInterfaceSubtype() throws IOException {
+ " public final class NestedBar {}"
+ "}";
String translation = translateSourceFile(sourceContent, "FooBar", "com/foo/bar/FooBar.h");
assertInTranslation(translation, "NS_SWIFT_NAME(MyEnumEnum)");
assertInTranslation(translation, "NS_SWIFT_NAME(MyEnum)");
assertInTranslation(translation, "NS_SWIFT_NAME(NestedBar)");
assertInTranslation(translation, "NS_SWIFT_NAME(FooBarMyEnumEnum)");
assertInTranslation(translation, "NS_SWIFT_NAME(FooBarMyEnum)");
assertInTranslation(translation, "NS_SWIFT_NAME(FooBarNestedBar)");
}

public void testSwiftNameDeepNestingInInterface() throws IOException {
addSourceFile(
"@SwiftName "
+ "package com.foo.bar;"
+ ""
+ "import com.google.j2objc.annotations.SwiftName;",
"com/foo/bar/package-info.java");

String sourceContent =
"""
package com.foo.bar;
public interface OuterInterface {
public class InnerClass {
public enum InnerEnum { A, B }
}
}
""";
String translation =
translateSourceFile(sourceContent, "OuterInterface", "com/foo/bar/OuterInterface.h");
assertInTranslation(translation, "NS_SWIFT_NAME(OuterInterfaceInnerClass)");
assertInTranslation(translation, "NS_SWIFT_NAME(OuterInterfaceInnerClass.InnerEnum)");
assertInTranslation(translation, "NS_SWIFT_NAME(OuterInterfaceInnerClass.InnerEnumEnum)");
}

public void testSwiftNameExplicitAnnotationOnNestedType() throws IOException {
addSourceFile(
"@SwiftName "
+ "package com.foo.bar;"
+ ""
+ "import com.google.j2objc.annotations.SwiftName;",
"com/foo/bar/package-info.java");

String sourceContent =
"""
package com.foo.bar;
import com.google.j2objc.annotations.SwiftName;
public interface OuterInterface {
@SwiftName("CustomName")
public class InnerClass {
public enum InnerEnum { A, B }
}
}
""";
String translation =
translateSourceFile(sourceContent, "OuterInterface", "com/foo/bar/OuterInterface.h");
assertInTranslation(translation, "NS_SWIFT_NAME(CustomName)");
assertInTranslation(translation, "NS_SWIFT_NAME(CustomName.InnerEnum)");
assertInTranslation(translation, "NS_SWIFT_NAME(CustomName.InnerEnumEnum)");
}

public void testSwiftNameExplicitAnnotationOnInterface() throws IOException {
String sourceContent =
"""
package com.foo.bar;
import com.google.j2objc.annotations.SwiftName;
@SwiftName("CustomInterface")
public interface OuterInterface {
public enum InnerEnum { A, B }
}
""";
String translation =
translateSourceFile(sourceContent, "OuterInterface", "com/foo/bar/OuterInterface.h");
assertInTranslation(translation, "NS_SWIFT_NAME(CustomInterfaceInnerEnum)");
assertInTranslation(translation, "NS_SWIFT_NAME(CustomInterfaceInnerEnumEnum)");
}

public void testSwiftNameInterfaceInsideClass() throws IOException {
addSourceFile(
"@SwiftName "
+ "package com.foo.bar;"
+ ""
+ "import com.google.j2objc.annotations.SwiftName;",
"com/foo/bar/package-info.java");

String sourceContent =
"""
package com.foo.bar;
public class Foo {
public interface Bar {
public enum Baz { A, B }
}
public class Biz {}
}
""";
String translation =
translateSourceFile(sourceContent, "Foo", "com/foo/bar/Foo.h");
assertInTranslation(translation, "NS_SWIFT_NAME(FooBar)");
assertInTranslation(translation, "NS_SWIFT_NAME(FooBarBaz)");
assertInTranslation(translation, "NS_SWIFT_NAME(FooBarBazEnum)");
assertInTranslation(translation, "NS_SWIFT_NAME(Foo.Biz)");
}

public void testSwiftNameInterfaceInsideNestedClass() throws IOException {
addSourceFile(
"@SwiftName "
+ "package com.foo.bar;"
+ ""
+ "import com.google.j2objc.annotations.SwiftName;",
"com/foo/bar/package-info.java");

String sourceContent =
"""
package com.foo.bar;
public class Outer {
public class Middle {
public interface Inner {
public enum Baz { A, B }
}
}
}
""";
String translation =
translateSourceFile(sourceContent, "Outer", "com/foo/bar/Outer.h");
assertInTranslation(translation, "NS_SWIFT_NAME(Outer.Middle)");
assertInTranslation(translation, "NS_SWIFT_NAME(OuterMiddleInner)");
assertInTranslation(translation, "NS_SWIFT_NAME(OuterMiddleInnerBaz)");
assertInTranslation(translation, "NS_SWIFT_NAME(OuterMiddleInnerBazEnum)");
}

public void testMethodSorting() throws IOException {
Expand Down