From 77ace5f83028808eadc3faea00b23b3934a3df0e Mon Sep 17 00:00:00 2001 From: jrosen081 Date: Mon, 18 Nov 2024 21:09:46 -0500 Subject: [PATCH 1/2] Add support for failing build when multiple types are mapped to the same type --- Sources/Java2Swift/JavaToSwift.swift | 3 + .../JavaTranslator+Validation.swift | 72 +++++++++++++++++++ .../JavaTranslatorValidationTests.swift | 40 +++++++++++ 3 files changed, 115 insertions(+) create mode 100644 Sources/Java2SwiftLib/JavaTranslator+Validation.swift create mode 100644 Tests/Java2SwiftTests/JavaTranslatorValidationTests.swift diff --git a/Sources/Java2Swift/JavaToSwift.swift b/Sources/Java2Swift/JavaToSwift.swift index 1434629f..bc7af4af 100644 --- a/Sources/Java2Swift/JavaToSwift.swift +++ b/Sources/Java2Swift/JavaToSwift.swift @@ -267,6 +267,9 @@ struct JavaToSwift: ParsableCommand { allClassesToVisit.append(contentsOf: nestedClasses) } + // Validate configurations before writing any files + try translator.validateClassConfiguration() + // Translate all of the Java classes into Swift classes. for javaClass in javaClasses { translator.startNewFile() diff --git a/Sources/Java2SwiftLib/JavaTranslator+Validation.swift b/Sources/Java2SwiftLib/JavaTranslator+Validation.swift new file mode 100644 index 00000000..3cfe5d40 --- /dev/null +++ b/Sources/Java2SwiftLib/JavaTranslator+Validation.swift @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import Foundation + +package extension JavaTranslator { + struct SwiftTypeName: Hashable { + let swiftType: String + let swiftModule: String? + + package init(swiftType: String, swiftModule: String?) { + self.swiftType = swiftType + self.swiftModule = swiftModule + } + } + + struct SwiftToJavaMapping: Equatable { + let swiftType: SwiftTypeName + let javaTypes: [String] + + package init(swiftType: SwiftTypeName, javaTypes: [String]) { + self.swiftType = swiftType + self.javaTypes = javaTypes + } + } + + enum ValidationError: Error, CustomStringConvertible { + case multipleClassesMappedToSameName(swiftToJavaMapping: [SwiftToJavaMapping]) + + package var description: String { + switch self { + case .multipleClassesMappedToSameName(let swiftToJavaMapping): + """ + The following Java classes were mapped to the same Swift type: + \(swiftToJavaMapping.map { mapping in + "Swift Type: \(mapping.swiftType.swiftModule ?? "").\(mapping.swiftType.swiftType), Java Types: \(mapping.javaTypes.sorted().joined(separator: ", "))" + }.joined(separator: "\n")) + """ + } + } + + } + func validateClassConfiguration() throws { + // Group all classes by swift name + let groupedDictionary: [SwiftTypeName: [(String, (String, String?))]] = Dictionary(grouping: translatedClasses, by: { SwiftTypeName(swiftType: $0.value.swiftType, swiftModule: $0.value.swiftModule) }) + // Find all that are mapped to multiple names + let multipleClassesMappedToSameName: [SwiftTypeName: [(String, (String, String?))]] = groupedDictionary.filter { (key: SwiftTypeName, value: [(String, (String, String?))]) in + value.count > 1 + } + + if !multipleClassesMappedToSameName.isEmpty { + // Convert them to swift object and throw + var errorMappings = [SwiftToJavaMapping]() + for (swiftType, swiftJavaMappings) in multipleClassesMappedToSameName { + errorMappings.append(SwiftToJavaMapping(swiftType: swiftType, javaTypes: swiftJavaMappings.map(\.0))) + } + throw ValidationError.multipleClassesMappedToSameName(swiftToJavaMapping: errorMappings) + } + + } +} diff --git a/Tests/Java2SwiftTests/JavaTranslatorValidationTests.swift b/Tests/Java2SwiftTests/JavaTranslatorValidationTests.swift new file mode 100644 index 00000000..e5c3a951 --- /dev/null +++ b/Tests/Java2SwiftTests/JavaTranslatorValidationTests.swift @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import Java2SwiftLib +import XCTest + +final class JavaTranslatorValidationTests: XCTestCase { + func testValidationError() throws { + let translator = try JavaTranslator(swiftModuleName: "SwiftModule", environment: jvm.environment()) + translator.translatedClasses = [ + "TestClass": ("Class1", "Module1"), + "TestClass2": ("Class1", "Module2"), + "TestClass3": ("Class1", "Module1"), + "TestClass4": ("Class1", nil) + ] + + XCTAssertThrowsError(try translator.validateClassConfiguration()) { error in + XCTAssertTrue(error is JavaTranslator.ValidationError) + let validationError = error as! JavaTranslator.ValidationError + switch validationError { + case .multipleClassesMappedToSameName(let swiftToJavaMapping): + XCTAssertEqual(swiftToJavaMapping, [ + JavaTranslator.SwiftToJavaMapping(swiftType: .init(swiftType: "Class1", swiftModule: "Module1"), + javaTypes: ["TestClass", "TestClass3"]) + ]) + } + } + } +} From 250bb24083c0447f62e7b63b09f628cefcac2b34 Mon Sep 17 00:00:00 2001 From: jrosen081 Date: Wed, 20 Nov 2024 22:31:44 -0500 Subject: [PATCH 2/2] PR Feedback --- .../Java2SwiftLib/JavaTranslator+Validation.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Sources/Java2SwiftLib/JavaTranslator+Validation.swift b/Sources/Java2SwiftLib/JavaTranslator+Validation.swift index 3cfe5d40..c85cedb0 100644 --- a/Sources/Java2SwiftLib/JavaTranslator+Validation.swift +++ b/Sources/Java2SwiftLib/JavaTranslator+Validation.swift @@ -12,8 +12,6 @@ // //===----------------------------------------------------------------------===// -import Foundation - package extension JavaTranslator { struct SwiftTypeName: Hashable { let swiftType: String @@ -42,14 +40,17 @@ package extension JavaTranslator { switch self { case .multipleClassesMappedToSameName(let swiftToJavaMapping): """ - The following Java classes were mapped to the same Swift type: - \(swiftToJavaMapping.map { mapping in - "Swift Type: \(mapping.swiftType.swiftModule ?? "").\(mapping.swiftType.swiftType), Java Types: \(mapping.javaTypes.sorted().joined(separator: ", "))" - }.joined(separator: "\n")) + The following Java classes were mapped to the same Swift type name: + \(swiftToJavaMapping.map(mappingDescription(mapping:)).joined(separator: "\n")) """ } } + private func mappingDescription(mapping: SwiftToJavaMapping) -> String { + let javaTypes = mapping.javaTypes.map { "'\($0)'" }.joined(separator: ", ") + return "Swift Type: '\(mapping.swiftType.swiftModule ?? "")'.'\(mapping.swiftType.swiftType)', Java Types: \(javaTypes)" + + } } func validateClassConfiguration() throws { // Group all classes by swift name @@ -63,7 +64,7 @@ package extension JavaTranslator { // Convert them to swift object and throw var errorMappings = [SwiftToJavaMapping]() for (swiftType, swiftJavaMappings) in multipleClassesMappedToSameName { - errorMappings.append(SwiftToJavaMapping(swiftType: swiftType, javaTypes: swiftJavaMappings.map(\.0))) + errorMappings.append(SwiftToJavaMapping(swiftType: swiftType, javaTypes: swiftJavaMappings.map(\.0).sorted())) } throw ValidationError.multipleClassesMappedToSameName(swiftToJavaMapping: errorMappings) }