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..c85cedb0 --- /dev/null +++ b/Sources/Java2SwiftLib/JavaTranslator+Validation.swift @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +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 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 + 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).sorted())) + } + 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"]) + ]) + } + } + } +}