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 Enclosure::Shape result{};
45 if (value >= static_cast<uint8_t>(Enclosure::Shape::NoEnclosure) &&
46 value <= static_cast<uint8_t>(Enclosure::Shape::Octogon)) {
47 result = static_cast<Enclosure::Shape>(value);
48 } else {
49 MUSX_UNKNOWN_XML("Invalid <sides> value in XML for enclosure: " + std::to_string(value));
50 }
51 return result;
52}
53
54template <>
55struct FieldPopulator<DrumStaffStyle> : private FieldPopulator<DrumStaff>
56{
57 using FieldPopulator<DrumStaff>::populate;
58};
59
60template <>
61struct FieldPopulator<MarkingCategoryName> : private FieldPopulator<OthersName>
62{
63 using FieldPopulator<OthersName>::populate;
64};
65
66template <>
67struct FieldPopulator<NamePositionAbbreviated> : private FieldPopulator<NamePositioning>
68{
69 using FieldPopulator<NamePositioning>::populate;
70};
71
72template <>
73struct FieldPopulator<NamePositionStyleAbbreviated> : private FieldPopulator<NamePositioning>
74{
75 using FieldPopulator<NamePositioning>::populate;
76};
77
78template <>
79struct FieldPopulator<NamePositionFull> : private FieldPopulator<NamePositioning>
80{
81 using FieldPopulator<NamePositioning>::populate;
82};
83
84template <>
85struct FieldPopulator<NamePositionStyleFull> : private FieldPopulator<NamePositioning>
86{
87 using FieldPopulator<NamePositioning>::populate;
88};
89
90template <>
91struct FieldPopulator<RepeatBackIndividualPositioning> : private FieldPopulator<RepeatIndividualPositioning>
92{
93 using FieldPopulator<RepeatIndividualPositioning>::populate;
94};
95
96template <>
97struct FieldPopulator<RepeatEndingStartIndividualPositioning> : private FieldPopulator<RepeatIndividualPositioning>
98{
99 using FieldPopulator<RepeatIndividualPositioning>::populate;
100};
101
102template <>
103struct FieldPopulator<RepeatEndingTextIndividualPositioning> : private FieldPopulator<RepeatIndividualPositioning>
104{
105 using FieldPopulator<RepeatIndividualPositioning>::populate;
106};
107
108template <>
109struct FieldPopulator<TextRepeatIndividualPositioning> : private FieldPopulator<RepeatIndividualPositioning>
110{
111 using FieldPopulator<RepeatIndividualPositioning>::populate;
112};
113
114template <>
115struct FieldPopulator<StaffListCategoryName> : private FieldPopulator<OthersName>
116{
117 using FieldPopulator<OthersName>::populate;
118};
119
120template <>
121struct FieldPopulator<StaffListCategoryParts> : private FieldPopulator<StaffList>
122{
123 using FieldPopulator<StaffList>::populate;
124};
125
126template <>
127struct FieldPopulator<StaffListCategoryScore> : private FieldPopulator<StaffList>
128{
129 using FieldPopulator<StaffList>::populate;
130};
131
132template <>
133struct FieldPopulator<StaffListRepeatName> : private FieldPopulator<OthersName>
134{
135 using FieldPopulator<OthersName>::populate;
136};
137
138template <>
139struct FieldPopulator<StaffListRepeatParts> : private FieldPopulator<StaffList>
140{
141 using FieldPopulator<StaffList>::populate;
142};
143
144template <>
145struct FieldPopulator<StaffListRepeatPartsForced> : private FieldPopulator<StaffList>
146{
147 using FieldPopulator<StaffList>::populate;
148};
149
150template <>
151struct FieldPopulator<StaffListRepeatScore> : private FieldPopulator<StaffList>
152{
153 using FieldPopulator<StaffList>::populate;
154};
155
156template <>
157struct FieldPopulator<StaffListRepeatScoreForced> : private FieldPopulator<StaffList>
158{
159 using FieldPopulator<StaffList>::populate;
160};
161
162template <>
163struct FieldPopulator<TextExpressionEnclosure> : private FieldPopulator<Enclosure>
164{
165 using FieldPopulator<Enclosure>::populate;
166};
167
168template <>
169struct FieldPopulator<TextRepeatEnclosure> : private FieldPopulator<Enclosure>
170{
171 using FieldPopulator<Enclosure>::populate;
172};
173
174MUSX_RESOLVER_ENTRY(KeyMapArray, {
175 [](const dom::DocumentPtr& document) {
176 auto arrays = document->getOthers()->getArray<KeyMapArray>(SCORE_PARTID);
177 for (const auto& array : arrays) {
178 KeyMapArray* mutableArray = const_cast<KeyMapArray*>(array.get());
179 auto trimSteps = [&](size_t newSize) {
180 while (array->steps.size() > newSize) {
181 const auto& elt = array->steps[array->steps.size() - 1];
182 if (elt->diatonic || elt->hlevel != 0) {
183 break; // itegrity check below will catch this error
184 }
185 mutableArray->steps.pop_back();
186 }
187 };
188 if (auto keyFormat = document->getOthers()->get<others::KeyFormat>(SCORE_PARTID, array->getCmper())) {
189 trimSteps(keyFormat->semitones);
190 if (keyFormat->scaleTones != array->countDiatonicSteps() || keyFormat->semitones != array->steps.size()) {
191 MUSX_INTEGRITY_ERROR("KeyMapArray " + std::to_string(array->getCmper()) + " does not match KeyFormat.");
192 }
193 } else {
194 trimSteps(12);
195 if (array->countDiatonicSteps() != 7 || array->steps.size() != 12) { // default diatonic
196 MUSX_INTEGRITY_ERROR("KeyMapArray " + std::to_string(array->getCmper()) + " has no KeyFormat but does not match default values.");
197 }
198 }
199 }
200 }
201});
202
203MUSX_RESOLVER_ENTRY(LayerAttributes, {
204 [](const dom::DocumentPtr& document) {
205 auto layers = document->getOthers()->getArray<LayerAttributes>(SCORE_PARTID);
206 if (layers.size() != 4) {
207 MUSX_INTEGRITY_ERROR("Expected exactly 4 <layerAtts> elements.");
208 }
209 for (size_t i = 0; i < layers.size(); i++) {
210 if (layers[i]->getCmper() != i) {
211 MUSX_INTEGRITY_ERROR("Expected <layerAtts> elements to have cmper values 0, 1, 2, 3 in order.");
212 }
213 }
214 }
215});
216
217MUSX_RESOLVER_ENTRY(MarkingCategory, {
218 [](const dom::DocumentPtr& document) {
219 auto cats = document->getOthers()->getArray<MarkingCategory>(SCORE_PARTID);
220 for (const auto& cat : cats) {
221 if (cat->categoryType == MarkingCategory::CategoryType::Invalid) {
222 MUSX_INTEGRITY_ERROR("Encountered <markingsCategory> node (cmper " + std::to_string(cat->getCmper()) + ") with no categoryType");
223 }
224 }
225 }
226});
227
228MUSX_RESOLVER_ENTRY(Measure, Measure::checkMeasureCmperSequence);
229
230MUSX_RESOLVER_ENTRY(MultiStaffGroupId, {
231 [](const dom::DocumentPtr& document) {
232 auto parts = document->getOthers()->getArray<PartDefinition>(SCORE_PARTID);
233 for (const auto& part : parts) {
234 auto instGroups = document->getOthers()->getArray<MultiStaffGroupId>(part->getCmper());
235 for (const auto& instance : instGroups) {
236 if (auto group = document->getDetails()->get<details::StaffGroup>(part->getCmper(), document->calcScrollViewCmper(part->getCmper()), instance->staffGroupId)) {
237 details::StaffGroup* mutableGroup = const_cast<details::StaffGroup*>(group.get());
238 mutableGroup->multiStaffGroupId = instance->getCmper();
239 } else if (instance->staffGroupId != 0) {
240 MUSX_INTEGRITY_ERROR("Group " + std::to_string(instance->staffGroupId) + " appears in MultiStaffGroupId "
241 + std::to_string(instance->getCmper()) + " but does not exist.");
242 }
243 }
244 }
245 }
246});
247
248MUSX_RESOLVER_ENTRY(MultiStaffInstrumentGroup, MultiStaffInstrumentGroup::calcAllMultiStaffGroupIds);
249
250MUSX_RESOLVER_ENTRY(Page, Page::calcSystemInfo);
251
252MUSX_RESOLVER_ENTRY(PartDefinition, {
253 [](const dom::DocumentPtr& document) {
254 auto parts = document->getOthers()->getArray<PartDefinition>(SCORE_PARTID);
255 for (const auto& part : parts) {
256 auto partGlobals = document->getOthers()->get<others::PartGlobals>(part->getCmper(), MUSX_GLOBALS_CMPER);
257 if (!partGlobals) {
258 MUSX_INTEGRITY_ERROR("Part " + std::to_string(part->getCmper()) + " has no PartGlobals.");
259 }
260 }
261 }
262});
263
264MUSX_RESOLVER_ENTRY(ShapeExpressionDef, {
265 [](const dom::DocumentPtr& document) {
266 auto exps = document->getOthers()->getArray<ShapeExpressionDef>(SCORE_PARTID);
267 for (const auto& instance : exps) {
268 if (instance->categoryId) {
269 auto markingCat = document->getOthers()->get<MarkingCategory>(instance->getSourcePartId(), instance->categoryId);
270 if (markingCat) {
271 auto mutableMarkingCat = const_cast<MarkingCategory*>(markingCat.get());
272 mutableMarkingCat->shapeExpressions.emplace(instance->getCmper(), instance);
273 } else {
274 util::Logger::log(util::Logger::LogLevel::Info, "Marking category for shape expression " + std::to_string(instance->getCmper()) + " does not exist.");
275 }
276 }
277 }
278 }
279});
280
281MUSX_RESOLVER_ENTRY(Staff, {
282 [](const dom::DocumentPtr& document) {
283 others::Staff::calcAllRuntimeValues<others::Staff>(document);
284 auto instGroups = document->getOthers()->getArray<MultiStaffInstrumentGroup>(SCORE_PARTID);
285 // If no MultiStaffInstrumentGroup records exist, then we need to do this here.
286 if (instGroups.empty()) {
288 }
289 }
290});
291
292MUSX_RESOLVER_ENTRY(StaffStyle, {
293 [](const dom::DocumentPtr& document) {
294 others::Staff::calcAllRuntimeValues<others::StaffStyle>(document);
295 }
296});
297
298MUSX_RESOLVER_ENTRY(TextExpressionDef, {
299 [](const dom::DocumentPtr& document) {
300 auto exps = document->getOthers()->getArray<TextExpressionDef>(SCORE_PARTID);
301 for (const auto& instance : exps) {
302 if (instance->categoryId) {
303 auto markingCat = document->getOthers()->get<MarkingCategory>(instance->getSourcePartId(), instance->categoryId);
304 if (markingCat) {
305 auto mutableMarkingCat = const_cast<MarkingCategory*>(markingCat.get());
306 mutableMarkingCat->textExpressions.emplace(instance->getCmper(), instance);
307 } else {
308 util::Logger::log(util::Logger::LogLevel::Info, "Marking category for text expression " + std::to_string(instance->getCmper()) + " does not exist.");
309 }
310 }
311 }
312 }
313});
314
315} // namespace factory
316} // namespace musx
317
318#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
@ Info
Informational messages for general program operation.
static void log(LogLevel level, const std::string &message)
Logs a message with a specific severity level.
Definition Logger.h:87
Classes in the OthersPool.
Definition CommonClasses.h:48
constexpr Cmper MUSX_GLOBALS_CMPER
The prefs cmper for global variables (used sparingly since Finale 26.2)
Definition Fundamentals.h:78
constexpr Cmper SCORE_PARTID
The part id of the score.
Definition Fundamentals.h:81
std::shared_ptr< Document > DocumentPtr
Shared Document pointer.
Definition DocumentElement.h:35
Provides interfaces and optional implementations for traversing XML.
Definition PugiXmlImpl.h:33
object model for musx file (enigmaxml)
Definition BaseClasses.h:38