Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/include/mx/api/AccordionRegistrationData.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "mx/api/ApiCommon.h"
#include "mx/api/ColorData.h"
#include "mx/api/FontData.h"
#include "mx/api/Id.h"
#include "mx/api/PositionData.h"

#include <optional>
Expand All @@ -31,7 +32,7 @@ class AccordionRegistrationData
PositionData positionData;
FontData fontData;
std::optional<ColorData> color;
std::optional<std::string> id;
std::optional<Id> id;

AccordionRegistrationData() : high{false}, middle{}, low{false}, positionData{}, fontData{}, color{}, id{}
{
Expand Down
11 changes: 0 additions & 11 deletions src/include/mx/api/ApiCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,6 @@ constexpr int NUMBER_LEVEL_UNSPECIFIED = -1; // MusicXML 'number' attributes, e.
constexpr int VALUE_UNSPECIFIED = -1; // other absent-able ints, e.g. DirectionData::voice
constexpr Double DOUBLE_UNSPECIFIED = -1.0; // absent-able doubles, e.g. StaffData::staffSize

// MusicXML lets most elements carry an optional id attribute, a name that identifies that one
// element within the document. Software uses it to point at a particular note, measure, or
// marking -- to line playback up with the score, to hang an annotation on a note, or to link one
// file to another. Every mx::api type that models such an element has an `id` member. Leave it
// empty and no id attribute is written.
//
// An id must be unique within the document and must follow the XML name rules: a letter or an
// underscore first, then letters, digits, dots, hyphens, or underscores. mx repairs an id that
// breaks the name rules when it writes the file, but it does not check uniqueness, so an id you
// invent must not collide with one already in the score.

// Intentional ternary: absent-able bools use Bool::unspecified, not std::optional<bool>.
// See "mx::api conventions" in AGENTS.md.
enum class Bool
Expand Down
5 changes: 3 additions & 2 deletions src/include/mx/api/BarlineData.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "mx/api/ApiCommon.h"
#include "mx/api/Id.h"
#include "mx/api/PositionData.h"

#include <optional>
Expand Down Expand Up @@ -116,8 +117,8 @@ class BarlineData
RepeatWinged repeatWinged;
HorizontalAlignment location;

// The <barline> element's id attribute (see ApiCommon.h).
std::optional<std::string> id;
// The <barline> element's id attribute (see Id.h).
std::optional<Id> id;

BarlineData()
: tickTimePosition{0}, barlineType{BarlineType::normal}, ending{}, repeat{false}, repeatTimes{0},
Expand Down
5 changes: 3 additions & 2 deletions src/include/mx/api/ClefData.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "mx/api/ApiCommon.h"
#include "mx/api/Id.h"

#include <optional>
#include <string>
Expand Down Expand Up @@ -71,8 +72,8 @@ class ClefData
// unspecified -> omit the attribute, yes/no -> write print-object verbatim.
Bool printObject;

// The <clef> element's id attribute (see ApiCommon.h).
std::optional<std::string> id;
// The <clef> element's id attribute (see Id.h).
std::optional<Id> id;

std::string toString() const;

Expand Down
11 changes: 6 additions & 5 deletions src/include/mx/api/CodaData.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
#include "mx/api/ApiCommon.h"
#include "mx/api/ColorData.h"
#include "mx/api/FontData.h"
#include "mx/api/Id.h"
#include "mx/api/PositionData.h"

#include <optional>
#include <string>

namespace mx
Expand All @@ -28,12 +30,12 @@ class CodaData
ColorData colorData;
bool isSmuflSpecified;
std::string smufl;
bool isIdSpecified;
std::string id;

// The <coda> element's id attribute (see Id.h).
std::optional<Id> id;

CodaData()
: positionData{}, fontData{}, isColorSpecified{false}, colorData{}, isSmuflSpecified{false}, smufl{},
isIdSpecified{false}, id{}
: positionData{}, fontData{}, isColorSpecified{false}, colorData{}, isSmuflSpecified{false}, smufl{}, id{}
{
}
};
Expand All @@ -45,7 +47,6 @@ MXAPI_EQUALS_MEMBER(isColorSpecified)
MXAPI_EQUALS_MEMBER(colorData)
MXAPI_EQUALS_MEMBER(isSmuflSpecified)
MXAPI_EQUALS_MEMBER(smufl)
MXAPI_EQUALS_MEMBER(isIdSpecified)
MXAPI_EQUALS_MEMBER(id)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(CodaData);
Expand Down
13 changes: 7 additions & 6 deletions src/include/mx/api/CurveData.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "mx/api/ApiCommon.h"
#include "mx/api/ColorData.h"
#include "mx/api/Id.h"
#include "mx/api/LineData.h"
#include "mx/api/PositionData.h"
#include "mx/api/SpannerNumber.h"
Expand Down Expand Up @@ -104,8 +105,8 @@ struct CurveStart
ColorData colorData;

// The id attribute of the element this curve end is written as -- <slur> for a slur,
// <tied> for a tie (see ApiCommon.h).
std::optional<std::string> id;
// <tied> for a tie (see Id.h).
std::optional<Id> id;

CurveStart(CurveType inCurveType)
: curveType{inCurveType}, number{}, curvePoints{}, curveOrientation{CurveOrientation::unspecified},
Expand All @@ -127,8 +128,8 @@ struct CurveContinue
double bezierOffset2;

// The id attribute of the element this curve end is written as -- <slur> for a slur,
// <tied> for a tie (see ApiCommon.h).
std::optional<std::string> id;
// <tied> for a tie (see Id.h).
std::optional<Id> id;

CurveContinue(CurveType inCurveType)
: curveType{inCurveType}, number{}, curvePoints{}, isBezierX2Specified{false}, bezierX2{0.0},
Expand All @@ -144,8 +145,8 @@ struct CurveStop
CurvePoints curvePoints;

// The id attribute of the element this curve end is written as -- <slur> for a slur,
// <tied> for a tie (see ApiCommon.h).
std::optional<std::string> id;
// <tied> for a tie (see Id.h).
std::optional<Id> id;

CurveStop(CurveType inCurveType) : curveType{inCurveType}, number{}, curvePoints{}, id{}
{
Expand Down
5 changes: 3 additions & 2 deletions src/include/mx/api/DampData.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "mx/api/ApiCommon.h"
#include "mx/api/ColorData.h"
#include "mx/api/FontData.h"
#include "mx/api/Id.h"
#include "mx/api/PositionData.h"

#include <optional>
Expand All @@ -26,7 +27,7 @@ class DampData
PositionData positionData;
FontData fontData;
std::optional<ColorData> color;
std::optional<std::string> id;
std::optional<Id> id;

DampData() : positionData{}, fontData{}, color{}, id{}
{
Expand All @@ -49,7 +50,7 @@ class DampAllData
PositionData positionData;
FontData fontData;
std::optional<ColorData> color;
std::optional<std::string> id;
std::optional<Id> id;

DampAllData() : positionData{}, fontData{}, color{}, id{}
{
Expand Down
5 changes: 3 additions & 2 deletions src/include/mx/api/DirectionData.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "mx/api/ChordData.h"
#include "mx/api/DirectionChoice.h"
#include "mx/api/FiguredBassData.h"
#include "mx/api/Id.h"
#include "mx/api/SoundData.h"

#include <optional>
Expand Down Expand Up @@ -81,8 +82,8 @@ struct DirectionData
// serialize as their own <figured-bass> elements, not as direction-type content.
std::vector<FiguredBassData> figuredBasses;

// The <direction> element's id attribute (see ApiCommon.h).
std::optional<std::string> id;
// The <direction> element's id attribute (see Id.h).
std::optional<Id> id;

DirectionData()
: tickTimePosition{0}, placement{Placement::unspecified}, systemRelation{SystemRelation::unspecified}, offset{},
Expand Down
3 changes: 2 additions & 1 deletion src/include/mx/api/EyeglassesData.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "mx/api/ApiCommon.h"
#include "mx/api/ColorData.h"
#include "mx/api/FontData.h"
#include "mx/api/Id.h"
#include "mx/api/PositionData.h"

#include <optional>
Expand All @@ -27,7 +28,7 @@ class EyeglassesData
PositionData positionData;
FontData fontData;
std::optional<ColorData> color;
std::optional<std::string> id;
std::optional<Id> id;

EyeglassesData() : positionData{}, fontData{}, color{}, id{}
{
Expand Down
5 changes: 3 additions & 2 deletions src/include/mx/api/FiguredBassData.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "mx/api/ApiCommon.h"
#include "mx/api/Id.h"

#include <optional>
#include <string>
Expand Down Expand Up @@ -49,8 +50,8 @@ class FiguredBassData
// The optional <duration>, in ticks. A value less than 0 means 'unspecified' (no duration child).
int durationTimeTicks;

// The <figured-bass> element's id attribute (see ApiCommon.h).
std::optional<std::string> id;
// The <figured-bass> element's id attribute (see Id.h).
std::optional<Id> id;

FiguredBassData() : figures{}, parentheses{Bool::unspecified}, durationTimeTicks{VALUE_UNSPECIFIED}, id{}
{
Expand Down
3 changes: 2 additions & 1 deletion src/include/mx/api/HarpPedalsData.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "mx/api/ApiCommon.h"
#include "mx/api/ColorData.h"
#include "mx/api/FontData.h"
#include "mx/api/Id.h"
#include "mx/api/PitchData.h"
#include "mx/api/PositionData.h"

Expand Down Expand Up @@ -57,7 +58,7 @@ class HarpPedalsData
PositionData positionData;
FontData fontData;
std::optional<ColorData> color;
std::optional<std::string> id;
std::optional<Id> id;

HarpPedalsData() : pedalTunings{}, positionData{}, fontData{}, color{}, id{}
{
Expand Down
103 changes: 103 additions & 0 deletions src/include/mx/api/Id.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// MusicXML Class Library
// Copyright (c) by Matthew James Briggs
// Distributed under the MIT License

#pragma once

#include "mx/api/ApiCommon.h"

#include <cstddef>
#include <functional>
#include <memory>
#include <string>

namespace mx
{
namespace api
{

// The id attribute of a MusicXML element. An id is a name that identifies one element within the
// document. Software uses it to point at a particular note, measure, or marking -- to line playback
// up with the score, to hang an annotation on a note, or to link one file to another.
//
// An id must follow the XML name rules. The first character is a letter or an underscore. The rest
// are letters, digits, dots, hyphens, or underscores. Building an Id scrubs out anything else, so
// an id that breaks the rules cannot exist. Text with nothing usable left, empty text included,
// becomes "X". Building the Id is the only place this happens: mx writes the id exactly as the Id
// holds it.
//
// Scrubbing is silent. To find out whether your text was already a legal id, compare it with the
// id you built:
//
// const auto id = Id{myText};
// if (id.value() != myText)
// {
// // myText was not a legal id; decide what to do about it
// }
//
// An id must also be unique within the document. Uniqueness is a property of the whole score, not
// of one name, so Id cannot check it. An id you invent must not collide with one already in the
// score.
//
// To write no id attribute at all, leave the std::optional that holds the Id empty.
//
// Copy, assign, compare, sort, and hash an Id the way you would any other value. Two Ids are equal
// when their text is equal, and std::hash is specialized for Id, so an Id works as a key in both
// std::map and std::unordered_map.
class Id
{
public:
Id(std::string text);
Id(const char *text);

Id(const Id &other);
Id(Id &&other) noexcept;
Id &operator=(const Id &other);
Id &operator=(Id &&other) noexcept;
~Id();

const std::string &value() const;

bool operator==(const Id &other) const;
bool operator<(const Id &other) const;

private:
// Holds the same type mx writes the attribute with, which is why the text cannot be scrubbed a
// second time. The definition is private to mx.
class Impl;
explicit Id(std::shared_ptr<const Impl> impl);
friend struct IdAccess;

std::shared_ptr<const Impl> myImpl;
};

MXAPI_NOT_EQUALS_AND_VECTORS(Id);

inline bool operator>(const Id &lhs, const Id &rhs)
{
return rhs < lhs;
}

inline bool operator<=(const Id &lhs, const Id &rhs)
{
return !(rhs < lhs);
}

inline bool operator>=(const Id &lhs, const Id &rhs)
{
return !(lhs < rhs);
}

} // namespace api
} // namespace mx

namespace std
{
template <> struct hash<mx::api::Id>
{
std::size_t operator()(const mx::api::Id &id) const noexcept
{
return std::hash<std::string>{}(id.value());
}
};
} // namespace std
3 changes: 2 additions & 1 deletion src/include/mx/api/ImageData.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "mx/api/ApiCommon.h"
#include "mx/api/Id.h"
#include "mx/api/PositionData.h"

#include <optional>
Expand All @@ -29,7 +30,7 @@ class ImageData
std::optional<double> height;
std::optional<double> width;
PositionData positionData;
std::optional<std::string> id;
std::optional<Id> id;

ImageData() : source{}, type{}, height{}, width{}, positionData{}, id{}
{
Expand Down
5 changes: 3 additions & 2 deletions src/include/mx/api/KeyData.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#pragma once
#include "mx/api/ApiCommon.h"
#include "mx/api/Id.h"
#include "mx/api/KeyComponent.h"

#include <optional>
Expand Down Expand Up @@ -124,8 +125,8 @@ struct KeyData
// alterations. When custom is non-empty, then fifths and mode are ignored.
std::vector<KeyComponent> nonTraditional;

// The <key> element's id attribute (see ApiCommon.h).
std::optional<std::string> id;
// The <key> element's id attribute (see Id.h).
std::optional<Id> id;

KeyData()
: fifths{0}, cancel{0}, cancelLocation{CancelLocation::unspecified}, mode{KeyMode::unspecified},
Expand Down
Loading
Loading