Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
//
// json-attribute-merging.swift
// json2swift
//
// Created by Joshua Smith on 10/11/16.
// Copyright © 2016 iJoshSmith. All rights reserved.
//
// MARK: - JSONElementSchema extension
extension JSONElementSchema {
static func inferredByMergingAttributes(of schemas: [JSONElementSchema], named name: String) -> JSONElementSchema {
switch schemas.count {
case 0: return JSONElementSchema(name: name)
case 1: return schemas[0]
default: return schemas.dropFirst().reduce(schemas[0], merge(schema:withSchema:))
}
}
private static func merge(schema: JSONElementSchema, withSchema otherSchema: JSONElementSchema) -> JSONElementSchema {
return schema.merged(with: otherSchema)
}
fileprivate func merged(with schema: JSONElementSchema) -> JSONElementSchema {
let mergedAttributes = JSONType.merge(attributes: attributes, with: schema.attributes)
return JSONElementSchema(name: name, attributes: mergedAttributes)
}
}
// MARK: - JSONType extension
extension JSONType {
fileprivate static func merge(attributes: JSONAttributeMap, with otherAttributes: JSONAttributeMap) -> JSONAttributeMap {
let attributeNames = Set(Array(attributes.keys) + Array(otherAttributes.keys))
let mergedAttributes = attributeNames.map { name -> (String, JSONType) in
let type = attributes[name] ?? .nullable
let otherType = otherAttributes[name] ?? .nullable
let compatibleType = type.findCompatibleType(with: otherType)
return (name, compatibleType)
}
return JSONAttributeMap(entries: mergedAttributes)
}
internal func findCompatibleType(with type: JSONType) -> JSONType {
return JSONType.compatible(with: self, and: type)
}
private static func compatible(with type1: JSONType, and type2: JSONType) -> JSONType {
switch (type1, type2) {
// Element
case let (.element(r1, s1), .element(r2, s2)): return .element(isRequired: r1 && r2, schema: s1.merged(with: s2))
case let (.element(_, s), .nullable): return .element(isRequired: false, schema: s)
case let (.nullable, .element(_, s)): return .element(isRequired: false, schema: s)
// Element Array
case let (.elementArray(r1, s1, n1), .elementArray(r2, s2, n2)): return .elementArray(isRequired: r1 && r2, elementSchema: s1.merged(with: s2), hasNullableElements: n1 || n2)
case let (.elementArray(_, s, n), .nullable): return .elementArray(isRequired: false, elementSchema: s, hasNullableElements: n)
case let (.nullable, .elementArray(_, s, n)): return .elementArray(isRequired: false, elementSchema: s, hasNullableElements: n)
// Value Array
case let (.valueArray(r1, t1), .valueArray(r2, t2)): return .valueArray(isRequired: r1 && r2, valueType: t1.findCompatibleType(with: t2))
case let (.valueArray(_, t), .nullable): return .valueArray(isRequired: false, valueType: t)
case let (.nullable, .valueArray(_, t)): return .valueArray(isRequired: false, valueType: t)
// Numeric
case let (.number(r1, fp1), .number(r2, fp2)): return .number(isRequired: r1 && r2, isFloatingPoint: fp1 || fp2)
case let (.number(_, fp), .nullable): return .number(isRequired: false, isFloatingPoint: fp)
case let (.nullable, .number(_, fp)): return .number(isRequired: false, isFloatingPoint: fp)
// Text
case let (.date(r1, f), .date(r2, _)): return .date( isRequired: r1 && r2, format: f)
case let (.date(r1, _), .url(r2)): return .string(isRequired: r1 && r2)
case let (.date(r1, f), .string(r2)): return .date( isRequired: r1 && r2, format: f)
case let (.date(_, f), .nullable): return .date( isRequired: false, format: f)
case let (.nullable, .date(_, f)): return .date( isRequired: false, format: f)
case let (.url(r1), .date(r2, _)): return .string(isRequired: r1 && r2)
case let (.url(r1), .url(r2)): return .url( isRequired: r1 && r2)
case let (.url(r1), .string(r2)): return .string(isRequired: r1 && r2)
case (.url, .nullable): return .url( isRequired: false)
case (.nullable, .url): return .url( isRequired: false)
case let (.string(r1), .date(r2, f)): return .date( isRequired: r1 && r2, format: f)
case let (.string(r1), .url(r2)): return .string(isRequired: r1 && r2)
case let (.string(r1), .string(r2)): return .string(isRequired: r1 && r2)
case (.string, .nullable): return .string(isRequired: false)
case (.nullable, .string): return .string(isRequired: false)
// Boolean
case let (.bool(r1), .bool(r2)): return .bool(isRequired: r1 && r2)
case (.bool, .nullable): return .bool(isRequired: false)
case (.nullable, .bool): return .bool(isRequired: false)
// Nullable (e.g. both attribute values were null)
case (.nullable, .nullable): return .nullable
// Empty array
case (.emptyArray, .emptyArray): return .emptyArray
case (.elementArray, .emptyArray): return type1
case (.valueArray, .emptyArray): return type1
case (.emptyArray, .elementArray): return type2
case (.emptyArray, .valueArray): return type2
// Unrelated types
default: return .anything
}
}
}