From b1aeb73561299b4b89707aadb6102c48dba7e445 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Oct 2024 16:30:11 -0700 Subject: [PATCH] Java2Swift: Separate "reference type" from "value type" mappings While in general we want to map Java classes to Swift value types, there are places where we need to work with the class type, such as generic parameters. Identify those places where it is okay to bridge to a value type (e.g., parameters, results, field types) and do so. Elsewhere, use the Swift type that directly wraps the Java class. For example, when dealing with superclasses or with generic arguments to Java generic classes. As part of this, provide a class mapping for java.lang.String (as JavaString) and Java arrays (as the non-generic JavaArray) so we have a way to refer to such types in positions that cannot use the value type. Test this out with a few APIs that need it. Fixes issue #128. --- Sources/Java2Swift/JavaToSwift.swift | 5 +- .../Java2SwiftLib/JavaClassTranslator.swift | 37 ++- .../JavaTranslator+Configuration.swift | 3 +- Sources/Java2SwiftLib/JavaTranslator.swift | 115 +++++--- Sources/Java2SwiftLib/OptionalKind.swift | 9 + Sources/JavaKit/Java2Swift.config | 2 + Sources/JavaKit/generated/JavaArray.swift | 96 +++++++ Sources/JavaKit/generated/JavaCharacter.swift | 62 ++-- Sources/JavaKit/generated/JavaString.swift | 264 ++++++++++++++++++ .../JavaKitCollection/generated/HashMap.swift | 86 ------ .../JavaKitCollection/generated/TreeMap.swift | 86 ++++++ .../JavaKitJar/generated/JarInputStream.swift | 12 +- .../generated/JarOutputStream.swift | 6 +- Sources/JavaTypes/JavaType+SwiftNames.swift | 9 +- Tests/Java2SwiftTests/Java2SwiftTests.swift | 89 ++++-- 15 files changed, 689 insertions(+), 192 deletions(-) create mode 100644 Sources/JavaKit/generated/JavaArray.swift create mode 100644 Sources/JavaKit/generated/JavaString.swift diff --git a/Sources/Java2Swift/JavaToSwift.swift b/Sources/Java2Swift/JavaToSwift.swift index 205c6a43..15e346ae 100644 --- a/Sources/Java2Swift/JavaToSwift.swift +++ b/Sources/Java2Swift/JavaToSwift.swift @@ -252,7 +252,7 @@ struct JavaToSwift: ParsableCommand { let swiftName = "\(currentSwiftName).\(swiftUnqualifiedName)" - translator.translatedClasses[javaClassName] = (swiftName, nil, true) + translator.translatedClasses[javaClassName] = (swiftName, nil) return nestedClass } @@ -279,7 +279,8 @@ struct JavaToSwift: ParsableCommand { """ - let swiftFileName = try! translator.getSwiftTypeName(javaClass).swiftName.replacing(".", with: "+") + ".swift" + let swiftFileName = try! translator.getSwiftTypeName(javaClass, preferValueTypes: false) + .swiftName.replacing(".", with: "+") + ".swift" try writeContents( swiftFileText, to: swiftFileName, diff --git a/Sources/Java2SwiftLib/JavaClassTranslator.swift b/Sources/Java2SwiftLib/JavaClassTranslator.swift index 232c9338..1df6d683 100644 --- a/Sources/Java2SwiftLib/JavaClassTranslator.swift +++ b/Sources/Java2SwiftLib/JavaClassTranslator.swift @@ -103,7 +103,11 @@ struct JavaClassTranslator { let fullName = javaClass.getName() self.javaClass = javaClass self.translator = translator - self.swiftTypeName = try translator.getSwiftTypeNameFromJavaClassName(fullName, escapeMemberNames: false) + self.swiftTypeName = try translator.getSwiftTypeNameFromJavaClassName( + fullName, + preferValueTypes: false, + escapeMemberNames: false + ) // Type parameters. self.javaTypeParameters = javaClass.getTypeParameters().compactMap { $0 } @@ -112,7 +116,7 @@ struct JavaClassTranslator { // Superclass. if !javaClass.isInterface(), let javaSuperclass = javaClass.getSuperclass() { do { - self.swiftSuperclass = try translator.getSwiftTypeName(javaSuperclass).swiftName + self.swiftSuperclass = try translator.getSwiftTypeName(javaSuperclass, preferValueTypes: false).swiftName } catch { translator.logUntranslated("Unable to translate '\(fullName)' superclass: \(error)") self.swiftSuperclass = nil @@ -128,7 +132,11 @@ struct JavaClassTranslator { } do { - let typeName = try translator.getSwiftTypeNameAsString(javaType, outerOptional: .nonoptional) + let typeName = try translator.getSwiftTypeNameAsString( + javaType, + preferValueTypes: false, + outerOptional: .nonoptional + ) return "\(typeName)" } catch { translator.logUntranslated("Unable to translate '\(fullName)' interface '\(javaType.getTypeName())': \(error)") @@ -314,7 +322,10 @@ extension JavaClassTranslator { /// Render any nested classes that will not be rendered separately. func renderNestedClasses() -> [DeclSyntax] { - return nestedClasses.compactMap { clazz in + return nestedClasses + .sorted { + $0.getName() < $1.getName() + }.compactMap { clazz in do { return try translator.translateClass(clazz) } catch { @@ -456,7 +467,11 @@ extension JavaClassTranslator { // Map the result type. let resultTypeStr: String - let resultType = try translator.getSwiftTypeNameAsString(javaMethod.getGenericReturnType()!, outerOptional: .implicitlyUnwrappedOptional) + let resultType = try translator.getSwiftTypeNameAsString( + javaMethod.getGenericReturnType()!, + preferValueTypes: true, + outerOptional: .implicitlyUnwrappedOptional + ) if resultType != "Void" { resultTypeStr = " -> \(resultType)" } else { @@ -477,7 +492,11 @@ extension JavaClassTranslator { /// Render a single Java field into the corresponding Swift property, or /// throw an error if that is not possible for any reason. package func renderField(_ javaField: Field) throws -> DeclSyntax { - let typeName = try translator.getSwiftTypeNameAsString(javaField.getGenericType()!, outerOptional: .implicitlyUnwrappedOptional) + let typeName = try translator.getSwiftTypeNameAsString( + javaField.getGenericType()!, + preferValueTypes: true, + outerOptional: .implicitlyUnwrappedOptional + ) let fieldAttribute: AttributeSyntax = javaField.isStatic ? "@JavaStaticField" : "@JavaField"; let swiftFieldName = javaField.getName().escapedSwiftName return """ @@ -544,7 +563,11 @@ extension JavaClassTranslator { return try parameters.compactMap { javaParameter in guard let javaParameter else { return nil } - let typeName = try translator.getSwiftTypeNameAsString(javaParameter.getParameterizedType()!, outerOptional: .optional) + let typeName = try translator.getSwiftTypeNameAsString( + javaParameter.getParameterizedType()!, + preferValueTypes: true, + outerOptional: .optional + ) let paramName = javaParameter.getName() return "_ \(raw: paramName): \(raw: typeName)" } diff --git a/Sources/Java2SwiftLib/JavaTranslator+Configuration.swift b/Sources/Java2SwiftLib/JavaTranslator+Configuration.swift index f13a8cce..b84d7a1e 100644 --- a/Sources/Java2SwiftLib/JavaTranslator+Configuration.swift +++ b/Sources/Java2SwiftLib/JavaTranslator+Configuration.swift @@ -27,8 +27,7 @@ extension JavaTranslator { for (javaClassName, swiftName) in config.classes { translatedClasses[javaClassName] = ( swiftType: swiftName, - swiftModule: swiftModule, - isOptional: true + swiftModule: swiftModule ) } } diff --git a/Sources/Java2SwiftLib/JavaTranslator.swift b/Sources/Java2SwiftLib/JavaTranslator.swift index 9f5fa292..5889a193 100644 --- a/Sources/Java2SwiftLib/JavaTranslator.swift +++ b/Sources/Java2SwiftLib/JavaTranslator.swift @@ -28,11 +28,20 @@ package class JavaTranslator { let environment: JNIEnvironment let format: BasicFormat - /// A mapping from the canonical name of Java classes to the corresponding - /// Swift type name, its Swift module, and whether we need to be working - /// with optionals. - package var translatedClasses: [String: (swiftType: String, swiftModule: String?, isOptional: Bool)] = - defaultTranslatedClasses + /// A mapping from the name of each known Java class to the corresponding + /// Swift type name and its Swift module. + package var translatedClasses: [String: (swiftType: String, swiftModule: String?)] = [:] + + /// A mapping from the name of each known Java class with the Swift value type + /// (and its module) to which it is mapped. + /// + /// The Java classes here can also be part of `translatedClasses`. The entry in + /// `translatedClasses` should map to a representation of the Java class (i.e., + /// an AnyJavaObject-conforming type) whereas the entry here should map to + /// a value type. + package let translatedToValueTypes: [String: (swiftType: String, swiftModule: String) ] = [ + "java.lang.String": ("String", "JavaKit"), + ] /// The set of Swift modules that need to be imported to make the generated /// code compile. Use `getImportDecls()` to format this into a list of @@ -80,13 +89,6 @@ extension JavaTranslator { "JavaKit", "JavaRuntime", ] - - /// The default set of translated classes that do not come from JavaKit - /// itself. This should only be used to refer to types that are built-in to - /// JavaKit and therefore aren't captured in any configuration file. - package static let defaultTranslatedClasses: [String: (swiftType: String, swiftModule: String?, isOptional: Bool)] = [ - "java.lang.String": ("String", "JavaKit", false), - ] } // MARK: Import translation @@ -104,13 +106,21 @@ extension JavaTranslator { // MARK: Type translation extension JavaTranslator { /// Turn a Java type into a string. - func getSwiftTypeNameAsString(_ javaType: Type, outerOptional: OptionalKind) throws -> String { + func getSwiftTypeNameAsString( + _ javaType: Type, + preferValueTypes: Bool, + outerOptional: OptionalKind + ) throws -> String { // Replace type variables with their bounds. if let typeVariable = javaType.as(TypeVariable.self), typeVariable.getBounds().count == 1, let bound = typeVariable.getBounds()[0] { - return try getSwiftTypeNameAsString(bound, outerOptional: outerOptional) + return try getSwiftTypeNameAsString( + bound, + preferValueTypes: preferValueTypes, + outerOptional: outerOptional + ) } // Replace wildcards with their upper bound. @@ -119,13 +129,30 @@ extension JavaTranslator { let bound = wildcardType.getUpperBounds()[0] { // Replace a wildcard type with its first bound. - return try getSwiftTypeNameAsString(bound, outerOptional: outerOptional) + return try getSwiftTypeNameAsString( + bound, + preferValueTypes: preferValueTypes, + outerOptional: outerOptional + ) } // Handle array types by recursing into the component type. if let arrayType = javaType.as(GenericArrayType.self) { - let elementType = try getSwiftTypeNameAsString(arrayType.getGenericComponentType()!, outerOptional: .optional) - return "[\(elementType)]" + if preferValueTypes { + let elementType = try getSwiftTypeNameAsString( + arrayType.getGenericComponentType()!, + preferValueTypes: preferValueTypes, + outerOptional: .optional + ) + return "[\(elementType)]" + } + + let (swiftName, _) = try getSwiftTypeName( + JavaClass().as(JavaClass.self)!, + preferValueTypes: false + ) + + return outerOptional.adjustTypeName(swiftName) } // Handle parameterized types by recursing on the raw type and the type @@ -133,7 +160,11 @@ extension JavaTranslator { if let parameterizedType = javaType.as(ParameterizedType.self), let rawJavaType = parameterizedType.getRawType() { - var rawSwiftType = try getSwiftTypeNameAsString(rawJavaType, outerOptional: outerOptional) + var rawSwiftType = try getSwiftTypeNameAsString( + rawJavaType, + preferValueTypes: false, + outerOptional: outerOptional + ) let optionalSuffix: String if let lastChar = rawSwiftType.last, lastChar == "?" || lastChar == "!" { @@ -145,7 +176,7 @@ extension JavaTranslator { let typeArguments = try parameterizedType.getActualTypeArguments().compactMap { typeArg in try typeArg.map { typeArg in - try getSwiftTypeNameAsString(typeArg, outerOptional: .nonoptional) + try getSwiftTypeNameAsString(typeArg, preferValueTypes: false, outerOptional: .nonoptional) } } @@ -157,38 +188,50 @@ extension JavaTranslator { throw TranslationError.unhandledJavaType(javaType) } - let (swiftName, isOptional) = try getSwiftTypeName(javaClass) + let (swiftName, isOptional) = try getSwiftTypeName(javaClass, preferValueTypes: preferValueTypes) var resultString = swiftName if isOptional { - switch outerOptional { - case .implicitlyUnwrappedOptional: - resultString += "!" - case .optional: - resultString += "?" - case .nonoptional: - break - } + resultString = outerOptional.adjustTypeName(resultString) } return resultString } /// Translate a Java class into its corresponding Swift type name. - package func getSwiftTypeName(_ javaClass: JavaClass) throws -> (swiftName: String, isOptional: Bool) { + package func getSwiftTypeName( + _ javaClass: JavaClass, + preferValueTypes: Bool + ) throws -> (swiftName: String, isOptional: Bool) { let javaType = try JavaType(javaTypeName: javaClass.getName()) - let isSwiftOptional = javaType.isSwiftOptional - return ( - try javaType.swiftTypeName { javaClassName in - try self.getSwiftTypeNameFromJavaClassName(javaClassName) - }, - isSwiftOptional - ) + let isSwiftOptional = javaType.isSwiftOptional(stringIsValueType: preferValueTypes) + + let swiftTypeName: String + if !preferValueTypes, case .array(_) = javaType { + swiftTypeName = try self.getSwiftTypeNameFromJavaClassName("java.lang.reflect.Array", preferValueTypes: false) + } else { + swiftTypeName = try javaType.swiftTypeName { javaClassName in + try self.getSwiftTypeNameFromJavaClassName(javaClassName, preferValueTypes: preferValueTypes) + } + } + + return (swiftTypeName, isSwiftOptional) } /// Map a Java class name to its corresponding Swift type. func getSwiftTypeNameFromJavaClassName( _ name: String, + preferValueTypes: Bool, escapeMemberNames: Bool = true ) throws -> String { + // If we want a value type, look for one. + if preferValueTypes, let translatedValueType = translatedToValueTypes[name] { + // Note that we need to import this Swift module. + if translatedValueType.swiftModule != swiftModuleName { + importedSwiftModules.insert(translatedValueType.swiftModule) + } + + return translatedValueType.swiftType + } + if let translated = translatedClasses[name] { // Note that we need to import this Swift module. if let swiftModule = translated.swiftModule, swiftModule != swiftModuleName { diff --git a/Sources/Java2SwiftLib/OptionalKind.swift b/Sources/Java2SwiftLib/OptionalKind.swift index 57951fa4..c05bff18 100644 --- a/Sources/Java2SwiftLib/OptionalKind.swift +++ b/Sources/Java2SwiftLib/OptionalKind.swift @@ -22,4 +22,13 @@ enum OptionalKind { /// The value uses an implicitly-unwrapped optional. case implicitlyUnwrappedOptional + + /// Adjust the given type name string based on the optionality of this type. + func adjustTypeName(_ string: String) -> String { + switch self { + case .implicitlyUnwrappedOptional: return string + "!" + case .optional: return string + "?" + case .nonoptional: return string + } + } } diff --git a/Sources/JavaKit/Java2Swift.config b/Sources/JavaKit/Java2Swift.config index bace061f..ed284d31 100644 --- a/Sources/JavaKit/Java2Swift.config +++ b/Sources/JavaKit/Java2Swift.config @@ -1,5 +1,6 @@ { "classes" : { + "java.lang.reflect.Array" : "JavaArray", "java.lang.Boolean" : "JavaBoolean", "java.lang.Byte" : "JavaByte", "java.lang.Character" : "JavaCharacter", @@ -14,6 +15,7 @@ "java.lang.Object" : "JavaObject", "java.lang.RuntimeException" : "RuntimeException", "java.lang.Short" : "JavaShort", + "java.lang.String" : "JavaString", "java.lang.Throwable" : "Throwable", "java.lang.Void" : "JavaVoid" } diff --git a/Sources/JavaKit/generated/JavaArray.swift b/Sources/JavaKit/generated/JavaArray.swift new file mode 100644 index 00000000..805b3929 --- /dev/null +++ b/Sources/JavaKit/generated/JavaArray.swift @@ -0,0 +1,96 @@ +// Auto-generated by Java-to-Swift wrapper generator. +import JavaRuntime + +@JavaClass("java.lang.reflect.Array", extends: JavaObject.self) +public struct JavaArray { + @JavaMethod + public func equals(_ arg0: JavaObject?) -> Bool + + @JavaMethod + public func toString() -> String + + @JavaMethod + public func hashCode() -> Int32 + + @JavaMethod + public func getClass() -> JavaClass! + + @JavaMethod + public func notify() + + @JavaMethod + public func notifyAll() + + @JavaMethod + public func wait(_ arg0: Int64) throws + + @JavaMethod + public func wait(_ arg0: Int64, _ arg1: Int32) throws + + @JavaMethod + public func wait() throws +} +extension JavaClass { + @JavaStaticMethod + public func get(_ arg0: JavaObject?, _ arg1: Int32) throws -> JavaObject! + + @JavaStaticMethod + public func getLength(_ arg0: JavaObject?) throws -> Int32 + + @JavaStaticMethod + public func getBoolean(_ arg0: JavaObject?, _ arg1: Int32) throws -> Bool + + @JavaStaticMethod + public func getByte(_ arg0: JavaObject?, _ arg1: Int32) throws -> Int8 + + @JavaStaticMethod + public func getShort(_ arg0: JavaObject?, _ arg1: Int32) throws -> Int16 + + @JavaStaticMethod + public func getChar(_ arg0: JavaObject?, _ arg1: Int32) throws -> UInt16 + + @JavaStaticMethod + public func getInt(_ arg0: JavaObject?, _ arg1: Int32) throws -> Int32 + + @JavaStaticMethod + public func getLong(_ arg0: JavaObject?, _ arg1: Int32) throws -> Int64 + + @JavaStaticMethod + public func getFloat(_ arg0: JavaObject?, _ arg1: Int32) throws -> Float + + @JavaStaticMethod + public func getDouble(_ arg0: JavaObject?, _ arg1: Int32) throws -> Double + + @JavaStaticMethod + public func newInstance(_ arg0: JavaClass?, _ arg1: Int32) throws -> JavaObject! + + @JavaStaticMethod + public func newInstance(_ arg0: JavaClass?, _ arg1: [Int32]) throws -> JavaObject! + + @JavaStaticMethod + public func set(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: JavaObject?) throws + + @JavaStaticMethod + public func setBoolean(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: Bool) throws + + @JavaStaticMethod + public func setByte(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: Int8) throws + + @JavaStaticMethod + public func setChar(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: UInt16) throws + + @JavaStaticMethod + public func setShort(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: Int16) throws + + @JavaStaticMethod + public func setInt(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: Int32) throws + + @JavaStaticMethod + public func setLong(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: Int64) throws + + @JavaStaticMethod + public func setFloat(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: Float) throws + + @JavaStaticMethod + public func setDouble(_ arg0: JavaObject?, _ arg1: Int32, _ arg2: Double) throws +} diff --git a/Sources/JavaKit/generated/JavaCharacter.swift b/Sources/JavaKit/generated/JavaCharacter.swift index 739cc260..1ebb7625 100644 --- a/Sources/JavaKit/generated/JavaCharacter.swift +++ b/Sources/JavaKit/generated/JavaCharacter.swift @@ -42,6 +42,37 @@ public struct JavaCharacter { @JavaMethod public func wait() throws } +extension JavaCharacter { + @JavaClass("java.lang.Character$Subset", extends: JavaObject.self) + public struct Subset { + @JavaMethod + public func equals(_ arg0: JavaObject?) -> Bool + + @JavaMethod + public func toString() -> String + + @JavaMethod + public func hashCode() -> Int32 + + @JavaMethod + public func getClass() -> JavaClass! + + @JavaMethod + public func notify() + + @JavaMethod + public func notifyAll() + + @JavaMethod + public func wait(_ arg0: Int64) throws + + @JavaMethod + public func wait(_ arg0: Int64, _ arg1: Int32) throws + + @JavaMethod + public func wait() throws + } +} extension JavaCharacter { @JavaClass("java.lang.Character$UnicodeBlock", extends: JavaCharacter.Subset.self) public struct UnicodeBlock { @@ -3146,37 +3177,6 @@ extension JavaClass { @JavaStaticMethod public func of(_ arg0: Int32) -> JavaCharacter.UnicodeScript! } -extension JavaCharacter { - @JavaClass("java.lang.Character$Subset", extends: JavaObject.self) - public struct Subset { - @JavaMethod - public func equals(_ arg0: JavaObject?) -> Bool - - @JavaMethod - public func toString() -> String - - @JavaMethod - public func hashCode() -> Int32 - - @JavaMethod - public func getClass() -> JavaClass! - - @JavaMethod - public func notify() - - @JavaMethod - public func notifyAll() - - @JavaMethod - public func wait(_ arg0: Int64) throws - - @JavaMethod - public func wait(_ arg0: Int64, _ arg1: Int32) throws - - @JavaMethod - public func wait() throws - } -} extension JavaClass { @JavaStaticField(isFinal: true) public var MIN_RADIX: Int32 diff --git a/Sources/JavaKit/generated/JavaString.swift b/Sources/JavaKit/generated/JavaString.swift new file mode 100644 index 00000000..c062dc0d --- /dev/null +++ b/Sources/JavaKit/generated/JavaString.swift @@ -0,0 +1,264 @@ +// Auto-generated by Java-to-Swift wrapper generator. +import JavaRuntime + +@JavaClass("java.lang.String", extends: JavaObject.self) +public struct JavaString { + @JavaMethod + public init(_ arg0: [Int8], _ arg1: String, environment: JNIEnvironment? = nil) throws + + @JavaMethod + public init(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32, environment: JNIEnvironment? = nil) + + @JavaMethod + public init(_ arg0: [Int8], environment: JNIEnvironment? = nil) + + @JavaMethod + public init(_ arg0: [UInt16], _ arg1: Int32, _ arg2: Int32, environment: JNIEnvironment? = nil) + + @JavaMethod + public init(_ arg0: [UInt16], environment: JNIEnvironment? = nil) + + @JavaMethod + public init(_ arg0: String, environment: JNIEnvironment? = nil) + + @JavaMethod + public init(environment: JNIEnvironment? = nil) + + @JavaMethod + public init(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32, _ arg3: String, environment: JNIEnvironment? = nil) throws + + @JavaMethod + public init(_ arg0: [Int8], _ arg1: Int32, environment: JNIEnvironment? = nil) + + @JavaMethod + public init(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32, _ arg3: Int32, environment: JNIEnvironment? = nil) + + @JavaMethod + public init(_ arg0: [Int32], _ arg1: Int32, _ arg2: Int32, environment: JNIEnvironment? = nil) + + @JavaMethod + public func equals(_ arg0: JavaObject?) -> Bool + + @JavaMethod + public func length() -> Int32 + + @JavaMethod + public func toString() -> String + + @JavaMethod + public func hashCode() -> Int32 + + @JavaMethod + public func getChars(_ arg0: Int32, _ arg1: Int32, _ arg2: [UInt16], _ arg3: Int32) + + @JavaMethod + public func compareTo(_ arg0: JavaObject?) -> Int32 + + @JavaMethod + public func compareTo(_ arg0: String) -> Int32 + + @JavaMethod + public func indexOf(_ arg0: String, _ arg1: Int32, _ arg2: Int32) -> Int32 + + @JavaMethod + public func indexOf(_ arg0: String) -> Int32 + + @JavaMethod + public func indexOf(_ arg0: Int32) -> Int32 + + @JavaMethod + public func indexOf(_ arg0: Int32, _ arg1: Int32) -> Int32 + + @JavaMethod + public func indexOf(_ arg0: Int32, _ arg1: Int32, _ arg2: Int32) -> Int32 + + @JavaMethod + public func indexOf(_ arg0: String, _ arg1: Int32) -> Int32 + + @JavaMethod + public func charAt(_ arg0: Int32) -> UInt16 + + @JavaMethod + public func codePointAt(_ arg0: Int32) -> Int32 + + @JavaMethod + public func codePointBefore(_ arg0: Int32) -> Int32 + + @JavaMethod + public func codePointCount(_ arg0: Int32, _ arg1: Int32) -> Int32 + + @JavaMethod + public func offsetByCodePoints(_ arg0: Int32, _ arg1: Int32) -> Int32 + + @JavaMethod + public func getBytes() -> [Int8] + + @JavaMethod + public func getBytes(_ arg0: String) throws -> [Int8] + + @JavaMethod + public func getBytes(_ arg0: Int32, _ arg1: Int32, _ arg2: [Int8], _ arg3: Int32) + + @JavaMethod + public func regionMatches(_ arg0: Bool, _ arg1: Int32, _ arg2: String, _ arg3: Int32, _ arg4: Int32) -> Bool + + @JavaMethod + public func regionMatches(_ arg0: Int32, _ arg1: String, _ arg2: Int32, _ arg3: Int32) -> Bool + + @JavaMethod + public func startsWith(_ arg0: String) -> Bool + + @JavaMethod + public func startsWith(_ arg0: String, _ arg1: Int32) -> Bool + + @JavaMethod + public func lastIndexOf(_ arg0: String) -> Int32 + + @JavaMethod + public func lastIndexOf(_ arg0: Int32, _ arg1: Int32) -> Int32 + + @JavaMethod + public func lastIndexOf(_ arg0: String, _ arg1: Int32) -> Int32 + + @JavaMethod + public func lastIndexOf(_ arg0: Int32) -> Int32 + + @JavaMethod + public func substring(_ arg0: Int32) -> String + + @JavaMethod + public func substring(_ arg0: Int32, _ arg1: Int32) -> String + + @JavaMethod + public func isEmpty() -> Bool + + @JavaMethod + public func replace(_ arg0: UInt16, _ arg1: UInt16) -> String + + @JavaMethod + public func matches(_ arg0: String) -> Bool + + @JavaMethod + public func replaceFirst(_ arg0: String, _ arg1: String) -> String + + @JavaMethod + public func replaceAll(_ arg0: String, _ arg1: String) -> String + + @JavaMethod + public func split(_ arg0: String) -> [String] + + @JavaMethod + public func split(_ arg0: String, _ arg1: Int32) -> [String] + + @JavaMethod + public func splitWithDelimiters(_ arg0: String, _ arg1: Int32) -> [String] + + @JavaMethod + public func toLowerCase() -> String + + @JavaMethod + public func toUpperCase() -> String + + @JavaMethod + public func trim() -> String + + @JavaMethod + public func strip() -> String + + @JavaMethod + public func stripLeading() -> String + + @JavaMethod + public func stripTrailing() -> String + + @JavaMethod + public func `repeat`(_ arg0: Int32) -> String + + @JavaMethod + public func isBlank() -> Bool + + @JavaMethod + public func toCharArray() -> [UInt16] + + @JavaMethod + public func equalsIgnoreCase(_ arg0: String) -> Bool + + @JavaMethod + public func compareToIgnoreCase(_ arg0: String) -> Int32 + + @JavaMethod + public func endsWith(_ arg0: String) -> Bool + + @JavaMethod + public func concat(_ arg0: String) -> String + + @JavaMethod + public func indent(_ arg0: Int32) -> String + + @JavaMethod + public func stripIndent() -> String + + @JavaMethod + public func translateEscapes() -> String + + @JavaMethod + public func formatted(_ arg0: [JavaObject?]) -> String + + @JavaMethod + public func intern() -> String + + @JavaMethod + public func getClass() -> JavaClass! + + @JavaMethod + public func notify() + + @JavaMethod + public func notifyAll() + + @JavaMethod + public func wait(_ arg0: Int64) throws + + @JavaMethod + public func wait(_ arg0: Int64, _ arg1: Int32) throws + + @JavaMethod + public func wait() throws +} +extension JavaClass { + @JavaStaticMethod + public func valueOf(_ arg0: Int64) -> String + + @JavaStaticMethod + public func valueOf(_ arg0: [UInt16]) -> String + + @JavaStaticMethod + public func valueOf(_ arg0: JavaObject?) -> String + + @JavaStaticMethod + public func valueOf(_ arg0: [UInt16], _ arg1: Int32, _ arg2: Int32) -> String + + @JavaStaticMethod + public func valueOf(_ arg0: Float) -> String + + @JavaStaticMethod + public func valueOf(_ arg0: Double) -> String + + @JavaStaticMethod + public func valueOf(_ arg0: UInt16) -> String + + @JavaStaticMethod + public func valueOf(_ arg0: Bool) -> String + + @JavaStaticMethod + public func valueOf(_ arg0: Int32) -> String + + @JavaStaticMethod + public func format(_ arg0: String, _ arg1: [JavaObject?]) -> String + + @JavaStaticMethod + public func copyValueOf(_ arg0: [UInt16]) -> String + + @JavaStaticMethod + public func copyValueOf(_ arg0: [UInt16], _ arg1: Int32, _ arg2: Int32) -> String +} diff --git a/Sources/JavaKitCollection/generated/HashMap.swift b/Sources/JavaKitCollection/generated/HashMap.swift index 90e50295..287593f5 100644 --- a/Sources/JavaKitCollection/generated/HashMap.swift +++ b/Sources/JavaKitCollection/generated/HashMap.swift @@ -88,92 +88,6 @@ public struct HashMap { @JavaMethod public func wait() throws } -extension HashMap { - @JavaClass("java.util.AbstractMap$SimpleImmutableEntry", extends: JavaObject.self) - public struct SimpleImmutableEntry { - @JavaMethod - public init(_ arg0: JavaObject?, _ arg1: JavaObject?, environment: JNIEnvironment? = nil) - - @JavaMethod - public func equals(_ arg0: JavaObject?) -> Bool - - @JavaMethod - public func toString() -> String - - @JavaMethod - public func hashCode() -> Int32 - - @JavaMethod - public func getValue() -> JavaObject! - - @JavaMethod - public func getKey() -> JavaObject! - - @JavaMethod - public func setValue(_ arg0: JavaObject?) -> JavaObject! - - @JavaMethod - public func getClass() -> JavaClass! - - @JavaMethod - public func notify() - - @JavaMethod - public func notifyAll() - - @JavaMethod - public func wait(_ arg0: Int64) throws - - @JavaMethod - public func wait(_ arg0: Int64, _ arg1: Int32) throws - - @JavaMethod - public func wait() throws - } -} -extension HashMap { - @JavaClass("java.util.AbstractMap$SimpleEntry", extends: JavaObject.self) - public struct SimpleEntry { - @JavaMethod - public init(_ arg0: JavaObject?, _ arg1: JavaObject?, environment: JNIEnvironment? = nil) - - @JavaMethod - public func equals(_ arg0: JavaObject?) -> Bool - - @JavaMethod - public func toString() -> String - - @JavaMethod - public func hashCode() -> Int32 - - @JavaMethod - public func getValue() -> JavaObject! - - @JavaMethod - public func getKey() -> JavaObject! - - @JavaMethod - public func setValue(_ arg0: JavaObject?) -> JavaObject! - - @JavaMethod - public func getClass() -> JavaClass! - - @JavaMethod - public func notify() - - @JavaMethod - public func notifyAll() - - @JavaMethod - public func wait(_ arg0: Int64) throws - - @JavaMethod - public func wait(_ arg0: Int64, _ arg1: Int32) throws - - @JavaMethod - public func wait() throws - } -} extension JavaClass { @JavaStaticMethod public func newHashMap(_ arg0: Int32) -> HashMap! where ObjectType == HashMap diff --git a/Sources/JavaKitCollection/generated/TreeMap.swift b/Sources/JavaKitCollection/generated/TreeMap.swift index 3c45b998..706d5339 100644 --- a/Sources/JavaKitCollection/generated/TreeMap.swift +++ b/Sources/JavaKitCollection/generated/TreeMap.swift @@ -106,3 +106,89 @@ public struct TreeMap { @JavaMethod public func getOrDefault(_ arg0: JavaObject?, _ arg1: JavaObject?) -> JavaObject! } +extension TreeMap { + @JavaClass("java.util.AbstractMap$SimpleEntry", extends: JavaObject.self) + public struct SimpleEntry { + @JavaMethod + public init(_ arg0: JavaObject?, _ arg1: JavaObject?, environment: JNIEnvironment? = nil) + + @JavaMethod + public func equals(_ arg0: JavaObject?) -> Bool + + @JavaMethod + public func toString() -> String + + @JavaMethod + public func hashCode() -> Int32 + + @JavaMethod + public func getValue() -> JavaObject! + + @JavaMethod + public func getKey() -> JavaObject! + + @JavaMethod + public func setValue(_ arg0: JavaObject?) -> JavaObject! + + @JavaMethod + public func getClass() -> JavaClass! + + @JavaMethod + public func notify() + + @JavaMethod + public func notifyAll() + + @JavaMethod + public func wait(_ arg0: Int64) throws + + @JavaMethod + public func wait(_ arg0: Int64, _ arg1: Int32) throws + + @JavaMethod + public func wait() throws + } +} +extension TreeMap { + @JavaClass("java.util.AbstractMap$SimpleImmutableEntry", extends: JavaObject.self) + public struct SimpleImmutableEntry { + @JavaMethod + public init(_ arg0: JavaObject?, _ arg1: JavaObject?, environment: JNIEnvironment? = nil) + + @JavaMethod + public func equals(_ arg0: JavaObject?) -> Bool + + @JavaMethod + public func toString() -> String + + @JavaMethod + public func hashCode() -> Int32 + + @JavaMethod + public func getValue() -> JavaObject! + + @JavaMethod + public func getKey() -> JavaObject! + + @JavaMethod + public func setValue(_ arg0: JavaObject?) -> JavaObject! + + @JavaMethod + public func getClass() -> JavaClass! + + @JavaMethod + public func notify() + + @JavaMethod + public func notifyAll() + + @JavaMethod + public func wait(_ arg0: Int64) throws + + @JavaMethod + public func wait(_ arg0: Int64, _ arg1: Int32) throws + + @JavaMethod + public func wait() throws + } +} diff --git a/Sources/JavaKitJar/generated/JarInputStream.swift b/Sources/JavaKitJar/generated/JarInputStream.swift index 580202e4..de063bb9 100644 --- a/Sources/JavaKitJar/generated/JarInputStream.swift +++ b/Sources/JavaKitJar/generated/JarInputStream.swift @@ -4,9 +4,6 @@ import JavaRuntime @JavaClass("java.util.jar.JarInputStream") public struct JarInputStream { - @JavaMethod - public func getNextJarEntry() throws -> JarEntry! - @JavaMethod public func read(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) throws -> Int32 @@ -14,7 +11,7 @@ public struct JarInputStream { public func getManifest() -> Manifest! @JavaMethod - public func closeEntry() throws + public func getNextJarEntry() throws -> JarEntry! @JavaMethod public func read() throws -> Int32 @@ -26,10 +23,10 @@ public struct JarInputStream { public func readAllBytes() throws -> [Int8] @JavaMethod - public func readNBytes(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) throws -> Int32 + public func readNBytes(_ arg0: Int32) throws -> [Int8] @JavaMethod - public func readNBytes(_ arg0: Int32) throws -> [Int8] + public func readNBytes(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) throws -> Int32 @JavaMethod public func skip(_ arg0: Int64) throws -> Int64 @@ -40,6 +37,9 @@ public struct JarInputStream { @JavaMethod public func skipNBytes(_ arg0: Int64) throws + @JavaMethod + public func closeEntry() throws + @JavaMethod public func reset() throws diff --git a/Sources/JavaKitJar/generated/JarOutputStream.swift b/Sources/JavaKitJar/generated/JarOutputStream.swift index 57a13841..542a784f 100644 --- a/Sources/JavaKitJar/generated/JarOutputStream.swift +++ b/Sources/JavaKitJar/generated/JarOutputStream.swift @@ -4,9 +4,6 @@ import JavaRuntime @JavaClass("java.util.jar.JarOutputStream") public struct JarOutputStream { - @JavaMethod - public func closeEntry() throws - @JavaMethod public func write(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) throws @@ -25,6 +22,9 @@ public struct JarOutputStream { @JavaMethod public func setLevel(_ arg0: Int32) + @JavaMethod + public func closeEntry() throws + @JavaMethod public func flush() throws diff --git a/Sources/JavaTypes/JavaType+SwiftNames.swift b/Sources/JavaTypes/JavaType+SwiftNames.swift index 8577640e..e1604eaa 100644 --- a/Sources/JavaTypes/JavaType+SwiftNames.swift +++ b/Sources/JavaTypes/JavaType+SwiftNames.swift @@ -19,12 +19,15 @@ public typealias JavaToSwiftClassNameResolver = (String) throws -> String extension JavaType { /// Whether this Java type needs to be represented by a Swift optional. - public var isSwiftOptional: Bool { + public func isSwiftOptional(stringIsValueType: Bool) -> Bool { switch self { case .boolean, .byte, .char, .short, .int, .long, .float, .double, .void, - .array, .class(package: "java.lang", name: "String"): + .array: return false + case .class(package: "java.lang", name: "String"): + return !stringIsValueType + case .class: return true } @@ -56,7 +59,7 @@ extension JavaType { case .void: return "Void" case .array(let elementType): let elementTypeName = try elementType.swiftTypeName(resolver: resolver) - let elementIsOptional = elementType.isSwiftOptional + let elementIsOptional = elementType.isSwiftOptional(stringIsValueType: true) return "[\(elementTypeName)\(elementIsOptional ? "?" : "")]" case .class: return try resolver(description) diff --git a/Tests/Java2SwiftTests/Java2SwiftTests.swift b/Tests/Java2SwiftTests/Java2SwiftTests.swift index 85240305..80c86fc0 100644 --- a/Tests/Java2SwiftTests/Java2SwiftTests.swift +++ b/Tests/Java2SwiftTests/Java2SwiftTests.swift @@ -63,8 +63,7 @@ class Java2SwiftTests: XCTestCase { JavaClass.self, swiftTypeName: "MyJavaClass", translatedClasses: [ - "java.lang.Object": ("JavaObject", nil, true), - "java.lang.String": ("JavaString", nil, true), + "java.lang.Object": ("JavaObject", nil), ], expectedChunks: [ "import JavaKit", @@ -74,7 +73,7 @@ class Java2SwiftTests: XCTestCase { """, """ @JavaStaticMethod - public func forName(_ arg0: JavaString) throws -> MyJavaClass! where ObjectType == MyJavaClass + public func forName(_ arg0: String) throws -> MyJavaClass! where ObjectType == MyJavaClass """, ] ) @@ -115,13 +114,19 @@ class Java2SwiftTests: XCTestCase { MyArrayList.self, swiftTypeName: "JavaArrayList", translatedClasses: [ - "java.lang.Object": ("JavaObject", nil, true), - "java.util.List": ("JavaList", nil, true), + "java.lang.Object": ("JavaObject", nil), + "java.lang.reflect.Array": ("JavaArray", nil), + "java.util.List": ("JavaList", nil), + "java.util.function.IntFunction": ("MyJavaIntFunction", nil), ], expectedChunks: [ """ @JavaMethod public func subList(_ arg0: Int32, _ arg1: Int32) -> JavaList! + """, + """ + @JavaMethod + public func toArray(_ arg0: MyJavaIntFunction?) -> [JavaObject?] """ ] ) @@ -132,8 +137,8 @@ class Java2SwiftTests: XCTestCase { MyLinkedList.self, swiftTypeName: "JavaLinkedList", translatedClasses: [ - "java.lang.Object": ("JavaObject", nil, true), - "java.util.List": ("JavaList", nil, true), + "java.lang.Object": ("JavaObject", nil), + "java.util.List": ("JavaList", nil), ], expectedChunks: [ """ @@ -149,9 +154,9 @@ class Java2SwiftTests: XCTestCase { ProcessBuilder.self, swiftTypeName: "ProcessBuilder", translatedClasses: [ - "java.lang.ProcessBuilder": ("ProcessBuilder", nil, true), - "java.lang.ProcessBuilder$Redirect": ("ProcessBuilder.Redirect", nil, true), - "java.lang.ProcessBuilder$Redirect$Type": ("ProcessBuilder.Redirect.Type", nil, true), + "java.lang.ProcessBuilder": ("ProcessBuilder", nil), + "java.lang.ProcessBuilder$Redirect": ("ProcessBuilder.Redirect", nil), + "java.lang.ProcessBuilder$Redirect$Type": ("ProcessBuilder.Redirect.Type", nil), ], nestedClasses: [ "java.lang.ProcessBuilder": [JavaClass().as(JavaClass.self)!], @@ -189,9 +194,9 @@ class Java2SwiftTests: XCTestCase { ProcessBuilder.self, swiftTypeName: "ProcessBuilder", translatedClasses: [ - "java.lang.ProcessBuilder": ("ProcessBuilder", nil, true), - "java.lang.ProcessBuilder$Redirect": ("ProcessBuilder.PBRedirect", nil, true), - "java.lang.ProcessBuilder$Redirect$Type": ("ProcessBuilder.PBRedirect.JavaType", nil, true), + "java.lang.ProcessBuilder": ("ProcessBuilder", nil), + "java.lang.ProcessBuilder$Redirect": ("ProcessBuilder.PBRedirect", nil), + "java.lang.ProcessBuilder$Redirect$Type": ("ProcessBuilder.PBRedirect.JavaType", nil), ], nestedClasses: [ "java.lang.ProcessBuilder": [JavaClass().as(JavaClass.self)!], @@ -223,6 +228,44 @@ class Java2SwiftTests: XCTestCase { ] ) } + + func testJavaString() throws { + try assertTranslatedClass( + MyJavaString.self, + swiftTypeName: "JavaString", + expectedChunks: [ + """ + @JavaClass("java.lang.String") + public struct JavaString { + """ + ] + ) + } + + func testJavaObjects() throws { + try assertTranslatedClass( + MyObjects.self, + swiftTypeName: "MyJavaObjects", + translatedClasses: [ + "java.lang.Object" : ("JavaObject", "JavaKit"), + "java.util.function.Supplier" : ("MySupplier", "JavaKitFunction"), + "java.lang.String" : ("JavaString", "JavaKit"), + ], + expectedChunks: [ + """ + import JavaKitFunction + """, + """ + @JavaClass("java.util.Objects", extends: JavaObject.self) + public struct MyJavaObjects { + """, + """ + @JavaStaticMethod + public func requireNonNull(_ arg0: JavaObject?, _ arg1: MySupplier?) -> JavaObject! + """, + ] + ) + } } @JavaClass("java.util.ArrayList") @@ -233,14 +276,28 @@ public struct MyArrayList { public struct MyLinkedList { } +@JavaClass("java.lang.String") +public struct MyJavaString { +} + +@JavaClass("java.util.Objects") +public struct MyObjects { } + +@JavaInterface("java.util.function.Supplier") +public struct MySupplier { } + +@JavaInterface("java.util.function.IntFunction") +public struct MyJavaIntFunction { +} + /// Translate a Java class and assert that the translated output contains /// each of the expected "chunks" of text. func assertTranslatedClass( _ javaType: JavaClassType.Type, swiftTypeName: String, translatedClasses: [ - String: (swiftType: String, swiftModule: String?, isOptional: Bool) - ] = JavaTranslator.defaultTranslatedClasses, + String: (swiftType: String, swiftModule: String?) + ] = [:], nestedClasses: [String: [JavaClass]] = [:], expectedChunks: [String], file: StaticString = #filePath, @@ -253,7 +310,7 @@ func assertTranslatedClass( ) translator.translatedClasses = translatedClasses - translator.translatedClasses[javaType.fullJavaClassName] = (swiftTypeName, nil, true) + translator.translatedClasses[javaType.fullJavaClassName] = (swiftTypeName, nil) translator.nestedClasses = nestedClasses translator.startNewFile() let translatedDecls = try translator.translateClass(