Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Stabat Mater test #126

Merged
merged 7 commits into from
Oct 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Sources/MusicXML/Complex Types/Direction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,17 @@ public struct Direction {
public let staff: UInt?
public let sound: Sound?

public init(placement: AboveBelow? = nil, directive: Bool? = nil, directionType: DirectionType, offset: Offset? = nil, footnote: FormattedText? = nil, level: Level? = nil, voice: String? = nil, staff: UInt? = nil, sound: Sound? = nil) {
public init(
placement: AboveBelow? = nil,
directive: Bool? = nil,
directionType: DirectionType,
offset: Offset? = nil,
footnote: FormattedText? = nil,
level: Level? = nil,
voice: String? = nil,
staff: UInt? = nil,
sound: Sound? = nil
) {
self.placement = placement
self.directive = directive
self.directionType = directionType
Expand Down
16 changes: 14 additions & 2 deletions Sources/MusicXML/Complex Types/DirectionType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ public enum DirectionType {
/// Humdrum has at least 3 representation formats related to dynamics. The MusicXML format
/// captures what is in the score, but does not try to be optimal for analysis or synthesis of
/// dynamics.
case dynamics([Dynamics] /* NonEmpty */)
///
#warning("FIXME: This _should_ be `[Dynamics]`, but this is what we can get working at the moment.")
case dynamics(Dynamics)
/// The eyeglasses element specifies the eyeglasses symbol, common in commercial music.
case eyeglasses(EmptyPrintStyleAlign)
/// The harp-pedals type is used to create harp pedal diagrams. The pedal-step and pedal-alter
Expand Down Expand Up @@ -103,6 +105,10 @@ public enum DirectionType {
/// crescendo, or the type is stop for a wedge that began with a diminuendo type. The line-type
/// is solid by default.
case wedge(Wedge)
/// The words element specifies a standard text direction. Left justification is
/// assumed if not specified. Language is Italian ("it") by default. Enclosure is none
/// by default.
case words(FormattedText)
}

extension DirectionType: Equatable { }
Expand Down Expand Up @@ -130,6 +136,8 @@ extension DirectionType: Codable {
case segno
case stringMute = "string-mute"
case wedge
case words

}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
Expand Down Expand Up @@ -176,6 +184,8 @@ extension DirectionType: Codable {
try container.encode(value, forKey: .stringMute)
case let .wedge(value):
try container.encode(value, forKey: .wedge)
case let .words(value):
try container.encode(value, forKey: .words)
}
}
public init(from decoder: Decoder) throws {
Expand All @@ -194,7 +204,7 @@ extension DirectionType: Codable {
} else if container.contains(.dashes) {
self = .dashes(try container.decode(Dashes.self, forKey: .dashes))
} else if container.contains(.dynamics) {
self = .dynamics(try container.decode([Dynamics].self, forKey: .dynamics))
self = .dynamics(try container.decode(Dynamics.self, forKey: .dynamics))
} else if container.contains(.eyeglasses) {
self = .eyeglasses(try container.decode(EmptyPrintStyleAlign.self, forKey: .eyeglasses))
} else if container.contains(.harpPedals) {
Expand Down Expand Up @@ -223,6 +233,8 @@ extension DirectionType: Codable {
self = .stringMute(try container.decode(StringMute.self, forKey: .stringMute))
} else if container.contains(.wedge) {
self = .wedge(try container.decode(Wedge.self, forKey: .wedge))
} else if container.contains(.words) {
self = .words(try container.decode(FormattedText.self, forKey: .words))
} else {
throw DecodingError.typeMismatch(
DirectionType.self,
Expand Down
43 changes: 42 additions & 1 deletion Sources/MusicXML/Complex Types/Dynamic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
// Created by James Bean on 5/19/19.
//

import XMLCoder

public enum Dynamic: String {
case p
case pp
Expand Down Expand Up @@ -36,5 +38,44 @@ public enum Dynamic: String {
//case other(OtherDynamics)
}

extension Dynamic: XMLChoiceCodingKey { }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long live XMLChoiceCodingKey.


extension Dynamic: Equatable { }
extension Dynamic: Codable { }
extension Dynamic: Codable {

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Dynamic.self)
if container.contains(.p) { self = .p }
else if container.contains(.pp) { self = .pp }
else if container.contains(.ppp) { self = .ppp }
else if container.contains(.pppp) { self = .pppp }
else if container.contains(.ppppp) { self = .ppppp }
else if container.contains(.pppppp) { self = .pppppp }
else if container.contains(.f) { self = .f }
else if container.contains(.ff) { self = .ff }
else if container.contains(.fff) { self = .fff }
else if container.contains(.ffff) { self = .ffff }
else if container.contains(.fffff) { self = .fffff }
else if container.contains(.ffffff) { self = .ffffff }
else if container.contains(.mp) { self = .mp }
else if container.contains(.mf) { self = .mf }
else if container.contains(.sf) { self = .sf }
else if container.contains(.sfp) { self = .sfp }
else if container.contains(.sfpp) { self = .sfpp }
else if container.contains(.fp) { self = .fp }
else if container.contains(.rf) { self = .rf }
else if container.contains(.rfz) { self = .rfz }
else if container.contains(.sfz) { self = .sfz }
else if container.contains(.sffz) { self = .sffz }
else if container.contains(.fz) { self = .fz }
else if container.contains(.n) { self = .n }
else if container.contains(.pf) { self = .pf }
else if container.contains(.sfzp) { self = .sfzp }
else {
throw DecodingError.typeMismatch(Dynamic.self, DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Unkown Dynamic value")
)
}
}
}
51 changes: 46 additions & 5 deletions Sources/MusicXML/Complex Types/Dynamics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,35 @@
/// formats related to dynamics. The MusicXML format captures what is in the score, but does not
/// try to be optimal for analysis or synthesis of dynamics.
public struct Dynamics {

// MARK: - Instance Properties

// MARK: Values

public let values: [Dynamic]
public let printStyleAlign: PrintStyleAlign?

// MARK: Attributes Groups

public let printStyleAlign: PrintStyleAlign
public let textDecoration: TextDecoration

// MARK: One-off Attributes

public let placement: AboveBelow?
public let textDecoration: TextDecoration?
public let enclosure: EnclosureShape?
}

extension Dynamics {

public init(values: [Dynamic], printStyleAlign: PrintStyleAlign? = nil, placement: AboveBelow? = nil, textDecoration: TextDecoration? = nil, enclosure: EnclosureShape? = nil) {
// MARK: - Initializers

public init(
_ values: [Dynamic],
printStyleAlign: PrintStyleAlign = PrintStyleAlign(),
placement: AboveBelow? = nil,
textDecoration: TextDecoration = TextDecoration(),
enclosure: EnclosureShape? = nil
) {
self.values = values
self.printStyleAlign = printStyleAlign
self.placement = placement
Expand All @@ -33,6 +55,25 @@ public struct Dynamics {
}
}


extension Dynamics: Equatable { }
extension Dynamics: Codable { }
extension Dynamics: Codable {
public init(from decoder: Decoder) throws {

// Decode values
var values: [Dynamic] = []
var valuesContainer = try decoder.unkeyedContainer()
while !valuesContainer.isAtEnd {
values.append(try valuesContainer.decode(Dynamic.self))
}
self.values = values

// Decode attribute groups
self.printStyleAlign = try PrintStyleAlign(from: decoder)
self.textDecoration = try TextDecoration(from: decoder)

// Decode one-off attributes
let attributesContainer = try decoder.container(keyedBy: CodingKeys.self)
self.placement = try attributesContainer.decodeIfPresent(AboveBelow.self, forKey: .placement)
self.enclosure = try attributesContainer.decodeIfPresent(EnclosureShape.self, forKey: .enclosure)
}
}
27 changes: 23 additions & 4 deletions Sources/MusicXML/Complex Types/FormattedText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

/// The formatted-text type represents a text element with text-formatting attributes.
public struct FormattedText {

// MARK: - Value
public let value: String

// MARK: - Attributes
public let justify: LeftCenterRight?
public let printStyle: PrintStyle
Expand All @@ -22,9 +26,24 @@ public struct FormattedText {
public let enclosure: EnclosureShape?

// MARK: - Elements
public let value: String

public init(justify: LeftCenterRight? = nil, printStyle: PrintStyle = PrintStyle(), hAlign: LeftCenterRight? = nil, vAlign: VAlign? = nil, underline: Int? = nil, overline: Int? = nil, lineThrough: Int? = nil, rotation: Double? = nil, letterSpacing: NumberOrNormal? = nil, lineHeight: NumberOrNormal? = nil, direction: TextDirection? = nil, enclosure: EnclosureShape? = nil, value: String) {

public init(
_ value: String,
justify: LeftCenterRight? = nil,
printStyle: PrintStyle = PrintStyle(),
hAlign: LeftCenterRight? = nil,
vAlign: VAlign? = nil,
underline: Int? = nil,
overline: Int? = nil,
lineThrough: Int? = nil,
rotation: Double? = nil,
letterSpacing: NumberOrNormal? = nil,
lineHeight: NumberOrNormal? = nil,
direction: TextDirection? = nil,
enclosure: EnclosureShape? = nil
) {
self.value = value
self.justify = justify
self.printStyle = printStyle
self.hAlign = hAlign
Expand All @@ -37,7 +56,7 @@ public struct FormattedText {
self.lineHeight = lineHeight
self.direction = direction
self.enclosure = enclosure
self.value = value

}
}

Expand Down Expand Up @@ -78,6 +97,6 @@ extension FormattedText: Codable {

extension FormattedText: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self.init(value: value)
self.init(value)
}
}
22 changes: 19 additions & 3 deletions Sources/MusicXML/Complex Types/Offset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,30 @@
/// If an element within a direction includes a default-x attribute, the offset value will be
/// ignored when determining the appearance of that element.
public struct Offset {

// MARK: - Value

public let value: Divisions
public let sound: Bool

public init(value: Divisions, sound: Bool) {
// MARK: - Attributes

public let sound: Bool?
}

extension Offset {

// MARK: - Initializers

public init(_ value: Divisions, sound: Bool? = nil) {
self.value = value
self.sound = sound
}
}

extension Offset: Equatable { }
extension Offset: Codable { }
extension Offset: Codable {
enum CodingKeys: String, CodingKey {
case value = ""
case sound
}
}
8 changes: 4 additions & 4 deletions Sources/MusicXML/Complex Types/TextDecoration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
/// for text to be underlined, overlined, or struck-through. It extends the CSS version by allow
/// double or triple lines instead of just being on or off.
public struct TextDecoration {
public let underline: Int
public let overline: Int
public let lineThrough: Int
public let underline: Int?
public let overline: Int?
public let lineThrough: Int?

public init(underline: Int, overline: Int, lineThrough: Int) {
public init(underline: Int? = nil, overline: Int? = nil, lineThrough: Int? = nil) {
self.underline = underline
self.overline = overline
self.lineThrough = lineThrough
Expand Down
88 changes: 88 additions & 0 deletions Tests/MusicXMLTests/Complex Types/StabatMaterTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// StabatMaterTests.swift
// MusicXMLTests
//
// Created by James Bean on 10/9/19.
//

import XCTest
import MusicXML
import XMLCoder

class StabatMaterTests: XCTestCase {

func testWordsDirection() throws {
let xml = """
<direction placement="above">
<direction-type>
<words font-weight="bold">Largo</words>
</direction-type>
</direction>
"""
let decoded = try XMLDecoder().decode(Direction.self, from: xml.data(using: .utf8)!)
let expected = Direction(
placement: .above,
directionType: .words(
FormattedText("Largo", printStyle: PrintStyle(font: Font(weight: .bold)))
)
)
XCTAssertEqual(decoded, expected)
}

func testWordsDirectionType() throws {
let xml = """
<direction-type>
<words font-weight="bold">Largo</words>
</direction-type>
"""
let decoded = try XMLDecoder().decode(DirectionType.self, from: xml.data(using: .utf8)!)
let expected = DirectionType.words(
FormattedText("Largo", printStyle: PrintStyle(font: Font(weight: .bold)))
)
XCTAssertEqual(decoded, expected)
}

func testDynamicsDirection() throws {
let xml = """
<direction placement="below">
<direction-type>
<dynamics>
<fp/>
</dynamics>
</direction-type>
<offset>3</offset>
</direction>
"""
let decoded = try XMLDecoder().decode(Direction.self, from: xml.data(using: .utf8)!)
let expected = Direction(
placement: .below,
directionType: .dynamics(Dynamics([.fp])),
offset: Offset(3)
)
XCTAssertEqual(decoded, expected)
}

func testDynamicsDirectionType() throws {
let xml = """
<direction-type>
<dynamics>
<fp/>
</dynamics>
</direction-type>
"""
let decoded = try XMLDecoder().decode(DirectionType.self, from: xml.data(using: .utf8)!)
let expected = DirectionType.dynamics(Dynamics([.fp]))
XCTAssertEqual(decoded, expected)
}

func testDynamics() throws {
let xml = """
<dynamics>
<fp/>
</dynamics>
"""
let decoded = try XMLDecoder().decode(Dynamics.self, from: xml.data(using: .utf8)!)
let expected = Dynamics([.fp])
XCTAssertEqual(decoded, expected)
}
}