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<MarkingCategoryName> : private FieldPopulator<OthersName>
60{
61 using FieldPopulator<OthersName>::populate;
62};
63
64template <>
65struct FieldPopulator<NamePositionAbbreviated> : private FieldPopulator<NamePositioning>
66{
67 using FieldPopulator<NamePositioning>::populate;
68};
69
70template <>
71struct FieldPopulator<NamePositionStyleAbbreviated> : private FieldPopulator<NamePositioning>
72{
73 using FieldPopulator<NamePositioning>::populate;
74};
75
76template <>
77struct FieldPopulator<NamePositionFull> : private FieldPopulator<NamePositioning>
78{
79 using FieldPopulator<NamePositioning>::populate;
80};
81
82template <>
83struct FieldPopulator<NamePositionStyleFull> : private FieldPopulator<NamePositioning>
84{
85 using FieldPopulator<NamePositioning>::populate;
86};
87
88template <>
89struct FieldPopulator<StaffListCategoryName> : private FieldPopulator<OthersName>
90{
91 using FieldPopulator<OthersName>::populate;
92};
93
94template <>
95struct FieldPopulator<StaffListCategoryParts> : private FieldPopulator<StaffList>
96{
97 using FieldPopulator<StaffList>::populate;
98};
99
100template <>
101struct FieldPopulator<StaffListCategoryScore> : private FieldPopulator<StaffList>
102{
103 using FieldPopulator<StaffList>::populate;
104};
105
106template <>
107struct FieldPopulator<StaffListRepeatName> : private FieldPopulator<OthersName>
108{
109 using FieldPopulator<OthersName>::populate;
110};
111
112template <>
113struct FieldPopulator<StaffListRepeatParts> : private FieldPopulator<StaffList>
114{
115 using FieldPopulator<StaffList>::populate;
116};
117
118template <>
119struct FieldPopulator<StaffListRepeatPartsForced> : private FieldPopulator<StaffList>
120{
121 using FieldPopulator<StaffList>::populate;
122};
123
124template <>
125struct FieldPopulator<StaffListRepeatScore> : private FieldPopulator<StaffList>
126{
127 using FieldPopulator<StaffList>::populate;
128};
129
130template <>
131struct FieldPopulator<StaffListRepeatScoreForced> : private FieldPopulator<StaffList>
132{
133 using FieldPopulator<StaffList>::populate;
134};
135
136template <>
137struct FieldPopulator<TextExpressionEnclosure> : private FieldPopulator<Enclosure>
138{
139 using FieldPopulator<Enclosure>::populate;
140};
141
142template <>
143struct FieldPopulator<TextRepeatEnclosure> : private FieldPopulator<Enclosure>
144{
145 using FieldPopulator<Enclosure>::populate;
146};
147
148MUSX_RESOLVER_ENTRY(KeyMapArray, {
149 [](const dom::DocumentPtr& document) {
150 auto arrays = document->getOthers()->getArray<KeyMapArray>(SCORE_PARTID);
151 for (const auto& array : arrays) {
152 KeyMapArray* mutableArray = const_cast<KeyMapArray*>(array.get());
153 auto trimSteps = [&](size_t newSize) {
154 while (array->steps.size() > newSize) {
155 const auto& elt = array->steps[array->steps.size() - 1];
156 if (elt->diatonic || elt->hlevel != 0) {
157 break; // itegrity check below will catch this error
158 }
159 mutableArray->steps.pop_back();
160 }
161 };
162 if (auto keyFormat = document->getOthers()->get<others::KeyFormat>(SCORE_PARTID, array->getCmper())) {
163 trimSteps(keyFormat->semitones);
164 if (keyFormat->scaleTones != array->countDiatonicSteps() || keyFormat->semitones != array->steps.size()) {
165 MUSX_INTEGRITY_ERROR("KeyMapArray " + std::to_string(array->getCmper()) + " does not match KeyFormat.");
166 }
167 } else {
168 trimSteps(12);
169 if (array->countDiatonicSteps() != 7 || array->steps.size() != 12) { // default diatonic
170 MUSX_INTEGRITY_ERROR("KeyMapArray " + std::to_string(array->getCmper()) + " has no KeyFormat but does not match default values.");
171 }
172 }
173 }
174 }
175});
176
177MUSX_RESOLVER_ENTRY(LayerAttributes, {
178 [](const dom::DocumentPtr& document) {
179 auto layers = document->getOthers()->getArray<LayerAttributes>(SCORE_PARTID);
180 if (layers.size() != 4) {
181 MUSX_INTEGRITY_ERROR("Expected exactly 4 <layerAtts> elements.");
182 }
183 for (size_t i = 0; i < layers.size(); i++) {
184 if (layers[i]->getCmper() != i) {
185 MUSX_INTEGRITY_ERROR("Expected <layerAtts> elements to have cmper values 0, 1, 2, 3 in order.");
186 }
187 }
188 }
189});
190
191MUSX_RESOLVER_ENTRY(MarkingCategory, {
192 [](const dom::DocumentPtr& document) {
193 auto cats = document->getOthers()->getArray<MarkingCategory>(SCORE_PARTID);
194 for (const auto& cat : cats) {
195 if (cat->categoryType == MarkingCategory::CategoryType::Invalid) {
196 MUSX_INTEGRITY_ERROR("Encountered <markingsCategory> node (cmper " + std::to_string(cat->getCmper()) + ") with no categoryType");
197 }
198 }
199 }
200});
201
202MUSX_RESOLVER_ENTRY(MultiStaffGroupId, {
203 [](const dom::DocumentPtr& document) {
204 auto parts = document->getOthers()->getArray<PartDefinition>(SCORE_PARTID);
205 for (const auto& part : parts) {
206 auto instGroups = document->getOthers()->getArray<MultiStaffGroupId>(part->getCmper());
207 for (const auto& instance : instGroups) {
208 if (auto group = document->getDetails()->get<details::StaffGroup>(part->getCmper(), BASE_SYSTEM_ID, instance->staffGroupId)) {
209 details::StaffGroup* mutableGroup = const_cast<details::StaffGroup*>(group.get());
210 mutableGroup->multiStaffGroupId = instance->getCmper();
211 } else if (instance->staffGroupId != 0) {
212 MUSX_INTEGRITY_ERROR("Group " + std::to_string(instance->staffGroupId) + " appears in MultiStaffGroupId "
213 + std::to_string(instance->getCmper()) + " but does not exist.");
214 }
215 }
216 }
217 }
218});
219
220MUSX_RESOLVER_ENTRY(MultiStaffInstrumentGroup, MultiStaffInstrumentGroup::calcAllMultiStaffGroupIds);
221
222MUSX_RESOLVER_ENTRY(Page, Page::calcSystemInfo);
223
224MUSX_RESOLVER_ENTRY(PartDefinition, {
225 [](const dom::DocumentPtr& document) {
226 auto parts = document->getOthers()->getArray<PartDefinition>(SCORE_PARTID);
227 for (const auto& part : parts) {
228 auto partGlobals = document->getOthers()->get<others::PartGlobals>(part->getCmper(), MUSX_GLOBALS_CMPER);
229 if (!partGlobals) {
230 MUSX_INTEGRITY_ERROR("Part " + std::to_string(part->getCmper()) + " has no PartGlobals.");
231 }
232 }
233 }
234});
235
236MUSX_RESOLVER_ENTRY(ShapeExpressionDef, {
237 [](const dom::DocumentPtr& document) {
238 auto exps = document->getOthers()->getArray<ShapeExpressionDef>(SCORE_PARTID);
239 for (const auto& instance : exps) {
240 if (instance->categoryId) {
241 auto markingCat = document->getOthers()->get<MarkingCategory>(instance->getSourcePartId(), instance->categoryId);
242 if (!markingCat) {
243 MUSX_INTEGRITY_ERROR("Marking category for shape expression " + std::to_string(instance->getCmper()) + " does not exist.");
244 }
245 auto mutableMarkingCat = const_cast<MarkingCategory*>(markingCat.get());
246 mutableMarkingCat->shapeExpressions.emplace(instance->getCmper(), instance);
247 }
248 }
249 }
250});
251
252MUSX_RESOLVER_ENTRY(Staff, {
253 [](const dom::DocumentPtr& document) {
254 others::Staff::calcAllRuntimeValues<others::Staff>(document);
255 auto instGroups = document->getOthers()->getArray<MultiStaffInstrumentGroup>(SCORE_PARTID);
256 // If no MultiStaffInstrumentGroup records exist, then we need to do this here.
257 if (instGroups.empty()) {
259 }
260 }
261});
262
263MUSX_RESOLVER_ENTRY(StaffStyle, {
264 [](const dom::DocumentPtr& document) {
265 others::Staff::calcAllRuntimeValues<others::StaffStyle>(document);
266 }
267});
268
269MUSX_RESOLVER_ENTRY(TextExpressionDef, {
270 [](const dom::DocumentPtr& document) {
271 auto exps = document->getOthers()->getArray<TextExpressionDef>(SCORE_PARTID);
272 for (const auto& instance : exps) {
273 if (instance->categoryId) {
274 auto markingCat = document->getOthers()->get<MarkingCategory>(instance->getSourcePartId(), instance->categoryId);
275 if (!markingCat) {
276 MUSX_INTEGRITY_ERROR("Marking category for text expression " + std::to_string(instance->getCmper()) + " does not exist.");
277 }
278 auto mutableMarkingCat = const_cast<MarkingCategory*>(markingCat.get());
279 mutableMarkingCat->textExpressions.emplace(instance->getCmper(), instance);
280 }
281 }
282 }
283});
284
285} // namespace factory
286} // namespace musx
287
288#endif // DOXYGEN_SHOULD_IGNORE THIS
static void calcAllAutoNumberValues(const DocumentPtr &document)
Get the auto-numbering value for this staff, if applicable.
Definition Staff.cpp:37
Classes in the OthersPool.
Definition CommonClasses.h:43
constexpr Cmper MUSX_GLOBALS_CMPER
The prefs cmper for global variables (used sparingly since Finale 26.2)
Definition Fundamentals.h:76
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:55
constexpr Cmper BASE_SYSTEM_ID
The base system cmper that gives a list of all available staves and their score order (others::StaffU...
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:36