Skip to content

Adds support for optional mapping #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,27 @@ Java2Swift: $(BUILD_DIR)/debug/Java2Swift

generate-JavaKit: Java2Swift
mkdir -p Sources/JavaKit/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKit -o Sources/JavaKit/generated Sources/JavaKit/Java2Swift.config
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKit -o Sources/JavaKit/generated Sources/JavaKit/swift-java.config

generate-JavaKitCollection: Java2Swift
mkdir -p Sources/JavaKitCollection/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitCollection --depends-on JavaKit=Sources/JavaKit/Java2Swift.config -o Sources/JavaKitCollection/generated Sources/JavaKitCollection/Java2Swift.config
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitCollection --depends-on JavaKit=Sources/JavaKit/swift-java.config -o Sources/JavaKitCollection/generated Sources/JavaKitCollection/swift-java.config

generate-JavaKitFunction: Java2Swift
mkdir -p Sources/JavaKitFunction/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitFunction --depends-on JavaKit=Sources/JavaKit/Java2Swift.config -o Sources/JavaKitFunction/generated Sources/JavaKitFunction/Java2Swift.config
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitFunction --depends-on JavaKit=Sources/JavaKit/swift-java.config -o Sources/JavaKitFunction/generated Sources/JavaKitFunction/swift-java.config

generate-JavaKitReflection: Java2Swift generate-JavaKit generate-JavaKitCollection
mkdir -p Sources/JavaKitReflection/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitReflection --depends-on JavaKit=Sources/JavaKit/Java2Swift.config --depends-on JavaKitCollection=Sources/JavaKitCollection/Java2Swift.config -o Sources/JavaKitReflection/generated Sources/JavaKitReflection/Java2Swift.config
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitReflection --depends-on JavaKit=Sources/JavaKit/swift-java.config --depends-on JavaKitCollection=Sources/JavaKitCollection/swift-java.config -o Sources/JavaKitReflection/generated Sources/JavaKitReflection/swift-java.config

generate-JavaKitJar: Java2Swift generate-JavaKit generate-JavaKitCollection
mkdir -p Sources/JavaKitJar/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitJar --depends-on JavaKit=Sources/JavaKit/Java2Swift.config --depends-on JavaKitCollection=Sources/JavaKitCollection/Java2Swift.config -o Sources/JavaKitJar/generated Sources/JavaKitJar/Java2Swift.config
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitJar --depends-on JavaKit=Sources/JavaKit/swift-java.config --depends-on JavaKitCollection=Sources/JavaKitCollection/swift-java.config -o Sources/JavaKitJar/generated Sources/JavaKitJar/swift-java.config

generate-JavaKitNetwork: Java2Swift generate-JavaKit generate-JavaKitCollection
mkdir -p Sources/JavaKitNetwork/generated
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitNetwork --depends-on JavaKit=Sources/JavaKit/Java2Swift.config --depends-on JavaKitCollection=Sources/JavaKitCollection/Java2Swift.config -o Sources/JavaKitNetwork/generated Sources/JavaKitNetwork/Java2Swift.config
$(BUILD_DIR)/debug/Java2Swift --module-name JavaKitNetwork --depends-on JavaKit=Sources/JavaKit/swift-java.config --depends-on JavaKitCollection=Sources/JavaKitCollection/swift-java.config -o Sources/JavaKitNetwork/generated Sources/JavaKitNetwork/swift-java.config

javakit-generate: generate-JavaKit generate-JavaKitReflection generate-JavaKitJar generate-JavaKitNetwork

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,27 @@ extension HelloSwift: HelloSwiftNativeMethods {
}

// Make sure that the thread safe class is sendable
let threadSafe: Sendable = ThreadSafeHelperClass(environment: javaEnvironment)
let helper = ThreadSafeHelperClass(environment: javaEnvironment)
let threadSafe: Sendable = helper

checkOptionals(helper: helper)

return i * j
}

func checkOptionals(helper: ThreadSafeHelperClass) {
let text: JavaString? = helper.textOptional
let value: String? = helper.getValueOptional(Optional<JavaString>.none)
let textFunc: JavaString? = helper.getTextOptional()
let doubleOpt: Double? = helper.valOptional
let longOpt: Int64? = helper.fromOptional(21 as Int32?)
print("Optional text = \(text)")
print("Optional string value = \(value)")
print("Optional text function returned \(textFunc)")
print("Optional double function returned \(doubleOpt)")
print("Optional long function returned \(longOpt)")
}

@JavaMethod
func throwMessageFromSwift(_ message: String) throws -> String {
throw SwiftWrappedError.message(message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,28 @@

package com.example.swift;

import java.util.Optional;
import java.util.OptionalLong;
import java.util.OptionalInt;
import java.util.OptionalDouble;

@ThreadSafe
public class ThreadSafeHelperClass {
public ThreadSafeHelperClass() { }

public Optional<String> text = Optional.of("");

public final OptionalDouble val = OptionalDouble.of(2);

public String getValue(Optional<String> name) {
return name.orElse("");
}

public Optional<String> getText() {
return text;
}

public OptionalLong from(OptionalInt value) {
return OptionalLong.of(value.getAsInt());
}
}
63 changes: 59 additions & 4 deletions Sources/Java2SwiftLib/JavaClassTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -567,9 +567,39 @@ extension JavaClassTranslator {
let overrideOpt = (translateAsClass && !javaMethod.isStatic && isOverride(javaMethod))
? "override "
: ""
return """
\(methodAttribute)\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause)
"""

if resultType.optionalWrappedType() != nil || parameters.contains(where: { $0.type.trimmedDescription.optionalWrappedType() != nil }) {
let parameters = parameters.map { param -> (clause: FunctionParameterSyntax, passedArg: String) in
let name = param.secondName!.trimmedDescription

return if let optionalType = param.type.trimmedDescription.optionalWrappedType() {
(clause: "_ \(raw: name): \(raw: optionalType)", passedArg: "\(name).toJavaOptional()")
} else {
(clause: param, passedArg: "\(name)")
}
}

let resultOptional: String = resultType.optionalWrappedType() ?? resultType
let baseBody: ExprSyntax = "\(raw: javaMethod.throwsCheckedException ? "try " : "")\(raw: swiftMethodName)(\(raw: parameters.map(\.passedArg).joined(separator: ", ")))"
let body: ExprSyntax = if let optionalType = resultType.optionalWrappedType() {
"Optional(javaOptional: \(baseBody))"
} else {
baseBody
}


return """
\(methodAttribute)\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause)

\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)Optional\(raw: genericParameterClause)(\(raw: parameters.map(\.clause.description).joined(separator: ", ")))\(raw: throwsStr) -> \(raw: resultOptional)\(raw: whereClause) {
\(body)
}
"""
} else {
return """
\(methodAttribute)\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause)
"""
}
}

/// Render a single Java field into the corresponding Swift property, or
Expand All @@ -582,10 +612,35 @@ extension JavaClassTranslator {
)
let fieldAttribute: AttributeSyntax = javaField.isStatic ? "@JavaStaticField" : "@JavaField";
let swiftFieldName = javaField.getName().escapedSwiftName
return """

if let optionalType = typeName.optionalWrappedType() {
let setter = if !javaField.isFinal {
"""

set {
\(swiftFieldName) = newValue.toJavaOptional()
}
"""
} else {
""
}
return """
\(fieldAttribute)(isFinal: \(raw: javaField.isFinal))
public var \(raw: swiftFieldName): \(raw: typeName)


public var \(raw: swiftFieldName)Optional: \(raw: optionalType) {
get {
Optional(javaOptional: \(raw: swiftFieldName))
}\(raw: setter)
}
"""
} else {
return """
\(fieldAttribute)(isFinal: \(raw: javaField.isFinal))
public var \(raw: swiftFieldName): \(raw: typeName)
"""
}
}

package func renderEnum(name: String) -> [DeclSyntax] {
Expand Down
15 changes: 15 additions & 0 deletions Sources/Java2SwiftLib/StringExtras.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,19 @@ extension String {
public func replacing(_ character: Character, with replacement: Character) -> String {
return replacingOccurrences(of: String(character), with: String(replacement))
}

public func optionalWrappedType() -> String? {
if self == "JavaOptionalInt!" || self == "JavaOptionalInt?" {
return "Int32?"
} else if self == "JavaOptionalLong!" || self == "JavaOptionalLong?" {
return "Int64?"
} else if self == "JavaOptionalDouble!" || self == "JavaOptionalDouble?" {
return "Double?"
} else if hasPrefix("JavaOptional<") {
let offsetCount = "JavaOptional<".count
return "\(String(self[index(startIndex, offsetBy: offsetCount)..<lastIndex(of: ">")!]))?"
} else {
return nil
}
}
}
6 changes: 0 additions & 6 deletions Sources/JavaKit/AnyJavaObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@ public protocol CustomJavaClassLoader: AnyJavaObject {
static func getJavaClassLoader(in environment: JNIEnvironment) throws -> JavaClassLoader!
}

/// Add getClassLoader() to JavaObject as it is otherwise recursively defined
extension JavaObject {
@JavaMethod
public func getClassLoader() throws -> JavaClassLoader!
}

extension AnyJavaObject {
/// Retrieve the underlying Java object.
public var javaThis: jobject {
Expand Down
81 changes: 81 additions & 0 deletions Sources/JavaKit/Optional+JavaOptional.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

public extension Optional where Wrapped: AnyJavaObject {
func toJavaOptional() -> JavaOptional<Wrapped> {
return try! JavaClass<JavaOptional<Wrapped>>().ofNullable(self?.as(JavaObject.self)).as(JavaOptional<Wrapped>.self)!
}

init(javaOptional: JavaOptional<Wrapped>?) {
if let javaOptional {
self = javaOptional.isPresent() ? javaOptional.get().as(Wrapped.self) : Optional<Wrapped>.none
} else {
self = nil
}
}
}

public extension Optional where Wrapped == Double {
func toJavaOptional() -> JavaOptionalDouble {
if let self {
return try! JavaClass<JavaOptionalDouble>().of(self)!
} else {
return try! JavaClass<JavaOptionalDouble>().empty()!
}
}

init(javaOptional: JavaOptionalDouble?) {
if let javaOptional {
self = javaOptional.isPresent() ? javaOptional.getAsDouble() : nil
} else {
self = nil
}
}
}

public extension Optional where Wrapped == Int32 {
func toJavaOptional() -> JavaOptionalInt {
if let self {
return try! JavaClass<JavaOptionalInt>().of(self)!
} else {
return try! JavaClass<JavaOptionalInt>().empty()!
}
}

init(javaOptional: JavaOptionalInt?) {
if let javaOptional {
self = javaOptional.isPresent() ? javaOptional.getAsInt() : nil
} else {
self = nil
}
}
}

public extension Optional where Wrapped == Int64 {
func toJavaOptional() -> JavaOptionalLong {
if let self {
return try! JavaClass<JavaOptionalLong>().of(self)!
} else {
return try! JavaClass<JavaOptionalLong>().empty()!
}
}

init(javaOptional: JavaOptionalLong?) {
if let javaOptional {
self = javaOptional.isPresent() ? javaOptional.getAsLong() : nil
} else {
self = nil
}
}
}
6 changes: 6 additions & 0 deletions Sources/JavaKit/generated/JavaClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ open class JavaClass<T: AnyJavaObject>: JavaObject {
@JavaMethod
open func isRecord() -> Bool

@JavaMethod
open func getClassLoader() -> JavaClassLoader!

@JavaMethod
open func newInstance() throws -> JavaObject!

Expand Down Expand Up @@ -130,6 +133,9 @@ open class JavaClass<T: AnyJavaObject>: JavaObject {
open func isSealed() -> Bool
}
extension JavaClass {
@JavaStaticMethod
public func forName<T: AnyJavaObject>(_ arg0: String, _ arg1: Bool, _ arg2: JavaClassLoader?) throws -> JavaClass<JavaObject>! where ObjectType == JavaClass<T>

@JavaStaticMethod
public func forName<T: AnyJavaObject>(_ arg0: String) throws -> JavaClass<JavaObject>! where ObjectType == JavaClass<T>

Expand Down
4 changes: 2 additions & 2 deletions Sources/JavaKit/generated/JavaClassLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ open class JavaClassLoader: JavaObject {
open func resolveClass(_ arg0: JavaClass<JavaObject>?)

@JavaMethod
open func defineClass(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) throws -> JavaClass<JavaObject>!
open func defineClass(_ arg0: String, _ arg1: [Int8], _ arg2: Int32, _ arg3: Int32) throws -> JavaClass<JavaObject>!

@JavaMethod
open func defineClass(_ arg0: String, _ arg1: [Int8], _ arg2: Int32, _ arg3: Int32) throws -> JavaClass<JavaObject>!
open func defineClass(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32) throws -> JavaClass<JavaObject>!

@JavaMethod
open func findLibrary(_ arg0: String) -> String
Expand Down
7 changes: 7 additions & 0 deletions Sources/JavaKit/generated/JavaDouble.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ open class JavaDouble: JavaNumber {
@JavaMethod
open override func doubleValue() -> Double

@JavaMethod
open func describeConstable() -> JavaOptional<JavaDouble>!

open func describeConstableOptional() -> JavaDouble? {
Optional(javaOptional: describeConstable())
}

@JavaMethod
open func isNaN() -> Bool
}
Expand Down
7 changes: 7 additions & 0 deletions Sources/JavaKit/generated/JavaFloat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ open class JavaFloat: JavaNumber {
@JavaMethod
open override func doubleValue() -> Double

@JavaMethod
open func describeConstable() -> JavaOptional<JavaFloat>!

open func describeConstableOptional() -> JavaFloat? {
Optional(javaOptional: describeConstable())
}

@JavaMethod
open func isNaN() -> Bool
}
Expand Down
7 changes: 7 additions & 0 deletions Sources/JavaKit/generated/JavaInteger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ open class JavaInteger: JavaNumber {

@JavaMethod
open override func doubleValue() -> Double

@JavaMethod
open func describeConstable() -> JavaOptional<JavaInteger>!

open func describeConstableOptional() -> JavaInteger? {
Optional(javaOptional: describeConstable())
}
}
extension JavaClass<JavaInteger> {
@JavaStaticField(isFinal: true)
Expand Down
7 changes: 7 additions & 0 deletions Sources/JavaKit/generated/JavaLong.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ open class JavaLong: JavaNumber {

@JavaMethod
open override func doubleValue() -> Double

@JavaMethod
open func describeConstable() -> JavaOptional<JavaLong>!

open func describeConstableOptional() -> JavaLong? {
Optional(javaOptional: describeConstable())
}
}
extension JavaClass<JavaLong> {
@JavaStaticField(isFinal: true)
Expand Down
Loading