MUSX Document Model
Loading...
Searching...
No Matches
FieldPopulatorsOthers.h
1/*
2 * Copyright (C) 2025, Robert Patterson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22#pragma once
23
24#include "musx/dom/BaseClasses.h"
25#include "musx/dom/Others.h"
26#include "musx/dom/Details.h"
27#include "musx/xml/XmlInterface.h"
28#include "FactoryBase.h"
29
30#ifndef DOXYGEN_SHOULD_IGNORE_THIS
31
32namespace musx {
33namespace factory {
34
35using namespace ::musx::xml;
36using namespace ::musx::dom::others;
37
38// Field populators are maintained to populate in the order that nodes are observed to occur in EnigmaXml.
39// The goal is that this may facilitate serialization in the future.
40
41template <>
42inline Enclosure::Shape toEnum<Enclosure::Shape>(const uint8_t& value)
43{
44 if (value >= static_cast<uint8_t>(Enclosure::Shape::NoEnclosure) &&
45 value <= static_cast<uint8_t>(Enclosure::Shape::Octogon)) {
46 return static_cast<Enclosure::Shape>(value);
47 }
48 MUSX_UNKNOWN_XML("Invalid <sides> value in XML for enclosure: " + std::to_string(value));
49 return {};
50}
51
52template <>
53struct FieldPopulator<DrumStaffStyle> : private FieldPopulator<DrumStaff>
54{
55 using FieldPopulator<DrumStaff>::populate;
56};
57
58template <>
59struct FieldPopulator<NamePositionAbbreviated> : private FieldPopulator<NamePositioning>
60{
61 using FieldPopulator<NamePositioning>::populate;
62};
63
64template <>
65struct FieldPopulator<NamePositionStyleAbbreviated> : private FieldPopulator<NamePositioning>
66{
67 using FieldPopulator<NamePositioning>::populate;
68};
69
70template <>
71struct FieldPopulator<NamePositionFull> : private FieldPopulator<NamePositioning>
72{
73 using FieldPopulator<NamePositioning>::populate;
74};
75
76template <>
77struct FieldPopulator<NamePositionStyleFull> : private FieldPopulator<NamePositioning>
78{
79 using FieldPopulator<NamePositioning>::populate;
80};
81
82template <>
83struct FieldPopulator<TextExpressionEnclosure> : private FieldPopulator<Enclosure>
84{
85 using FieldPopulator<Enclosure>::populate;
86};
87
88template <>
89struct FieldPopulator<TextRepeatEnclosure> : private FieldPopulator<Enclosure>
90{
91 using FieldPopulator<Enclosure>::populate;
92};
93
94MUSX_RESOLVER_ENTRY(KeyMapArray, {
95 [](const dom::DocumentPtr& document) {
96 auto arrays = document->getOthers()->getArray<KeyMapArray>(SCORE_PARTID);
97 for (const auto& array : arrays) {
98 auto trimSteps = [&](size_t newSize) {
99 while (array->steps.size() > newSize) {
100 const auto& elt = array->steps[array->steps.size() - 1];
101 if (elt->diatonic || elt->hlevel != 0) {
102 break; // itegrity check below will catch this error
103 }
104 array->steps.pop_back();
105 }
106 };
107 if (auto keyFormat = document->getOthers()->get<others::KeyFormat>(SCORE_PARTID, array->getCmper())) {
108 trimSteps(keyFormat->semitones);
109 if (keyFormat->scaleTones != array->countDiatonicSteps() || keyFormat->semitones != array->steps.size()) {
110 MUSX_INTEGRITY_ERROR("KeyMapArray " + std::to_string(array->getCmper()) + " does not match KeyFormat.");
111 }
112 } else {
113 trimSteps(12);
114 if (array->countDiatonicSteps() != 7 || array->steps.size() != 12) { // default diatonic
115 MUSX_INTEGRITY_ERROR("KeyMapArray " + std::to_string(array->getCmper()) + " has no KeyFormat but does not match default values.");
116 }
117 }
118 }
119 }
120});
121
122MUSX_RESOLVER_ENTRY(LayerAttributes, {
123 [](const dom::DocumentPtr& document) {
124 auto layers = document->getOthers()->getArray<LayerAttributes>(SCORE_PARTID);
125 if (layers.size() != 4) {
126 MUSX_INTEGRITY_ERROR("Expected exactly 4 <layerAtts> elements.");
127 }
128 for (size_t i = 0; i < layers.size(); i++) {
129 if (layers[i]->getCmper() != i) {
130 MUSX_INTEGRITY_ERROR("Expected <layerAtts> elements to have cmper values 0, 1, 2, 3 in order.");
131 }
132 }
133 }
134});
135
136MUSX_RESOLVER_ENTRY(MarkingCategory, {
137 [](const dom::DocumentPtr& document) {
138 auto cats = document->getOthers()->getArray<MarkingCategory>(SCORE_PARTID);
139 for (const auto& cat : cats) {
140 if (cat->categoryType == MarkingCategory::CategoryType::Invalid) {
141 MUSX_INTEGRITY_ERROR("Encountered <markingsCategory> node (cmper " + std::to_string(cat->getCmper()) + ") with no categoryType");
142 }
143 }
144 }
145});
146
147MUSX_RESOLVER_ENTRY(MultiStaffGroupId, {
148 [](const dom::DocumentPtr& document) {
149 auto parts = document->getOthers()->getArray<PartDefinition>(SCORE_PARTID);
150 for (const auto& part : parts) {
151 auto instGroups = document->getOthers()->getArray<MultiStaffGroupId>(part->getCmper());
152 for (const auto& instance : instGroups) {
153 if (auto group = document->getDetails()->get<details::StaffGroup>(part->getCmper(), BASE_SYSTEM_ID, instance->staffGroupId)) {
154 group->multiStaffGroupId = instance->getCmper();
155 } else {
156 MUSX_INTEGRITY_ERROR("Group " + std::to_string(instance->staffGroupId) + " appears in MultiStaffGroupId "
157 + std::to_string(instance->getCmper()) + " but does not exist.");
158 }
159 }
160 }
161 }
162});
163
164MUSX_RESOLVER_ENTRY(MultiStaffInstrumentGroup, MultiStaffInstrumentGroup::calcAllMultiStaffGroupIds);
165
166MUSX_RESOLVER_ENTRY(Page, Page::calcSystemInfo);
167
168MUSX_RESOLVER_ENTRY(ShapeExpressionDef, {
169 [](const dom::DocumentPtr& document) {
170 auto exps = document->getOthers()->getArray<ShapeExpressionDef>(SCORE_PARTID);
171 for (const auto& instance : exps) {
172 if (instance->categoryId) {
173 auto markingCat = document->getOthers()->get<MarkingCategory>(instance->getPartId(), instance->categoryId);
174 if (!markingCat) {
175 MUSX_INTEGRITY_ERROR("Marking category for shape expression " + std::to_string(instance->getCmper()) + " does not exist.");
176 }
177 markingCat->shapeExpressions.emplace(instance->getCmper(), instance);
178 }
179 }
180 }
181});
182
183MUSX_RESOLVER_ENTRY(Staff, {
184 [](const dom::DocumentPtr& document) {
185 others::Staff::calcAllRuntimeValues<others::Staff>(document);
186 auto instGroups = document->getOthers()->getArray<MultiStaffInstrumentGroup>(SCORE_PARTID);
187 // If no MultiStaffInstrumentGroup records exist, then we need to do this here.
188 if (instGroups.empty()) {
190 }
191 }
192});
193
194MUSX_RESOLVER_ENTRY(StaffStyle, {
195 [](const dom::DocumentPtr& document) {
196 others::Staff::calcAllRuntimeValues<others::StaffStyle>(document);
197 }
198});
199
200MUSX_RESOLVER_ENTRY(TextExpressionDef, {
201 [](const dom::DocumentPtr& document) {
202 auto exps = document->getOthers()->getArray<TextExpressionDef>(SCORE_PARTID);
203 for (const auto& instance : exps) {
204 if (instance->categoryId) {
205 auto markingCat = document->getOthers()->get<MarkingCategory>(instance->getPartId(), instance->categoryId);
206 if (!markingCat) {
207 MUSX_INTEGRITY_ERROR("Marking category for text expression " + std::to_string(instance->getCmper()) + " does not exist.");
208 }
209 markingCat->textExpressions.emplace(instance->getCmper(), instance);
210 }
211 }
212 }
213});
214
215} // namespace factory
216} // namespace musx
217
218#endif // DOXYGEN_SHOULD_IGNORE THIS
static void calcAllAutoNumberValues(const DocumentPtr &document)
Get the auto-numbering value for this staff, if applicable.
Definition Implementations.cpp:3055
Classes in the OthersPool.
Definition CommonClasses.h:43
constexpr Cmper SCORE_PARTID
The part id of the score.
Definition Fundamentals.h:79
std::shared_ptr< Document > DocumentPtr
Shared Document pointer.
Definition BaseClasses.h:54
constexpr Cmper BASE_SYSTEM_ID
The base system cmper that gives a list of all available staves and their score order (others::Instru...
Definition Fundamentals.h:80
Provides interfaces and optional implementations for traversing XML.
Definition PugiXmlImpl.h:33
object model for musx file (enigmaxml)
Definition BaseClasses.h:35