Skip to content

Commit a26cb1a

Browse files
authored
Merge pull request #182 from jrosen081/jrosen/same-swift-type
Add support for failing build when multiple types are mapped to the same type
2 parents 28fb69f + 250bb24 commit a26cb1a

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

Sources/Java2Swift/JavaToSwift.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ struct JavaToSwift: ParsableCommand {
267267
allClassesToVisit.append(contentsOf: nestedClasses)
268268
}
269269

270+
// Validate configurations before writing any files
271+
try translator.validateClassConfiguration()
272+
270273
// Translate all of the Java classes into Swift classes.
271274
for javaClass in javaClasses {
272275
translator.startNewFile()
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
package extension JavaTranslator {
16+
struct SwiftTypeName: Hashable {
17+
let swiftType: String
18+
let swiftModule: String?
19+
20+
package init(swiftType: String, swiftModule: String?) {
21+
self.swiftType = swiftType
22+
self.swiftModule = swiftModule
23+
}
24+
}
25+
26+
struct SwiftToJavaMapping: Equatable {
27+
let swiftType: SwiftTypeName
28+
let javaTypes: [String]
29+
30+
package init(swiftType: SwiftTypeName, javaTypes: [String]) {
31+
self.swiftType = swiftType
32+
self.javaTypes = javaTypes
33+
}
34+
}
35+
36+
enum ValidationError: Error, CustomStringConvertible {
37+
case multipleClassesMappedToSameName(swiftToJavaMapping: [SwiftToJavaMapping])
38+
39+
package var description: String {
40+
switch self {
41+
case .multipleClassesMappedToSameName(let swiftToJavaMapping):
42+
"""
43+
The following Java classes were mapped to the same Swift type name:
44+
\(swiftToJavaMapping.map(mappingDescription(mapping:)).joined(separator: "\n"))
45+
"""
46+
}
47+
}
48+
49+
private func mappingDescription(mapping: SwiftToJavaMapping) -> String {
50+
let javaTypes = mapping.javaTypes.map { "'\($0)'" }.joined(separator: ", ")
51+
return "Swift Type: '\(mapping.swiftType.swiftModule ?? "")'.'\(mapping.swiftType.swiftType)', Java Types: \(javaTypes)"
52+
53+
}
54+
}
55+
func validateClassConfiguration() throws {
56+
// Group all classes by swift name
57+
let groupedDictionary: [SwiftTypeName: [(String, (String, String?))]] = Dictionary(grouping: translatedClasses, by: { SwiftTypeName(swiftType: $0.value.swiftType, swiftModule: $0.value.swiftModule) })
58+
// Find all that are mapped to multiple names
59+
let multipleClassesMappedToSameName: [SwiftTypeName: [(String, (String, String?))]] = groupedDictionary.filter { (key: SwiftTypeName, value: [(String, (String, String?))]) in
60+
value.count > 1
61+
}
62+
63+
if !multipleClassesMappedToSameName.isEmpty {
64+
// Convert them to swift object and throw
65+
var errorMappings = [SwiftToJavaMapping]()
66+
for (swiftType, swiftJavaMappings) in multipleClassesMappedToSameName {
67+
errorMappings.append(SwiftToJavaMapping(swiftType: swiftType, javaTypes: swiftJavaMappings.map(\.0).sorted()))
68+
}
69+
throw ValidationError.multipleClassesMappedToSameName(swiftToJavaMapping: errorMappings)
70+
}
71+
72+
}
73+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
import Java2SwiftLib
16+
import XCTest
17+
18+
final class JavaTranslatorValidationTests: XCTestCase {
19+
func testValidationError() throws {
20+
let translator = try JavaTranslator(swiftModuleName: "SwiftModule", environment: jvm.environment())
21+
translator.translatedClasses = [
22+
"TestClass": ("Class1", "Module1"),
23+
"TestClass2": ("Class1", "Module2"),
24+
"TestClass3": ("Class1", "Module1"),
25+
"TestClass4": ("Class1", nil)
26+
]
27+
28+
XCTAssertThrowsError(try translator.validateClassConfiguration()) { error in
29+
XCTAssertTrue(error is JavaTranslator.ValidationError)
30+
let validationError = error as! JavaTranslator.ValidationError
31+
switch validationError {
32+
case .multipleClassesMappedToSameName(let swiftToJavaMapping):
33+
XCTAssertEqual(swiftToJavaMapping, [
34+
JavaTranslator.SwiftToJavaMapping(swiftType: .init(swiftType: "Class1", swiftModule: "Module1"),
35+
javaTypes: ["TestClass", "TestClass3"])
36+
])
37+
}
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)