Skip to content

Commit 2cf42ce

Browse files
authored
Adds support for optional mapping (#189)
1 parent c769b1a commit 2cf42ce

22 files changed

+483
-70
lines changed

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,27 +68,27 @@ Java2Swift: $(BUILD_DIR)/debug/Java2Swift
6868

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

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

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

8181
generate-JavaKitReflection: Java2Swift generate-JavaKit generate-JavaKitCollection
8282
mkdir -p Sources/JavaKitReflection/generated
83-
$(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
83+
$(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
8484

8585
generate-JavaKitJar: Java2Swift generate-JavaKit generate-JavaKitCollection
8686
mkdir -p Sources/JavaKitJar/generated
87-
$(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
87+
$(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
8888

8989
generate-JavaKitNetwork: Java2Swift generate-JavaKit generate-JavaKitCollection
9090
mkdir -p Sources/JavaKitNetwork/generated
91-
$(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
91+
$(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
9292

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

Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,27 @@ extension HelloSwift: HelloSwiftNativeMethods {
7777
}
7878

7979
// Make sure that the thread safe class is sendable
80-
let threadSafe: Sendable = ThreadSafeHelperClass(environment: javaEnvironment)
80+
let helper = ThreadSafeHelperClass(environment: javaEnvironment)
81+
let threadSafe: Sendable = helper
82+
83+
checkOptionals(helper: helper)
8184

8285
return i * j
8386
}
8487

88+
func checkOptionals(helper: ThreadSafeHelperClass) {
89+
let text: JavaString? = helper.textOptional
90+
let value: String? = helper.getValueOptional(Optional<JavaString>.none)
91+
let textFunc: JavaString? = helper.getTextOptional()
92+
let doubleOpt: Double? = helper.valOptional
93+
let longOpt: Int64? = helper.fromOptional(21 as Int32?)
94+
print("Optional text = \(text)")
95+
print("Optional string value = \(value)")
96+
print("Optional text function returned \(textFunc)")
97+
print("Optional double function returned \(doubleOpt)")
98+
print("Optional long function returned \(longOpt)")
99+
}
100+
85101
@JavaMethod
86102
func throwMessageFromSwift(_ message: String) throws -> String {
87103
throw SwiftWrappedError.message(message)

Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/ThreadSafeHelperClass.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,28 @@
1414

1515
package com.example.swift;
1616

17+
import java.util.Optional;
18+
import java.util.OptionalLong;
19+
import java.util.OptionalInt;
20+
import java.util.OptionalDouble;
21+
1722
@ThreadSafe
1823
public class ThreadSafeHelperClass {
1924
public ThreadSafeHelperClass() { }
25+
26+
public Optional<String> text = Optional.of("");
27+
28+
public final OptionalDouble val = OptionalDouble.of(2);
29+
30+
public String getValue(Optional<String> name) {
31+
return name.orElse("");
32+
}
33+
34+
public Optional<String> getText() {
35+
return text;
36+
}
37+
38+
public OptionalLong from(OptionalInt value) {
39+
return OptionalLong.of(value.getAsInt());
40+
}
2041
}

Sources/Java2SwiftLib/JavaClassTranslator.swift

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,39 @@ extension JavaClassTranslator {
567567
let overrideOpt = (translateAsClass && !javaMethod.isStatic && isOverride(javaMethod))
568568
? "override "
569569
: ""
570-
return """
571-
\(methodAttribute)\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause)
572-
"""
570+
571+
if resultType.optionalWrappedType() != nil || parameters.contains(where: { $0.type.trimmedDescription.optionalWrappedType() != nil }) {
572+
let parameters = parameters.map { param -> (clause: FunctionParameterSyntax, passedArg: String) in
573+
let name = param.secondName!.trimmedDescription
574+
575+
return if let optionalType = param.type.trimmedDescription.optionalWrappedType() {
576+
(clause: "_ \(raw: name): \(raw: optionalType)", passedArg: "\(name).toJavaOptional()")
577+
} else {
578+
(clause: param, passedArg: "\(name)")
579+
}
580+
}
581+
582+
let resultOptional: String = resultType.optionalWrappedType() ?? resultType
583+
let baseBody: ExprSyntax = "\(raw: javaMethod.throwsCheckedException ? "try " : "")\(raw: swiftMethodName)(\(raw: parameters.map(\.passedArg).joined(separator: ", ")))"
584+
let body: ExprSyntax = if let optionalType = resultType.optionalWrappedType() {
585+
"Optional(javaOptional: \(baseBody))"
586+
} else {
587+
baseBody
588+
}
589+
590+
591+
return """
592+
\(methodAttribute)\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause)
593+
594+
\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)Optional\(raw: genericParameterClause)(\(raw: parameters.map(\.clause.description).joined(separator: ", ")))\(raw: throwsStr) -> \(raw: resultOptional)\(raw: whereClause) {
595+
\(body)
596+
}
597+
"""
598+
} else {
599+
return """
600+
\(methodAttribute)\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause)
601+
"""
602+
}
573603
}
574604

575605
/// Render a single Java field into the corresponding Swift property, or
@@ -582,10 +612,35 @@ extension JavaClassTranslator {
582612
)
583613
let fieldAttribute: AttributeSyntax = javaField.isStatic ? "@JavaStaticField" : "@JavaField";
584614
let swiftFieldName = javaField.getName().escapedSwiftName
585-
return """
615+
616+
if let optionalType = typeName.optionalWrappedType() {
617+
let setter = if !javaField.isFinal {
618+
"""
619+
620+
set {
621+
\(swiftFieldName) = newValue.toJavaOptional()
622+
}
623+
"""
624+
} else {
625+
""
626+
}
627+
return """
628+
\(fieldAttribute)(isFinal: \(raw: javaField.isFinal))
629+
public var \(raw: swiftFieldName): \(raw: typeName)
630+
631+
632+
public var \(raw: swiftFieldName)Optional: \(raw: optionalType) {
633+
get {
634+
Optional(javaOptional: \(raw: swiftFieldName))
635+
}\(raw: setter)
636+
}
637+
"""
638+
} else {
639+
return """
586640
\(fieldAttribute)(isFinal: \(raw: javaField.isFinal))
587641
public var \(raw: swiftFieldName): \(raw: typeName)
588642
"""
643+
}
589644
}
590645

591646
package func renderEnum(name: String) -> [DeclSyntax] {

Sources/Java2SwiftLib/StringExtras.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,19 @@ extension String {
3939
public func replacing(_ character: Character, with replacement: Character) -> String {
4040
return replacingOccurrences(of: String(character), with: String(replacement))
4141
}
42+
43+
public func optionalWrappedType() -> String? {
44+
if self == "JavaOptionalInt!" || self == "JavaOptionalInt?" {
45+
return "Int32?"
46+
} else if self == "JavaOptionalLong!" || self == "JavaOptionalLong?" {
47+
return "Int64?"
48+
} else if self == "JavaOptionalDouble!" || self == "JavaOptionalDouble?" {
49+
return "Double?"
50+
} else if hasPrefix("JavaOptional<") {
51+
let offsetCount = "JavaOptional<".count
52+
return "\(String(self[index(startIndex, offsetBy: offsetCount)..<lastIndex(of: ">")!]))?"
53+
} else {
54+
return nil
55+
}
56+
}
4257
}

Sources/JavaKit/AnyJavaObject.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,6 @@ public protocol CustomJavaClassLoader: AnyJavaObject {
5656
static func getJavaClassLoader(in environment: JNIEnvironment) throws -> JavaClassLoader!
5757
}
5858

59-
/// Add getClassLoader() to JavaObject as it is otherwise recursively defined
60-
extension JavaObject {
61-
@JavaMethod
62-
public func getClassLoader() throws -> JavaClassLoader!
63-
}
64-
6559
extension AnyJavaObject {
6660
/// Retrieve the underlying Java object.
6761
public var javaThis: jobject {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
public extension Optional where Wrapped: AnyJavaObject {
16+
func toJavaOptional() -> JavaOptional<Wrapped> {
17+
return try! JavaClass<JavaOptional<Wrapped>>().ofNullable(self?.as(JavaObject.self)).as(JavaOptional<Wrapped>.self)!
18+
}
19+
20+
init(javaOptional: JavaOptional<Wrapped>?) {
21+
if let javaOptional {
22+
self = javaOptional.isPresent() ? javaOptional.get().as(Wrapped.self) : Optional<Wrapped>.none
23+
} else {
24+
self = nil
25+
}
26+
}
27+
}
28+
29+
public extension Optional where Wrapped == Double {
30+
func toJavaOptional() -> JavaOptionalDouble {
31+
if let self {
32+
return try! JavaClass<JavaOptionalDouble>().of(self)!
33+
} else {
34+
return try! JavaClass<JavaOptionalDouble>().empty()!
35+
}
36+
}
37+
38+
init(javaOptional: JavaOptionalDouble?) {
39+
if let javaOptional {
40+
self = javaOptional.isPresent() ? javaOptional.getAsDouble() : nil
41+
} else {
42+
self = nil
43+
}
44+
}
45+
}
46+
47+
public extension Optional where Wrapped == Int32 {
48+
func toJavaOptional() -> JavaOptionalInt {
49+
if let self {
50+
return try! JavaClass<JavaOptionalInt>().of(self)!
51+
} else {
52+
return try! JavaClass<JavaOptionalInt>().empty()!
53+
}
54+
}
55+
56+
init(javaOptional: JavaOptionalInt?) {
57+
if let javaOptional {
58+
self = javaOptional.isPresent() ? javaOptional.getAsInt() : nil
59+
} else {
60+
self = nil
61+
}
62+
}
63+
}
64+
65+
public extension Optional where Wrapped == Int64 {
66+
func toJavaOptional() -> JavaOptionalLong {
67+
if let self {
68+
return try! JavaClass<JavaOptionalLong>().of(self)!
69+
} else {
70+
return try! JavaClass<JavaOptionalLong>().empty()!
71+
}
72+
}
73+
74+
init(javaOptional: JavaOptionalLong?) {
75+
if let javaOptional {
76+
self = javaOptional.isPresent() ? javaOptional.getAsLong() : nil
77+
} else {
78+
self = nil
79+
}
80+
}
81+
}

Sources/JavaKit/generated/JavaClass.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ open class JavaClass<T: AnyJavaObject>: JavaObject {
5151
@JavaMethod
5252
open func isRecord() -> Bool
5353

54+
@JavaMethod
55+
open func getClassLoader() -> JavaClassLoader!
56+
5457
@JavaMethod
5558
open func newInstance() throws -> JavaObject!
5659

@@ -130,6 +133,9 @@ open class JavaClass<T: AnyJavaObject>: JavaObject {
130133
open func isSealed() -> Bool
131134
}
132135
extension JavaClass {
136+
@JavaStaticMethod
137+
public func forName<T: AnyJavaObject>(_ arg0: String, _ arg1: Bool, _ arg2: JavaClassLoader?) throws -> JavaClass<JavaObject>! where ObjectType == JavaClass<T>
138+
133139
@JavaStaticMethod
134140
public func forName<T: AnyJavaObject>(_ arg0: String) throws -> JavaClass<JavaObject>! where ObjectType == JavaClass<T>
135141

Sources/JavaKit/generated/JavaClassLoader.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ open class JavaClassLoader: JavaObject {
3131
open func resolveClass(_ arg0: JavaClass<JavaObject>?)
3232

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

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

3939
@JavaMethod
4040
open func findLibrary(_ arg0: String) -> String

Sources/JavaKit/generated/JavaDouble.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ open class JavaDouble: JavaNumber {
4545
@JavaMethod
4646
open override func doubleValue() -> Double
4747

48+
@JavaMethod
49+
open func describeConstable() -> JavaOptional<JavaDouble>!
50+
51+
open func describeConstableOptional() -> JavaDouble? {
52+
Optional(javaOptional: describeConstable())
53+
}
54+
4855
@JavaMethod
4956
open func isNaN() -> Bool
5057
}

0 commit comments

Comments
 (0)