From 5232b7f32ecf174a2aa893991a04edd08b1bfe3e Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Fri, 22 May 2026 11:35:31 +0200 Subject: [PATCH 1/4] docs(gen): add revgen + M2 project planning, logs, and triage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the docs/ai/projects/gen project directory used to drive the reverse-engineering of the mx/core codegen pipeline and the subsequent fix-up work tracked as Milestone 2 (fix-gen). Project structure: - index.md, plan.md, state.md - milestone roadmap (M1 revgen → M2 fix-gen → M3 coverage → M4 better-gen → M5 mxml4-types) and current session pointers. - log.md plus log-archive/ - append-only session log for the current phase, with prior phases rotated into log-archive/. - m2/ - Milestone 2 working materials: - triage.md classifies every non-generated change in the "src: issues caused by revgen" commit as BUG / BENIGN / WEIRD with severity. - triage-tests.md clusters the 62 make test-all failures left after Issues A-F into root-cause groups R1-R7 and assigns each test. - revgen-issues.diff freezes the raw diff under triage. - test-all-*.log files freeze make test-all output at each iteration baseline (R2, R4 i1-i6, R5, D3+D1 candidate, D4 candidate - the last being the 0-failure M2 exit). - strategy-subagent.md - subagent-per-iteration loop and prompt template used to chew through the M2 failing-test backlog. - design/ - current-state design docs: - forensics.md analyzes the original Ruby codegen and the resulting mx/core patterns. - overrides.md catalogs overrides by taxonomy (RULE/EXC/FIX/SUBSTRATE/ANOMALY). Also drops docs/sounds.xml (MusicXML 4.0 sounds reference data) and archives the older revgen scoring doc under design/.archived/, since make test-all pass/fail - not the diff penalty - is the fitness function from M2 onward. --- docs/ai/projects/gen/design/forensics.md | 436 ----------- docs/ai/projects/gen/design/overrides.md | 186 ----- docs/ai/projects/gen/design/scoring.md | 203 ----- docs/ai/projects/gen/index.md | 72 +- docs/ai/projects/gen/iteration-notes.md | 425 ----------- docs/ai/projects/gen/log.md | 140 +++- docs/ai/projects/gen/plan.md | 73 +- docs/ai/projects/gen/state.md | 66 +- docs/sounds.xml | 920 +++++++++++++++++++++++ 9 files changed, 1101 insertions(+), 1420 deletions(-) delete mode 100644 docs/ai/projects/gen/design/forensics.md delete mode 100644 docs/ai/projects/gen/design/overrides.md delete mode 100644 docs/ai/projects/gen/design/scoring.md delete mode 100644 docs/ai/projects/gen/iteration-notes.md create mode 100644 docs/sounds.xml diff --git a/docs/ai/projects/gen/design/forensics.md b/docs/ai/projects/gen/design/forensics.md deleted file mode 100644 index cab45076..00000000 --- a/docs/ai/projects/gen/design/forensics.md +++ /dev/null @@ -1,436 +0,0 @@ -# Codegen Forensics - -This document records what we learned by statically analyzing (a) the historical Ruby codegen -scripts in `gen/version-a/` and (b) the C++ output they produced in `src/private/mx/core/`. The goal -is to understand what a new generator must reproduce, what was bespoke human judgment, and where the -original process had gaps. - -* * * - -## Part 1: Ruby Pipeline Analysis (`gen/version-a/`) - -### Pipeline Overview - -The pipeline was **not** a clean XSD-to-C++ pipeline. It operated in four historical phases with -significant human intervention between each, and it cannot be re-run today. - -**Phase 1 - Original hand-built monolith (lost):** A single massive `Elements.h` (`~37,000+` lines) -and matching `Elements.cpp` contained every class declaration and definition. Brute force, -iteratively generated with Ruby scripts that are lost. - -**Phase 2 - Splitting:** `writeHFiles.rb` and `writeElementsCppFiles.rb` read the monolithic files -and emitted one `.h`/`.cpp` pair per class into `src/mx/core/elements/`. The split was driven by -line-range detection via `parseElementsH.rb` (scanned for `class X : public ElementInterface` and -matching `};`) and `parseElementsCpp.rb` (scanned for `ClassName::method` patterns, hardcoded -terminal line 37340). - -**Phase 3 - Patching `fromXElement`:** `fromXElement.rb` and `attributes.rb`/`attributesLoop.rb` -read the split files and surgically inserted `fromXElement`/`fromXElementImpl` method bodies. The 14 -`*Group.rb` scripts each generated a group element's `fromXElement` body to stdout; those snippets -were copy-pasted into the relevant `.cpp` files by hand. - -**Phase 4 - Ad-hoc maintenance:** `adjustElementsH.rb`, `adjustElementsCpp.rb`, -`findFilesWithBadCopyright.rb`, `smuflHeader.rb`, `parseElementsHOrder.rb` were run one-off as -needed. - -The `mxdeploy*.rb` scripts are unrelated to codegen - they built iOS/macOS frameworks for a -different app. - -### XSD Parsing - -`parseXsd.rb` is a stub. It uses Ruby's REXML to dump `xs:element` names from `docs/musicxml.xsd` to -stdout and does nothing else. It was never integrated into the emit pipeline. The scripts that -generated the monolithic Elements.h/cpp have been lost. - -### Code Emission - -**Header splitting (`writeHFiles.rb`):** -- Detects class boundaries by scanning for `class X : public ElementInterface` / - `struct X : public AttributesInterface` and matching `};` -- Per-class `.h` file: copyright banner, `#pragma once`, conditional includes (only Decimals.h, - Enums.h, Integers.h, Strings.h), forward declarations, then the class body verbatim -- Include selection uses `isSymbolFound.rb`: regex `(\s#{symbol}\s|\s#{symbol}&)` - only matches - tokens surrounded by whitespace or followed by `&`. Misses tokens in template args, after `*`, - after `(`, or at line start - -**CPP splitting (`writeElementsCppFiles.rb`):** -- Groups function bodies by class name (scanned from `ClassName::methodName` patterns) -- Hardcodes line 37340 as the end of `Elements.cpp` - -**Attributes `fromXElement` (`attributes.rb`, `attributesLoop.rb`):** -- Parses `public:` section of a split `.h` to build member list -- Derives XML attribute name from camelCase member name via - `name.split(/([[:upper:]][[:lower:]]*)/).join("-").downcase` -- Pairs `has___` booleans with members to determine optionality -- Emits `parseAttribute(...)` calls; enum members get `&parseEnumType` function pointer -- `attributesLoop.rb` batch-applies to all `*Attributes.h` files - -**Element `fromXElement` (`fromXElement.rb`):** -- Classifies members by naming convention: `myAttributes`, `myHas___` flag, `___Set`, - `___GroupPtr/Set`, `___ChoicePtr/Set` -- Emits five body patterns based on classification: - 1. Empty element (no members): `return true;` - 2. Attributes only: delegate to `myAttributes->fromXElement(...)` - 3. Value + attributes: parse attributes, then `myValue.parse(xelement.getValue())` - 4. Value only: `myValue.parse(xelement.getValue())` - 5. Full element: iterate child elements, dispatch on element name string - -**Group scripts (`*Group.rb`):** -- 14 standalone scripts, each nearly identical, differing only in class name and members array -- Each member is hand-enumerated with XML element name string, C++ class name, and cardinality - (`required`, `optional`, or `collection`) -- All write to stdout; copy-pasted into `.cpp` files by hand - -### Bespoke Touches in the Ruby Scripts - -1. **`Properties` class name override.** The XSD element is named `attributes`, which clashes with - the C++ keyword and member naming conventions. The C++ class is `Properties`; its `streamName` - outputs `"attributes"`. This is the only element where C++ class name does not match XSD element - name. Noted in `todo.txt`. - -2. **`EditorialVoiceDirectionGroup` manual registration.** Added by hand to `listElementClasses.rb` - (`element_classes << "EditorialVoiceDirectionGroup"`) because it doesn't inherit from - `ElementInterface` in the way the parser could auto-detect. - -3. **Hardcoded line 37340** in `parseElementsCpp.rb` as the last line of the monolithic - `Elements.cpp`. Stale if the file ever changed. - -4. **Six hardcoded "special classes"** in `listSpecialClasses.rb`: `Color`, `Date`, `FontSize`, - `NumberOrNormal`, `PositiveIntegerOrEmpty`, `YesNoNumber`. These live in `core/` not `elements/` - and required manual registration. `writeHFiles.rb` also hardcodes - `EmptyPrintObjectStyleAlignAttributes`, `Document`, `DocumentPartwise`, `DocumentTimewise`, - `DocumentHeader`, `DocumentSpec` with standalone include calls. - -5. **`listDecimals.rb` manual fixups.** `kDefaultPrecision` and `kNonZeroAmount` are manually - appended after parsing `Decimals.h`; `"impl"` is manually deleted. - -6. **Required-attribute error message bug.** In `attributes.rb`, the required-attribute error is - hardcoded as `"'number' is a required attribute but was not found"` regardless of which attribute - is actually required. This bug propagates to all attributes structs. - -7. **Misnamed group scripts.** `midiDeviceInstrumentGroup.rb` generates `NormalTypeNormalDotGroup` - (not a MIDI group). `layoutGroup.rb` generates `MetronomeRelationGroup`. - `displayStepOctaveGroup.rb` generates `EditorialGroup`. These were copy-pasted and filenames were - never updated. - -8. **`cueNoteGroup.rb` placeholder bug.** `FullNoteGroup` (a group, not a leaf element) has no XSD - element name; the script inserts the string `"TODO fix this"` as its element name, generating - `if( elementName == "TODO fix this" )` - which never matches. - -9. **Group scripts output to stdout only.** No script automatically writes a group's `fromXElement` - body to disk. All group `fromXElement` bodies were copy-pasted by hand. - -10. **`elementsHUsingFile.rb` was a one-time op.** Creates C++ `using` alias headers for cases where - multiple XSD names map to the same C++ class. The call sites in `writeHFiles.rb` are commented - out. - -11. **`MetronomeRelation` frozen value.** Per `todo.txt`, the only valid MusicXML value is - `"equals"`, so `setValue` is commented out and the constructor hardcodes `myValue("equals")`. - -12. **SMUFL glyph map** (`smuflHeader.rb`) generated from `data/smufl/glyphnames.json`, producing - `src/mx/impl/SMUFLGlyphMap.h`. Entirely outside the MusicXML XSD. - -13. **`isSymbolFound` regex fragility.** Matches only `SomeType` or ` SomeType&`. Tokens in - ``, after `*`, after `(`, or at line start are missed - potentially producing missing - includes or forward declarations. - -14. **Scripts reference stale paths.** All scripts reference `../../src/mx/core/`; the current path - is `src/private/mx/core/`. Scripts cannot be re-run without path fixes and the absent monolithic - input files. - -### Gaps / Unknowns - -- The scripts which produced the original monolithic `Elements.h` are lost. -- 10 group classes have no corresponding group script (some correspond to the misnamed scripts - listed above; others are simply absent). -- How `*Choice` classes and complex groups were bootstrapped in the monolith is unknown. -- `addStubs.rb` is 0 bytes; its intended purpose is unknown. -- `ElementsOrder.xlsx` is binary; it appears to have established class declaration ordering in the - monolith, but its content cannot be statically analyzed. -- `adjustElementsCpp.rb` produces a `.replace` file; whether it was ever used to overwrite the - original is unknown. -- The exact script run order is undocumented; there is no master driver script. - -* * * - -## Part 2: `mx/core` Code Analysis - -### Type Taxonomy - -**Primitive / scalar types** (`src/private/mx/core/*.h`, not in `elements/`): - -| Category | Examples | -|-------------------|------------------------------------------------------------------------------| -| String wrappers | `XsString`, `XsToken`, `XsID`, `XsIDREF`, `XsNMToken`, `XsAnyUri` | -| Integer types | `Integer` -> `IntRange` -> `BeamLevel`, `OctaveValue`, `Midi128`, etc. (~15) | -| Decimal types | `Decimal` -> `DecimalRange` -> `TenthsValue`, `Percent`, etc. (~10) | -| Union types | `FontSize` (`Decimal`) | -| pImpl union types | `NumberOrNormal`, `PositiveIntegerOrEmpty` | -| Complex scalars | `Color` (ARGB/RGB, custom parse/serialize), `CommaSeparatedText`, | -| | `CommaSeparatedListOfPositiveIntegers` | -| Type aliases | `TimeOnly`, `EndingNumber`, `XlinkHref`, `XlinkRole`, `XlinkTitle` | - -**Enums** (`Enums.h`, `EnumsBuiltin.h`): - -89 total (85 + 4 builtins). Each has `parse`, `tryParse` (returns `std::optional`), -`toString`, `toStream`, `operator<<`. Builtins (`XlinkType`, `XmlSpace`, `XlinkActuate`, -`XlinkShow`) lack `tryParse`. - -Suffix rules: -- No suffix (47): XSD type name has no C++ collision (`AboveBelow`, `CssFontSize`, `YesNo`). -- `Enum` suffix (23): XSD type name collides with an element class name (`BarStyleEnum`, - `DynamicsEnum`, `StepEnum`). -- `Value` suffix (15): XSD extensible/open type (`BeamValue`, `AccidentalValue`, `BeaterValue`). -- 4 "open extension" wrapper classes: wrap an `enum class` + `std::string myCustomValue` for - `other-*` values (`DistanceType`, `DynamicsValue`, `LineWidthType`, `ModeValue`). - -**Element classes** (`elements/`, 1182 files = 591 pairs): - -| Subcategory | Count (approx) | Notes | -|---------------------------|----------------|-----------------------------------------| -| `*Attributes` structs | 174 pairs | One per element that has XSD attributes | -| `*Choice` classes | 21 pairs | XSD `xs:choice` constructs | -| `*Group` classes | 24 pairs | XSD `xs:group` named groups | -| Leaf / container elements | ~372 pairs | Normal element classes | - -**Infrastructure** (`core/` non-element headers): `ElementInterface`, `AttributesInterface`, -`ForwardDeclare.h` macros, `FromXElement.h` template helpers, `Document`, `DocumentSpec`, -`ScoreConversions`, `ProcessingInstruction`, `EmptyPrintObjectStyleAlignAttributes`. - -### Standard Element Template - -Every "leaf element with a value" follows this exact pattern: - -**Header:** -```cpp -MX_FORWARD_DECLARE_ATTRIBUTES(XxxAttributes) // if element has attributes -MX_FORWARD_DECLARE_ELEMENT(ClassName) - -inline ClassNamePtr makeClassName() { ... } -inline ClassNamePtr makeClassName(const ValueType&) { ... } -inline ClassNamePtr makeClassName(ValueType&&) { ... } - -class ClassName : public ElementInterface { -public: - ClassName(); - ClassName(const ValueType& value); - virtual bool hasAttributes() const; - virtual bool hasContents() const; - virtual std::ostream& streamAttributes(std::ostream& os) const; - virtual std::ostream& streamName(std::ostream& os) const; - virtual std::ostream& streamContents(std::ostream& os, int indentLevel, - bool& isOneLineOnly) const; - XxxAttributesPtr getAttributes() const; - void setAttributes(const XxxAttributesPtr& attributes); - ValueType getValue() const; - void setValue(const ValueType& value); -private: - virtual bool fromXElementImpl(std::ostream& message, ::ezxml::XElement& xelement); -private: - ValueType myValue; - XxxAttributesPtr myAttributes; -}; -``` - -**Attributes struct:** -```cpp -struct XxxAttributes : public AttributesInterface { - XxxAttributes(); - virtual bool hasValues() const; - virtual std::ostream& toStream(std::ostream& os) const; - Type1 field1; bool hasField1; - Type2 field2; bool hasField2; -private: - virtual bool fromXElementImpl(std::ostream& message, ::ezxml::XElement& xelement); -}; -``` - -Constructor initializes all `has*` to `false`. `hasValues()` is OR of all `has*` flags. `toStream` -calls `streamAttribute(os, field, "xsd-attr-name", hasField)` per attribute. `fromXElementImpl` -iterates `xelement.attributesBegin()..End()` with `if (parseAttribute(...)) continue;` chains; -enum-typed attributes pass a `&parseXxx` pointer. - -### Variance Map - -| Aspect | How it varies | XSD source | -|----------------------------|-------------------------------------------------------------|-------------------------| -| Class name | PascalCase of XSD element name | `xs:element name=""` | -| `streamName` string | Verbatim XSD kebab-case name | `xs:element name=""` | -| Value type | enum, XsString, XsToken, Integer/Decimal subclass, or none | `simpleType`/content | -| `hasContents()` | `true` if text content or children; `false` if empty | Content model | -| Attributes struct | Own `XxxAttributes` pair or shared `EmptyXxx` from `core/` | Attribute group | -| `makeXxx(value)` overloads | Present only if element has a value type | same | -| Child accessors | See cardinality table below | `minOccurs`/`maxOccurs` | -| `fromXElementImpl` | `importElement(...)` required; sets `myHasXxx` for optional | Content model | - -**Child accessor pattern by cardinality:** - -- `min=1, max=1` (required): member `ChildPtr myChild;`, accessors `getChild()` / `setChild()`. -- `min=0, max=1` (optional): member `ChildPtr myChild; bool myHasChild;`, accessors `getChild()` / - `setChild()` / `getHasChild()` / `setHasChild(bool)`. -- `max=unbounded` (set): member `ChildSet myChildSet;`, accessors `getChildSet()` / `addChild()` / - `removeChild(iter)` / `clearChildSet()`. - -For `min=1, max=unbounded`: pre-populated with one default instance; `clearChildSet()` re-adds one; -`removeChild()` refuses to erase the last item. - -**Group elements:** `streamName` returns empty string (no XML tag). `hasAttributes()` returns -`false`. They are transparent wrappers for XSD named groups. - -**Choice elements:** Hold an inner `enum class Choice { ... }` (values start at 0 in some, 1 in -others - inconsistent). All alternatives are pre-allocated in the constructor. `streamContents` -switches on `myChoice`. `fromXElementImpl` dispatches on `xelement.getName()`. - -### Naming Conventions - -**XSD element name -> C++ class name:** kebab-case to PascalCase. `score-partwise` -> -`ScorePartwise`, `time-modification` -> `TimeModification`. - -**Member variable prefix:** `my` for all members: `myValue`, `myAttributes`, `myHasChild`. - -**Typedef pattern (generated by `MX_FORWARD_DECLARE_ELEMENT`):** `ClassNamePtr`, `ClassNameUPtr`, -`ClassNameSet`, `ClassNameSetIter`, `ClassNameSetIterConst`. - -**Factory functions:** `makeClassName()`, `makeClassName(const T&)`, `makeClassName(T&&)` as inline -free functions in the header. - -**XSD attribute name -> C++ field name:** kebab-case to camelCase. `default-x` -> `defaultX`, -`font-family` -> `fontFamily`. Presence flag: `has` + PascalCase: `hasDefaultX`, `hasFontFamily`. - -**Enum type suffix disambiguation:** -- No suffix when the XSD type name has no class-name collision. -- `Enum` suffix when a class with the same PascalCase name also exists (e.g., `BarStyle` element -> - type becomes `BarStyleEnum`). -- `Value` suffix when the XSD type is extensible / ends in `-value`. - -**Enum value C++ names:** PascalCase from the XSD string. Reserved words get a trailing `_`: -`continue_`, `double_`, `short_`, `new_`, `default_`. - -**XML string literals in `streamName` / `streamAttribute`:** always the original XSD kebab-case -string verbatim. - -### Sequence vs. Choice vs. Group - -- **`xs:sequence`:** child members are declared in order on the class. `streamContents` outputs them - in sequence with newlines between. `fromXElementImpl` uses `importElement(...)` template or manual - `if (elementName == "xxx")` dispatch. -- **`xs:choice`:** generates a `*Choice` class with inner `enum class Choice`. All alternatives are - pre-allocated. `streamContents` switches on `myChoice`. Some choices implement `fromXElementImpl` - by dispatching on `xelement.getName()`; others use `MX_FROM_XELEMENT_UNUSED` when the parent - handles dispatch. -- **`xs:group` (named groups):** generates `*Group` class. `streamName` returns empty string. Parent - streams the group's contents directly via `group->streamContents(...)`. - -### Bespoke and Anomalous Items - -**`Properties` class** (`elements/Properties.h`): C++ class name is `Properties`; `streamName` -outputs `"attributes"`. Only element where class name does not match XSD element name. The XSD -element name `attributes` would clash with C++ keywords and naming conventions. - -**`Accidental.cpp`**: Hand-written error tolerance: if the parsed value is `"double-flat"` (not -valid in MusicXML 3.0), it is silently remapped to `"flat-flat"` with a log message. Not derivable -from the XSD. - -**`Dynamics.h/.cpp`**: Two structural deviations. (1) `setAttributes` takes -`const DynamicsAttributes& attributes` by const-ref value rather than the universal -`const DynamicsAttributesPtr&` pointer pattern. (2) `streamContents` hand-writes inner XML -sub-elements (``, `text`), breaking the standard "element text -= `os << myValue`" pattern. `fromXElementImpl` reads `xelement.begin()->getName()` to reconstruct -the dynamic mark from a child element name. The only element that self-serializes child tags inside -`streamContents`. - -**`Note.h/.cpp`**: The most complex element. Three private parse helpers: `parseNoteChoice`, -`parseFullNoteGroup`, `parseEditorialVoiceGroup`. Large hand-written `fromXElementImpl`. Optional -children use JIT allocation with `MX_MUTEX`/`MX_LOCK`/ `MX_JIT_ALLOCATE` macros and `mutable` -members. `addBeam` enforces `maxOccurs=8` with a size guard (the only finite-`maxOccurs` enforcement -anywhere). - -**JIT-allocated elements** (`MX_MUTEX`/`MX_LOCK`/`MX_JIT_ALLOCATE`): `Note`, `MusicDataChoice`, -`FullNoteGroup`, `FullNoteTypeChoice`, `PartwiseMeasure`. No clear structural rule separates these -from other complex elements that pre-allocate. Likely applied manually where profiling showed -allocation cost. - -**`MX_FROM_XELEMENT_UNUSED` elements:** `NoteChoice`, `KeyChoice`, `EditorialGroup` (and possibly -others). These are parsed by their parent elements rather than independently. - -**`ScorePartwise.cpp`**: `fromXElementImpl` explicitly checks child element names to skip -header-group elements in the `` loop, delegating to `myScoreHeaderGroup->fromXElement(...)`. -Hand-written cross-delegation logic. - -**`EmptyPrintObjectStyleAlignAttributes.h`** (in `core/`, not `elements/`): Only attributes struct -at the `core/` level. Declares `fromXElementImpl` as `public` rather than `private` - the sole -inconsistency of this kind across all attributes structs. - -**`PlaybackSound.h`**: Uses X-macro technique (`Enum.h` utility) rather than the hand-written -`if`/`else` chains used in `Enums.h`. Generates `toString`/`fromString` but not the full -`parse`/`tryParse`/`operator<<`/`toStream` suite the other enums have. Likely added later when the -instrument sound list grew too large for manual maintenance. - -**`DocumentSpec.h`**: Defines `enum DocumentChoice` (old-style, not `enum class`) alongside two -`enum class` definitions. Inconsistent with the rest of the codebase. - -**`PositiveDivisionsValue`** in `Decimals.h`: Marked with -`/// TODO - this needs to be properly generated`. Appears to be an incomplete or redundant type -duplicating `PositiveDivisions`. - -**`TupletReader.h`**: Empty file (1 line). No `.cpp`. Dead placeholder. - -**`AttributesIterface.h`** (note typo: `Iterface`): Stub struct with no fields, no `.cpp`. Used only -in `Elements.h`. Appears to be an unused stub with a typo in the name. - -**`Choice` enum starting values:** Inner `enum class Choice` starts at 0 in some choice classes and -1 in others. The inconsistency has no apparent semantic significance. - -### Gaps and Open Questions - -1. **Enum suffix selection** (`Enum` vs. `Value` vs. no suffix) requires collision detection against - the full set of element class names. A new generator must build that set first. - -2. **`EmptyPrintObjectStyleAlignAttributes` placement** in `core/` vs. `elements/` has no clear - structural criterion. May simply have been promoted to `core/` because multiple elements share it - via attribute groups. - -3. **JIT allocation trigger**: Which elements use `MX_JIT_ALLOCATE` vs. pre-allocate is not - derivable from the XSD. Treat pre-allocation as the default; JIT allocation can be applied - manually to performance-critical paths. - -4. **`MX_FROM_XELEMENT_UNUSED` application**: Which groups and choices delegate to their parent for - parsing is not fully derivable from the XSD. Groups generally do; some choices do. - -5. **`PlaybackSound` X-macro pattern**: Needs a separate codegen path or manual treatment in a new - generator. - -6. **`PositiveDivisionsValue`** relationship to `PositiveDivisions` needs XSD verification before a - new generator handles it. - -7. **`Note::addBeam` maxOccurs=8 guard**: The only enforced finite `maxOccurs > 1`. Whether a new - generator should emit such guards generally needs a policy decision. - -8. **Shared `EmptyXxx` attribute structs**: Multiple elements share one struct (e.g., - `EmptyPlacementAttributes`). XSD uses attribute groups for this. The mapping from attribute group - -> shared struct needs explicit handling in a new generator. - -* * * - -## Summary: What a New Generator Must Handle - -| Concern | Derivable from XSD? | Notes | -|-------------------------------------------|------------------------|-----------------------------------------------------------| -| Class names (kebab -> PascalCase) | Yes | | -| XML string literals (`streamName`) | Yes | | -| Enum definitions (most) | Yes | Suffix disambiguation requires class-name collision check | -| Enum open-extension wrappers | Yes | Extensible types with `other-*` values | -| Attributes struct pattern | Yes | | -| Child cardinality (optional/required/set) | Yes | | -| Sequence/choice/group structure | Yes | | -| `Properties` class name | No | `attributes` -> `Properties` override | -| `MetronomeRelation` frozen value | No | Only valid value is `"equals"` | -| `Accidental` double-flat remap | No | Error-tolerance hand-patch | -| `Dynamics` child-tag serialization | No | Non-standard serialization pattern | -| `Note` complexity / JIT alloc | No | Manual performance work | -| `PlaybackSound` X-macro pattern | No | Or unify with Enums.h approach | -| SMUFL glyph map | No | Separate data source (`glyphnames.json`) | -| `DocumentSpec` old-style enum | No | Minor inconsistency; could normalize | -| `PositiveDivisionsValue` TODO | No | Needs resolution | -| Required-attribute error strings | Partially | Bug in original; a new generator should fix | -| Shared attribute group structs | Yes (attribute groups) | Explicit mapping step needed | diff --git a/docs/ai/projects/gen/design/overrides.md b/docs/ai/projects/gen/design/overrides.md deleted file mode 100644 index 9e48bb05..00000000 --- a/docs/ai/projects/gen/design/overrides.md +++ /dev/null @@ -1,186 +0,0 @@ -# Codegen Override Mechanism - -Are hand-coded exceptions to codegen rules allowed? What form do they take, and how does -`CODEGEN_PROGRAM_QUALITY` treat them? The -design below is grounded in a catalog of the actual non-derivable points in `mx/core`, found -by deriving code from `docs/musicxml.xsd` and diffing against the checked-in source. - -## Context - -Two facts shape every decision here: - -- **4.0 codegen is a one-time run.** There is no recurring build to protect, so "hard-fail on - schema drift" is not the right tool. The output of the 4.0 run becomes the new - hand-maintained `mx/core`. -- **The spec is too large to eyeball.** The risk is not score-gaming. It is that an override - correct for MusicXML 3.x silently produces wrong code among thousands of 4.0 symbols, with - no human able to review all of it. - -## The mechanism - -**Overrides are a first-class declarative input to the generator**, not an afterthought: -`generator(musicxml.xsd, overrides) -> mx/core`. `CODEGEN_PROGRAM_QUALITY` judges the design -of the override *mechanism*, not the number of override entries. - -**Generality is the organizing axis.** A general rule is keyed on a structural condition -(collides with a C++ keyword, XSD type is extensible, name clashes with an element class). It -scales to 4.0's new symbols for free and needs zero per-symbol human attention. A named -per-symbol exception covers exactly one 3.x symbol, does not scale, and is one more thing a -human must re-check at 4.0 time. - -**The optimization target is minimizing the count of named per-symbol exceptions.** General -rules are effectively free. The rule is the default and the named exception is the last -resort: a named exception is admissible only with a justification that (a) no structural -condition can express the behavior and (b) records the 3.x schema assumption it freezes. That -recorded assumption is the entire 4.0 review checklist - the only realistic substitute for the -hard-fail that a one-time run rules out. `attributes` -> `Properties` is the canonical test: -it is attempted first as a general "XSD name collides with a C++ keyword/convention -> -deterministic rename" rule, not written as a one-off table row. - -**Irreducible code exceptions enter via snippet injection into named template slots.** The -element template declares slots (`parse_body`, `extra_private_methods`, `stream_contents`, -...). The declarative manifest binds a slot to a small verbatim `.cpp`/`.h` snippet file. The -generator renders a schema-derived skeleton and fills declared slots; it never names a symbol -in generator code. There are no whole-file escape hatches: even `Note.cpp` is brought into the -mechanism by decomposition into several small, individually justified slots -(`parse_note_choice`, `jit_allocation`, `add_beam_maxoccurs_guard`), not one mega-slot. - -**Anti-cheating is structural, not size-based:** - -1. The skeleton is a pure function of the XSD - class declaration, member layout (content - model + cardinality), accessors, `makeXxx` factories, `streamName`, standard interface - methods. A file whose skeleton is not schema-derived is a checked-in file and is - disqualified. -2. Slots carry only justified, irreducible bodies, decomposed small. -3. A slot is illegitimate if it has no valid justification, reproduces content a structural - rule could derive, or subsumes the skeleton. There is no hard per-slot size cap; - justification plus impartial reviewer is the gate, because an arbitrary cap only moves the - gaming to artificial slot-splitting. - -**Substrate is frozen and namespace-derived.** Foundational types the generator depends on -rather than emits: types resolving to an XSD built-in (`xs:string` -> `XsString`, `xs:token`, -`xs:ID`, `xs:anyURI`, `xs:decimal`, `xs:integer`), to an imported foreign namespace (XLink -> -`XlinkHref/Role/Title`; `xml:` -> `XmlLang/XmlSpace`), the generic numeric base machinery -(`Integer`, `IntRange`, `Decimal`, `DecimalRange`, `PreciseDecimal` - pure clamp/compare/store, -no MusicXML semantics), and runtime infrastructure (`ElementInterface`, `FromXElement.h`, -`ForwardDeclare` macros). Anything defined in the MusicXML target namespace is never -substrate: `Color` looks like a leaf scalar but `color` is a MusicXML `simpleType`, and its -hand-rolled ARGB/RGB parse freezes a 3.x assumption, so it stays generated-or-slotted. -Substrate membership is derived from namespace origin and enforced by static analysis, not -declared by the author; the analyzer rejects any MusicXML-namespace type that resolves to -substrate. "Everything in musicxml.xsd must result in generated output" stays intact because -substrate is the non-MusicXML foundation the schema imports. - -## Classification taxonomy - -Every non-derivable point in `mx/core` is exactly one of: - -- **RULE** - reducible to a structural rule; scales to 4.0 for free; the large majority. -- **EXC** - irreducible named per-symbol exception; needs a slot + justification + recorded - schema assumption. -- **SUBSTRATE** - frozen, not generation output; membership namespace-derived and - analyzer-enforced. -- **FIX** - XSD content `mx/core` never emitted. The faithful generator emits it and accepts - the diff. Treating a FIX as an override-to-preserve, to lower `CHANGE_PENALTY`, is the - cheating the agenda forbids. -- **ANOMALY** - a suspected legacy bug; verify against the XSD before deciding whether to - reproduce or correct it. - -EXC and FIX are handled oppositely: EXC preserves a deliberate human choice; FIX corrects a -legacy gap and *expects* a diff. The mechanism must therefore classify, not merely override. - -## Catalog of non-derivable points - -Found by deriving from `docs/musicxml.xsd` (5428 lines; 421 `xs:element`, 116 `xs:simpleType`, -179 `xs:complexType`) and diffing against `mx/core`. Quantitative claims below are measured, -not estimated. - -### What is RULE (the derivable bulk) - -The generator derives the following without per-symbol input. These scale to 4.0. - -| Concern | Rule | Coverage measured | -|---|---|---| -| Class name | PascalCase of XSD element/type name | - | -| `streamName` | verbatim XSD name | - | -| Enum class name | PascalCase; append `Enum` iff it collides with an element/complexType class name | 81 of 85 exact | -| Enum members | camelCase(value split on `-`/`_`/space); append `_` iff C++ keyword; `""` -> `emptystring`; numbered 0..n in XSD order | 77 of 81 exact | -| Numeric type collision suffix | `Value` (vs `Enum` for enums) | - | -| Attributes struct | complexType own attrs + transitive `attributeGroup` refs, flattened in document order; named after the complexType (or the element if the type is anonymous); shared by all elements of that type | 79 of 129 matched exact (rest are FIX-tainted, not rule failures) | -| Sequence content | ordered `Ptr my`; optional adds `bool myHas`; unbounded -> `Set`; order = XSD sequence order | - | -| Numeric scalars | ` : public IntRange`/`DecimalRange`, bounds from `min`/`maxInclusive` facets (unbounded if absent) | - | -| `measure`/`part` partwise vs timewise | same element name, two content models -> `Partwise*`/`Timewise*` split, both stream the bare name | - | -| Open-extension types | `Enum` enum + ``/`Value` wrapper (`enum + std::string myCustomValue`); inner enum gets `Enum` from collision with the wrapper | - | -| Imported `xml:` attributes | `xml:lang`/`xml:space` -> `XmlLang lang;`/`XmlSpace space;` (SUBSTRATE boundary) | - | -| `xs:group` wrapper classes | `*Group` class with empty `streamName` (transparent wrapper); parent streams its contents | all `*Group` except real elements `Group`/`PartGroup` and the 1 `LayoutGroup` anomaly | -| Document/PI/conversion infrastructure | `Document`, `DocumentHeader`, `ProcessingInstruction`, `ScoreConversions` - top-level machinery and the `XDoc`/ezxml bridge | SUBSTRATE (not MusicXML schema content) | - -### EXC - irreducible named exceptions - -This is the complete irreducible set. It is small and bounded. - -| XSD symbol | C++ artifact | Special handling | Frozen 3.x assumption to record | -|---|---|---|---| -| `attributes` (complexType/element) | `Properties` class, `streamName` "attributes" | renamed; collides with the `Attributes` struct naming convention | `attributes` is the only element whose name collides with the attributes-struct convention | -| `accidental-value`/`accidental` | `Accidental.cpp` remaps `"double-flat"` -> `"flat-flat"` + logs | semantic error-tolerance | `accidental-value` enumeration excludes `double-flat` in 3.x | -| `metronome-relation` | `MetronomeRelation` ctor hardcodes `myValue("equals")`; `setValue` neutered | value frozen | `metronome-relation` enumeration == {`equals`} | -| `dynamics` | `setAttributes` by const-ref value; hand-written `streamContents` emits child tags | non-standard setter + self-serialized child XML | `dynamics` content model is the fixed mark set + `other-dynamics` | -| `note`, `full-note`, music-data group, `measure` (partwise) | `MX_JIT_ALLOCATE`/`MX_MUTEX` lazy child allocation | performance choice, no XSD source | none (performance hint, not a schema fact) | -| `distance-type`, `line-width-type`, `mode` | enum member lists | values mined from `xs:documentation` prose; the schema has no `xs:enumeration` | the value list is the 3.x doc-prose enumeration | -| `note-type-value` | `NoteTypeValue` members | digit-leading values hand-spelled to English (`1024th` -> `oneThousandTwentyFourth`) | the digit-to-English value mapping | -| instrument-sound list | `PlaybackSound.h` X-macro, not the standard enum suite | different enum codegen path | the sound list contents | -| `color`, `comma-separated-text`, `number-or-normal`, `positive-integer-or-empty`, `yes-no-number`, `font-size`, date/time types | `Color`, `CommaSeparated*`, `NumberOrNormal`, `PositiveIntegerOrEmpty`, `YesNoNumber`, `FontSize`, `Date`, `TimeOnly`, `EndingNumber` | bespoke C++ shapes (pImpl, custom parse), not the generic range pattern | each type's lexical space per its 3.x restriction | - -### FIX - legacy infidelity the faithful generator must correct - -`mx/core` does not faithfully represent the XSD attribute model. These are bugs to fix, not -overrides to preserve. - -| XSD symbol | Gap | Measured | -|---|---|---| -| `color` attribute reachable via `print-style` | struct omits the `color` field non-uniformly | 71 complexTypes reach `color` and have a struct; 26 emit it; **45 omit it** (`AccidentalAttributes` confirmed) | -| `text-formatting` `dir`, `valign` | fields omitted in some structs | - | -| required-attribute error string | 46 `.cpp` hardcode `'number' is a required attribute` regardless of the actual attribute | 46 files | - -### ANOMALY - verify against the XSD before reproducing - -| Symbol | Suspected issue | -|---|---| -| `LayoutGroup` | streams `"work"`; the sole `*Group` with an erroneous non-empty `streamName` (matches the forensics misnamed-group-script finding) | -| `*Choice` inner `enum class Choice` start value | inconsistent with no semantic significance: 15 start at `1` (`ArticulationsChoice`, `BendChoice`, `CreditChoice`, `EncodingChoice`, `FullNoteTypeChoice`, `HarmonicInfoChoice`, `HarmonicTypeChoice`, `LyricTextChoice`, `MusicDataChoice`, `NotationsChoice`, `NoteChoice`, `OrnamentsChoice`, `PercussionChoice`, `SoloOrEnsembleChoice`, `TechnicalChoice`), 6 start at `0`. Normalize to one convention | -| `note-type-value` | hand-spelled value contains a typo: `twoHundredFifthySixth` ("Fifthy") | -| `DocumentChoice` | `DocumentSpec.h` uses old-style `enum`, not `enum class` | -| `PositiveDivisionsValue` vs `PositiveDivisions` vs `DivisionsValue` | redundant DecimalRange subclasses; one carries `/// TODO` | -| `EmptyPrintObjectStyleAlignAttributes` | only `*Attributes` placed in `core/` not `elements/`; criterion unclear | -| wrapper suffix (`DistanceType` vs `DynamicsValue` vs `LineWidthType` vs `ModeValue`) | inconsistent `Type`/`Value` suffix with no derivable reason | -| `dynamics` choice-of-empties -> enum collapse | confirm the structural-collapse rule generalizes rather than being a one-off | -| `articulations`/`encoding`/`notehead-text`/`technical` `*Choice` | confirm streamName-of-parent vs the empty-streamName choice pattern | - -## Consequences for the wider plan - -- **The mechanism must classify, not just override.** EXC and FIX require opposite treatment. - A mechanism that cannot express "this delta is a FIX, score it as correct" forces an agent - to either cheat (preserve the bug) or absorb an unfair `CHANGE_PENALTY`. -- **`scoring.md` and the systematic-gap design must give faithfulness precedence over diff - size.** The missing-`color` case is concrete proof: ~45 structs of correct `.h` additions - (5x multiplier) plus `.cpp` would lose under the rubric as written, which would reward - reproducing a legacy bug. This finding belongs in those two grill sessions. -- **An EXC's justification must record its frozen 3.x schema assumption** (see the EXC table's - last column). That column is the 4.0 reviewer's checklist. - -## Method and scope - -Derivation probes live in `gen/` (Python, stdlib `xml.etree`, chosen for -zero-dependency iteration in this investigation; the production generator language is a -separate Phase 1 question and is not decided here). Each probe derives one slice from the XSD -and diffs against `mx/core`; the measured counts above are reproducible by re-running them. - -All slices have been probed: enum names, enum members, attributes structs, element content -models, numeric scalars, choice classes, group classes, the bespoke `core/` scalars, and -document/conversion infrastructure. After `FIX` emerged no further category appeared, and the -later slices produced only further instances of existing categories, now enumerated in the -tables above (the 21 `*Choice` start-value split, the sole `LayoutGroup` group anomaly, the 10 -bespoke scalars, the infrastructure SUBSTRATE set). The catalog is both category-complete and -instance-complete for the override-mechanism decision; the ANOMALY rows marked "confirm" are -deliberately left as verification tasks for the Phase 1 implementation, not open design -questions. diff --git a/docs/ai/projects/gen/design/scoring.md b/docs/ai/projects/gen/design/scoring.md deleted file mode 100644 index 9e3ef1d0..00000000 --- a/docs/ai/projects/gen/design/scoring.md +++ /dev/null @@ -1,203 +0,0 @@ -# CHANGE_PENALTY Threshold and Phase 1 Exit Criterion - -What numeric score is "good enough" to exit Phase 1? A generous numeric "good enough" is the wrong -instrument. The exit criterion is a conjunction of gates; the numeric penalty's job is to be driven -to zero on the *unexplained residual*, not to define a tolerance budget. This doc depends on the -classification taxonomy established in `overrides.md` (RULE / EXC / SUBSTRATE / FIX / ANOMALY). - -## Context - -Two facts shape every decision here, both inherited from `overrides.md`: - -- **4.0 codegen is a one-time run.** Phase 1 reproduces the current `mx/core` from the current - schema so the generator can be trusted before it is pointed at 4.0. The pass/fail bar exists to - certify that trust, not to protect a recurring build. -- **`mx/core` contains legacy XSD-infidelity (the FIX category).** A faithful generator must emit - code that differs from the checked-in source. The exit criterion must score that difference as - *correct*, or it certifies a generator that reproduces bugs. - -## The flaw in CHANGE_PENALTY as written - -The agenda computes penalty over the raw diff between regenerated output and checked-in `mx/core`: 1 -point per added or deleted line, multiplied (1 whitespace, 1.5 comment, 2 `core/*.cpp`, 5 -`core/*.h`, 10 `mx/impl`, FAIL for `mx/api`). Lower is better. - -Worked example, verified in code. `docs/musicxml.xsd` reaches the `color` attribute via -`print-style` for 71 complexTypes that have an attributes struct. 26 emit the field; 45 omit it. -`src/private/mx/core/elements/AccidentalAttributes.h` is a confirmed omission - it has no `color` -member, while `ArrowAttributes.h` and 25 others do. A faithful generator emits `color` in all 71. -That is roughly 45 added `.h` lines at the 5x multiplier (`~225 points`) plus the matching `.cpp` -accessor and stream lines at 2x, for one FIX. - -So the raw-diff rubric ranks the *unfaithful* generator - the one that hardcodes "skip color in -these 45 structs" - strictly above the faithful one. That hardcoded skip is precisely the cheating -the agenda forbids ("no cheating", "Everything in musicxml.xsd must result in generated code -output"). Any single numeric "good enough" threshold over the raw diff has this inversion built in. -Picking a number does not fix it; it sets the price of the bug. - -A second, structural flaw: the metric counts a modified line as delete + add (2 points) and a moved -block as N deletes + N adds. A single wrong ordering rule applied by an otherwise correct generator -reorders entire files and accrues penalty proportional to file size for what is one root-cause -defect. The raw metric punishes one mistake thousands of times. - -## The mechanism: penalty is computed over the unexplained residual - -CHANGE_PENALTY must not be computed over the raw diff. It is computed over the **residual** - the -diff that remains after every differing line is attributed to a category from `overrides.md`. -The generator already takes a declarative overrides input; that same manifest is the ledger that -attributes diff lines: - -- **RULE-correct**: lines that match checked-in `mx/core`. Zero diff, zero penalty. -- **FIX**: lines that differ because the generator correctly emits XSD content `mx/core` never - emitted. Declared in the manifest, each entry validated against `docs/musicxml.xsd` by an - analyzer. Scored **zero**. A FIX whose declared XSD justification does not check out is not a FIX - \- it falls through to residual. -- **EXC**: lines produced by a justified slot reproducing a deliberate human choice. Scored zero - when the slot output matches the checked-in source; any delta is residual. -- **SUBSTRATE**: not generation output; out of diff scope by construction (namespace-derived, - analyzer-enforced per `overrides.md`). -- **ANOMALY**: each anomaly carries a recorded decision (reproduce or correct). The resulting diff - is scored zero *because the decision is recorded and justified*, not because it is small. An - anomaly with no recorded decision falls to residual. -- **Residual**: every diff line with no valid classification. This, and only this, is what - CHANGE_PENALTY scores, using the agenda's multiplier table. - -This converts the metric from "size of the diff" to "size of the *unexplained* diff" and is the -concrete faithfulness-over-diff-size mechanism the agenda asks both this session and -`scoring-gap.md` to supply. FIX no longer competes with cheating: emitting `color` in 45 structs is -a declared, XSD-validated FIX worth zero; *not* emitting it is now the defect, because the missing -lines become unexplained residual against the faithful target. - -## The exit criterion: four gates, not one number - -Phase 1 exits when a generator passes all four. The first three are binary. The fourth carries the -number, and the number is small by design. - -**Gate A - No-skip (binary, already in the agenda).** Static analysis of the generator confirms -every `xs:element`, `xs:simpleType`, `xs:complexType`, `xs:attribute`, `xs:attributeGroup`, and -`xs:group` in `docs/musicxml.xsd` reaches a code-emitting path. Skipping disqualifies. The -scoring-gap note's "very large score for not skipping" is best realized as this hard gate, not as -points; a score, however large, is a budget, and a budget is a cheating surface. - -**Gate B - Quality gates (binary).** The regenerated tree passes -`make fmt && make check && make test-all` (test-all because the change touches -`src/private/mx/core/`). Non-negotiable and independent of any diff measurement. - -**Gate C - Classification completeness (binary).** 100% of diff lines are attributed to a category -above. An unclassified diff line is a defect, not a tolerated cost. This gate is what makes Gate D -safe: with no unclassified lines permitted, there is no budget to spend on unjustified deviation -spread thin across 1182 files. - -**Gate D - Residual target (numeric).** The residual penalty, computed with the agenda's multipliers -over only the unexplained residual, must be **zero**, with a single published, enumerated allowance -list for irreducible noise (see below). There is no generous "good enough" band. A nonzero tolerance -is itself the cheating surface this whole design removes: spread across a 113k-line tree it silently -funds exactly the legacy-bug reproduction the agenda warns about. - -### Why zero, and why an enumerated allowance instead of a band - -A tolerance band answers "how much unjustified wrongness do we accept?" The correct answer for a -one-time generator that becomes the new hand-maintained source is none - every nonzero diff line is -either explained (classified, zero penalty) or a defect (fix it or justify it). The allowance list -is not a band; it is a closed, named set of noise sources agreed in advance, each of which is then -itself zero: - -- Formatting is normalized away. `make fmt` applies the same pinned clang-format-18 Microsoft style - to both the regenerated output and the checked-in tree (the tree was reformatted in commit - 662c7ff9). Whitespace residual after `make fmt` is expected to be 0; if it is not, that is a - generator defect, not noise. -- The license banner (`// MusicXML Class Library` / copyright / MIT, verified as the first three - lines of `AccidentalAttributes.h`) is RULE-derivable and reproduced verbatim. There is no - historical "generated by" banner to reconstruct. -- Any other claimed-irreducible source must be added to this list by name with justification before - it is allowed, and the goal remains to drive it to a RULE that produces zero. - -If the first reverse-engineering attempt cannot reach zero residual, that is expected and is handled -by the ranking metric below - it does not lower Gate D. Phase 1 iterates (the agenda's "repeat the -process improving the winning program") until Gate D is met. Gate D is the exit bar; it is not the -within-iteration progress signal. - -## Two distinct uses the agenda conflates - -The agenda uses one metric for two jobs. They need different treatment. - -1. **Ranking competing generators** ("scored by an impartial agent ... take the best one"). Rank by - residual penalty (Gates A-C as prerequisites to be ranked at all). Ranking must use - residual-after-classification, never raw diff - otherwise the cheater that reproduces the - missing-`color` bug out-ranks the faithful generator, and the competition selects the wrong - winner to carry into the improvement round. Among generators with equal residual, rank by **fewer - distinct root-cause defects**, then by raw line-penalty as the final tiebreaker. - -2. **Exiting Phase 1** (the pass/fail this doc resolves). Gates A-D. This is not a ranking; it is a - certification that the winner is trustworthy to point at 4.0. - -The unit for ranking and for "is this defect-free" is the **distinct root-cause defect**, not the -line. One wrong ordering rule that reorders 40 files is one defect, not thousands of points. -Line-weighted penalty is retained only as telemetry while iterating and as the last tiebreaker. -Counting a systematic gap that recurs 300 times as one defect rather than 300x penalty is the -partial-credit problem owned by `scoring-gap.md`; this doc establishes the shared foundation -(residual-after-classification, defect-as-unit) and explicitly defers the recurrence-counting -mechanism there. The two docs must not diverge on the foundation. - -## Multiplier table review - -The agenda's table is mostly sound for scoring residual. Three adjustments: - -- **`mx/impl` at 10x is too lenient as a mere cost.** A core-only generator that produces any - `mx/impl` diff has changed a generated core API signature the conversion layer consumes (added - fields cannot break `mx/impl`; renamed or removed ones can). Treat any `mx/impl` residual as a - **Gate C / Gate D failure requiring explicit justification**, not a payable 10x cost. `mx/api` = - FAIL is correct and stays. -- **Modify = 2 points and move = 2N points stand**, but only for residual. Because residual excludes - classified lines and the unit of judgment is the defect, the double-count no longer punishes one - mistake thousands of times - it inflates the telemetry number for a single defect, which is - acceptable for a tiebreaker. -- **`.h` 5x vs `.cpp` 2x vs comment 1.5x vs whitespace 1x** are reasonable interface-weighted values - and need no change for residual scoring. - -## Recommendation summary - -1. Compute CHANGE_PENALTY over the **unexplained residual**, never the raw diff. Classified lines - (FIX, EXC, SUBSTRATE, recorded ANOMALY) score zero. -2. Phase 1 exits on the conjunction of **Gate A** (no-skip, disqualifying), **Gate B** - (`make fmt && make check && make test-all`), **Gate C** (100% diff lines classified), and **Gate - D** (residual penalty = 0 modulo a closed, named allowance list). -3. Replace the "good enough numeric band" question with "residual = 0"; a tolerance band is a - cheating surface on a 113k-line tree. -4. Use residual penalty to **rank** competing generators; use the four gates to **exit**. Make the - unit of both ranking and exit the distinct root-cause defect, with line-penalty as telemetry and - final tiebreaker only. -5. Promote `mx/impl` residual from a 10x cost to a justify-or-fail gate condition. - -## Boundary with the other open questions - -- `scoring-gap.md` (systematic-gap / partial credit): owns counting an N-times-recurring gap as one - defect. Inherits this doc's residual-after-classification foundation and defect-as-unit; must not - redefine them. -- `codegen-program-quality.md`: `CODEGEN_PROGRAM_QUALITY` judges the override mechanism's design - (per `overrides.md`), independent of CHANGE_PENALTY. The two scores are orthogonal; a - generator can pass Gate D with a poorly designed override mechanism and still fail Phase 1 on - quality. Note the cross-dependency, do not merge the metrics. -- `language-constraints.md`: unaffected by this doc. - -## Method and evidence - -- Missing-`color` FIX verified directly: `grep` confirms no `color` member in - `src/private/mx/core/elements/AccidentalAttributes.h`; `Color color` present in - `ArrowAttributes.h` and 25 siblings. Counts (71 reach `color`, 26 emit, 45 omit) are from - `overrides.md`, reproducible via the probes in `gen/`. -- Tree scale: `src/private/mx/core` is 1182 files and ~113k lines; the 5x `.h` multiplier over 45 - FIX additions (~225 points for one FIX) is the concrete inversion that forces - residual-after-classification rather than a tuned threshold. -- Formatting normalization assumption rests on `build-and-ci-design.md` (pinned clang-format-18, - Microsoft style, applied to `mx/core`) and the reformat in commit 662c7ff9; Gate D treats - post-`make fmt` whitespace residual as a defect, not noise, on that basis. - -## Deferred to Phase 1 implementation - -- The analyzer that validates each declared FIX against `docs/musicxml.xsd` (the mechanism that - makes FIX-scored-zero non-gameable) is a build task, not a design question. -- The exact diff tooling that attributes lines to manifest entries is an implementation choice; the - requirement is per-line attribution with no unclassified lines, not a specific tool. -- ANOMALY decisions (reproduce vs correct) are recorded per `overrides.md`'s ANOMALY table; this - doc only requires that the decision exists and is justified for the line to score zero. diff --git a/docs/ai/projects/gen/index.md b/docs/ai/projects/gen/index.md index 84dbcbda..f174e79b 100644 --- a/docs/ai/projects/gen/index.md +++ b/docs/ai/projects/gen/index.md @@ -1,5 +1,7 @@ --- created: 2026-05-18 +m1_completed: 2026-05-21 +m2_completed: 2026-05-22 --- # gen @@ -7,58 +9,58 @@ created: 2026-05-18 ## Goal Reverse engineer the codegen process that produced `mx/core` from MusicXML XSD. Build a generator -that re-produces the existing C++ code from `docs/musicxml.xsd` with minimal unexplained diff, then -point it at MusicXML 4.0 to generate updated types. +that re-produces the existing C++ code from `docs/musicxml.xsd`, then point it at MusicXML 4.0 to +generate updated types. -## Index +## Files -- `plan.md` - milestones from reverse-engineering through MusicXML 4.0 PR +- `plan.md` - milestones and exit criteria - `state.md` - current session state and next-session instructions - `log.md` - append-only session log -- `iteration-notes.md` - detailed per-iteration findings from the experiment phase (iters 1-7) -- `design/` - design docs (current-state snapshots, not historical) - - `overrides.md` - override taxonomy (RULE/EXC/FIX/SUBSTRATE/ANOMALY) and catalog - - `scoring.md` - CHANGE_PENALTY mechanism and Phase 1 exit gates - - `forensics.md` - analysis of the original Ruby codegen and mx/core patterns -## Instructions +## Generator location and how to run -### Generator location and how to run - -The generator and eval tools live at `gen/` (not a subdirectory of the project docs): +The generator lives at `gen/` (not under this project directory): ``` -python3 gen/generate.py # regenerates C++ on top of src/private/mx/core/elements/ -python3 gen/eval.py # scores the diff -python3 gen/eval.py --verbose # shows individual penalized lines +python3 gen/generate.py # regenerates C++ into src/private/mx/core/elements/ +python3 gen/eval.py # scores diff against checked-in mx/core (secondary signal) ``` -After studying the diff, reset with `git checkout -- src/private/mx/core/`. +Workflow: `python3 gen/generate.py && make fmt && make test-all`, then reset: +`git checkout -- src/private/mx/core/ && git clean -fd src/private/mx/core/`. Quality gates: `make fmt && make check && make test-all`. -### Key files outside the project directory +## Fitness function + +`make test-all` pass/fail. **Always use `make test-all`, never `make test`** — the latter builds +`dev` and skips the slow `mxtest/file/` round-trippers, under-reporting failures. `make test-all` +takes >10 minutes. Every iteration ends with a recorded `make test-all` result. -- `gen/generate.py` - the generator (~2800 lines Python) -- `gen/eval.py` - diff scoring script -- `gen/eval_config.yaml` - scoring category rules -- `docs/musicxml.xsd` - the input XSD schema -- `src/private/mx/core/elements/` - the target output directory (591 .h/.cpp pairs) -- `docs/ai/projects/build-and-ci-design.md` - repo-wide build/CI/formatting docs +## Cardinal rules -### Eval config policy +- Never change tests. +- Never change `mx/api`. +- Minimize changes to `mx/impl`; prefer fixing the generator. +- Do not autonomously edit `gen/eval_config.yaml`. Flag patterns to the user with sample diff + reasoning. +- Reset generated `mx/core/` before commit. -Do NOT autonomously add or change entries in `gen/eval_config.yaml`. When studying the diff, if you -see a pattern that should be zero or low cost, flag it to the user with the pattern, a sample diff -line, and your reasoning. The user decides whether to add it to the config. +## Bespoke generator functions -### Commit discipline +Six bespoke handlers exist (credit, lyric, part-list, harmony, score-wrapper-family, note, +direction), registered in `BESPOKE_ELEMENTS` in `gen/generate.py`. They are acceptable when an +element's shape cannot be expressed by the shared rule-based path, but they must still read the +parsed XSD model so spec changes propagate. Pattern: "custom algorithm, schema-driven data" — not +a hardcoded string dump. -Each iteration should end with a commit containing generator changes, eval changes, and updated -project docs (state.md, log.md, and any design doc changes). Reset generated code before committing -(`git checkout -- src/private/mx/core/`). +Always prefer extending a reusable rule-based path (`TREE_ELEMENTS`, `TREE_ELEMENT_CONFIG` flags, +shared helpers) over a new bespoke handler. If a pattern could plausibly recur for another +element, the fix belongs in the shared path with a config-driven flag. -### Design doc maintenance +## Key external files -Design docs in `design/` describe current design state only. When the design evolves, update the doc -in place. Record design changes and rationale in `log.md`. +- `gen/generate.py` — the generator (~2800 lines of Python) +- `gen/eval.py`, `gen/eval_config.yaml` — diff scoring +- `docs/musicxml.xsd` — input schema (currently MusicXML 3.x; swapped to 4.0 in M5) +- `src/private/mx/core/elements/` — target output (591 .h/.cpp pairs) diff --git a/docs/ai/projects/gen/iteration-notes.md b/docs/ai/projects/gen/iteration-notes.md deleted file mode 100644 index 507c679a..00000000 --- a/docs/ai/projects/gen/iteration-notes.md +++ /dev/null @@ -1,425 +0,0 @@ -# Codegen Experiment Notes - -## Iteration 1 (2026-05-18) - -Generator: `gen/experiment/generate.py` -Coverage: 373 of 383 elements (10 choice-type skipped), 137 attributes structs -Diff: 764 files changed, +4119 -8149 - -### Exact matches -- 297/631 .h files exact -- 200/629 .cpp files exact - -### Diff categories found - -**1. Constructor initializer list formatting (RULE)** -The generator puts ctor init lists on one line. The real code wraps them at ~100-110 chars with -continuation indented to align with the first initializer. After `make fmt` this should resolve - need -to verify. - -**2. Method ordering inconsistency in original code (ANOMALY)** -Two patterns exist for virtual method declarations: -- Pattern A (373 files): `hasAttributes, hasContents, streamAttributes, streamName, streamContents` -- Pattern B (67 files): `hasAttributes, streamAttributes, streamName, hasContents, streamContents` -Generator uses Pattern A (majority). The 67 Pattern B files produce diffs. Normalize to Pattern A. - -**3. Forward declares: sequence order vs alphabetical (RULE gap)** -The checked-in code sorts forward declares alphabetically in some files and by sequence order in -others. The generator uses XSD sequence order. Need to investigate which is canonical and pick one. -Distance/Appearance example: real code has alphabetical, generator has sequence order. - -**4. Group wrapper classes missing (RULE gap - major)** -The real code creates wrapper classes for XSD `xs:group` definitions with a `Group` suffix: -- `EditorialVoiceGroup`, `EditorialVoiceDirectionGroup`, `FullNoteGroup`, etc. -The generator doesn't create these wrapper classes. It uses the group name directly without the suffix. -This causes missing forward declares, wrong type names, and missing accessor methods. -~20+ elements reference group wrappers. - -**5. Choice wrapper classes missing (not yet generated)** -10 choice-type elements are skipped entirely: `time`, `measure-style`, `direction-type`, `dynamics`, -`metronome`, `percussion`, `encoding`, `technical`, `articulations`, `arrow`. -These need `*Choice` class generation. - -**6. XsString/XsToken value parsing: setValue vs parse (RULE)** -String-like types (`XsString`, `XsToken`, `XsID`, `XsIDREF`, `XsNMToken`, `XsAnyUri`) use -`myValue.setValue(xelement.getValue())` in `fromXElementImpl`. -Numeric/enum types use `myValue.parse(xelement.getValue())`. -Generator currently uses `parse` for everything. - -**7. setAttributes parameter name (cosmetic)** -Real code uses `void setAttributes(const XPtr &value)`. -Generator uses `void setAttributes(const XPtr &attributes)`. - -**8. Color attribute FIX diffs (expected, correct)** -45 attributes structs gain a `color` field they were missing. This is a known FIX per -gen-overrides.md. The generator correctly emits these. They produce +additions in .h and .cpp. - -**9. Private helper methods in complex elements (EXC)** -Some complex elements have hand-written private helper methods for specialized parsing: -- `Direction`: `importDirectionTypeSet`, `createDirectionType`, `isDirectionType`, - `isMultiDirectionType` -- `Note`: similar specialized parsing helpers -These are EXC per gen-overrides.md - irreducible per-element code. - -**10. ezxml forward declarations (RULE)** -Some real elements forward-declare `ezxml::XElementIterator`. The generator doesn't emit these. -They correlate with the private helper methods (pattern 9). - -**11. Beam maxOccurs discrepancy (investigation needed)** -The XSD says `beam` in `note` has `maxOccurs="8"`, but my parser reads `maxOccurs` as an int. -The real code uses `BeamSet` (unbounded). Need to handle bounded-but-gt-1 as Set. - -**12. `empty` complexType handling** -`empty` is a named complexType in the XSD with no content. Elements using this type (like -`accordion-high`) should have no attributes, no content - just the element shell. - -**13. Attributes struct naming: type name vs element name** -The generator names attributes structs after the complexType (`FormattedTextAttributes`, -`BarStyleColorAttributes`). The real code names them after the element that uses them. When multiple -elements share a complexType, the real code uses one shared struct but it's named after the type. When -an element has an anonymous type, the struct is named after the element. The generator creates spurious -files for type-named structs that don't exist in the real code. Need to align naming. - -Major spurious files: `FormattedTextAttributes`, `BarStyleColorAttributes`, -`EmptyPrintStyleAlignAttributes`, `EmptyPrintObjectStyleAlignAttributes`, `HammerOnPullOffAttributes`, -`HeelToeAttributes`, `HorizontalTurnAttributes`, `NameDisplayAttributes`, `NoteTypeAttributes`, -`PlacementTextAttributes`, `StyleTextAttributes`, `TextElementDataAttributes`, -`TextFontColorAttributes`, `TypedTextAttributes`. - -**14. Dynamics child elements generated as separate files (should not exist)** -The `dynamics` XSD type is a choice of empty elements (`f`, `ff`, `fff`, etc.). The real code -handles these as inner members of the `Dynamics` class, not as separate element files. Generator -created 24 files (F.h/cpp, Ff.h/cpp, etc.) that should not exist. - -### Priority fixes for next iteration -1. ~~Fix `setValue` vs `parse` for string types~~ DONE -2. Add group wrapper class generation -3. ~~Fix `setAttributes` parameter name~~ DONE (.h=`attributes`, .cpp=`value` matches real code) -4. ~~Handle bounded maxOccurs > 1 as Set~~ DONE -5. Fix forward declare ordering (alphabetical) -6. ~~Don't generate separate files for dynamics child marks~~ DONE -7. ~~Fix attributes struct naming~~ DONE (element-named, except empty-* shared types) -8. Start generating choice classes - -## Iteration 2 (2026-05-18) - -Fixes applied: setValue/parse, setAttributes param, maxOccurs>1 as Set, dynamics suppression, -attrs naming (element-named except shared empty-* types), skip `attributes` element (EXC: Properties). - -Diff: 728 files changed, +4069 -8410 -- .h: 233/592 exact (up from 175 mid-iteration, down from 297 iter-1 due to attrs changes) -- .cpp: 222/590 exact - -### New findings - -**15. Include ordering** -Real code orders includes: `ElementInterface.h`, type headers (`Enums.h`, `Integers.h`, etc.), -`ForwardDeclare.h`, then attrs header. Generator sorts alphabetically, producing different order. - -**16. Private member ordering (value before attrs)** -For text-with-attrs elements, the real code declares `myValue` before `myAttributes` in private -members. Generator puts `myAttributes` first. - -**17. Empty second `private:` block** -Generator adds a second `private:` section with no members for empty elements. Real code doesn't -have this. - -**18. Attributes struct naming: element-named is the rule** -Discovered that 33 element-named attrs structs in the real code are UNREFERENCED dead code. Elements -always use the type-named struct when sharing a type. But most types with >1 user use `empty-*` -naming. Non-empty shared types (like `formatted-text`) generate BOTH a type-named and an -element-named attrs file in the original code, but only the type-named one is referenced. - -Actually, deeper investigation showed the original code uses element-named attrs for all non-empty-* -types. `DisplayText.h` uses `DisplayTextAttributes.h` (element-named), not -`FormattedTextAttributes.h`. The type-named files that exist (`FormattedTextAttributes.h` etc.) are -dead code never committed to the repo. - -**19. CORE_ROOT_ATTRS location anomaly** -`EmptyPrintObjectStyleAlignAttributes` lives in `mx/core/` not `mx/core/elements/`. This is the -anomaly noted in gen-overrides.md. `EmptyPrintStyleAlignAttributes` was never committed anywhere. - -**20. setAttributes .h vs .cpp parameter name mismatch (intentional)** -Real code: .h uses `&attributes`, .cpp uses `&value`. This is an original inconsistency the -generator should reproduce. - -### Priority fixes for iteration 3 -1. ~~Fix include ordering~~ DONE (alphabetical sort matches clang-format) -2. ~~Fix private member ordering (value before attrs)~~ DONE -3. ~~Remove empty second private block for empty elements~~ DONE -4. ~~Fix text-with-attrs ctor to init myAttributes~~ DONE -5. Add group wrapper class generation -6. Fix forward declare ordering -7. Start generating choice classes - -## Iteration 3 (2026-05-18) - -Fixes: include alphabetical sort, private member ordering (value before attrs), removed empty second -private block, text-with-attrs ctor now initializes attrs, core-root-attrs location handling. - -Diff: +3619 -8063 (567 changed files, down from 728 in iter 2) -- .h: 386/592 exact (65%) -- .cpp: 229/590 exact (39%) - -### Findings - -**21. MX_UNUSED(xelement) inconsistency** -Original code always includes `MX_UNUSED(xelement)` for simple-value elements, even for string types -where xelement IS used (via `xelement.getValue()`). This is technically wrong but consistent. -Generator matches this pattern. - -### Remaining diff sources (estimated impact) -- Forward declares in wrong order (~100+ h diffs): children in XSD sequence order vs alphabetical -- Missing group/choice wrapper classes (~50+ diffs): no Group/Choice generation yet -- Method order Pattern B in 67 files: `hasAttributes, streamAttributes, streamName, hasContents` -- Attrs struct content diffs (~45 FIX + structural): color field, extra attrs -- Complex element cpp: missing child includes, wrong ctor init order, etc. -- 33 unreferenced dead-code attrs files: generator doesn't create them - -### Priority fixes for iteration 4 -1. ~~Sort forward declares alphabetically~~ DONE -2. Fix method ordering for Pattern B files (67 files) -3. Generate Group wrapper classes -4. Generate Choice classes - -## Iteration 4 (2026-05-18) - -Fixes: sorted forward declares alphabetically. - -Diff: +3556 -8000 (567 changed files, same as iter 3) -- .h: 386/592 exact (65%, same as iter 3 - forward declares didn't change .h exact count much) -- .cpp: 229/590 exact (39%, same as iter 3) - -Note: The forward declare sort fix was small (3 lines changed in generate.py). It likely improved -some files but the exact-match counts stayed roughly the same because the remaining diffs have -multiple overlapping causes per file. - -### Diff analysis by .h file change size -- 43 files with 1-2 changed lines (likely single-cause fixes) -- 67 files with 3-4 changed lines -- 29 files with 5-8 changed lines -- 67 files with >8 changed lines (multi-cause or structural) - -### Priority fixes for iteration 5 -1. Fix method ordering for Pattern B files (67 files with hasAttributes, streamAttributes, - streamName, hasContents, streamContents order instead of Pattern A) -2. Generate Group wrapper classes (EditorialVoiceGroup, FullNoteGroup, etc.) -3. Generate Choice classes (ArticulationsChoice, NoteChoice, etc.) -4. Fix complex element .cpp child includes and ctor init order - -## Iteration 5 (2026-05-18) - -Fixes applied: -- Pattern B method ordering: elements with complexContent, xs:sequence, xs:choice, or xs:group - references use `hasAttributes, streamAttributes, streamName, hasContents, streamContents` order. - Detection heuristic covers 99.5% of cases. `PATTERN_B_EXCEPTIONS = {"String"}` for the one - text-with-attrs element that uses Pattern B. Affected ~144 files. -- Group wrapper class generation (.h and .cpp) for 11 XSD groups: beat-unit, - display-step-octave, editorial, editorial-voice, editorial-voice-direction, full-note, - harmony-chord, layout, music-data, score-header, time-signature. -- Group children in parent elements: `->streamContents()` call, `->hasContents()` check, Group - suffix naming for groups in GENERATE_GROUPS. -- ElementInterface() ctor fix: only included when element has no value AND no children. -- MX_RETURN_IS_SUCCESS for text-with-attrs fromXElementImpl (was `return isSuccess`). Matches - 113 of 133 real files; 20 outliers use `return isSuccess` (no systematic pattern found). - -Diff: +3083 -7846 (540 changed files, down from 567 in iter 4) -- .h: 402/592 exact (67.9%, up from 65.2%) -- .cpp: 240/590 exact (40.6%, up from 38.8%) - -### Findings - -**22. Pattern B detection heuristic** -Elements with complexContent, xs:sequence, xs:choice, or xs:group in their complexType use Pattern B -method ordering. Only two exceptions found: `String` (Pattern B despite text-with-attrs) and -`Directive` (Pattern A despite having complexContent, but Directive is an EXC/skipped element). - -**23. Group streamContents patterns are per-group hand-coded** -Each of the 11 generated group classes has a unique streamContents implementation: -- 2-child all-optional (EditorialGroup): no firstItem variable, manual inline checks -- 3-child all-optional (EditorialVoiceGroup): firstItem pattern, first child skips check -- Required-only (DisplayStepOctaveGroup, TimeSignatureGroup): direct stream with endl -- Mixed required/optional/set (BeatUnitGroup, LayoutGroup, ScoreHeaderGroup): each unique -- Choice-based (HarmonyChordGroup, FullNoteGroup): switch/special handling -The generator uses a generic template that doesn't match any group exactly. All 11 group .cpp files -have diffs. Group .h files: 6 of 11 match exactly. - -**24. MX_RETURN_IS_SUCCESS vs return isSuccess inconsistency** -For text-with-attrs elements, 113 use `MX_RETURN_IS_SUCCESS` and ~20 use `return isSuccess` in -fromXElementImpl. No systematic pattern distinguishes them (both setValue and parse types appear in -each group). Generator uses the majority pattern (MX_RETURN_IS_SUCCESS). - -**25. MX_UNUSED(xelement) inconsistency in simple-value elements** -Most simple-value elements include `MX_UNUSED(xelement)` even though xelement IS used via -`getValue()`. A few (CreditType, Divisions, SystemDistance, Volume) do NOT include it. Generator -includes it to match the majority. - -**26. Constructor default values** -Some elements have explicit default values in their ctor init list (e.g., `Fifths::myValue{0}`, -`Duration::myValue(1.0)`). The generator uses `myValue()` (default-constructed). These are -type-specific defaults that would require per-type knowledge to reproduce. - -### Priority fixes for iteration 6 -1. ~~Fix complex element .cpp fromXElementImpl - use correct importElement/importGroup signatures~~ DONE -2. ~~Fix complex element .cpp streamContents - correct patterns for required/optional/set/group~~ DONE -3. ~~Fix complex element .cpp hasContents - return true for elements with required children~~ DONE -4. ~~Fix streamAttributes - use return myAttributes->toStream(os) not null check~~ DONE -5. ~~Inline non-GENERATE_GROUPS groups (duration, all-margins, staff, etc.)~~ DONE -6. ~~Handle complexType-level group refs (page-margins etc.)~~ DONE -7. ~~Sort .cpp child includes alphabetically~~ DONE -8. ~~Wrap ctor init lists at ~100 chars~~ DONE -9. Generate Choice classes (deferred to iteration 7) - -## Iteration 6 (2026-05-18) - -Fixes applied: -- **Group inlining**: Groups not in GENERATE_GROUPS (duration, all-margins, left-right-margins, - staff, tuning, voice, footnote, level, non-traditional-key, slash, traditional-key, part-group, - score-part) have their children inlined into parent elements. Propagates minOccurs from group ref - to inlined children (e.g. `` makes staff child optional). -- **Top-level group refs in complexTypes**: Types like `page-margins` that use - `` directly (no xs:sequence wrapper) now correctly parse children. -- **fromXElementImpl**: Changed from wrong `importElement(message, it, endIter, ...)` overloads to - correct signatures: `importElement(message, *it, isSuccess, *myX, myHasX)` with `continue` for - optional children, same with `isXFound` for required, `importGroup(...)` for groups, - `importElementSet(...)` for sets. Added `isDurationFound`-style local bools for required children. -- **streamContents**: Required children stream unconditionally with `->toStream()`. Groups wrapped - in `hasContents()` check with `->streamContents()`. Elements with required non-group children - get `isOneLineOnly = false;` at start + unconditional trailing endl. All-optional elements get - `isOneLineOnly = !hasContents();` at end + conditional endl. -- **hasContents**: Elements with any required non-group child return `true` directly. -- **streamAttributes**: Changed from `if (myAttributes) { ... }` guard to direct - `return myAttributes->toStream(os);` matching real code. -- **.cpp include sort**: Child element includes in .cpp files now sorted alphabetically. -- **Ctor init list wrapping**: Init lists wrap at ~110 chars (matching clang-format behavior). -- Added `make fmt` step to the workflow (run after generate.py, before eval.py). - -Diff: +2813 -6519 (593 changed files, up from 540 in iter 5 due to newly-generated children) -- .h: 403/592 exact (68.1%, slight increase from 67.9%) -- .cpp: 186/590 exact (31.5%, down from 40.6% - more files now generate children but with small - diffs, reducing average penalty per file) -- Eval penalty: 39,297 (down from 44,389 in iter 5, -11.5%) - -### Findings - -**27. XSD groups that are inlined vs wrapped** -The real code treats some XSD groups as direct children (inlined) and others as Group wrapper -classes. Groups in GENERATE_GROUPS get wrapper classes; all others are inlined. Key inlined groups: -`duration` (single element), `staff` (single optional element), `all-margins` (recursive inline to -4 margin elements), `tuning` (3 elements), `voice` (single element). - -**28. Required-child error messages are inconsistent** -Some elements (Clef) have `if (!isSignFound) { message << ... }` error messages after the loop in -fromXElementImpl, but most (Backup, Forward, etc.) do not. The generator currently omits these -messages to match the majority pattern. - -**29. isOneLineOnly placement: END pattern dominates** -Of 14 all-optional elements using `isOneLineOnly = !hasContents()`, 13 place it AFTER child -streaming (END) and use `if (!isOneLineOnly)`. Only AccordionRegistration places it BEFORE -children (START) and uses `if (hasContents())` at the end. - -**30. All-optional streamContents wrapping** -Some elements (Defaults, Properties) wrap the entire streamContents body in -`if (hasContents()) { ... } else { isOneLineOnly = true; }`. The generator uses the simpler -flat pattern. This accounts for some STRUCTURAL diffs in those files. - -**31. Direction.cpp custom helpers may be replaceable** -Direction.cpp has 680 lines with hand-written private helpers (`importDirectionTypeSet`, -`createDirectionType`, etc.). The generated version is 240 lines using standard patterns. These -may be functionally equivalent - testing needed. Same question applies to Credit.cpp. - -**32. Choice handling is the dominant remaining penalty source** -The top 10 penalty files are all related to xs:choice handling: Direction (choice within -sequence), Note (complex choice), Notations, Key, Harmony, Harmonic, Play, etc. The 10 skipped -choice elements plus choice members in complex elements account for ~60% of remaining STRUCTURAL -penalty. Some choice elements have dedicated Choice wrapper classes in real code; others inline the -choice logic directly. - -**33. Bounded maxOccurs (e.g. fermata maxOccurs=2)** -Barline's fermata uses maxOccurs=2 (not unbounded). The real code has bounds checking in -addFermata (`size() < 2`) and removeFermata (`size() > 0`). The generator treats all maxOccurs>1 -as unbounded sets without bounds. - -### Priority fixes for iteration 7 -1. Generate Choice classes - this is the single biggest penalty reduction opportunity (~60% of - remaining STRUCTURAL). Study the real code patterns for ArticulationsChoice, NoteChoice, - LyricTextChoice, KeyChoice etc. -2. Test whether generated Direction.cpp/Credit.cpp pass `make test` - if so, the custom helpers - are unnecessary and can be removed from the EXC list -3. Handle bounded maxOccurs (maxOccurs=2 for fermata, maxOccurs=8 for beam) -4. Fix all-optional streamContents wrapping (if hasContents() { ... } else { isOneLineOnly = true }) -5. Add blank line before MX_RETURN_IS_SUCCESS in fromXElementImpl loop - -## Iteration 7 - -### Fixes applied -- **Enum value parse fix**: Added `is_enum_value_type()` three-way check in `fromXElementImpl` for - simple-value elements. Enums now use `myValue = parseXxx(xelement.getValue())` instead of - `myValue.parse(...)`. Covers both `*Enum` suffix types and types in `needs_parse_func`. -- **Added to needs_parse_func**: BeaterValue, BreathMarkValue, HoleClosedValue, HoleClosedLocation, - TimeSeparator, PrincipalVoiceSymbol, ModeValue. -- **xs:integer type fix**: Changed mapping from `IntType` to `Integer`, added `Integer` to the - Integers.h include list. -- **Bespoke type mappings**: Added `distance-type` -> DistanceType, `line-width-type` -> - LineWidthType, `mode` -> ModeValue. -- **Group unbounded maxOccurs**: Fixed streamContents, hasContents, and fromXElementImpl to handle - group children with maxOccurs > 1 using Set iteration pattern (only harmony-chord had this). -- **Optional group importGroup**: Added `myHas` flag parameter for groups with minOccurs=0 - (e.g., DisplayStepOctaveGroup in Rest). -- **SKIP_ELEMENTS expanded**: Added part-list, harmony, credit, key, lyric, notations, note, - ornaments, part-abbreviation-display, part-name-display, score-instrument, score-part, - time-modification, instrument-sound. All have Choice classes or special patterns the generator - cannot yet produce. -- **GENERATE_GROUPS reduced**: Removed time-signature (has Interchangeable not in XSD group), - harmony-chord (has Choice logic), music-data (has MusicDataChoice), full-note (has - FullNoteTypeChoice). -- **mx/impl fixes** (minimal, with TODO: fixme comments): ArpeggiateFunctions.cpp, - NotationsWriter.cpp, OrnamentsFunctions.cpp - UpDownNone -> UpDown, hasLong -> hasLong_. - -### Eval score -- Total penalty: 29,998 (down from 39,297, -24%) -- .h: 427/592 exact (72.1%, up from 68.1%) -- .cpp: 202/590 exact (34.2%, up from 31.5%) -- Category breakdown: GOOD 535, EXPECTED 378, COSMETIC 386 (193 penalty), STRUCTURAL 5961 - (29,805 penalty), WRONG 0 -- `make check` passes (library compiles clean) -- `make test-all` fails due to test files referencing original API shapes - -### New findings - -**Finding 25**: Attribute group expansion is the single biggest remaining systemic issue. Many -attribute structs (MordentAttributes, AccidentalTextAttributes, HeelAttributes, ToeAttributes, -StrongAccentAttributes, etc.) are missing attributes inherited through `attributeGroup ref` and -`complexContent extension` chains. The XSD parser currently only collects direct `xs:attribute` -declarations, not those from referenced attribute groups like `print-style`, `trill-sound`, -`print-style-align`, etc. This causes ~500+ penalty points from missing attributes in `.cpp` -files and prevents test compilation. - -**Finding 26**: The `long` C++ keyword escaping produces `hasLong_` but the real code uses -`hasLong`. The real code avoids the keyword clash differently (the member is named `long_` but the -has-flag is `hasLong` without underscore). The generator should use `has{PascalName}` where -PascalName is the unescaped attribute name. - -**Finding 27**: Many elements with xs:choice children cannot be generated yet because the real code -wraps them in dedicated `*Choice` wrapper classes (NotationsChoice, NoteChoice, KeyChoice, -CreditChoice, OrnamentsChoice, LyricTextChoice, etc.). These follow the same set-choice/single- -choice patterns identified in iteration 6 but require per-element configuration. Currently 14 -elements and 4 groups are skipped due to missing Choice class generation. - -**Finding 28**: Some XSD simpleType restrictions on xs:string or xs:token are mapped to custom C++ -types in the real code (e.g., `mode` -> ModeValue, `instrument-sound` -> PlaybackSound, -`line-width-type` -> LineWidthType, `distance-type` -> DistanceType). These need explicit bespoke -type mappings since the generator defaults to XsString/XsToken. - -### Priority fixes for iteration 8 -1. **Attribute group expansion** - This is the highest-impact remaining fix. The XSD parser must - follow `attributeGroup ref` references and `complexContent extension base` chains to collect all - inherited attributes. This will fix ~500+ STRUCTURAL penalty points and unblock test compilation. - The groups to handle: print-style, print-style-align, trill-sound, text-formatting, etc. -2. **Expand Choice class generation** - Add more elements to CHOICE_ELEMENT_CONFIG to generate - their Choice wrapper classes. Priority: notations, note, ornaments, key, credit, lyric. -3. **Fix keyword escaping for has-flags** - `hasLong` not `hasLong_` for the bool flag when the - attribute name is a C++ keyword. -4. **Fix all-optional streamContents wrapping** - Still needed from iteration 6. -5. **Continue test compilation fixes** - Once attribute groups are expanded, retry `make test-all` - and fix remaining test compilation errors. diff --git a/docs/ai/projects/gen/log.md b/docs/ai/projects/gen/log.md index 0b03aba7..d01172ad 100644 --- a/docs/ai/projects/gen/log.md +++ b/docs/ai/projects/gen/log.md @@ -1,41 +1,103 @@ # gen Log -## 2026-05-18 (pre-project) - -Work began before the /project skill existed. Seven iterations of the codegen experiment were -completed in a single day. The generator (`gen/generate.py`) was built from scratch, starting with -basic element class generation and progressively adding attributes structs, group wrapper classes, -choice classes, enum handling, and fromXElementImpl/streamContents patterns. - -Iteration-by-iteration details and 28 numbered findings are preserved in `iteration-notes.md`. - -Summary of progress across iterations 1-7: -- Iter 1: basic element generation, 297/631 .h exact. Identified 14 diff categories. -- Iter 2: setValue/parse fix, attrs naming. 233/592 .h exact (regression from attrs refactor). -- Iter 3: include ordering, member ordering, empty private block fix. 386/592 .h exact. -- Iter 4: forward declare alphabetical sort. 386/592 .h exact (minimal change). -- Iter 5: Pattern B method ordering, group wrapper generation for 11 groups. 402/592 .h exact. -- Iter 6: group inlining, fromXElementImpl/streamContents patterns, hasContents, streamAttributes. - Eval penalty 39,297. 403/592 .h, 186/590 .cpp exact. -- Iter 7: choice classes for 5 elements, enum parse fix, type mappings, 14 elements skipped. - Eval penalty 29,998. 427/592 .h, 202/590 .cpp exact. - -Three design docs were produced through /grill-me sessions: overrides taxonomy, scoring mechanism, -and codegen forensics (all now in `design/`). - -## 2026-05-19 12:21 - -Project created under `docs/ai/projects/gen/`. Stub files only. Prior work existed in agenda.md, -gen-overrides.md, change-penalty.md, and gen-todo-move-me-later/. - -## 2026-05-19 12:42 - -Migrated all prior work into the /project structure: -- Promoted `gen/experiment/*` to `gen/*` (generator is no longer an "experiment") -- Moved design docs into `docs/ai/projects/gen/design/` (overrides.md, scoring.md, forensics.md) -- Moved experiment notes to `iteration-notes.md` as historical reference -- Updated eval.py path references from `gen/experiment/` to `gen/` -- Wrote index.md with instructions for coding agents -- Wrote state.md with current progress and next-session priorities -- Removed old `gen-todo-move-me-later/` directory -- Updated /project skill to clarify that subdirectories are allowed +Append chronologically, oldest on top. + +## M1: revgen (2026-05-18 — 2026-05-21, 40 iterations) + +Reverse-engineered the codegen. Iteratively shrank `SKIP_ELEMENTS` and `CHOICE_SKIP` to empty. +Closed 2026-05-21 with the generator producing every C++ class in `mx/core`. Tests failing — the +commit `d4f25ee6` "src: issues caused by revgen" hand-edits non-generated consumers to keep +the build working; those hand-edits were the input to M2. + +## M2: fix-gen (2026-05-21 — 2026-05-22) + +Triaged `d4f25ee6` into 6 root-cause issues (A–F), then triaged 62 (later 129 after a clean +build) `make test-all` failures into clusters. Fixed iteratively, ending at 0 failures. + +### Triage of `d4f25ee6` hand-edits + +- **A — UpDownNone collapsed to UpDown** (WEIRD / Low): Hand-applied MusicXML 4.0 backport + in original `mx/core` overwritten by schema-faithful 3.x regen. Deferred to M5; TODO + comments left in `mx/impl`. +- **B — `hasLong` escaped to `hasLong_`** (BUG / Low): Keyword-escape over-applied to the + has-prefix flag. Fix: `has_flag_name` strips trailing underscore added by `camel()` for + keywords. +- **C — MetronomeTuplet group flattened** (BUG / High): Regen flattened + `TimeModificationNormalTypeNormalDot` into `MetronomeTuplet`, making `` + unconditional. Fix: added `EXTENSION_OPTIONAL_GROUP_RENAME` (per-extending-type override) + + `SUPPRESS_GROUP_SUFFIX` + `WRAPPER_AS_ELEMENT_SYNTH_GROUPS` so the wrapper survives as a + sub-element rather than being inlined. +- **D — `setPerMinuteOtBeatUnitChoice` typo** (BENIGN): Historical typo in original; accepted + small test edit, no generator change. +- **E/F — Missing `importGroup` overloads for traditional-/non-traditional-key** (BUG / + Medium): Fix path option 1: emit `Key::importTraditionalKey` / `Key::importNonTraditionalKey` + as private member functions via new `TREE_ELEMENT_CONFIG["key"] = {"parent_imports_choice_groups": True}`. + Reverted hand-edits in `FromXElement.{h,cpp}`. + +### Failing-test clusters + +Original triage scheme R1–R7 was rewritten mid-M2 as the situation evolved. Final shape: + +- **R1 — direction-family parents emit `MX_FROM_XELEMENT_UNUSED`** (19 tests): split into + R1a (ArrowGroup needs real `fromXElementImpl` body — added "arrow" to + `GROUPS_WITH_REAL_FROM_X_ELEMENT`) and R1b (Direction parent bespoke — added + `_emit_direction_family` handler driven by `model.complex_types["direction-type"].content_tree.branches`). +- **R5 — required-set seeding** (10 tests including HarpPedals SIGSEGV): added structural + rule in `generate_element_cpp` for `min_occurs>=1, max_occurs!=1, not is_group` children: + ctor pre-seeds default, `hasContents()` returns true, `remove*` gates on `size>1`, + `clear*Set` re-seeds. +- **R3 — group-class `streamContents` inter-child `endl`** (~10 tests): rewrote + `generate_group_cpp` separator logic — required-after-required: unconditional endl before; + required-after-optional: optional's block emits; index-0 optional: endl after. +- **R2 — choice-class spurious leading `endl`** (~7 tests): gated on + `config.get("skip_parent")` in `generate_choice_class_cpp` so only `direction-type`-style + wrapper choices keep the leading endl. +- **R4 — attribute default initializers** (bulk fix, ~22 tests): seeded 17 entries in + `ATTR_DEFAULT_OVERRIDE` (lang="it" defaults, lineEnd=down, number="1", etc.). Continued + in i5 with `CHILD_INIT_VALUE_OVERRIDE` for `Scaling`/`StaffDetails`. +- **hasContents direction cluster** (6 tests): Rest/Unpitched gated optional groups on + `myHas`; MeasureLayout/NoteheadText/Slash/BeatRepeat needed + `ELEMENT_HAS_CONTENTS_ALWAYS_TRUE` / `CHILD_MIN_OCCURS_OVERRIDE` / notehead-text + `seed_choice_set`. +- **group/tree-group isFirst separator** (i4/i6, ~30 tests): `generate_group_cpp` and + `generate_tree_group_cpp` were emitting unconditional `endl` before each set-based child + even when all preceding optional singletons were absent. Fixed with `isFirst` flag + pattern. +- **MeasureLayout isOneLineOnly**: explicit child-presence check instead of `hasContents()` + for elements with `ELEMENT_HAS_CONTENTS_ALWAYS_TRUE`. + +### Final D-cluster (Metronome/Tempo round-trip) + +After mass cleanup, 13 failures remained, all metronome/tempo. Re-triaged as D1–D4: + +- **D3 — `MetronomeAttributes` missing `halign`/`justify` defaults** (`LeftCenterRight::center`): + trivial `ATTR_DEFAULT_OVERRIDE` entries. +- **D1 — choice classes called `toStream` for empty-`streamName` containers** (BeatUnitPer, + NoteRelationNote produced literal `<>...`): added `is_container` field to + `TreeChoiceBranch`; emit `streamContents` instead of `toStream` for container branches in + `generate_tree_choice_cpp`. +- **D2 — `DirectionWriter.cpp:370` throws on non-BPM tempos**: bisect confirmed pre-revgen + `a0500803` did pass these tests, so revgen introduced the regression in the reader/writer + path. Subsumed by D4. +- **D4 — Metronome container-branch `fromXElementImpl` missing**: `generate_tree_parent_cpp` + emitted no dispatch for `is_container=True` branches. Synthetic containers have no XML + tag, so the reader silently skipped all metronome children. Fix: added `trigger_names` + field to `TreeChoiceBranch` (computed from first child element name); emit + `importContainer` declarations and bodies in the parent; dispatch in + `fromXElementImpl` keyed on trigger names. Generated `Metronome::fromXElementImpl` matches + HEAD exactly. 9 → 0 failed. **M2 complete.** + +### Lessons / invariants captured + +- `git checkout -- src/private/mx/core/` preserves mtimes; incremental cmake then links + partly-old `.o`s, giving stale test counts. Use `make clean && make test-all` for any + authoritative measurement. +- `make test-all` must run with generated files present (Issue A's `UpDownNone` + hand-backport in HEAD is incompatible with the schema-faithful regen, so a reset-first + build fails to compile). +- When removing a previously-emitted byte from a shared template, survey the whole HEAD + population that template emits, not just one or two representative files (R2's + DirectionType regression). +- Bespoke handlers should still read the parsed XSD model — pattern is "custom algorithm, + schema-driven data" (Direction's element-name tables derived from + `model.complex_types["direction-type"].content_tree.branches`). diff --git a/docs/ai/projects/gen/plan.md b/docs/ai/projects/gen/plan.md index 94e4a06d..a18c1b36 100644 --- a/docs/ai/projects/gen/plan.md +++ b/docs/ai/projects/gen/plan.md @@ -1,64 +1,37 @@ # gen Plan -## Milestone 1: Reverse-engineer codegen (Phase 1) +## Milestone 1: revgen — reverse-engineer codegen ✅ -Produce a generator that, given `docs/musicxml.xsd`, regenerates `mx/core` with minimal unexplained -diff. Exit criteria (from `design/scoring.md`): +Build a generator that produces every C++ class in `mx/core` from `docs/musicxml.xsd` with no +skipped elements. -- **Gate A** (no-skip): every XSD element, type, attribute, attributeGroup, and group reaches a - code-emitting path. No skipping. -- **Gate B** (quality): `make fmt && make check && make test-all` passes. -- **Gate C** (classification): 100% of diff lines attributed to a category (RULE/EXC/FIX/ANOMALY). -- **Gate D** (residual): unexplained residual penalty = 0. +**Complete** (2026-05-21, iteration 40). SKIP_ELEMENTS and CHOICE_SKIP both empty. -Current status: Gate A not met (14 elements skipped). Gate B partially met (make check passes, -make test-all fails). Gates C and D not yet measured against the classification framework. +## Milestone 2: fix-gen — fix generator bugs surfaced as failing tests ✅ -### Remaining work for Milestone 1 +**Complete** (2026-05-22). All 2678 tests pass. -Ordered by expected impact on gates: +The commit "src: issues caused by revgen" contained hand-edits to non-generated code (mx/impl, +mx/core/FromXElement, some tests) that worked around generator bugs. Each was triaged as BUG / +BENIGN / WEIRD, generator bugs were fixed, and the workarounds reverted. -1. Attribute group expansion (unblocks Gate A and Gate B) -2. Choice class generation for remaining skipped elements (Gate A) -3. Keyword escaping fixes (Gate B) -4. streamContents pattern refinements (Gate D) -5. Classification of all remaining diff lines (Gate C) -6. Residual reduction to zero (Gate D) +One WEIRD item deferred to M5: original `mx/core` had a hand-applied MusicXML 4.0 `UpDownNone` +backport that a schema-faithful 3.x regen overwrites. The `// TODO: fixme - MusicXML 4.0 ...` +comments in `mx/impl` (NotationsWriter.cpp:398, ArpeggiateFunctions.cpp:35) bookmark this. -### Open design questions (need /grill-me sessions) +## Milestone 3: increase test coverage -Two grill-me sessions were completed (overrides -> `design/overrides.md`, scoring -> -`design/scoring.md`). Three remain: +Add a lot more MusicXML round-trip input files. Build a dedicated mx/core-level round-trip test +harness (not just api-level freezing tests). No specific design yet. -- [ ] **CODEGEN_PROGRAM_QUALITY criteria** - how is program quality measured concretely? Static - analysis tools, manual rubric, or both? -- [ ] **Language constraints** - which languages are permitted for competing agents? (Currently - Python is the de facto choice.) -- [ ] **Systematic-gap scoring** - how does scoring handle a program that is systematically correct - for 95% of elements but misses one XSD pattern that appears 300 times? The missing-color FIX - (45 structs) is a concrete instance of the inverse problem. +## Milestone 4: better-gen — fix garbage -## Milestone 2: Improve codegen / modernize C++ (Phase 2) +The gen program is 12k lines of bad Python. Fix it. Use dedicated mx/core round-trip tests as the +north star for correctness. -Improve the generated code from the current MusicXML spec: -- more consistent patterns -- modern C++ (e.g. std::variant to reduce object sizes) -- fewer bespoke alterations +## Milestone 5: mxml4-types — generate MusicXML 4.0 types -## Milestone 3: Analyze mx/api coverage (Phase 3) - -Determine which MusicXML features are inaccessible from `mx/api`. - -## Milestone 4: Generate MusicXML 4.0 types (Phase 4) - -Replace `docs/musicxml.xsd` with MusicXML 4.0, regenerate. Fix all existing tests. Watch for -backported or bolted-on features like SMuFL that were added with hacks to 3.0/3.1 but are now -first-class in 4.0. Be backward compatible with files that `mx` may have written with those hacks. - -## Milestone 5: Surface 4.0 features to mx/api (Phase 5) - -Expose new MusicXML 4.0 features through the public API. Prioritize those users will want. - -## Milestone 6: PR - -Open a PR introducing MusicXML 4.0 support. +Replace `docs/musicxml.xsd` with MusicXML 4.0, regenerate, fix all existing tests. Watch for +backported or bolted-on features (SMuFL, UpDown, etc.) that were added with hacks to 3.0/3.1 but +are first-class in 4.0. Be backward-compatible with files `mx` may have written using those hacks. +Restore the `mx/impl` TODOs from revgen. diff --git a/docs/ai/projects/gen/state.md b/docs/ai/projects/gen/state.md index 3bd58723..e48c2075 100644 --- a/docs/ai/projects/gen/state.md +++ b/docs/ai/projects/gen/state.md @@ -2,52 +2,26 @@ ## Milestone -Milestone 1: Reverse-engineer codegen. Working toward Gate B (`make test-all` passing) and reducing -eval penalty toward zero unexplained residual. - -## What was done last session - -Completed iteration 7. Key changes to `gen/generate.py`: -- Choice class generation for 5 elements (direction-type, articulations, bend, technical, percussion) -- Enum parse three-way fix (enum types now use `parseXxx()` not `.parse()`) -- xs:integer mapped to Integer, bespoke type mappings for distance-type, line-width-type, mode -- Group unbounded maxOccurs handling (harmony-chord) -- Optional group importGroup fix (DisplayStepOctaveGroup in Rest) -- Expanded SKIP_ELEMENTS to 14 elements, reduced GENERATE_GROUPS to avoid Choice-dependent groups - -Results: eval penalty 29,998 (down from 39,297, -24%). .h 427/592 exact (72.1%), .cpp 202/590 -(34.2%). `make check` passes. `make test-all` fails. +**M1 and M2 complete.** `make test-all`: 0 failed (2678 cases, 9914 assertions). ## What the next session should do -Pick the highest-impact fix from the priority list and implement it. Run the generator, eval, study -the diff, update `iteration-notes.md` and this file, commit. - -### Priority fixes (ordered by impact) - -1. **Attribute group expansion** - the single biggest systemic issue. The XSD parser must follow - `attributeGroup ref` and `complexContent extension base` chains to collect all inherited - attributes. Missing attributes cause ~500+ STRUCTURAL penalty and block test compilation. Groups: - print-style, print-style-align, trill-sound, text-formatting, bend-sound, etc. - -2. **Choice class generation for skipped elements** - 14 elements are in SKIP_ELEMENTS to avoid API - shape mismatches with mx/impl. The goal is to generate them properly. Priority: notations, note, - ornaments, key, credit, lyric. Each needs a CHOICE_ELEMENT_CONFIG entry. - -3. **Keyword escaping for has-flags** - use `hasLong` not `hasLong_` when the attribute name is a - C++ keyword. The member is `long_` but the bool flag omits the underscore. - -4. **All-optional streamContents wrapping** - some elements use `if (hasContents()) { ... } else { - isOneLineOnly = true; }` wrapping. The generator uses a simpler flat pattern. - -### Gotchas for the next agent - -- The 14 SKIP_ELEMENTS hide real problems. Removing an element from SKIP_ELEMENTS will likely cause - compilation failures until its Choice class and attribute groups are handled. -- Attribute ordering in structs matters. The existing code uses XSD document order for attributes - within a group, but the order of group expansion (which group's attrs come first) must match the - original code. Study a few real attribute structs before implementing. -- Three mx/impl files have `TODO: fixme` comments from iter 7 (UpDownNone->UpDown, - hasLong->hasLong_ changes): ArpeggiateFunctions.cpp, NotationsWriter.cpp, - OrnamentsFunctions.cpp. These need to be reverted or the generated code needs to match. -- `gen/eval_config.yaml` must not be modified without user approval. Flag patterns, don't fix them. +**Milestone 3: increase test coverage.** Per `plan.md`: more MusicXML round-trip files and a +dedicated mx/core-level round-trip harness. + +No design yet. Next session should: +1. Review existing test suite to understand coverage gaps. +2. Propose a coverage plan to the user. +3. Wait for user direction before implementing. + +## Gotchas + +- **`make test-all` must run WITH generated files present.** HEAD's `ArpeggiateAttributes.h` has + a hand-applied `UpDownNone direction` (MusicXML 4.0 backport) but `NotationsWriter.cpp` assigns + `UpDown::down` — incompatible. The generator emits `UpDown` (from 3.x XSD), so the build only + succeeds with generated files present. Workflow: `python3 gen/generate.py && make fmt` THEN + `make test-all`. Reset AFTER it passes. +- **`git checkout -- src/private/mx/core/` preserves mtimes**, so incremental cmake builds skip + recompiling unchanged-mtime .cpp files. Stale-build measurements bit us mid-M2. Use + `make clean && make test-all` for an authoritative count. +- **Use `make test-all`, never `make test`.** Build target for tests is `make core`. diff --git a/docs/sounds.xml b/docs/sounds.xml new file mode 100644 index 00000000..b604c19a --- /dev/null +++ b/docs/sounds.xml @@ -0,0 +1,920 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8ab01ed5164e90bf17a0a107c02363a83234307b Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Fri, 22 May 2026 11:35:30 +0200 Subject: [PATCH 2/4] gen: add revgen generator for reproducing mx/core from MusicXML XSD Lands the reverse-engineered code generator that reproduces the existing src/private/mx/core/elements/ classes from docs/musicxml.xsd. This is the Milestone 1 (revgen) deliverable from docs/ai/projects/gen/plan.md: a generator that emits every C++ element class in mx/core with no skipped elements (SKIP_ELEMENTS and CHOICE_SKIP are both empty as of revgen iteration 40). Contents: - gen/generate.py - the generator (~13.8k lines of Python). Parses the XSD, walks the model, and emits .h/.cpp pairs into src/private/mx/core/elements/. Combines a shared rule-based path (TREE_ELEMENTS / TREE_ELEMENT_CONFIG) with six bespoke handlers (credit, lyric, part-list, harmony, score-wrapper-family, note) registered in BESPOKE_ELEMENTS for elements whose shape cannot be expressed by the shared path. Bespoke handlers still drive off the parsed XSD model so spec changes propagate automatically. - gen/eval.py - diff scoring tool that compares generated output to the checked-in mx/core. Used as a secondary diagnostic signal during revgen; make test-all is the primary fitness function from M2 on. - gen/eval_config.yaml - scoring category rules consumed by eval.py. Edits to this file require user approval (see project index.md). - gen/.gitignore - ignores local generator scratch output. - gen/gen_attrs.py, gen/gen_enum_members.py, gen/gen_enums.py - promoted out of gen/experiment/ now that they are part of the supported toolchain. Generated mx/core code is not checked in; the workflow is `python3 gen/generate.py && make fmt && make test-all`, then reset with `git checkout -- src/private/mx/core/ && git clean -fd src/private/mx/core/`. --- gen/.gitignore | 1 + gen/eval.py | 492 + gen/eval_config.yaml | 78 + gen/{experiment => }/gen_attrs.py | 0 gen/{experiment => }/gen_enum_members.py | 0 gen/{experiment => }/gen_enums.py | 0 gen/generate.py | 13802 +++++++++++++++++++++ 7 files changed, 14373 insertions(+) create mode 100644 gen/.gitignore create mode 100644 gen/eval.py create mode 100644 gen/eval_config.yaml rename gen/{experiment => }/gen_attrs.py (100%) rename gen/{experiment => }/gen_enum_members.py (100%) rename gen/{experiment => }/gen_enums.py (100%) create mode 100644 gen/generate.py diff --git a/gen/.gitignore b/gen/.gitignore new file mode 100644 index 00000000..c18dd8d8 --- /dev/null +++ b/gen/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/gen/eval.py b/gen/eval.py new file mode 100644 index 00000000..fe1fff91 --- /dev/null +++ b/gen/eval.py @@ -0,0 +1,492 @@ +#!/usr/bin/env python3 +""" +Score the diff between generated and original mx/core files. + +Usage: + python3 gen/eval.py [--config path/to/config.yaml] [--verbose] + +Run gen/generate.py first, then this script scores the resulting +git diff against src/private/mx/core/. +""" + +import importlib +import re +import subprocess +import sys +from collections import defaultdict +from dataclasses import dataclass, field +from pathlib import Path + +import yaml + + +@dataclass +class DiffLine: + content: str + is_added: bool + file_path: str + hunk_id: int + + +@dataclass +class DiffHunk: + file_path: str + hunk_id: int + lines: list # list of DiffLine + context: list # context lines (unchanged) for reference + + +@dataclass +class ClassifiedLine: + line: DiffLine + category: str + rule_name: str + penalty: float + + +def get_diff(): + result = subprocess.run( + ["git", "diff", "-U3", "--", "src/private/mx/core/"], + capture_output=True, + text=True, + ) + return result.stdout + + +def parse_diff(diff_text: str) -> list: + """Parse unified diff into DiffHunks.""" + hunks = [] + current_file = None + current_hunk_lines = [] + current_context = [] + hunk_id = 0 + + for raw_line in diff_text.split("\n"): + if raw_line.startswith("diff --git"): + if current_hunk_lines: + hunks.append( + DiffHunk(current_file, hunk_id, current_hunk_lines, current_context) + ) + current_hunk_lines = [] + current_context = [] + match = re.search(r" b/(.+)$", raw_line) + if match: + current_file = match.group(1) + elif raw_line.startswith("@@"): + if current_hunk_lines: + hunks.append( + DiffHunk(current_file, hunk_id, current_hunk_lines, current_context) + ) + hunk_id += 1 + current_hunk_lines = [] + current_context = [] + elif raw_line.startswith("+") and not raw_line.startswith("+++"): + current_hunk_lines.append( + DiffLine(raw_line[1:], True, current_file, hunk_id) + ) + elif raw_line.startswith("-") and not raw_line.startswith("---"): + current_hunk_lines.append( + DiffLine(raw_line[1:], False, current_file, hunk_id) + ) + elif not raw_line.startswith("\\"): + current_context.append(raw_line) + + if current_hunk_lines: + hunks.append( + DiffHunk(current_file, hunk_id, current_hunk_lines, current_context) + ) + + return hunks + + +def is_whitespace_only_hunk(hunk: DiffHunk) -> bool: + """Check if a hunk's changes are purely whitespace/formatting.""" + removed = [dl.content for dl in hunk.lines if not dl.is_added] + added = [dl.content for dl in hunk.lines if dl.is_added] + + removed_stripped = "".join("".join(l.split()) for l in removed) + added_stripped = "".join("".join(l.split()) for l in added) + + if not removed_stripped and not added_stripped: + return True + + if removed_stripped == added_stripped: + return True + + return False + + +def detect_const_qualification(hunk: DiffHunk) -> list: + """Detect const bool -> bool changes in a hunk. Returns indices of matched lines.""" + removed = [(i, dl) for i, dl in enumerate(hunk.lines) if not dl.is_added] + added = [(i, dl) for i, dl in enumerate(hunk.lines) if dl.is_added] + + matched_indices = set() + for ri, rdl in removed: + rs = rdl.content.strip() + if "const bool" not in rs and "const " not in rs: + continue + without_const = rs.replace("const ", "", 1) + for ai, adl in added: + if ai in matched_indices: + continue + if adl.content.strip() == without_const: + matched_indices.add(ri) + matched_indices.add(ai) + break + + return matched_indices + + +def detect_enum_parse_change(hunk: DiffHunk) -> list: + """Detect parseXEnum() -> myValue.parse() changes.""" + matched = set() + for i, dl in enumerate(hunk.lines): + content = dl.content.strip() + if not dl.is_added and re.search(r"parse\w+Enum\(", content): + matched.add(i) + elif dl.is_added and "myValue.parse(" in content: + for j, other in enumerate(hunk.lines): + if not other.is_added and re.search(r"parse\w+Enum\(", other.content): + matched.add(i) + matched.add(j) + break + + return matched + + +def detect_ctor_default_change(hunk: DiffHunk) -> list: + """Detect myValue(SomeDefault) -> myValue() changes.""" + matched = set() + for i, dl in enumerate(hunk.lines): + content = dl.content.strip() + if not dl.is_added and re.search( + r"myValue\([A-Z]\w+::\w+\)", content + ): + for j, other in enumerate(hunk.lines): + if other.is_added and "myValue()" in other.content: + matched.add(i) + matched.add(j) + break + elif not dl.is_added and re.search(r"myValue\{\d+\}", content): + for j, other in enumerate(hunk.lines): + if other.is_added and "myValue()" in other.content: + matched.add(i) + matched.add(j) + break + elif not dl.is_added and re.search(r"myValue\(\d+\.\d+\)", content): + for j, other in enumerate(hunk.lines): + if other.is_added and "myValue()" in other.content: + matched.add(i) + matched.add(j) + break + + return matched + + +def classify_hunks(hunks: list, config: dict, + overwrite_files: set = None) -> list: + """Classify all diff lines using hunk-level and line-level rules.""" + results = [] + weights = config["weights"] + defaults = config.get("defaults", {}) + file_cats = config.get("file_categories", {}) + line_rules = config.get("line_rules", {}) + if overwrite_files is None: + overwrite_files = set() + + for hunk in hunks: + if hunk.file_path and overwrite_files: + basename = Path(hunk.file_path).stem if hunk.file_path else "" + if basename in overwrite_files: + for dl in hunk.lines: + results.append( + ClassifiedLine(dl, "EXPECTED", "overwrite element", 0) + ) + continue + + file_override = None + for cat, entries in file_cats.items(): + for entry in entries or []: + pattern = entry if isinstance(entry, str) else entry.get("pattern", "") + if re.search(pattern, hunk.file_path or ""): + file_override = cat + break + if file_override: + break + + if file_override: + for dl in hunk.lines: + results.append( + ClassifiedLine( + dl, file_override, "file override", weights.get(file_override, 0) + ) + ) + continue + + if is_whitespace_only_hunk(hunk): + for dl in hunk.lines: + results.append( + ClassifiedLine(dl, "EXPECTED", "whitespace/formatting only", 0) + ) + continue + + const_indices = detect_const_qualification(hunk) + enum_indices = detect_enum_parse_change(hunk) + ctor_indices = detect_ctor_default_change(hunk) + + for i, dl in enumerate(hunk.lines): + if i in const_indices: + results.append( + ClassifiedLine( + dl, "COSMETIC", "const qualification change", weights["COSMETIC"] + ) + ) + continue + + if i in enum_indices: + results.append( + ClassifiedLine( + dl, "STRUCTURAL", "enum parse pattern change", weights["STRUCTURAL"] + ) + ) + continue + + if i in ctor_indices: + results.append( + ClassifiedLine( + dl, + "STRUCTURAL", + "ctor default value missing", + weights["STRUCTURAL"], + ) + ) + continue + + classified = False + for cat in ["GOOD", "EXPECTED", "COSMETIC", "STRUCTURAL", "WRONG"]: + for rule in line_rules.get(cat, []): + if re.search(rule["pattern"], dl.content): + results.append( + ClassifiedLine( + dl, + cat, + rule.get("description", rule["pattern"]), + weights.get(cat, 0), + ) + ) + classified = True + break + if classified: + break + + if not classified: + if dl.content.strip() == "": + results.append( + ClassifiedLine(dl, "EXPECTED", "blank line", 0) + ) + else: + ext = ( + Path(dl.file_path).suffix if dl.file_path else ".cpp" + ) + default_cat = defaults.get(ext, "STRUCTURAL") + results.append( + ClassifiedLine( + dl, + default_cat, + f"unclassified ({ext})", + weights.get(default_cat, 5), + ) + ) + + return results + + +def report(classified: list, config: dict, verbose: bool = False, + skip_elements: set = None, overwrite_stems: set = None, + choice_skip_elements: set = None): + weights = config["weights"] + if skip_elements is None: + skip_elements = set() + if overwrite_stems is None: + overwrite_stems = set() + if choice_skip_elements is None: + choice_skip_elements = set() + + cat_lines = defaultdict(int) + cat_penalty = defaultdict(float) + rule_counts = defaultdict(lambda: defaultdict(int)) + file_penalties = defaultdict(float) + file_cats = defaultdict(lambda: defaultdict(int)) + file_lines = defaultdict(int) + + for cl in classified: + cat_lines[cl.category] += 1 + cat_penalty[cl.category] += cl.penalty + rule_counts[cl.category][cl.rule_name] += 1 + if cl.line.file_path: + file_penalties[cl.line.file_path] += cl.penalty + file_cats[cl.line.file_path][cl.category] += 1 + file_lines[cl.line.file_path] += 1 + + total_penalty = sum(cat_penalty.values()) + total_lines = sum(cat_lines.values()) + skip_penalty = len(skip_elements) * SKIP_PENALTY + choice_skip_penalty = len(choice_skip_elements) * SKIP_PENALTY + + zero_penalty_lines = cat_lines.get("GOOD", 0) + cat_lines.get("EXPECTED", 0) + zero_pct = (zero_penalty_lines / total_lines * 100) if total_lines else 0 + + print(f"\n{'='*70}") + print("CODEGEN DIFF SCORE") + print(f"{'='*70}") + print(f"Total changed lines: {total_lines}") + print(f"Diff penalty: {total_penalty:.1f}") + if skip_elements: + print(f"Skip penalty: {skip_penalty} ({len(skip_elements)} elements x {SKIP_PENALTY})") + sorted_skips = sorted(skip_elements) + for s in sorted_skips: + print(f" - {s}") + if choice_skip_elements: + print(f"Choice-skip penalty: {choice_skip_penalty} ({len(choice_skip_elements)} elements x {SKIP_PENALTY})") + for s in sorted(choice_skip_elements): + print(f" - {s}") + print(f"Total penalty: {total_penalty + skip_penalty + choice_skip_penalty:.1f}") + if overwrite_stems: + print(f"Overwrite files: {len(overwrite_stems)} (exempt from penalty)") + for s in sorted(overwrite_stems): + print(f" - {s}") + print(f"Zero-penalty lines: {zero_penalty_lines} ({zero_pct:.1f}%)") + print() + + print("Category breakdown:") + print(f" {'Category':12s} {'Lines':>6s} {'Weight':>6s} {'Penalty':>10s}") + print(f" {'-'*12} {'-'*6} {'-'*6} {'-'*10}") + for cat in ["GOOD", "EXPECTED", "COSMETIC", "STRUCTURAL", "WRONG"]: + lines = cat_lines.get(cat, 0) + weight = weights.get(cat, 0) + penalty = cat_penalty.get(cat, 0) + print(f" {cat:12s} {lines:6d} {weight:6.1f} {penalty:10.1f}") + print() + + print("Rules breakdown:") + for cat in ["GOOD", "EXPECTED", "COSMETIC", "STRUCTURAL", "WRONG"]: + rules = rule_counts.get(cat, {}) + if rules: + print(f" {cat}:") + for rule, count in sorted(rules.items(), key=lambda x: -x[1]): + print(f" {count:5d} {rule}") + print() + + penalized_files = { + fp: pen for fp, pen in file_penalties.items() if pen > 0 + } + print(f"Penalized files: {len(penalized_files)} of {len(file_penalties)}") + print() + print("Top 20 penalized files:") + print(f" {'Penalty':>8s} {'File':40s} Categories") + print(f" {'-'*8} {'-'*40} {'-'*30}") + for fp, pen in sorted(penalized_files.items(), key=lambda x: -x[1])[:20]: + basename = Path(fp).name if fp else "?" + cats = dict(file_cats[fp]) + cat_parts = [] + for c in ["GOOD", "EXPECTED", "COSMETIC", "STRUCTURAL", "WRONG"]: + if c in cats: + cat_parts.append(f"{c[:4]}:{cats[c]}") + cat_str = " ".join(cat_parts) + print(f" {pen:8.1f} {basename:40s} {cat_str}") + + if verbose: + print(f"\n{'='*70}") + print("VERBOSE: All penalized lines") + print(f"{'='*70}") + for cl in classified: + if cl.penalty > 0: + prefix = "+" if cl.line.is_added else "-" + basename = ( + Path(cl.line.file_path).name if cl.line.file_path else "?" + ) + content = cl.line.content[:60] + print( + f" {cl.penalty:5.1f} {cl.category:12s} {basename:30s} " + f"{prefix}{content}" + ) + + +SKIP_PENALTY = 1000 + + +def get_skip_elements(): + """Import SKIP_ELEMENTS from generate.py.""" + gen_dir = Path(__file__).parent + sys.path.insert(0, str(gen_dir)) + try: + mod = importlib.import_module("generate") + return set(mod.SKIP_ELEMENTS) + finally: + sys.path.pop(0) + + +def get_choice_skip_elements(): + """Import CHOICE_SKIP from generate.py.""" + gen_dir = Path(__file__).parent + sys.path.insert(0, str(gen_dir)) + try: + mod = importlib.import_module("generate") + return set(mod.CHOICE_SKIP) + finally: + sys.path.pop(0) + + +def get_overwrite_file_stems(): + """Import OVERWRITE_FILE_STEMS from generate.py. + + These are file stems (e.g. "Direction", "DirectionType") whose + generated code intentionally replaces the original bespoke + implementation. Diffs for these files are scored as EXPECTED (zero + penalty) because the generated output is the new source of truth. + """ + gen_dir = Path(__file__).parent + sys.path.insert(0, str(gen_dir)) + try: + mod = importlib.import_module("generate") + return set(getattr(mod, "OVERWRITE_FILE_STEMS", set())) + finally: + sys.path.pop(0) + + +def main(): + config_path = "gen/eval_config.yaml" + verbose = False + + args = sys.argv[1:] + while args: + if args[0] == "--config" and len(args) > 1: + config_path = args[1] + args = args[2:] + elif args[0] == "--verbose": + verbose = True + args = args[1:] + else: + args = args[1:] + + with open(config_path) as f: + config = yaml.safe_load(f) + + diff_text = get_diff() + if not diff_text.strip(): + print("No diff found. Run generate.py first.") + sys.exit(1) + + hunks = parse_diff(diff_text) + overwrite_stems = get_overwrite_file_stems() + classified = classify_hunks(hunks, config, overwrite_stems) + + skip_elements = get_skip_elements() + choice_skip_elements = get_choice_skip_elements() + report(classified, config, verbose, skip_elements, overwrite_stems, + choice_skip_elements) + + +if __name__ == "__main__": + main() diff --git a/gen/eval_config.yaml b/gen/eval_config.yaml new file mode 100644 index 00000000..3cd8fbaf --- /dev/null +++ b/gen/eval_config.yaml @@ -0,0 +1,78 @@ +# Codegen diff scoring configuration. +# +# To add a new zero-cost pattern, put it under the appropriate category +# in line_rules. Patterns are Python regex matched against the diff line +# content (without the +/- prefix). First matching rule wins. + +weights: + GOOD: 0 + EXPECTED: 0 + COSMETIC: 0.5 + STRUCTURAL: 5 + WRONG: 10 + +# Default category for lines that match no rule. +defaults: + ".h": STRUCTURAL + ".cpp": STRUCTURAL + +# Files where ALL changes get this category. +# Use for files with known acceptable differences. +file_categories: + EXPECTED: + - "NotationsChoice.h" + - "NotationsChoice.cpp" + - "Notations.cpp" + - "OrnamentsChoice.h" + - "OrnamentsChoice.cpp" + - "Ornaments.cpp" + - "Ornaments.h" + GOOD: [] + +# Line-level pattern rules. First match wins. +# "pattern" is Python regex matched against line content. +line_rules: + GOOD: + - pattern: "Color color" + description: "Color attribute member (fixing original omission)" + - pattern: "hasColor" + description: "Color hasColor flag (fixing original omission)" + - pattern: '#include "mx/core/Color.h"' + description: "Color include (fixing original omission)" + - pattern: "bool hasColor" + description: "Color hasColor member added" + + EXPECTED: + - pattern: "(?i)backport" + description: "MusicXML 4.0 backport reference" + - pattern: "UpDownNone" + description: "MusicXML 4.0 backport type (UpDownNone -> UpDown in 3.x)" + + # These attribute members are being correctly added by the generator + # for types where the original code was missing them. + - pattern: "Valign valign" + description: "Valign attribute added (fixing omission)" + - pattern: "hasValign" + description: "Valign flag added (fixing omission)" + - pattern: "XmlLang lang" + description: "XmlLang attribute added (fixing omission)" + - pattern: "TextDirection dir" + description: "TextDirection attribute added (fixing omission)" + - pattern: "XmlSpace space" + description: "XmlSpace attribute added (fixing omission)" + + COSMETIC: + - pattern: "MX_RETURN_IS_SUCCESS" + description: "Macro vs direct return inconsistency" + - pattern: "return isSuccess;" + description: "Direct return vs macro inconsistency" + - pattern: "MX_UNUSED\\(xelement\\)" + description: "MX_UNUSED(xelement) presence inconsistency" + - pattern: "setAttributes\\(const.*&attributes\\)" + description: "setAttributes parameter named attributes vs value" + - pattern: "setAttributes\\(const.*&value\\)" + description: "setAttributes parameter named value vs attributes" + - pattern: "/\\* _+" + description: "Section comment header formatting" + - pattern: "_+ \\*/" + description: "Section comment header formatting" diff --git a/gen/experiment/gen_attrs.py b/gen/gen_attrs.py similarity index 100% rename from gen/experiment/gen_attrs.py rename to gen/gen_attrs.py diff --git a/gen/experiment/gen_enum_members.py b/gen/gen_enum_members.py similarity index 100% rename from gen/experiment/gen_enum_members.py rename to gen/gen_enum_members.py diff --git a/gen/experiment/gen_enums.py b/gen/gen_enums.py similarity index 100% rename from gen/experiment/gen_enums.py rename to gen/gen_enums.py diff --git a/gen/generate.py b/gen/generate.py new file mode 100644 index 00000000..369a3502 --- /dev/null +++ b/gen/generate.py @@ -0,0 +1,13802 @@ +#!/usr/bin/env python3 +"""MusicXML codegen experiment: generate mx/core element classes from musicxml.xsd. + +Iteration 6: Group inlining, fromXElementImpl/streamContents/hasContents fixes. +""" +import os +import re +import sys +import xml.etree.ElementTree as ET +from collections import OrderedDict +from dataclasses import dataclass, field +from typing import Optional + +XS = "{http://www.w3.org/2001/XMLSchema}" +XSD_PATH = "docs/musicxml.xsd" +CORE_DIR = "src/private/mx/core" +ELEM_DIR = os.path.join(CORE_DIR, "elements") + +LICENSE = """\ +// MusicXML Class Library +// Copyright (c) by Matthew James Briggs +// Distributed under the MIT License +""" + +CPP_KEYWORDS = { + "continue", "double", "long", "short", "int", "float", "bool", "char", + "class", "struct", "enum", "union", "void", "for", "while", "do", "if", + "else", "switch", "case", "default", "break", "return", "new", "delete", + "this", "true", "false", "const", "static", "virtual", "public", "private", + "protected", "namespace", "using", "template", "typename", "operator", + "and", "or", "not", "xor", "auto", "register", "signed", "unsigned", + "goto", "throw", "try", "catch", "explicit", "string", +} + + +def pascal(name: str) -> str: + return "".join(p[:1].upper() + p[1:] for p in re.split(r"[-_]", name)) + + +def camel(name: str) -> str: + parts = re.split(r"[-_]", name) + result = parts[0].lower() + "".join(p[:1].upper() + p[1:] for p in parts[1:]) + if result in CPP_KEYWORDS: + result += "_" + return result + + +def has_flag_name(cpp_n: str) -> str: + # The presence flag is built from the unescaped identifier: the value field may + # be keyword-escaped (e.g. 'long_'), but the has-flag must not be ('hasLong', + # not 'hasLong_'). Strip a trailing underscore added by camel() for keywords. + base = cpp_n[:-1] if cpp_n.endswith("_") and cpp_n[:-1] in CPP_KEYWORDS else cpp_n + return "has" + base[0].upper() + base[1:] + + +# --------------------------------------------------------------------------- +# XSD Model +# --------------------------------------------------------------------------- + + +@dataclass +class XsdAttribute: + name: str + type_name: str + use: str = "optional" + xml_name: str = "" + + def get_xml_name(self) -> str: + return self.xml_name or self.name + + +@dataclass +class XsdEnumType: + name: str + values: list + + +@dataclass +class XsdChildRef: + """A reference to a child element within a sequence or choice.""" + element_name: str + min_occurs: int = 1 + max_occurs: int = 1 # -1 = unbounded + is_group: bool = False + + +# --------------------------------------------------------------------------- +# Content Tree Nodes (preserves nested choice/sequence structure) +# --------------------------------------------------------------------------- + + +@dataclass +class ElementRefNode: + element_name: str + min_occurs: int = 1 + max_occurs: int = 1 # -1 = unbounded + + +@dataclass +class GroupRefNode: + group_name: str + min_occurs: int = 1 + max_occurs: int = 1 + + +@dataclass +class SequenceNode: + children: list = field(default_factory=list) + min_occurs: int = 1 + max_occurs: int = 1 + + +@dataclass +class ChoiceNode: + branches: list = field(default_factory=list) + min_occurs: int = 1 + max_occurs: int = 1 + + +@dataclass +class XsdComplexType: + name: str + attributes: list = field(default_factory=list) + children: list = field(default_factory=list) + has_simple_content: bool = False + simple_content_base: str = "" + has_choice: bool = False + choice_children: list = field(default_factory=list) + mixed: bool = False + extension_base: str = "" + content_tree: Optional[object] = None + + +@dataclass +class XsdElement: + name: str + type_name: str = "" + anonymous_type: Optional[XsdComplexType] = None + + +class XsdModel: + def __init__(self, xsd_path): + self.tree = ET.parse(xsd_path) + self.root = self.tree.getroot() + self.enum_types: dict[str, XsdEnumType] = OrderedDict() + self.complex_types: dict[str, XsdComplexType] = OrderedDict() + self.simple_types: dict[str, dict] = OrderedDict() + self.attribute_groups: dict[str, list] = OrderedDict() + self.groups: dict[str, list] = OrderedDict() + self.elements: dict[str, XsdElement] = OrderedDict() + self.class_names: set = set() + self._parse() + + def _parse(self): + self._parse_attribute_groups() + self._parse_simple_types() + self._parse_complex_types() + self._parse_groups() + self._inline_non_generated_groups() + self._parse_elements() + self._build_class_names() + + def _parse_attribute_groups(self): + for ag in self.root.iter(f"{XS}attributeGroup"): + name = ag.get("name") + if name: + self.attribute_groups[name] = self._collect_attrs(ag) + + def _collect_attrs(self, node, seen=None): + if seen is None: + seen = set() + out = [] + for child in node: + tag = child.tag + if tag == f"{XS}attribute" and child.get("name"): + out.append(XsdAttribute( + name=child.get("name"), + type_name=child.get("type", "xs:string"), + use=child.get("use", "optional"), + )) + elif tag == f"{XS}attribute" and child.get("ref"): + ref = child.get("ref") + local_name = ref.split(":")[-1] if ":" in ref else ref + out.append(XsdAttribute( + name=local_name, + type_name=ref, + use=child.get("use", "optional"), + xml_name=ref, + )) + elif tag == f"{XS}attributeGroup" and child.get("ref"): + ref = child.get("ref") + if ref not in seen: + seen.add(ref) + if ref in self.attribute_groups: + out.extend(self.attribute_groups[ref]) + else: + deferred = self._collect_attrs_from_ref(ref) + out.extend(deferred) + elif tag in (f"{XS}simpleContent", f"{XS}complexContent", + f"{XS}extension", f"{XS}restriction"): + out.extend(self._collect_attrs(child, seen)) + return out + + def _collect_attrs_from_ref(self, ref): + for ag in self.root.iter(f"{XS}attributeGroup"): + if ag.get("name") == ref: + return self._collect_attrs(ag) + return [] + + def _parse_simple_types(self): + union_members = set() + for st in self.root.iter(f"{XS}simpleType"): + union = st.find(f"{XS}union") + if union is not None and union.get("memberTypes"): + for m in union.get("memberTypes").split(): + union_members.add(m) + + for st in self.root.iter(f"{XS}simpleType"): + name = st.get("name") + if not name: + continue + restr = st.find(f"{XS}restriction") + if restr is not None: + vals = [e.get("value") for e in restr.findall(f"{XS}enumeration")] + if vals: + self.enum_types[name] = XsdEnumType(name=name, values=vals) + self.simple_types[name] = { + "kind": "enum", + "base": restr.get("base", ""), + "values": vals, + "is_union_member": name in union_members, + } + else: + self.simple_types[name] = { + "kind": "restriction", + "base": restr.get("base", ""), + } + else: + union = st.find(f"{XS}union") + if union is not None: + self.simple_types[name] = { + "kind": "union", + "member_types": union.get("memberTypes", "").split(), + } + else: + self.simple_types[name] = {"kind": "other"} + + def _parse_complex_types(self): + for ct in self.root.iter(f"{XS}complexType"): + name = ct.get("name") + if not name: + continue + ctype = XsdComplexType(name=name, mixed=ct.get("mixed") == "true") + ctype.attributes = self._collect_attrs(ct) + + sc = ct.find(f"{XS}simpleContent") + if sc is not None: + ctype.has_simple_content = True + ext = sc.find(f"{XS}extension") + if ext is not None: + ctype.simple_content_base = ext.get("base", "") + ctype.attributes = self._collect_attrs(ext) + + cc = ct.find(f"{XS}complexContent") + if cc is not None: + ext = cc.find(f"{XS}extension") + if ext is not None: + ctype.extension_base = ext.get("base", "") + ext_seq = ext.find(f"{XS}sequence") + if ext_seq is not None: + ctype.children = self._parse_children(ext_seq, name) + + if not ctype.children: + seq = ct.find(f"{XS}sequence") + if seq is not None: + ctype.children = self._parse_children(seq, name) + + if not ctype.children: + grp = ct.find(f"{XS}group") + if grp is not None and grp.get("ref"): + ctype.children = self._parse_children(ct, name) + + choice = ct.find(f"{XS}choice") + if choice is not None: + ctype.has_choice = True + ctype.choice_children = self._parse_children(choice, name) + + seq_for_tree = ct.find(f"{XS}sequence") + choice_for_tree = ct.find(f"{XS}choice") + if seq_for_tree is not None: + min_o = int(seq_for_tree.get("minOccurs", "1")) + max_o = seq_for_tree.get("maxOccurs", "1") + max_o = -1 if max_o == "unbounded" else int(max_o) + tree_children = self._parse_content_tree(seq_for_tree) + ctype.content_tree = SequenceNode( + children=tree_children, min_occurs=min_o, max_occurs=max_o) + elif choice_for_tree is not None: + min_o = int(choice_for_tree.get("minOccurs", "1")) + max_o = choice_for_tree.get("maxOccurs", "1") + max_o = -1 if max_o == "unbounded" else int(max_o) + tree_branches = self._parse_content_tree(choice_for_tree) + ctype.content_tree = ChoiceNode( + branches=tree_branches, min_occurs=min_o, max_occurs=max_o) + + self.complex_types[name] = ctype + self._resolve_complex_content_extensions() + + def _resolve_complex_content_extensions(self): + for ctype in self.complex_types.values(): + if not ctype.extension_base: + continue + base = self.complex_types.get(ctype.extension_base) + if not base: + continue + ctype.attributes = base.attributes + ctype.attributes + if not ctype.children and base.children: + # The original codegen usually flattened inherited children: + # a type extending ``time-modification`` would expose the + # inner normal-type / normal-dot directly. For specific + # extending types (see EXTENSION_OPTIONAL_GROUP_RENAME) the + # original kept the inherited synthetic optional group as a + # distinctly-named wrapper sub-element on the extending + # type. Honor that override here; otherwise flatten as + # before. + rename_map = EXTENSION_OPTIONAL_GROUP_RENAME.get(ctype.name, {}) + inherited = [] + for c in base.children: + if (c.is_group + and c.element_name in SYNTHETIC_OPTIONAL_GROUPS): + if c.element_name in rename_map: + new_name = rename_map[c.element_name] + if new_name not in self.groups: + self.groups[new_name] = list( + self.groups.get(c.element_name, [])) + GENERATE_GROUPS.add(new_name) + SYNTHETIC_OPTIONAL_GROUPS.add(new_name) + SUPPRESS_GROUP_SUFFIX.add(new_name) + WRAPPER_AS_ELEMENT_SYNTH_GROUPS.add(new_name) + inherited.append(XsdChildRef( + element_name=new_name, + min_occurs=0, + max_occurs=1, + is_group=True, + )) + continue + for gm in self.groups.get(c.element_name, []): + inherited.append(XsdChildRef( + element_name=gm.element_name, + min_occurs=gm.min_occurs, + max_occurs=gm.max_occurs, + is_group=gm.is_group, + )) + else: + inherited.append(c) + ctype.children = inherited + ctype.children + + def _parse_children(self, container, parent_type_name: Optional[str] = None): + children = [] + for child in container: + if child.tag == f"{XS}element": + elem_name = child.get("ref") or child.get("name") + if elem_name: + min_o = int(child.get("minOccurs", "1")) + max_o = child.get("maxOccurs", "1") + max_o = -1 if max_o == "unbounded" else int(max_o) + children.append(XsdChildRef( + element_name=elem_name, min_occurs=min_o, max_occurs=max_o + )) + elif child.tag == f"{XS}group": + ref = child.get("ref") + if ref: + min_o = int(child.get("minOccurs", "1")) + max_o = child.get("maxOccurs", "1") + max_o = -1 if max_o == "unbounded" else int(max_o) + children.append(XsdChildRef( + element_name=ref, min_occurs=min_o, max_occurs=max_o, + is_group=True, + )) + elif child.tag == f"{XS}sequence": + seq_min = int(child.get("minOccurs", "1")) + seq_max = child.get("maxOccurs", "1") + seq_max = -1 if seq_max == "unbounded" else int(seq_max) + # An anonymous nested optional sequence (minOccurs=0, + # maxOccurs=1) inside a parent sequence is the XSD shape that + # the original code generation sometimes promoted to a + # synthetic group class (e.g. NormalTypeNormalDotGroup inside + # time-modification). The naming and the decision to promote + # were human choices and are not consistent across the + # schema -- page-layout, for example, flattens the same + # shape rather than promoting it. Opt-in via + # NESTED_OPTIONAL_SEQUENCE_AS_GROUP keyed on the enclosing + # complex-type name. + if (seq_min == 0 and seq_max == 1 + and parent_type_name in NESTED_OPTIONAL_SEQUENCE_AS_GROUP): + nested_children = self._parse_children(child) + if nested_children and all( + not c.is_group for c in nested_children + ): + group_name = NESTED_OPTIONAL_SEQUENCE_AS_GROUP[parent_type_name] + group_ref = self._synthesize_optional_group( + group_name, nested_children) + children.append(group_ref) + continue + if (seq_min == 0 and seq_max == -1 + and parent_type_name in UNBOUNDED_SEQUENCE_AS_GROUP): + nested_children = self._parse_children(child) + if nested_children and all( + not c.is_group for c in nested_children + ): + group_name = UNBOUNDED_SEQUENCE_AS_GROUP[parent_type_name] + group_ref = self._synthesize_unbounded_group( + group_name, nested_children) + children.append(group_ref) + continue + children.extend(self._parse_children(child, parent_type_name)) + elif child.tag == f"{XS}choice": + pass + return children + + def _synthesize_optional_group(self, group_name: str, + nested_children: list) -> "XsdChildRef": + # ``group_name`` is the hyphenated-lowercase ref name (e.g. + # "normal-type-normal-dot") so that pascal(name) + "Group" via + # child_class_name + GENERATE_GROUPS yields the expected class name + # (e.g. NormalTypeNormalDotGroup). + if group_name not in self.groups: + self.groups[group_name] = list(nested_children) + GENERATE_GROUPS.add(group_name) + SYNTHETIC_OPTIONAL_GROUPS.add(group_name) + return XsdChildRef( + element_name=group_name, + min_occurs=0, + max_occurs=1, + is_group=True, + ) + + def _synthesize_unbounded_group(self, group_name: str, + nested_children: list) -> "XsdChildRef": + # Members of an unbounded synthetic group are forced to min_occurs=0, + # max_occurs=1 because the unbounded multiplicity belongs to the + # wrapping sequence at the parent. The original codegen produced + # group classes (e.g. MidiDeviceInstrumentGroup) with all-optional + # members in this exact shape. + if group_name not in self.groups: + members = [] + for c in nested_children: + members.append(XsdChildRef( + element_name=c.element_name, + min_occurs=0, + max_occurs=1, + is_group=c.is_group, + )) + self.groups[group_name] = members + GENERATE_GROUPS.add(group_name) + SYNTHETIC_UNBOUNDED_GROUPS.add(group_name) + return XsdChildRef( + element_name=group_name, + min_occurs=0, + max_occurs=-1, + is_group=True, + ) + + def _parse_content_tree(self, container): + nodes = [] + for child in container: + min_o = int(child.get("minOccurs", "1")) + max_o = child.get("maxOccurs", "1") + max_o = -1 if max_o == "unbounded" else int(max_o) + if child.tag == f"{XS}element": + name = child.get("ref") or child.get("name") + if name: + nodes.append(ElementRefNode( + element_name=name, min_occurs=min_o, max_occurs=max_o)) + elif child.tag == f"{XS}group": + ref = child.get("ref") + if ref: + nodes.append(GroupRefNode( + group_name=ref, min_occurs=min_o, max_occurs=max_o)) + elif child.tag == f"{XS}sequence": + sub = self._parse_content_tree(child) + nodes.append(SequenceNode( + children=sub, min_occurs=min_o, max_occurs=max_o)) + elif child.tag == f"{XS}choice": + branches = self._parse_content_tree(child) + nodes.append(ChoiceNode( + branches=branches, min_occurs=min_o, max_occurs=max_o)) + return nodes + + def _parse_groups(self): + for grp in self.root.iter(f"{XS}group"): + name = grp.get("name") + if name: + seq = grp.find(f"{XS}sequence") + if seq is not None: + self.groups[name] = self._parse_children(seq) + else: + choice = grp.find(f"{XS}choice") + if choice is not None: + self.groups[name] = self._parse_children(choice) + + def _inline_group_children(self, children: list) -> list: + result = [] + for child in children: + if child.is_group and child.element_name not in GENERATE_GROUPS: + group_children = self.groups.get(child.element_name, []) + inlined = self._inline_group_children(group_children) + for gc in inlined: + min_o = gc.min_occurs + if child.min_occurs == 0: + min_o = 0 + gc_copy = XsdChildRef( + element_name=gc.element_name, + min_occurs=min_o, + max_occurs=gc.max_occurs, + is_group=gc.is_group, + ) + result.append(gc_copy) + else: + result.append(child) + return result + + def _inline_non_generated_groups(self): + for ct in self.complex_types.values(): + if ct.children: + ct.children = self._inline_group_children(ct.children) + + def _parse_elements(self): + for el in self.root.iter(f"{XS}element"): + name = el.get("name") + if not name: + continue + if name in self.elements: + continue + typ = el.get("type", "") + anon = None + ct = el.find(f"{XS}complexType") + if ct is not None: + anon = XsdComplexType( + name=f"_anon_{name}", mixed=ct.get("mixed") == "true") + anon.attributes = self._collect_attrs(ct) + sc = ct.find(f"{XS}simpleContent") + if sc is not None: + anon.has_simple_content = True + ext = sc.find(f"{XS}extension") + if ext is not None: + anon.simple_content_base = ext.get("base", "") + seq = ct.find(f"{XS}sequence") + if seq is not None: + anon.children = self._parse_children(seq) + self.elements[name] = XsdElement(name=name, type_name=typ, anonymous_type=anon) + + def _build_class_names(self): + for name in self.elements: + self.class_names.add(pascal(name)) + for name in self.complex_types: + self.class_names.add(pascal(name)) + self._build_type_usage_counts() + + def _build_type_usage_counts(self): + self.type_usage_count = {} + for elem in self.elements.values(): + if elem.type_name: + self.type_usage_count[elem.type_name] = \ + self.type_usage_count.get(elem.type_name, 0) + 1 + + +# --------------------------------------------------------------------------- +# C++ Type Mapping +# --------------------------------------------------------------------------- + +XSD_TO_CPP_TYPE = { + "xs:string": "XsString", + "xs:token": "XsToken", + "xs:ID": "XsID", + "xs:IDREF": "XsIDREF", + "xs:NMTOKEN": "XsNMToken", + "xs:anyURI": "XsAnyUri", + "xs:decimal": "DecimalType", + "xs:integer": "Integer", + "xs:nonNegativeInteger": "NonNegativeInteger", + "xs:positiveInteger": "PositiveInteger", + "xs:date": "Date", + "xs:time": "TimeOnly", + "xml:lang": "XmlLang", + "xml:space": "XmlSpace", + "xlink:href": "XlinkHref", + "xlink:type": "XlinkType", + "xlink:role": "XlinkRole", + "xlink:title": "XlinkTitle", + "xlink:show": "XlinkShow", + "xlink:actuate": "XlinkActuate", +} + +SIMPLE_TYPE_TO_CPP = { + "above-below": "AboveBelow", + "accidental-value": "AccidentalValue", + "backward-forward": "BackwardForward", + "bar-style": "BarStyleEnum", + "beam-value": "BeamValue", + "cancel-location": "CancelLocation", + "clef-sign": "ClefSign", + "css-font-size": "CssFontSize", + "degree-symbol-value": "DegreeSymbolValue", + "degree-type-value": "DegreeTypeValue", + "effect-value": "EffectValue", + "enclosure-shape": "EnclosureShape", + "fan": "Fan", + "fermata-shape": "FermataShape", + "font-style": "FontStyle", + "font-weight": "FontWeight", + "group-barline-value": "GroupBarlineValue", + "group-symbol-value": "GroupSymbolValue", + "handbell-value": "HandbellValue", + "harmony-type": "HarmonyType", + "kind-value": "KindValue", + "left-center-right": "LeftCenterRight", + "left-right": "LeftRight", + "line-end": "LineEnd", + "line-shape": "LineShape", + "line-type": "LineType", + "margin-type": "MarginType", + "measure-numbering-value": "MeasureNumberingValue", + "membrane-value": "MembraneValue", + "metal-value": "MetalValue", + "mute": "MuteEnum", + "notehead-value": "NoteheadValue", + "note-size-type": "NoteSizeType", + "note-type-value": "NoteTypeValue", + "on-off": "OnOff", + "over-under": "OverUnder", + "pitched-value": "PitchedValue", + "placement": "AboveBelow", + "right-left-middle": "RightLeftMiddle", + "semi-pitched": "SemiPitchedEnum", + "show-frets": "ShowFrets", + "show-tuplet": "ShowTuplet", + "staff-type": "StaffTypeEnum", + "start-note": "StartNote", + "start-stop": "StartStop", + "start-stop-change-continue": "StartStopChangeContinue", + "start-stop-continue": "StartStopContinue", + "start-stop-discontinue": "StartStopDiscontinue", + "start-stop-single": "StartStopSingle", + "stem-value": "StemValue", + "step": "StepEnum", + "syllabic": "SyllabicEnum", + "symbol-size": "SymbolSize", + "tap-hand": "TapHand", + "text-direction": "TextDirection", + "tied-type": "TiedType", + "time-relation": "TimeRelationEnum", + "time-symbol": "TimeSymbol", + "tip-direction": "TipDirection", + "top-bottom": "TopBottom", + "tremolo-type": "TremoloType", + "trill-step": "TrillStep", + "two-note-turn": "TwoNoteTurn", + "up-down": "UpDown", + "up-down-stop-continue": "UpDownStopContinue", + "upright-inverted": "UprightInverted", + "valign": "Valign", + "valign-image": "ValignImage", + "wedge-type": "WedgeType", + "winged": "Winged", + "wood-value": "WoodValue", + "yes-no": "YesNo", +} + +NUMERIC_TYPE_MAP = { + "accordion-middle": "AccordionMiddleValue", + "beam-level": "BeamLevel", + "divisions": "DivisionsValue", + "fifths": "FifthsValue", + "midi-128": "Midi128", + "midi-16": "Midi16", + "midi-16384": "Midi16384", + "millimeters": "MillimetersValue", + "non-negative-decimal": "NonNegativeDecimal", + "number-level": "NumberLevel", + "number-of-lines": "NumberOfLines", + "octave": "OctaveValue", + "percent": "Percent", + "positive-decimal": "PositiveDecimal", + "positive-divisions": "PositiveDivisionsValue", + "rotation-degrees": "RotationDegrees", + "semitones": "Semitones", + "staff-line": "StaffLine", + "staff-number": "StaffNumber", + "string-number": "StringNumber", + "tenths": "TenthsValue", + "trill-beats": "TrillBeats", + "tremolo-marks": "TremoloMarks", + "byte": "Byte", +} + +BESPOKE_TYPES = { + "color": "Color", + "comma-separated-text": "CommaSeparatedText", + "distance-type": "DistanceType", + "font-size": "FontSize", + "line-width-type": "LineWidthType", + "mode": "ModeValue", + "number-or-normal": "NumberOrNormal", + "positive-integer-or-empty": "PositiveIntegerOrEmpty", + "yes-no-number": "YesNoNumber", + "ending-number": "EndingNumber", + "date": "Date", + "time-only": "TimeOnly", +} + +STRING_LIKE_TYPES = { + "XsString", "XsToken", "XsID", "XsIDREF", "XsNMToken", "XsAnyUri", +} + + +def uses_set_value(cpp_type: str) -> bool: + return cpp_type in STRING_LIKE_TYPES + + +def is_enum_value_type(cpp_type: str) -> bool: + return needs_parse_func(cpp_type) or cpp_type.endswith("Enum") or cpp_type in XMACRO_ENUM_TYPES + + +def resolve_cpp_type(xsd_type: str, model: XsdModel) -> str: + if xsd_type in XSD_TO_CPP_TYPE: + return XSD_TO_CPP_TYPE[xsd_type] + if xsd_type.startswith("xs:"): + return XSD_TO_CPP_TYPE.get(xsd_type, "XsString") + if xsd_type in SIMPLE_TYPE_TO_CPP: + return SIMPLE_TYPE_TO_CPP[xsd_type] + if xsd_type in NUMERIC_TYPE_MAP: + return NUMERIC_TYPE_MAP[xsd_type] + if xsd_type in BESPOKE_TYPES: + return BESPOKE_TYPES[xsd_type] + if xsd_type in model.enum_types: + base = pascal(xsd_type) + if base in model.class_names: + return base + "Enum" + return base + if xsd_type in model.simple_types: + st = model.simple_types[xsd_type] + if st["kind"] == "restriction": + return resolve_cpp_type(st["base"], model) + if st["kind"] == "union": + return pascal(xsd_type) + return pascal(xsd_type) + + +def resolve_attr_cpp_type(attr: XsdAttribute, model: XsdModel) -> str: + return resolve_cpp_type(attr.type_name, model) + + +ENUM_PARSE_FUNCS = {} + + +def needs_parse_func(cpp_type: str) -> bool: + return cpp_type in { + "FontStyle", "FontWeight", "AboveBelow", "LeftCenterRight", "Valign", + "ValignImage", "OverUnder", "TopBottom", "EnclosureShape", "StartStop", + "StartStopContinue", "StartStopSingle", "StartStopChangeContinue", + "StartStopDiscontinue", "YesNo", "OnOff", "UpDown", "BackwardForward", + "LineType", "LineShape", "WedgeType", "BarStyleEnum", "Fan", + "TipDirection", "TextDirection", "UprightInverted", "LeftRight", + "RightLeftMiddle", "BeamValue", "AccidentalValue", "ClefSign", + "StemValue", "NoteheadValue", "StepEnum", "Syllabic", "SymbolSize", + "TiedType", "FermataShape", "KindValue", "HarmonyType", + "DegreeTypeValue", "DegreeSymbolValue", "GroupSymbolValue", + "GroupBarlineValue", "MarginType", "TimeSymbol", "CancelLocation", + "ShowTuplet", "NoteTypeValue", "HandbellValue", "EffectValue", + "MetalValue", "WoodValue", "PitchedValue", "MembraneValue", + "SemiPitched", "TapHand", "TimeRelation", "LineEnd", "ShowFrets", + "CssFontSize", "MeasureNumberingValue", "StaffTypeEnum", + "StartNote", "TrillStep", "TwoNoteTurn", "Winged", "TremoloType", + "UpDownStopContinue", "NoteSizeType", "MuteEnum", + "BeaterValue", "BreathMarkValue", "HoleClosedValue", + "HoleClosedLocation", "TimeSeparator", "PrincipalVoiceSymbol", + "ModeValue", "XmlSpace", "XlinkType", "XlinkShow", "XlinkActuate", + } + + +def parse_func_name(cpp_type: str) -> str: + if cpp_type in XMACRO_ENUM_TYPES: + return f"{cpp_type}FromString" + return f"parse{cpp_type}" + + +# --------------------------------------------------------------------------- +# Include resolution +# --------------------------------------------------------------------------- + +TYPE_TO_HEADER = { + "XsString": "mx/core/XsString.h", + "XsToken": "mx/core/XsToken.h", + "XsID": "mx/core/XsID.h", + "XsIDREF": "mx/core/XsIDREF.h", + "XsNMToken": "mx/core/XsNMToken.h", + "XsAnyUri": "mx/core/XsAnyUri.h", + "XmlLang": "mx/core/XmlLang.h", + "XlinkHref": "mx/core/XlinkHref.h", + "XlinkRole": "mx/core/XlinkRole.h", + "XlinkTitle": "mx/core/XlinkTitle.h", + "Color": "mx/core/Color.h", + "CommaSeparatedText": "mx/core/CommaSeparatedText.h", + "CommaSeparatedPositiveIntegers": "mx/core/CommaSeparatedPositiveIntegers.h", + "FontSize": "mx/core/FontSize.h", + "NumberOrNormal": "mx/core/NumberOrNormal.h", + "PositiveIntegerOrEmpty": "mx/core/PositiveIntegerOrEmpty.h", + "YesNoNumber": "mx/core/YesNoNumber.h", + "EndingNumber": "mx/core/EndingNumber.h", + "Date": "mx/core/Date.h", + "TimeOnly": "mx/core/TimeOnly.h", + "PlaybackSound": "mx/core/PlaybackSound.h", +} + + +def header_for_type(cpp_type: str) -> str: + if cpp_type in TYPE_TO_HEADER: + return TYPE_TO_HEADER[cpp_type] + if "Decimal" in cpp_type or "Tenths" in cpp_type or "Millimeters" in cpp_type or \ + "Percent" in cpp_type or "Semitones" in cpp_type or "TrillBeats" in cpp_type or \ + "RotationDegrees" in cpp_type or "Divisions" in cpp_type: + return "mx/core/Decimals.h" + if any(cpp_type == t for t in [ + "AccordionMiddleValue", "BeamLevel", "Byte", "FifthsValue", "Integer", + "Midi128", "Midi16", "Midi16384", "NonNegativeInteger", "NumberLevel", + "NumberOfLines", "OctaveValue", "PositiveInteger", "StaffLine", + "StaffNumber", "StringNumber", "TremoloMarks", + ]): + return "mx/core/Integers.h" + return "mx/core/Enums.h" + + +# --------------------------------------------------------------------------- +# Enums.h / Enums.cpp generation +# --------------------------------------------------------------------------- + + +def generate_enums_h(model: XsdModel) -> str: + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/EnumsBuiltin.h"\n') + lines.append("#include ") + lines.append("#include ") + lines.append("#include \n") + lines.append("namespace mx\n{") + lines.append("namespace core\n{") + + for xsd_name, et in model.enum_types.items(): + cpp_name = pascal(xsd_name) + if cpp_name in model.class_names: + cpp_name += "Enum" + + doc = "" + for st in model.root.iter(f"{XS}simpleType"): + if st.get("name") == xsd_name: + ann = st.find(f"{XS}annotation") + if ann is not None: + doc_el = ann.find(f"{XS}documentation") + if doc_el is not None and doc_el.text: + doc = doc_el.text.strip() + break + + lines.append(f"/// {cpp_name} " + "/" * (80 - len(cpp_name) - 5)) + lines.append("///") + if doc: + for dline in _wrap_doc(doc, 96): + lines.append(f"/// {dline}") + lines.append("///") + + lines.append(f"enum class {cpp_name}") + lines.append("{") + for i, val in enumerate(et.values): + member = camel(val) + if val == "": + member = "emptystring" + lines.append(f" {member} = {i}" + ("," if i < len(et.values) - 1 else "")) + lines.append("};\n") + + lines.append(f"{cpp_name} parse{cpp_name}(const std::string &value);") + lines.append(f"std::optional<{cpp_name}> tryParse{cpp_name}(const std::string &value);") + lines.append(f"std::string toString(const {cpp_name} value);") + lines.append(f"std::ostream &toStream(std::ostream &os, const {cpp_name} value);") + lines.append(f"std::ostream &operator<<(std::ostream &os, const {cpp_name} value);\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _wrap_doc(text, width): + words = text.split() + result = [] + current = "" + for w in words: + if current and len(current) + 1 + len(w) > width: + result.append(current) + current = w + else: + current = current + " " + w if current else w + if current: + result.append(current) + return result + + +# --------------------------------------------------------------------------- +# Attributes Struct Generation +# --------------------------------------------------------------------------- + + +def attrs_struct_name(ct_name: str, model: XsdModel) -> str: + return pascal(ct_name) + "Attributes" + + +def element_attrs_struct_name(elem_name: str, model: XsdModel) -> str: + return element_class_name(elem_name) + "Attributes" + + +CORE_ROOT_ATTRS = { + "EmptyPrintObjectStyleAlignAttributes", +} + +ATTRS_TYPE_ALIAS = { + "empty-print-style-align": "empty-print-object-style-align", +} + +ELEMENTS_DIR_SHARED_ATTRS = { + "EmptyPlacementAttributes", + "EmptyLineAttributes", + "EmptyTrillSoundAttributes", + "EmptyFontAttributes", + "EmptyPrintStyleAlignAttributes", +} + + +def resolve_attrs_name(elem_name: str, type_name: str, model: XsdModel) -> str: + """Determine the correct attributes struct name for an element. + Some empty-* types use the type name (shared). Others use element name.""" + aliased = ATTRS_TYPE_ALIAS.get(type_name, type_name) + type_attrs = pascal(aliased) + "Attributes" + if type_attrs in CORE_ROOT_ATTRS or type_attrs in ELEMENTS_DIR_SHARED_ATTRS: + return type_attrs + return element_class_name(elem_name) + "Attributes" + + +def generate_attrs_h(struct_name: str, attrs: list, model: XsdModel) -> str: + includes = set() + includes.add("mx/core/AttributesInterface.h") + includes.add("mx/core/ForwardDeclare.h") + for a in attrs: + cpp_t = resolve_attr_cpp_type(a, model) + h = header_for_type(cpp_t) + includes.add(h) + + lines = [LICENSE, "#pragma once\n"] + for inc in sorted(includes): + lines.append(f'#include "{inc}"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({struct_name})\n") + lines.append(f"struct {struct_name} : public AttributesInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {struct_name}();") + lines.append(" virtual bool hasValues() const;") + lines.append(" virtual std::ostream &toStream(std::ostream &os) const;") + + for a in attrs: + cpp_t = resolve_attr_cpp_type(a, model) + cpp_n = camel(a.name) + lines.append(f" {cpp_t} {cpp_n};") + + for a in attrs: + cpp_n = camel(a.name) + has_name = has_flag_name(cpp_n) + const_prefix = "const " if a.use == "required" else "" + lines.append(f" {const_prefix}bool {has_name};") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# Per-attribute default value override, keyed by (attrs struct name, camelCase +# attribute field name). The value is the literal C++ initializer expression +# (e.g. '"it"'). Used when the committed code initializes an attribute to a +# value that is not encoded in the XSD (typically a hand-applied convention). +ATTR_DEFAULT_OVERRIDE = { + # AccidentalText's xml:lang attribute default: hand-applied "it" by the + # original codegen. Test01_AccidentalText asserts that setting hasLang + # without a value yields xml:lang="it". Not in the XSD. + ("AccidentalTextAttributes", "lang"): '"it"', + ("DirectiveAttributes", "lang"): '"it"', + # Lyric's justify default is hand-applied; the XSD says only "The default + # value varies for different elements". The original codegen chose + # 'center' here based on the doc text in the XSD annotation. + ("LyricAttributes", "justify"): "LeftCenterRight::center", + # Score{Partwise,Timewise}Attributes both expose the 'version' attribute + # from the document-attributes group. XSD says default="1.0" but mx/core + # hand-applies "3.0" so that newly-constructed scores serialize with the + # most recent supported version. Schema-driven generators preserve the + # hand-applied value via this override. + ("ScorePartwiseAttributes", "version"): '"3.0"', + ("ScoreTimewiseAttributes", "version"): '"3.0"', + # R4: xml:lang defaults hand-applied as "it" across text-bearing elements. + ("WordsAttributes", "lang"): '"it"', + ("TextAttributes", "lang"): '"it"', + ("RehearsalAttributes", "lang"): '"it"', + ("LyricLanguageAttributes", "lang"): '"it"', + ("CreditWordsAttributes", "lang"): '"it"', + # R4: BracketAttributes line-end default is 'down', not first enum 'up'. + ("BracketAttributes", "lineEnd"): "LineEnd::down", + # R4: NoteSizeAttributes type default is 'large', not first enum 'cue'. + ("NoteSizeAttributes", "type"): "NoteSizeType::large", + # R4: EndingAttributes number default is "1". + ("EndingAttributes", "number"): '"1"', + # R4: GroupingAttributes number default is "1". + ("GroupingAttributes", "number"): 'XsToken("1")', + # R4: PageMarginsAttributes type default is 'both', not first enum 'odd'. + ("PageMarginsAttributes", "type"): "MarginType::both", + # R4: LinkAttributes show default is 'replace', not first enum 'new'. + # Field name in struct is 'show', not 'xlinkShow'. + ("LinkAttributes", "show"): "XlinkShow::replace", + # R4: OtherAppearanceAttributes type default is "undefined". + ("OtherAppearanceAttributes", "type"): '"undefined"', + # R4: OtherNotationAttributes type default is 'start', not first enum 'single'. + ("OtherNotationAttributes", "type"): "StartStopSingle::start", + # R4: OtherOrnament/TechnicalAttributes placement: default_value_for_type + # returns AboveBelow::below but the committed code uses the default ctor + # which is AboveBelow::above (= 0). + ("OtherOrnamentAttributes", "placement"): "AboveBelow::above", + ("OtherTechnicalAttributes", "placement"): "AboveBelow::above", + # R4: PrincipalVoiceAttributes symbol default is 'none'. + ("PrincipalVoiceAttributes", "symbol"): "PrincipalVoiceSymbol::none", + # R4: StringMuteAttributes type default is 'on', not 'off'. + ("StringMuteAttributes", "type"): "OnOff::on", + # R4: PartGroupAttributes number default is "1". + ("PartGroupAttributes", "number"): 'XsToken("1")', + # R4: MetronomeAttributes halign/justify defaults are 'center'. + ("MetronomeAttributes", "halign"): "LeftCenterRight::center", + ("MetronomeAttributes", "justify"): "LeftCenterRight::center", +} + +# Per-(parent-element-xml-name, child-element-xml-name) override for the +# constructor argument passed to make{Child}() when initializing the child +# on the parent's ctor init list. Used when HEAD initializes a required child +# with a non-default value (e.g. historical author choice rather than XSD spec). +CHILD_INIT_VALUE_OVERRIDE = { + # Scaling's millimeters and tenths use non-zero historical defaults. + ("scaling", "millimeters"): "MillimetersValue(7)", + ("scaling", "tenths"): "TenthsValue(40)", + # StaffDetails defaults staff-lines to 5 (author convention, not in XSD). + ("staff-details", "staff-lines"): "NonNegativeInteger(5)", +} + + +# Elements whose hasContents() should always return true regardless of what the +# XSD min/max-occurs analysis would produce. Keyed by element xml-name (not +# class name). Used when the committed HEAD hardcodes `return true;` for an +# element that has only optional children. +ELEMENT_HAS_CONTENTS_ALWAYS_TRUE = { + # MeasureLayout has a single optional child (measure-distance), but HEAD + # returns true unconditionally so that the element serialises as + # rather than . + "measure-layout", +} + +# Per-(element-name, child-xml-name) override for the min_occurs value that the +# generator uses when deciding whether a child needs a myHas flag. Keyed by +# (parent_element_xml_name, child_element_xml_name). Use this when XSD group +# inlining propagates minOccurs=0 from the enclosing group to an element that +# HEAD treats as unconditionally present (no getHas/setHas accessors). +CHILD_MIN_OCCURS_OVERRIDE = { + # The `slash` group (used by both slash and beat-repeat) has minOccurs="0", + # which causes group inlining to set min_occurs=0 on slash-type. HEAD treats + # slash-type as always-present (minOccurs=1 within the group), so override. + ("slash", "slash-type"): 1, + ("beat-repeat", "slash-type"): 1, +} + + +def _apply_child_min_occurs_override(elem_name: str, children: list) -> list: + """Return a new children list with CHILD_MIN_OCCURS_OVERRIDE applied.""" + if not any((elem_name, c.element_name) in CHILD_MIN_OCCURS_OVERRIDE + for c in children): + return children + result = [] + for c in children: + key = (elem_name, c.element_name) + if key in CHILD_MIN_OCCURS_OVERRIDE: + c = XsdChildRef( + element_name=c.element_name, + min_occurs=CHILD_MIN_OCCURS_OVERRIDE[key], + max_occurs=c.max_occurs, + is_group=c.is_group, + ) + result.append(c) + return result + + +def default_value_for_type(cpp_type: str) -> str: + defaults = { + "FontStyle": "FontStyle::normal", + "FontWeight": "FontWeight::normal", + "AboveBelow": "AboveBelow::below", + "Valign": "Valign::bottom", + "ValignImage": "ValignImage::bottom", + "LeftCenterRight": "LeftCenterRight::left", + "EnclosureShape": "EnclosureShape::none", + "YesNo": "YesNo::no", + "OnOff": "OnOff::off", + "FontSize": "CssFontSize::medium", + "StartStop": "StartStop::start", + "StartStopContinue": "StartStopContinue::start", + "StartStopSingle": "StartStopSingle::single", + "LineType": "LineType::solid", + "LineShape": "LineShape::straight", + "SymbolSize": "SymbolSize::full", + } + return defaults.get(cpp_type, "") + + +def generate_attrs_cpp(struct_name: str, attrs: list, model: XsdModel) -> str: + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{struct_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + # constructor + init_parts = [] + for a in attrs: + cpp_t = resolve_attr_cpp_type(a, model) + cpp_n = camel(a.name) + override = ATTR_DEFAULT_OVERRIDE.get((struct_name, cpp_n)) + if override: + init_parts.append(f"{cpp_n}({override})") + continue + dv = default_value_for_type(cpp_t) + if dv: + init_parts.append(f"{cpp_n}({dv})") + else: + init_parts.append(f"{cpp_n}()") + for a in attrs: + cpp_n = camel(a.name) + has_name = has_flag_name(cpp_n) + init_val = "true" if a.use == "required" else "false" + init_parts.append(f"{has_name}({init_val})") + + _emit_ctor_init(lines, f"{struct_name}::{struct_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + # hasValues + has_parts = [] + for a in attrs: + cpp_n = camel(a.name) + has_name = has_flag_name(cpp_n) + has_parts.append(has_name) + lines.append(f"bool {struct_name}::hasValues() const") + lines.append("{") + if has_parts: + lines.append(f" return {' || '.join(has_parts)};") + else: + lines.append(" return false;") + lines.append("}\n") + + # toStream + lines.append(f"std::ostream &{struct_name}::toStream(std::ostream &os) const") + lines.append("{") + lines.append(" if (hasValues())") + lines.append(" {") + for a in attrs: + cpp_n = camel(a.name) + has_name = has_flag_name(cpp_n) + lines.append(f' streamAttribute(os, {cpp_n}, "{a.get_xml_name()}", {has_name});') + lines.append(" }") + lines.append(" return os;") + lines.append("}\n") + + # fromXElementImpl + lines.append(f"bool {struct_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(f' const char *const className = "{struct_name}";') + lines.append(" bool isSuccess = true;") + required_locals = [] + for a in attrs: + if a.use == "required": + cpp_n = camel(a.name) + local_name = "is" + pascal(a.name) + "Found" + required_locals.append((a, local_name)) + lines.append(f" bool {local_name} = false;") + lines.append("") + lines.append(" auto it = xelement.attributesBegin();") + lines.append(" auto endIter = xelement.attributesEnd();\n") + lines.append(" for (; it != endIter; ++it)") + lines.append(" {") + required_local_map = {id(a): ln for a, ln in required_locals} + for a in attrs: + cpp_t = resolve_attr_cpp_type(a, model) + cpp_n = camel(a.name) + parse_has = required_local_map.get(id(a), has_flag_name(cpp_n)) + if needs_parse_func(cpp_t): + pf = parse_func_name(cpp_t) + lines.append(f" if (parseAttribute(message, it, className, isSuccess, {cpp_n}, {parse_has}, " + f'"{a.get_xml_name()}", &{pf}))') + else: + lines.append(f" if (parseAttribute(message, it, className, isSuccess, {cpp_n}, {parse_has}, " + f'"{a.get_xml_name()}"))') + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(" }\n") + for a, local_name in required_locals: + lines.append(f" if (!{local_name})") + lines.append(" {") + lines.append(" isSuccess = false;") + # Use the XSD attribute name (xml form, e.g. 'non-controlling') in + # the error message rather than hardcoding 'number'. The original + # codegen had a bug here that produced the wrong attribute name for + # any required attribute not named 'number' (visible in committed + # ScorePartAttributes.cpp, which says 'number' when it should say + # 'id'). + xml_name = a.get_xml_name() or a.name + lines.append(f' message << className << ": \'{xml_name}\' is a required attribute but was not found" << std::endl;') + lines.append(" }\n") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Element Class Generation +# --------------------------------------------------------------------------- + + +def classify_element(elem: XsdElement, model: XsdModel) -> str: + if elem.anonymous_type is not None: + ct = elem.anonymous_type + elif not elem.type_name: + return "unknown" + else: + ct = model.complex_types.get(elem.type_name) + if ct is None: + if elem.type_name in model.simple_types or elem.type_name in model.enum_types or \ + elem.type_name.startswith("xs:"): + return "simple-value" + return "unknown" + + if ct.has_simple_content: + if ct.attributes: + return "text-with-attrs" + return "text-value" + + if ct.has_choice and not ct.children: + return "choice" + + if ct.children: + if ct.has_choice: + return "sequence-with-choice" + if ct.attributes: + return "complex-with-attrs" + return "complex" + + if ct.attributes and not ct.children: + return "empty-with-attrs" + + if not ct.attributes and not ct.children: + return "empty" + + return "unknown" + + +def child_class_name(child: XsdChildRef) -> str: + name = pascal(child.element_name) + if child.is_group and child.element_name in GENERATE_GROUPS: + if child.element_name not in SUPPRESS_GROUP_SUFFIX: + name += "Group" + return name + + +PATTERN_B_EXCEPTIONS = {"String"} + + +def is_pattern_b(elem_type: str, ct: Optional[XsdComplexType], model: XsdModel, + class_name: str = "") -> bool: + if class_name in PATTERN_B_EXCEPTIONS: + return True + if ct is None: + return False + if ct.children or ct.has_choice: + return True + type_name = ct.name + if type_name in model.complex_types: + real_ct = model.complex_types[type_name] + if real_ct.children or real_ct.has_choice: + return True + return _ct_has_complex_content(type_name, model) + + +def _ct_has_complex_content(type_name: str, model: XsdModel) -> bool: + for ct_node in model.root.iter(f"{XS}complexType"): + if ct_node.get("name") == type_name: + if ct_node.find(f"{XS}complexContent") is not None: + return True + if ct_node.find(f"{XS}group") is not None: + return True + return False + + +def generate_element_h(elem_name: str, class_name: str, stream_name: str, + elem_type: str, ct: Optional[XsdComplexType], + model: XsdModel, type_name: str = "") -> str: + attrs_name = None + value_type = None + children = [] + + if ct: + if ct.attributes: + attrs_name = resolve_attrs_name(elem_name, type_name or ct.name, model) + if ct.has_simple_content: + value_type = resolve_cpp_type(ct.simple_content_base, model) + children = _apply_child_min_occurs_override(elem_name, ct.children) + + # Apply value-type override if configured for this element + vt_override = ELEMENT_VALUE_TYPE_OVERRIDE.get(elem_name) + if vt_override: + value_type = vt_override["cpp_type"] + + lines = [LICENSE, "#pragma once\n"] + project_includes = ['"mx/core/ElementInterface.h"', '"mx/core/ForwardDeclare.h"'] + if value_type: + project_includes.append(f'"{header_for_type(value_type)}"') + if vt_override: + for extra in vt_override.get("extra_includes", []): + project_includes.append(f'"{extra}"') + if attrs_name: + if attrs_name in CORE_ROOT_ATTRS: + project_includes.append(f'"mx/core/{attrs_name}.h"') + else: + project_includes.append(f'"mx/core/elements/{attrs_name}.h"') + for inc in sorted(project_includes): + lines.append(f"#include {inc}") + + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + + has_unbounded_synth = any( + c.is_group and c.element_name in SYNTHETIC_UNBOUNDED_GROUPS + for c in children + ) + if has_unbounded_synth: + lines.append("namespace ezxml\n{\nclass XElementIterator;\n}") + lines.append("") + + lines.append("namespace mx\n{\nnamespace core\n{\n") + + if attrs_name: + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + + child_classes = sorted(set(child_class_name(c) for c in children)) + for cc in child_classes: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({cc})") + + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + + if value_type: + if value_type in XMACRO_ENUM_TYPES: + lines.append(f"\ninline {class_name}Ptr make{class_name}({value_type} value)") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>(value);") + lines.append("}") + else: + lines.append(f"\ninline {class_name}Ptr make{class_name}(const {value_type} &value)") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>(value);") + lines.append("}") + lines.append(f"\ninline {class_name}Ptr make{class_name}({value_type} &&value)") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>(std::move(value));") + lines.append("}") + + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + + if value_type: + lines.append(f" {class_name}(const {value_type} &value);") + + lines.append("") + pattern_b = is_pattern_b(elem_type, ct, model, class_name) + if pattern_b: + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + else: + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + if attrs_name: + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &attributes);") + + if value_type: + lines.append(f" {value_type} getValue() const;") + lines.append(f" void setValue(const {value_type} &value);") + + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f"\n /* _________ {cc} minOccurs = {child.min_occurs}, maxOccurs = unbounded _________ */") + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + lines.append(f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;") + elif child.min_occurs == 0: + lines.append(f"\n /* _________ {cc} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + else: + lines.append(f"\n /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + + has_private_members = bool(attrs_name) or bool(value_type) or bool(children) + if has_private_members: + lines.append("") + lines.append(" private:") + if value_type: + lines.append(f" {value_type} myValue;") + if attrs_name: + lines.append(f" {attrs_name}Ptr myAttributes;") + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" {cc}Set my{cc}Set;") + else: + lines.append(f" {cc}Ptr my{cc};") + if child.min_occurs == 0: + lines.append(f" bool myHas{cc};") + + unbounded_synth_children = [ + c for c in children + if c.is_group and c.element_name in SYNTHETIC_UNBOUNDED_GROUPS + ] + for child in unbounded_synth_children: + cc = child_class_name(child) + lines.append("") + lines.append(f" void import{cc}Set(std::ostream &message, ::ezxml::XElementIterator &iter,") + indent = " " * (len(f" void import{cc}Set(")) + lines.append(f"{indent}::ezxml::XElementIterator &endIter, bool &isSuccess);") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _emit_ctor_init(lines: list, prefix: str, init_parts: list): + if not init_parts: + lines.append(prefix) + return + first_line = f"{prefix} : {init_parts[0]}" + if len(init_parts) == 1: + lines.append(first_line) + return + all_on_one = f"{prefix} : {', '.join(init_parts)}" + if len(all_on_one) <= 110: + lines.append(all_on_one) + return + indent = " " * 4 + ": " + current = f"{prefix}\n{indent}{init_parts[0]}" + for part in init_parts[1:]: + candidate = current + f", {part}" + last_line = candidate.split("\n")[-1] + if len(last_line) > 110: + current += f",\n {part}" + else: + current += f", {part}" + lines.append(current) + + +def generate_element_cpp(elem_name: str, class_name: str, stream_name: str, + elem_type: str, ct: Optional[XsdComplexType], + model: XsdModel, type_name: str = "") -> str: + attrs_name = None + value_type = None + children = [] + + if ct: + if ct.attributes: + attrs_name = resolve_attrs_name(elem_name, type_name or ct.name, model) + if ct.has_simple_content: + value_type = resolve_cpp_type(ct.simple_content_base, model) + children = _apply_child_min_occurs_override(elem_name, ct.children) + + # Apply value-type override if configured for this element + vt_override = ELEMENT_VALUE_TYPE_OVERRIDE.get(elem_name) + if vt_override: + value_type = vt_override["cpp_type"] + + has_contents = bool(value_type) or bool(children) + + # R5: elements whose XSD declares a `minOccurs >= 1, maxOccurs=unbounded` + # child set must pre-seed the set with one default member, return + # hasContents() unconditionally, and refuse to drop below size 1 in + # remove/clear. Matches HEAD's pattern for harp-pedals, scordatura, + # frame, figured-bass. + seeded_children = [ + c for c in (children or []) + if c.min_occurs >= 1 and c.max_occurs != 1 and not c.is_group + ] + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + include_set = set( + f'#include "mx/core/elements/{child_class_name(c)}.h"' + for c in children + ) + # The fromXElementImpl for a parent with a synthetic optional group + # inlines the parsing of the group's members; pull those member headers + # into the parent .cpp so make*() and getXxx() resolve to complete types. + for child in children: + if (child.is_group + and (child.element_name in SYNTHETIC_OPTIONAL_GROUPS + or child.element_name in SYNTHETIC_UNBOUNDED_GROUPS)): + for gm in model.groups.get(child.element_name, []): + include_set.add( + f'#include "mx/core/elements/{child_class_name(gm)}.h"' + ) + child_includes = sorted(include_set) + for inc in child_includes: + lines.append(inc) + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + # constructor + init_parts = [] + if value_type: + default_val = ELEMENT_DEFAULT_VALUE.get( + elem_name, TYPE_DEFAULT_VALUE.get(value_type, "")) + if default_val: + init_parts.append(f"myValue({default_val})") + else: + init_parts.append("myValue()") + if attrs_name: + init_parts.append(f"myAttributes(std::make_shared<{attrs_name}>())") + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + init_parts.append(f"my{cc}Set()") + else: + child_init_val = CHILD_INIT_VALUE_OVERRIDE.get((elem_name, child.element_name)) + if child_init_val: + init_parts.append(f"my{cc}(make{cc}({child_init_val}))") + else: + init_parts.append(f"my{cc}(make{cc}())") + if child.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + + use_ei_ctor = not value_type and not children + if not value_type: + if use_ei_ctor and init_parts: + all_init = ["ElementInterface()"] + init_parts + _emit_ctor_init(lines, f"{class_name}::{class_name}()", all_init) + elif use_ei_ctor: + lines.append(f"{class_name}::{class_name}() : ElementInterface()") + elif init_parts: + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + else: + lines.append(f"{class_name}::{class_name}() : ElementInterface()") + lines.append("{") + for sc in seeded_children: + scc = child_class_name(sc) + lines.append(f" my{scc}Set.push_back(make{scc}());") + lines.append("}\n") + else: + default_val = ELEMENT_DEFAULT_VALUE.get( + elem_name, TYPE_DEFAULT_VALUE.get(value_type, "")) + default_init = f"myValue({default_val})" if default_val else "myValue()" + all_init = [default_init] + if attrs_name: + all_init.append(f"myAttributes(std::make_shared<{attrs_name}>())") + ctor_prefix = f"{class_name}::{class_name}()" + _emit_ctor_init(lines, ctor_prefix, all_init) + lines.append("{") + lines.append("}\n") + val_init = ["myValue(value)"] + if attrs_name: + val_init.append(f"myAttributes(std::make_shared<{attrs_name}>())") + val_prefix = f"{class_name}::{class_name}(const {value_type} &value)" + _emit_ctor_init(lines, val_prefix, val_init) + lines.append("{") + lines.append("}\n") + + pattern_b = is_pattern_b(elem_type, ct, model, class_name) + + def _gen_hasAttributes(): + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + if attrs_name: + lines.append(" return myAttributes->hasValues();") + else: + lines.append(" return false;") + lines.append("}\n") + + def _gen_hasContents(): + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + if elem_name in ELEMENT_HAS_CONTENTS_ALWAYS_TRUE: + lines.append(" return true;") + elif value_type: + lines.append(" return true;") + elif children: + has_required = any( + c.min_occurs >= 1 and c.max_occurs == 1 and not c.is_group + for c in children + ) + if has_required or seeded_children: + lines.append(" return true;") + else: + parts = [] + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + parts.append(f"my{cc}Set.size() > 0") + elif child.is_group and child.max_occurs != 1: + parts.append(f"my{cc}Set.size() > 0") + elif child.is_group and child.min_occurs == 0: + # Optional group: gate on the has-flag, not on the + # group's own hasContents(), which may return true even + # when the group should not be emitted (e.g. + # DisplayStepOctaveGroup always returns hasContents=true + # because its members are required within the group). + parts.append(f"myHas{cc}") + elif child.is_group: + parts.append(f"my{cc}->hasContents()") + elif child.min_occurs == 0: + parts.append(f"myHas{cc}") + if parts: + lines.append(f" return {' || '.join(parts)};") + else: + lines.append(" return false;") + else: + lines.append(" return false;") + lines.append("}\n") + + def _gen_streamAttributes(): + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + if attrs_name and children: + lines.append(" return myAttributes->toStream(os);") + elif attrs_name: + lines.append(" if (myAttributes)") + lines.append(" {") + lines.append(" myAttributes->toStream(os);") + lines.append(" }") + lines.append(" return os;") + else: + lines.append(" return os;") + lines.append("}\n") + + def _gen_streamName(): + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{stream_name}";') + lines.append(" return os;") + lines.append("}\n") + + def _emit_stream_children(indent: str): + for child in children: + cc = child_class_name(child) + if child.is_group and child.max_occurs != 1: + is_unbounded_synth = ( + child.element_name in SYNTHETIC_UNBOUNDED_GROUPS) + lines.append(f"{indent}for (auto x : my{cc}Set)") + lines.append(f"{indent}{{") + if is_unbounded_synth: + # Synthetic unbounded group members are all-optional, so + # the wrapper class can be empty; gate the newline+stream + # on x->hasContents() to suppress blank entries. + lines.append(f"{indent} if (x->hasContents())") + lines.append(f"{indent} {{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(f"{indent} }}") + else: + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(f"{indent}}}") + elif child.is_group: + # Any optional group (minOccurs=0) gates on its myHas flag + # rather than on hasContents(). This matches the original code + # for time-modification / NormalTypeNormalDotGroup (synthetic + # optional groups) as well as inlined XSD groups like + # display-step-octave that are also optional on the parent. + if child.min_occurs == 0: + guard = f"myHas{cc}" + else: + guard = f"my{cc}->hasContents()" + lines.append(f"{indent}if ({guard})") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} my{cc}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(f"{indent}}}") + elif child.max_occurs != 1: + lines.append(f"{indent}for (auto x : my{cc}Set)") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} x->toStream(os, indentLevel + 1);") + lines.append(f"{indent}}}") + elif child.min_occurs == 0: + lines.append(f"{indent}if (myHas{cc})") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} my{cc}->toStream(os, indentLevel + 1);") + lines.append(f"{indent}}}") + else: + lines.append(f"{indent}os << std::endl;") + lines.append(f"{indent}my{cc}->toStream(os, indentLevel + 1);") + + def _gen_streamContents(): + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + if value_type: + lines.append(" MX_UNUSED(indentLevel);") + lines.append(" isOneLineOnly = true;") + if value_type in XMACRO_ENUM_TYPES: + lines.append(f" os << {value_type}ToString(myValue);") + else: + lines.append(" os << myValue;") + elif children: + has_required = any( + c.min_occurs >= 1 and c.max_occurs == 1 and not c.is_group + for c in children + ) + use_wrapping = elem_name in WRAPPING_STREAMCONTENTS + if has_required or seeded_children: + lines.append(" isOneLineOnly = false;") + _emit_stream_children(" ") + lines.append(" os << std::endl;") + elif use_wrapping: + lines.append(" if (hasContents())") + lines.append(" {") + _emit_stream_children(" ") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" isOneLineOnly = true;") + lines.append(" }") + else: + _emit_stream_children(" ") + # Build actual content check from children directly so that + # ELEMENT_HAS_CONTENTS_ALWAYS_TRUE elements (which return + # true unconditionally from hasContents()) still set + # isOneLineOnly correctly based on what was actually emitted. + content_parts = [] + for c in children: + cc = child_class_name(c) + if c.max_occurs != 1: + content_parts.append(f"my{cc}Set.size() > 0") + elif c.min_occurs == 0 and c.is_group: + content_parts.append(f"myHas{cc}") + elif c.min_occurs == 0: + content_parts.append(f"myHas{cc}") + else: + content_parts.append("true") + actual_check = " || ".join(content_parts) if content_parts else "false" + lines.append(f" if ({actual_check})") + lines.append(" {") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" isOneLineOnly = true;") + lines.append(" }") + else: + lines.append(" MX_UNUSED(indentLevel);") + lines.append(" isOneLineOnly = true;") + lines.append(" return os;") + + if pattern_b: + _gen_hasAttributes() + _gen_streamAttributes() + _gen_streamName() + _gen_hasContents() + _gen_streamContents() + else: + _gen_hasAttributes() + _gen_hasContents() + _gen_streamAttributes() + _gen_streamName() + _gen_streamContents() + lines.append("}\n") + + # getAttributes / setAttributes + if attrs_name: + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}\n") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}\n") + + # getValue / setValue + if value_type: + lines.append(f"{value_type} {class_name}::getValue() const") + lines.append("{") + lines.append(" return myValue;") + lines.append("}\n") + lines.append(f"void {class_name}::setValue(const {value_type} &value)") + lines.append("{") + lines.append(" myValue = value;") + lines.append("}\n") + + # child accessors + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + is_seeded = child in seeded_children + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + if is_seeded: + lines.append(f" if (my{cc}Set.size() > 1)") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + else: + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + if is_seeded: + lines.append(f" my{cc}Set.push_back(make{cc}());") + lines.append("}\n") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + elif child.min_occurs == 0: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + else: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + + # fromXElementImpl + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + if attrs_name and not value_type and not children: + lines.append(" return myAttributes->fromXElement(message, xelement);") + elif value_type and attrs_name: + if uses_set_value(value_type): + parse_call = "myValue.setValue(xelement.getValue());" + elif is_enum_value_type(value_type): + pfn = parse_func_name(value_type) + parse_call = f"myValue = {pfn}(xelement.getValue());" + else: + parse_call = "myValue.parse(xelement.getValue());" + lines.append(" bool isSuccess = true;") + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + lines.append(f" {parse_call}") + lines.append(" MX_RETURN_IS_SUCCESS;") + elif value_type: + is_enum = is_enum_value_type(value_type) + if uses_set_value(value_type): + parse_call = "myValue.setValue(xelement.getValue());" + elif is_enum: + pfn = parse_func_name(value_type) + parse_call = f"myValue = {pfn}(xelement.getValue());" + else: + parse_call = "myValue.parse(xelement.getValue());" + lines.append(" MX_UNUSED(message);") + if not is_enum: + lines.append(" MX_UNUSED(xelement);") + lines.append(f" {parse_call}") + lines.append(" return true;") + elif children: + lines.append(" bool isSuccess = true;") + if attrs_name: + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + required_children = [ + c for c in children + if c.min_occurs >= 1 and c.max_occurs == 1 and not c.is_group + ] + for child in required_children: + cc = child_class_name(child) + lines.append(f" bool is{cc}Found = false;") + lines.append("") + lines.append(" auto endIter = xelement.end();") + lines.append(" for (auto it = xelement.begin(); it != endIter; ++it)") + lines.append(" {") + for child in children: + cc = child_class_name(child) + if (child.is_group and child.max_occurs != 1 + and child.element_name in SYNTHETIC_UNBOUNDED_GROUPS): + # Synthetic unbounded group: emit a call to a private helper + # method that parses consecutive members into one wrapper + # group instance per pass. The helper is generated below. + lines.append( + f" import{cc}Set(message, it, endIter, isSuccess);") + elif (child.is_group and child.min_occurs == 0 + and child.element_name in WRAPPER_AS_ELEMENT_SYNTH_GROUPS): + # Wrapper-style synthetic optional group: parse via + # importElement on the wrapper class itself. The wrapper's + # empty streamName means this call never matches in practice + # (mirroring the original hand-written MetronomeTuplet.cpp), + # but it preserves the API shape. + lines.append( + f" if (importElement(message, *it, isSuccess, *my{cc}, myHas{cc}))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + elif (child.is_group and child.min_occurs == 0 + and child.element_name in SYNTHETIC_OPTIONAL_GROUPS): + # No importGroup() exists for an anonymous synthetic group. + # Inline the parsing of its members directly into the parent + # element's fromXElementImpl loop, matching the original + # TimeModification.cpp parser. + group_members = list(model.groups.get(child.element_name, [])) + for gm in group_members: + gm_cc = child_class_name(gm) + lines.append(f' if (it->getName() == "{gm.element_name}")') + lines.append(" {") + lines.append(f" myHas{cc} = true;") + if gm.max_occurs != 1: + local = camel(gm.element_name) + lines.append(f" auto {local} = make{gm_cc}();") + lines.append(f" isSuccess &= {local}->fromXElement(message, *it);") + lines.append(f" my{cc}->add{gm_cc}({local});") + else: + lines.append(f" isSuccess = my{cc}->get{gm_cc}()->fromXElement(message, *it);") + lines.append(" }") + elif child.is_group and child.min_occurs == 0: + lines.append(f" importGroup(message, it, endIter, isSuccess, my{cc}, myHas{cc});") + elif child.is_group: + lines.append(f" importGroup(message, it, endIter, isSuccess, my{cc});") + elif child.max_occurs != 1: + lines.append(f' importElementSet(message, it, endIter, isSuccess, "{child.element_name}", my{cc}Set);') + elif child.min_occurs == 0: + lines.append(f" if (importElement(message, *it, isSuccess, *my{cc}, myHas{cc}))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + else: + lines.append(f" if (importElement(message, *it, isSuccess, *my{cc}, is{cc}Found))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + else: + lines.append(" MX_UNUSED(message);") + lines.append(" MX_UNUSED(xelement);") + lines.append(" return true;") + lines.append("}\n") + + # Helper methods for synthetic unbounded groups (e.g. + # ScorePart::importMidiDeviceInstrumentGroupSet). + for child in children: + if (child.is_group and child.max_occurs != 1 + and child.element_name in SYNTHETIC_UNBOUNDED_GROUPS): + _emit_synthetic_unbounded_helper( + lines, class_name, child, model) + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _emit_synthetic_unbounded_helper(lines: list, parent_class: str, + child_ref: "XsdChildRef", + model: "XsdModel") -> None: + group_class = child_class_name(child_ref) + set_name = f"my{group_class}Set" + members = list(model.groups.get(child_ref.element_name, [])) + if not members: + return + name_check = " || ".join( + f'iter->getName() == "{m.element_name}"' for m in members) + name_not_check = " && ".join( + f'iter->getName() != "{m.element_name}"' for m in members) + has_check = " || ".join( + f"item->getHas{child_class_name(m)}()" for m in members) + + sig_first = f"void {parent_class}::import{group_class}Set(std::ostream &message, ::ezxml::XElementIterator &iter," + sig_indent = " " * len(f"void {parent_class}::import{group_class}Set(") + lines.append(sig_first) + lines.append(f"{sig_indent}::ezxml::XElementIterator &endIter, bool &isSuccess)") + lines.append("{") + lines.append(" bool doDecrementIter = false;") + lines.append(f" while (iter != endIter && ({name_check}))") + lines.append(" {") + lines.append(f" auto item = make{group_class}();") + for idx, m in enumerate(members): + mc = child_class_name(m) + local = camel(m.element_name) + guard = (f'iter->getName() == "{m.element_name}"' if idx == 0 + else f'iter != endIter && iter->getName() == "{m.element_name}"') + lines.append(f" if ({guard})") + lines.append(" {") + lines.append(f" item->setHas{mc}(true);") + lines.append(f" auto {local} = item->get{mc}();") + lines.append(f" isSuccess &= {local}->fromXElement(message, *iter);") + if m.element_name in SYNTHETIC_UNBOUNDED_GROUP_IMPORT_GROUP_AFTER: + lines.append(f" importGroup(message, iter, endIter, isSuccess, {local});") + lines.append(" doDecrementIter = true;") + lines.append(" ++iter;") + lines.append(" }") + lines.append("") + lines.append(f" if ({has_check})") + lines.append(" {") + lines.append(f" {set_name}.push_back(item);") + lines.append(" }") + lines.append("") + lines.append(f" if (iter != endIter && {name_not_check})") + lines.append(" {") + lines.append(" if (doDecrementIter)") + lines.append(" {") + lines.append(" --iter;") + lines.append(" }") + lines.append(" return;") + lines.append(" }") + lines.append(" }") + lines.append(" if (doDecrementIter)") + lines.append(" {") + lines.append(" --iter;") + lines.append(" }") + lines.append("}\n") + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +# Elements whose generated code intentionally replaces the original bespoke +# implementation. Diffs are exempt from eval penalty scoring. The set contains +# file stems (PascalCase, no extension) that eval.py matches against. +OVERWRITE_FILE_STEMS = { + "Direction", "DirectionType", "DirectionAttributes", +} + +# Maps XSD element names to C++ class names when they differ from pascal(elem_name). +# The XML stream name remains the original elem_name; only the C++ identifier changes. +ELEMENT_CLASS_NAME_OVERRIDE = { + "attributes": "Properties", # XSD 'attributes' -> C++ class 'Properties' +} + +# Override the value type for a simple-value or text-value element. +# Each entry maps elem_name -> dict with: +# cpp_type: the C++ type to use instead of whatever the XSD says +# header: the header file providing that type +# default: the default-value expression for the constructor +# extra_includes: additional headers to include (list of strings) +# The streaming / parsing pattern is inferred from XMACRO_ENUM_TYPES, +# is_enum_value_type, or uses_set_value, just like any other value type. +ELEMENT_VALUE_TYPE_OVERRIDE = { + "instrument-sound": { + "cpp_type": "PlaybackSound", + "header": "mx/core/PlaybackSound.h", + "default": "PlaybackSound::keyboardPiano", + "extra_includes": ["mx/core/Enums.h"], + }, +} + +# Types defined via X-macros (EnumWithString.h pattern) that provide +# XxxToString / XxxFromString free functions instead of operator<< / parseXxx. +XMACRO_ENUM_TYPES = { + "PlaybackSound", +} + + +def element_class_name(elem_name: str) -> str: + """Return the C++ class name for an element, consulting overrides first.""" + return ELEMENT_CLASS_NAME_OVERRIDE.get(elem_name, pascal(elem_name)) + + +SKIP_ELEMENTS = { + # score-partwise, score-timewise: handled by shared bespoke generator + # _emit_score_wrapper_family, parameterized via SCORE_WRAPPER_FLAVOR_CONFIG. + # Each emits {Outer, set holder, music-data holder} + their attrs structs. + # part, measure: claimed by the score-wrapper-family handler (both + # partwise and timewise dispatch entries claim each name under a + # different class prefix). Listed in BESPOKE_FAMILY_OWNED rather than + # SKIP_ELEMENTS because they ARE fully generated, just not by their + # own dispatch entry. + # directive: handled via anonymous_type path (text-with-attrs, anon CT) + # part-list: handled by bespoke generator (PartGroupOrScorePart) + # credit: handled by bespoke generator (CreditChoice + CreditWordsGroup) + # key: handled by tree-based generation + # lyric: handled by bespoke generator (LyricTextChoice + SyllabicTextGroup + # + ElisionSyllabicTextGroup + ElisionSyllabicGroup) + # notations, ornaments: handled by tree-based generation + # part-abbreviation-display, part-name-display: handled by tree-based generation + # score-instrument: handled by tree-based generation (SoloOrEnsembleChoice) + # score-part: handled via UNBOUNDED_SEQUENCE_AS_GROUP -> MidiDeviceInstrumentGroup + # time-modification: handled via synthetic NormalTypeNormalDotGroup + # (anonymous nested optional sequence promoted to a group class) +} + +# Elements whose code is emitted by some other bespoke handler as part of a +# family (e.g. score-partwise's family handler emits PartwisePart and +# PartwiseMeasure too). The main discovery loop must skip these so the default +# path doesn't try to generate competing files, but they are NOT counted as +# skipped because they ARE fully generated -- just not by their own dispatch +# entry. Distinct from SKIP_ELEMENTS which represents elements with no +# generator coverage at all. +BESPOKE_FAMILY_OWNED = { + "part", # PartwisePart (partwise) + TimewisePart (timewise) + "measure", # PartwiseMeasure (partwise) + TimewiseMeasure (timewise) +} + +TREE_ELEMENTS = { + "bend", + "group-abbreviation-display", + "group-name-display", + "harmonic", + "key", + "metronome", + "notations", + "notehead-text", + "ornaments", + "part-abbreviation-display", + "part-name-display", + "play", + "score-instrument", +} + +TREE_ELEMENT_CONFIG = { + "group-abbreviation-display": { + "choice_class": "DisplayTextOrAccidentalText", + }, + "group-name-display": { + "choice_class": "DisplayTextOrAccidentalText", + }, + "harmonic": { + "inline_choices": [ + {"choice_class": "HarmonicTypeChoice"}, + {"choice_class": "HarmonicInfoChoice"}, + ], + }, + "part-abbreviation-display": { + "choice_class": "DisplayTextOrAccidentalText", + }, + "part-name-display": { + "choice_class": "DisplayTextOrAccidentalText", + "always_has_contents": True, + }, + "notehead-text": { + "choice_class": "NoteheadTextChoice", + # HEAD seeds the choice set with one default item (displayText) so that + # hasContents() returns true and the element serialises with content. + "always_has_contents": True, + "seed_choice_set": True, + }, + "play": { + "inlined_choice": True, + }, + "score-instrument": { + "choice_class": "SoloOrEnsembleChoice", + }, + "metronome": { + "choice_class": "BeatUnitPerOrNoteRelationNoteChoice", + "container_names": { + 0: "BeatUnitPer", + 1: "NoteRelationNote", + }, + "branch_enum_names": { + 0: "beatUnitPer", + 1: "noteRelationNote", + }, + }, + # Issues E/F: route choice-group parsing through private member functions + # on the parent element (e.g. Key::importTraditionalKey, + # Key::importNonTraditionalKey) instead of through public importGroup(...) + # overloads in FromXElement.cpp. Each group branch in the parent's choice + # produces one private member: bool import(...). The parent's + # fromXElementImpl dispatches by calling those members; the choice's + # setChoice(...) is performed inside the member body. + "key": { + "parent_imports_choice_groups": True, + }, +} + +# Populated dynamically by XsdModel._synthesize_optional_group as we discover +# anonymous inside parent sequences. The names +# stored here are the lowercase-hyphenated form (e.g. "normal-type-normal-dot") +# which round-trips through pascal() to produce the synthetic group class +# (e.g. "NormalTypeNormalDotGroup"). +SYNTHETIC_OPTIONAL_GROUPS: set = set() + +# Populated dynamically by XsdModel._synthesize_unbounded_group when we +# discover an anonymous +# inside a parent sequence. The original codegen promoted some of these +# shapes to wrapper group classes used as Sets on the parent +# (e.g. score-part's midi-device + midi-instrument repeating sequence +# becomes MidiDeviceInstrumentGroup, held as a *Set on ScorePart). +SYNTHETIC_UNBOUNDED_GROUPS: set = set() + +# Opt-in: complex types whose anonymous nested +# should be promoted to a synthetic group rather than flattened. The XSD +# permits the same shape in several places (e.g. page-layout), but the +# original codegen only chose to promote it in specific spots. The value is +# the hyphenated-lowercase ref name used as the synthetic group's element_name. +NESTED_OPTIONAL_SEQUENCE_AS_GROUP: dict = { + "time-modification": "normal-type-normal-dot", +} + +# Opt-in: when an extending complexType inherits a synthetic optional group +# from its base, the default behavior is to flatten the group's members into +# the extending type. For specific extending types the original codegen +# instead kept the group as a *separately-named wrapper sub-element* with +# its own getHas/setHas accessors. The mapping is +# extending_type_name -> { base_synthetic_group_name -> renamed_wrapper_group_name } +# The renamed group's class name omits the usual "Group" suffix (see +# SUPPRESS_GROUP_SUFFIX), so a child reference to it renders as a regular +# wrapper element on the parent. Parsing of the wrapper uses importElement +# (matching the original hand-written MetronomeTuplet.cpp), not member +# inlining. +EXTENSION_OPTIONAL_GROUP_RENAME: dict = { + "metronome-tuplet": { + "normal-type-normal-dot": "time-modification-normal-type-normal-dot", + }, +} + +# Synthetic optional groups whose generated class name omits the "Group" +# suffix and whose parent parses them as a single optional sub-element +# (importElement) rather than by inlining member parsing. +WRAPPER_AS_ELEMENT_SYNTH_GROUPS: set = set() + +# Group names whose generated class name omits the trailing "Group" suffix. +SUPPRESS_GROUP_SUFFIX: set = set() + + +def group_class_name(group_name: str) -> str: + if group_name in SUPPRESS_GROUP_SUFFIX: + return pascal(group_name) + return pascal(group_name) + "Group" + +# Opt-in: complex types whose anonymous should be promoted to a synthetic unbounded group. +# Mapping parent_type_name -> hyphenated-lowercase synthetic group ref. +UNBOUNDED_SEQUENCE_AS_GROUP: dict = { + "score-part": "midi-device-instrument", +} + +# Element names whose generated synthetic-unbounded-group parser body should +# emit an additional importGroup(messsage, iter, endIter, isSuccess, elemPtr) +# call after parsing that element. The original codegen produced this call +# for midi-instrument (a no-op in practice because importGroup(MidiInstrument) +# inspects only sibling iterators that have already been consumed). Kept to +# minimize diff against committed. +SYNTHETIC_UNBOUNDED_GROUP_IMPORT_GROUP_AFTER = { + "midi-instrument", +} + +GENERATE_GROUPS = { + "beat-unit", "display-step-octave", "editorial", "editorial-voice", + "editorial-voice-direction", "layout", "score-header", + # full-note: EXC - real code has FullNoteTypeChoice class + # time-signature: EXC - real code adds Interchangeable not in XSD group + # harmony-chord: EXC - real code has Choice logic not in XSD group def + # music-data: EXC - real code wraps choice in MusicDataChoice class +} + +WRAPPING_STREAMCONTENTS = { + "defaults", "grouping", "identification", "part-group", "print", +} + +TYPE_DEFAULT_VALUE = { + "AccidentalValue": "AccidentalValue::natural", + "ArrowDirectionEnum": "ArrowDirectionEnum::up", + "ArrowStyleEnum": "ArrowStyleEnum::single", + "BarStyleEnum": "BarStyleEnum::regular", + "BeamValue": "BeamValue::begin", + "BeaterValue": "BeaterValue::snareStick", + "BreathMarkValue": "BreathMarkValue::emptystring", + "CircularArrowEnum": "CircularArrowEnum::clockwise", + "ClefSign": "ClefSign::g", + "DegreeTypeValue": "DegreeTypeValue::add", + "EffectEnum": "EffectEnum::anvil", + "FermataShape": "FermataShape::normal", + "GlassEnum": "GlassEnum::windChimes", + "GroupBarlineValue": "GroupBarlineValue::yes", + "GroupSymbolValue": "GroupSymbolValue::none", + "HandbellValue": "HandbellValue::damp", + "HoleClosedValue": "HoleClosedValue::no", + "KindValue": "KindValue::none", + "MeasureNumberingValue": "MeasureNumberingValue::none", + "MembraneEnum": "MembraneEnum::snareDrum", + "MetalEnum": "MetalEnum::bell", + "MuteEnum": "MuteEnum::off", + "NoteTypeValue": "NoteTypeValue::eighth", + "NoteheadValue": "NoteheadValue::normal", + "PitchedEnum": "PitchedEnum::xylophone", + "SemiPitchedEnum": "SemiPitchedEnum::medium", + "StaffTypeEnum": "StaffTypeEnum::regular", + "StemValue": "StemValue::none", + "StepEnum": "StepEnum::a", + "StickLocationEnum": "StickLocationEnum::center", + "StickMaterialEnum": "StickMaterialEnum::medium", + "StickTypeEnum": "StickTypeEnum::yarn", + "SyllabicEnum": "SyllabicEnum::begin", + "TimeRelationEnum": "TimeRelationEnum::equals", + "WoodEnum": "WoodEnum::claves", + "PlaybackSound": "PlaybackSound::keyboardPiano", +} + +ELEMENT_DEFAULT_VALUE = { + "type": "NoteTypeValue::quarter", + "duration": "1.0", + "tremolo": "3", + "metronome-relation": '"equals"', +} + + +def generate_group_h(group_name: str, children: list, model: XsdModel) -> str: + class_name = group_class_name(group_name) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + + child_classes = [child_class_name(c) for c in children] + for cc in sorted(set(child_classes)): + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({cc})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f"\n /* _________ {cc} minOccurs = {child.min_occurs}, maxOccurs = unbounded _________ */") + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + lines.append(f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;") + elif child.min_occurs == 0: + lines.append(f"\n /* _________ {cc} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + else: + lines.append(f"\n /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" {cc}Set my{cc}Set;") + else: + lines.append(f" {cc}Ptr my{cc};") + if child.min_occurs == 0: + lines.append(f" bool myHas{cc};") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_group_cpp(group_name: str, children: list, model: XsdModel) -> str: + class_name = group_class_name(group_name) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + child_includes = sorted(set( + f'#include "mx/core/elements/{child_class_name(c)}.h"' + for c in children + )) + for inc in child_includes: + lines.append(inc) + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [] + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + init_parts.append(f"my{cc}Set()") + else: + init_parts.append(f"my{cc}(make{cc}())") + if child.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + parts = [] + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + parts.append(f"my{cc}Set.size() > 0") + elif child.min_occurs == 0: + parts.append(f"myHas{cc}") + else: + parts.append("true") + if any("true" == p for p in parts): + lines.append(" return true;") + elif parts: + lines.append(f" return {' || '.join(parts)};") + else: + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + all_optional = all(c.min_occurs == 0 and c.max_occurs == 1 for c in children) + if all_optional: + lines.append(" bool firstItem = true;") + lines.append(" isOneLineOnly = true;") + for child in children: + cc = child_class_name(child) + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" if (!firstItem)") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" firstItem = false;") + lines.append(" }") + else: + lines.append(" bool isFirst = true;") + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" if (!isFirst)") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel);") + lines.append(" isFirst = false;") + lines.append(" }") + elif child.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" if (!isFirst)") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" isFirst = false;") + lines.append(" }") + else: + lines.append(" if (!isFirst)") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" isFirst = false;") + lines.append(" isOneLineOnly = !hasContents();") + lines.append(" return os;") + lines.append("}\n") + + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + elif child.min_occurs == 0: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + else: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + + # fromXElementImpl - most groups use the UNUSED macro since they are + # imported via importGroup helpers in the parent. score-header is the + # exception: ScorePartwise/ScoreTimewise call myScoreHeaderGroup->fromXElement + # directly, so it needs a real parsing body. + if _group_needs_real_from_x(group_name): + _emit_group_real_from_x_impl(lines, class_name, children) + else: + lines.append(f"MX_FROM_XELEMENT_UNUSED({class_name});\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +GROUPS_WITH_REAL_FROM_X_ELEMENT = { + "score-header", + # ArrowGroup is the inline-group branch of the inline-choice element + # (INLINE_CHOICE_CONFIG["arrow"]). Arrow::fromXElementImpl dispatches its + # group branch via myArrowGroup->fromXElement(message, xelement), so the + # group needs a real parsing body. + "arrow", +} + + +def _group_needs_real_from_x(group_name: str) -> bool: + # The original codegen emits a real fromXElementImpl body for the + # synthetic optional groups (e.g. NormalTypeNormalDotGroup), even though + # they are never invoked directly by the parent (which inlines its own + # parsing). Preserve that behavior to minimize diff against committed. + return (group_name in GROUPS_WITH_REAL_FROM_X_ELEMENT + or group_name in SYNTHETIC_OPTIONAL_GROUPS) + + +def _emit_group_real_from_x_impl(lines: list, class_name: str, children: list) -> None: + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + + required = [c for c in children if c.min_occurs >= 1 and c.max_occurs == 1] + for child in required: + cc = child_class_name(child) + lines.append(f" bool is{cc}Found = false;") + + unbounded = [c for c in children if c.max_occurs != 1] + for child in unbounded: + cc = child_class_name(child) + lines.append(f" bool isFirst{cc}Added = false;") + + lines.append(" for (auto it = xelement.begin(); it != xelement.end(); ++it)") + lines.append(" {") + lines.append(" const std::string elementName = it->getName();") + lines.append("") + for idx, child in enumerate(children): + cc = child_class_name(child) + elem_name = child.element_name + cond = "if" if idx == 0 else "else if" + lines.append(f' {cond} (elementName == "{elem_name}")') + lines.append(" {") + if child.max_occurs != 1: + local = camel(elem_name) + lines.append(f" auto {local} = make{cc}();") + lines.append(f" isSuccess &= {local}->fromXElement(message, *it);") + lines.append("") + lines.append(f" if (!isFirst{cc}Added && my{cc}Set.size() == 1)") + lines.append(" {") + lines.append(f" *(my{cc}Set.begin()) = {local};") + lines.append(f" isFirst{cc}Added = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" my{cc}Set.push_back({local});") + lines.append(f" isFirst{cc}Added = true;") + lines.append(" }") + elif child.min_occurs == 0: + lines.append(f" myHas{cc} = true;") + lines.append(f" isSuccess &= my{cc}->fromXElement(message, *it);") + else: + lines.append(f" is{cc}Found = true;") + lines.append(f" isSuccess &= my{cc}->fromXElement(message, *it);") + lines.append(" }") + + lines.append(" else") + lines.append(" {") + if required: + first_required = required[0] + rcc = child_class_name(first_required) + rname = first_required.element_name + lines.append(f" if (!is{rcc}Found)") + lines.append(" {") + lines.append(f' message << "{class_name}: a \'{rname}\' element is required but was not found" << std::endl;') + lines.append(" return false;") + lines.append(" }") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}\n") + +DYNAMICS_MARKS = { + "p", "pp", "ppp", "pppp", "ppppp", "pppppp", + "f", "ff", "fff", "ffff", "fffff", "ffffff", + "mp", "mf", "sf", "sfp", "sfpp", "fp", "rf", "rfz", "sfz", "sffz", "fz", + "other-dynamics", +} + +CHOICE_SKIP = set() + +ENUM_VALUE_CHOICE_CONFIG = { + "dynamics": { + "value_type": "DynamicsValue", + "enum_type": "DynamicsEnum", + "other_variant": "otherDynamics", + "other_xml_name": "other-dynamics", + }, +} + +INLINE_CHOICE_CONFIG = { + "arrow": { + "branches": [ + { + "enum_name": "arrowGroup", + "class_name": "ArrowGroup", + "is_group": True, + "children": [ + {"name": "arrow-direction", "min": 1, "max": 1}, + {"name": "arrow-style", "min": 0, "max": 1}, + ], + }, + { + "enum_name": "circularArrow", + "class_name": "CircularArrow", + "is_group": False, + "element_name": "circular-arrow", + }, + ], + "enum_start": 1, + }, +} + +CHOICE_ELEMENT_CONFIG = { + "articulations": { + "choice_class": "ArticulationsChoice", "is_set": True, "enum_start": 1, + "choice_from_x": "manual", + "choice_stream_start": "mx_unused", "choice_stream_end": None, + "choice_indent_offset": 0, "choice_braces": True, + "parent_from_x": "simple_loop", "parent_return": "bare", + "parent_else_iol": "true", "parent_if_iol": True, + }, + "technical": { + "choice_class": "TechnicalChoice", "is_set": True, "enum_start": 1, + "choice_from_x": "unused", + "choice_stream_start": None, "choice_stream_end": "is_one_line", + "choice_indent_offset": 0, "choice_braces": True, + "parent_from_x": "dispatch", "parent_return": "macro", + "parent_else_iol": "false", "parent_if_iol": False, + }, + "encoding": { + "choice_class": "EncodingChoice", "is_set": True, "enum_start": 1, + "choice_from_x": "macro", + "choice_stream_start": "endl", "choice_stream_end": "is_one_line", + "choice_indent_offset": 1, "choice_braces": True, + "parent_from_x": "simple_loop", "parent_return": "macro", + "parent_else_iol": None, "parent_if_iol": False, + "parent_stream_style": "is_first", + "parent_method_order": "remove_add", + "parent_no_get": True, + }, + "percussion": { + "choice_class": "PercussionChoice", "is_set": False, "enum_start": 1, + "choice_is_set": True, + "choice_qualified_ctor": True, + "choice_from_x": "manual_bad", + "choice_stream_start": None, "choice_stream_end": "is_one_line", + "choice_indent_offset": 0, "choice_braces": True, + "parent_from_x": "delegate", "parent_return": "macro", + "extra_children": ["stick-type", "stick-material"], + "extra_children_after": "stick", + }, + "measure-style": { + "choice_class": "MeasureStyleChoice", "is_set": False, "enum_start": 0, + "choice_from_x": "macro", + "choice_stream_start": "is_one_line", "choice_stream_end": None, + "choice_indent_offset": 1, "choice_braces": False, + "parent_from_x": "for_loop", "parent_return": "macro", + "parent_stream_iol_first": True, + "parent_stream_indent_offset": 0, + }, + "direction-type": { + "choice_class": "DirectionType", "is_set": True, "enum_start": 1, + "skip_parent": True, + "choice_from_x": "unused", + "choice_stream_start": "is_one_line", "choice_stream_end": "is_one_line_endl", + "choice_indent_offset": 1, "choice_braces": True, + }, + "time": { + "choice_class": "TimeChoice", "is_set": False, "enum_start": 0, + "bespoke_choice": True, + "parent_from_x": "time_group", + "parent_stream_iol_last": True, + "first_var_name": "TimeSignature", + }, +} + + +# --------------------------------------------------------------------------- +# Choice Class Generation +# --------------------------------------------------------------------------- + + +def generate_choice_class_h(choice_class: str, choice_children: list, + is_set: bool, enum_start: int, + parent_name: str, model: XsdModel) -> str: + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + + child_classes = sorted(set(pascal(c.element_name) for c in choice_children)) + for cc in child_classes: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({cc})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({choice_class})\n") + + lines.append(f"inline {choice_class}Ptr make{choice_class}()") + lines.append("{") + lines.append(f" return std::make_shared<{choice_class}>();") + lines.append("}") + + lines.append(f"\nclass {choice_class} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + for i, child in enumerate(choice_children): + cc_camel = camel(child.element_name) + val = i + enum_start + comma = "," if i < len(choice_children) - 1 else "" + lines.append(f" {cc_camel} = {val}{comma}") + lines.append(" };") + lines.append(f" {choice_class}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + if is_set: + lines.append(f"\n /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {choice_class}::Choice getChoice() const;") + lines.append(f" void setChoice(const {choice_class}::Choice value);") + else: + lines.append(f" Choice getChoice() const;") + lines.append(f" void setChoice(const Choice value);") + + for child in choice_children: + cc = pascal(child.element_name) + min_occurs = getattr(child, "min_occurs", 1) + max_occurs = getattr(child, "max_occurs", 1) + max_occurs_text = "unbounded" if max_occurs == -1 else str(max_occurs) + lines.append(f"\n /* _________ {cc} minOccurs = {min_occurs}, maxOccurs = {max_occurs_text} _________ */") + if max_occurs != 1: + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + lines.append(f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;") + else: + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + for child in choice_children: + cc = pascal(child.element_name) + if getattr(child, "max_occurs", 1) != 1: + lines.append(f" {cc}Set my{cc}Set;") + else: + lines.append(f" {cc}Ptr my{cc};") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_choice_class_cpp(choice_class: str, choice_children: list, + is_set: bool, enum_start: int, + parent_name: str, model: XsdModel, + config: dict = None) -> str: + if config is None: + config = {} + choice_from_x = config.get("choice_from_x", "manual") + stream_start = config.get("choice_stream_start", "mx_unused" if is_set else "is_one_line") + stream_end = config.get("choice_stream_end") + indent_offset = config.get("choice_indent_offset", 0 if is_set else 1) + use_braces = config.get("choice_braces", is_set) + # When skip_parent is True the choice class is itself the wrapper element + # (its streamName emits the tag), so each case branch must insert a leading + # newline before the child's toStream. When skip_parent is False the + # parent element handles inter-child newlines before calling + # choice->streamContents, so case branches must NOT emit a leading endl + # (doing so produces a spurious blank line + misindented child). + branch_leading_endl = bool(config.get("skip_parent")) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{choice_class}.h"') + lines.append('#include "mx/core/FromXElement.h"') + child_includes = sorted(set( + f'#include "mx/core/elements/{pascal(c.element_name)}.h"' + for c in choice_children + )) + for inc in child_includes: + lines.append(inc) + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + first_child_camel = camel(choice_children[0].element_name) + qualify_ctor = config.get("choice_qualified_ctor", not is_set) + choice_prefix = f"{choice_class}::" if qualify_ctor else "" + init_parts = [f"myChoice({choice_prefix}Choice::{first_child_camel})"] + branch_set_children = [] + for child in choice_children: + cc = pascal(child.element_name) + if getattr(child, "max_occurs", 1) != 1: + init_parts.append(f"my{cc}Set()") + branch_set_children.append(child) + else: + init_parts.append(f"my{cc}(make{cc}())") + _emit_ctor_init(lines, f"{choice_class}::{choice_class}()", init_parts) + lines.append("{") + for child in branch_set_children: + cc = pascal(child.element_name) + min_occurs = getattr(child, "min_occurs", 1) + lines.append(f" while (my{cc}Set.size() < {min_occurs})") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(make{cc}());") + lines.append(" }") + lines.append("}\n") + + lines.append(f"bool {choice_class}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{choice_class}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{choice_class}::streamName(std::ostream &os) const") + lines.append("{") + if is_set: + lines.append(f' os << "{parent_name}";') + lines.append(" return os;") + else: + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"bool {choice_class}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + indent_expr = "indentLevel" if indent_offset == 0 else f"indentLevel + {indent_offset}" + + lines.append(f"std::ostream &{choice_class}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + if stream_start == "mx_unused": + lines.append(" MX_UNUSED(isOneLineOnly);\n") + elif stream_start == "endl": + lines.append(" os << std::endl;") + elif stream_start == "is_one_line": + lines.append(" isOneLineOnly = false;") + + lines.append(" switch (myChoice)") + lines.append(" {") + for child in choice_children: + cc = pascal(child.element_name) + cc_camel = camel(child.element_name) + branch_is_set = getattr(child, "max_occurs", 1) != 1 + if use_braces: + lines.append(f" case Choice::{cc_camel}: {{") + if branch_is_set: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" x->toStream(os, {indent_expr});") + lines.append(" }") + else: + if branch_leading_endl: + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, {indent_expr});") + lines.append(" }") + lines.append(" break;") + else: + lines.append(f" case Choice::{cc_camel}:") + if branch_is_set: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" x->toStream(os, {indent_expr});") + lines.append(" }") + else: + if branch_leading_endl: + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, {indent_expr});") + lines.append(" break;") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + if stream_end == "is_one_line": + lines.append(" isOneLineOnly = false;") + elif stream_end == "is_one_line_endl": + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"{choice_class}::Choice {choice_class}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}\n") + + if is_set: + lines.append(f"void {choice_class}::setChoice(const {choice_class}::Choice value)") + else: + lines.append(f"void {choice_class}::setChoice(const {choice_class}::Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}\n") + + for child in choice_children: + cc = pascal(child.element_name) + min_occurs = getattr(child, "min_occurs", 1) + if getattr(child, "max_occurs", 1) != 1: + lines.append(f"const {cc}Set &{choice_class}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {choice_class}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {choice_class}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" if (my{cc}Set.size() > {min_occurs})") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {choice_class}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append(f" while (my{cc}Set.size() < {min_occurs})") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(make{cc}());") + lines.append(" }") + lines.append("}\n") + lines.append(f"{cc}Ptr {choice_class}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + else: + lines.append(f"{cc}Ptr {choice_class}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {choice_class}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + + if choice_from_x == "unused": + lines.append(f"MX_FROM_XELEMENT_UNUSED({choice_class});") + elif choice_from_x == "macro": + lines.append(f"bool {choice_class}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + for child in choice_children: + cc = pascal(child.element_name) + cc_camel = camel(child.element_name) + lines.append(f' MX_CHOICE_IF({cc_camel}, "{child.element_name}", {cc});') + lines.append(f" MX_BAD_ELEMENT_FAILURE({choice_class});") + lines.append("}\n") + elif choice_from_x == "manual_bad": + lines.append(f"bool {choice_class}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + for child in choice_children: + cc = pascal(child.element_name) + cc_camel = camel(child.element_name) + lines.append(f' if (xelement.getName() == "{child.element_name}")') + lines.append(" {") + lines.append(f" myChoice = Choice::{cc_camel};") + lines.append(f" return get{cc}()->fromXElement(message, xelement);") + lines.append(" }\n") + lines.append(f" MX_BAD_ELEMENT_FAILURE({choice_class});") + lines.append("}\n") + else: + lines.append(f"bool {choice_class}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + for child in choice_children: + cc = pascal(child.element_name) + cc_camel = camel(child.element_name) + lines.append(f' if (xelement.getName() == "{child.element_name}")') + lines.append(" {") + lines.append(f" myChoice = Choice::{cc_camel};") + lines.append(f" return get{cc}()->fromXElement(message, xelement);") + lines.append(" }\n") + lines.append(f' message << "{choice_class}: \'" << xelement.getName() << "\' is not allowed" << std::endl;') + lines.append(" return false;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Choice Parent Element Generation +# --------------------------------------------------------------------------- + + +def generate_choice_parent_h(elem_name: str, class_name: str, choice_class: str, + is_set: bool, has_attrs: bool, + attrs_name: Optional[str], model: XsdModel, + config: dict = None) -> str: + if config is None: + config = {} + parent_no_get = config.get("parent_no_get", False) + lines = [LICENSE, "#pragma once\n"] + project_includes = ['"mx/core/ElementInterface.h"', '"mx/core/ForwardDeclare.h"'] + if has_attrs and attrs_name: + if attrs_name in CORE_ROOT_ATTRS: + project_includes.append(f'"mx/core/{attrs_name}.h"') + else: + project_includes.append(f'"mx/core/elements/{attrs_name}.h"') + for inc in sorted(project_includes): + lines.append(f"#include {inc}") + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + + if has_attrs and attrs_name: + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({choice_class})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + if has_attrs and attrs_name: + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + + if is_set: + lines.append(f"\n /* _________ {choice_class} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {choice_class}Set &get{choice_class}Set() const;") + lines.append(f" void add{choice_class}(const {choice_class}Ptr &value);") + lines.append(f" void remove{choice_class}(const {choice_class}SetIterConst &value);") + lines.append(f" void clear{choice_class}Set();") + if not parent_no_get: + lines.append(f" {choice_class}Ptr get{choice_class}(const {choice_class}SetIterConst &setIterator) const;") + else: + lines.append(f" {choice_class}Ptr get{choice_class}() const;") + lines.append(f" void set{choice_class}(const {choice_class}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + if has_attrs and attrs_name: + lines.append(f" {attrs_name}Ptr myAttributes;") + if is_set: + lines.append(f" {choice_class}Set my{choice_class}Set;") + else: + lines.append(f" {choice_class}Ptr myChoice;") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_choice_parent_cpp(elem_name: str, class_name: str, choice_class: str, + is_set: bool, has_attrs: bool, + attrs_name: Optional[str], stream_name: str, + model: XsdModel, config: dict = None, + choice_children: list = None) -> str: + if config is None: + config = {} + parent_from_x = config.get("parent_from_x", "simple_loop" if is_set else "delegate") + parent_return = config.get("parent_return", "bare") + parent_else_iol = config.get("parent_else_iol", "true") + parent_if_iol = config.get("parent_if_iol", True) + parent_stream_style = config.get("parent_stream_style", "standard" if is_set else "single") + parent_method_order = config.get("parent_method_order", "add_remove") + parent_no_get = config.get("parent_no_get", False) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + + if parent_from_x == "dispatch" and choice_children: + child_includes = sorted(set( + f'#include "mx/core/elements/{pascal(c.element_name)}.h"' + for c in choice_children + )) + for inc in child_includes: + lines.append(inc) + + if parent_from_x == "time_group": + group_class = config.get("group_class", "TimeSignatureGroup") + single_class = config.get("single_class", "SenzaMisura") + lines.append(f'#include "mx/core/elements/{single_class}.h"') + + lines.append(f'#include "mx/core/elements/{choice_class}.h"') + + if parent_from_x == "time_group": + group_class = config.get("group_class", "TimeSignatureGroup") + lines.append(f'#include "mx/core/elements/{group_class}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [] + if has_attrs and attrs_name: + init_parts.append(f"myAttributes(std::make_shared<{attrs_name}>())") + if is_set: + init_parts.append(f"my{choice_class}Set()") + else: + init_parts.append(f"myChoice(make{choice_class}())") + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + if has_attrs: + lines.append(" return myAttributes->hasValues();") + else: + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + if has_attrs: + lines.append(" return myAttributes->toStream(os);") + else: + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{stream_name}";') + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + if is_set: + lines.append(f" return my{choice_class}Set.size() > 0;") + else: + lines.append(" return true;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + if is_set: + if parent_stream_style == "is_first": + lines.append(" bool isFirst = true;") + lines.append(f" for (auto x : my{choice_class}Set)") + lines.append(" {") + lines.append(" if (!isFirst)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" isFirst = false;") + lines.append(" }") + lines.append(" x->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" }") + lines.append(" if (hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(" isOneLineOnly = !hasContents();") + else: + lines.append(" if (hasContents())") + lines.append(" {") + lines.append(f" for (auto x : my{choice_class}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + lines.append(" os << std::endl;") + if parent_if_iol: + lines.append(" isOneLineOnly = false;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + if parent_else_iol is not None: + lines.append(f" isOneLineOnly = {'true' if parent_else_iol == 'true' else 'false'};") + lines.append(" }") + else: + p_indent_off = config.get("parent_stream_indent_offset", 1) + p_indent = "indentLevel" if p_indent_off == 0 else f"indentLevel + {p_indent_off}" + if config.get("parent_stream_iol_first"): + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(f" myChoice->streamContents(os, {p_indent}, isOneLineOnly);") + lines.append(" os << std::endl;") + elif config.get("parent_stream_iol_last"): + lines.append(" os << std::endl;") + lines.append(f" myChoice->streamContents(os, {p_indent}, isOneLineOnly);") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + else: + lines.append(" os << std::endl;") + lines.append(f" myChoice->streamContents(os, {p_indent}, isOneLineOnly);") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(" return os;") + lines.append("}\n") + + if has_attrs and attrs_name: + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}\n") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}\n") + + if is_set: + cc = choice_class + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + + if parent_method_order == "remove_add": + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + else: + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + if not parent_no_get: + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + else: + cc = choice_class + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myChoice = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + if has_attrs: + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + + if is_set and parent_from_x == "dispatch" and choice_children: + lines.append(f" ::ezxml::XElementIterator end = xelement.end();\n") + lines.append(" for (auto it = xelement.begin(); it != end; ++it)") + lines.append(" {") + lines.append(' const std::string elementName = it->getName();') + lines.append(f" auto choice = make{choice_class}();\n") + first = True + for child in choice_children: + cc = pascal(child.element_name) + cc_camel = camel(child.element_name) + prefix = " if" if first else " else if" + lines.append(f'{prefix} (elementName == "{child.element_name}")') + lines.append(" {") + lines.append(f" choice->setChoice({choice_class}::Choice::{cc_camel});") + lines.append(f" isSuccess &= choice->get{cc}()->fromXElement(message, *it);") + lines.append(f" my{choice_class}Set.push_back(choice);") + lines.append(" continue;") + lines.append(" }") + first = False + lines.append(" else") + lines.append(" {") + lines.append(f' message << "{class_name}: unexpected element \'" << elementName << "\' encountered" << std::endl;') + lines.append(" isSuccess = false;") + lines.append(" }") + lines.append(" }\n") + lines.append(" MX_RETURN_IS_SUCCESS;") + elif is_set: + lines.append("") + lines.append(" auto endIter = xelement.end();") + lines.append(" for (auto it = xelement.begin(); it != endIter; ++it)") + lines.append(" {") + lines.append(f" auto item = make{choice_class}();") + lines.append(" isSuccess &= item->fromXElement(message, *it);") + lines.append(f" my{choice_class}Set.push_back(item);") + lines.append(" }\n") + if parent_return == "macro": + lines.append(" MX_RETURN_IS_SUCCESS;") + else: + lines.append(" return isSuccess;") + elif parent_from_x == "time_group": + group_class = config.get("group_class", "TimeSignatureGroup") + single_elem = config.get("single_element", "senza-misura") + single_camel = config.get("single_camel", "senzaMisura") + single_class = config.get("single_class", "SenzaMisura") + group_camel = config.get("group_camel", "timeSignature") + first_var = config.get("first_var_name", "TimeSignature") + lines.append(f" bool isFirst{first_var}Added = false;") + lines.append("") + lines.append(" auto endIter = xelement.end();") + lines.append("") + lines.append(" for (auto it = xelement.begin(); it != endIter; ++it)") + lines.append(" {") + lines.append(f' if (it->getName() == "{single_elem}")') + lines.append(" {") + lines.append(f" myChoice->setChoice({choice_class}::Choice::{single_camel});") + lines.append(f" isSuccess &= myChoice->get{single_class}()->fromXElement(message, *it);") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" myChoice->setChoice({choice_class}::Choice::{group_camel});") + lines.append(f" auto timeSignature = make{group_class}();") + lines.append(" importGroup(message, it, endIter, isSuccess, timeSignature);") + lines.append("") + lines.append(f" if (!isFirst{first_var}Added && myChoice->get{group_class}Set().size() == 1)") + lines.append(" {") + lines.append(f" myChoice->add{group_class}(timeSignature);") + lines.append(f" myChoice->remove{group_class}(myChoice->get{group_class}Set().cbegin());") + lines.append(f" isFirst{first_var}Added = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" myChoice->add{group_class}(timeSignature);") + lines.append(f" isFirst{first_var}Added = true;") + lines.append(" }") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(" return isSuccess;") + elif parent_from_x == "delegate": + lines.append(f" isSuccess &= myChoice->fromXElement(message, xelement);") + lines.append(" MX_RETURN_IS_SUCCESS;") + else: + lines.append(" for (auto it = xelement.begin(); it != xelement.end(); ++it)") + lines.append(" {") + lines.append(" isSuccess &= myChoice->fromXElement(message, *it);") + lines.append(" }\n") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Bespoke: Time Element +# --------------------------------------------------------------------------- + + +TIME_SIGNATURE_GROUP_CHILDREN = [ + XsdChildRef(element_name="beats", min_occurs=1, max_occurs=1), + XsdChildRef(element_name="beat-type", min_occurs=1, max_occurs=1), + XsdChildRef(element_name="interchangeable", min_occurs=0, max_occurs=1), +] + + +def generate_time_choice_h() -> str: + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + lines.append("MX_FORWARD_DECLARE_ELEMENT(SenzaMisura)") + lines.append("MX_FORWARD_DECLARE_ELEMENT(TimeSignatureGroup)") + lines.append("MX_FORWARD_DECLARE_ELEMENT(TimeChoice)\n") + lines.append("inline TimeChoicePtr makeTimeChoice()") + lines.append("{") + lines.append(" return std::make_shared();") + lines.append("}") + lines.append("\nclass TimeChoice : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + lines.append(" timeSignature = 0,") + lines.append(" senzaMisura = 1") + lines.append(" };") + lines.append(" TimeChoice();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append("") + lines.append(" Choice getChoice() const;") + lines.append(" void setChoice(const Choice value);") + lines.append("") + lines.append(" /* _________ TimeSignature minOccurs = 1, maxOccurs = unbounded _________ */") + lines.append(" const TimeSignatureGroupSet &getTimeSignatureGroupSet() const;") + lines.append(" void addTimeSignatureGroup(const TimeSignatureGroupPtr &value);") + lines.append(" void removeTimeSignatureGroup(const TimeSignatureGroupSetIterConst &value);") + lines.append(" void clearTimeSignatureGroupSet();") + lines.append(" TimeSignatureGroupPtr getTimeSignatureGroup(const TimeSignatureGroupSetIterConst &setIterator) const;") + lines.append("") + lines.append(" SenzaMisuraPtr getSenzaMisura() const;") + lines.append(" void setSenzaMisura(const SenzaMisuraPtr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + lines.append(" TimeSignatureGroupSet myTimeSignatureGroupSet;") + lines.append(" SenzaMisuraPtr mySenzaMisura;") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_time_choice_cpp() -> str: + lines = [LICENSE] + lines.append('#include "mx/core/elements/TimeChoice.h"') + lines.append('#include "mx/core/FromXElement.h"') + lines.append('#include "mx/core/elements/BeatType.h"') + lines.append('#include "mx/core/elements/Beats.h"') + lines.append('#include "mx/core/elements/Interchangeable.h"') + lines.append('#include "mx/core/elements/SenzaMisura.h"') + lines.append('#include "mx/core/elements/TimeSignatureGroup.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + lines.append("TimeChoice::TimeChoice() : myChoice(Choice::timeSignature), myTimeSignatureGroupSet(), mySenzaMisura(makeSenzaMisura())") + lines.append("{") + lines.append(" myTimeSignatureGroupSet.push_back(makeTimeSignatureGroup());") + lines.append("}\n") + lines.append("bool TimeChoice::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + lines.append("std::ostream &TimeChoice::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append("std::ostream &TimeChoice::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append("bool TimeChoice::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + lines.append("std::ostream &TimeChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" switch (myChoice)") + lines.append(" {") + lines.append(" case Choice::timeSignature: {") + lines.append(" for (auto x : myTimeSignatureGroupSet)") + lines.append(" {") + lines.append(" x->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" }") + lines.append(" }") + lines.append(" break;") + lines.append(" case Choice::senzaMisura: {") + lines.append(" mySenzaMisura->toStream(os, indentLevel);") + lines.append(" }") + lines.append(" break;") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + lines.append(" return os;") + lines.append("}\n") + lines.append("TimeChoice::Choice TimeChoice::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}\n") + lines.append("void TimeChoice::setChoice(const Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}\n") + lines.append("const TimeSignatureGroupSet &TimeChoice::getTimeSignatureGroupSet() const") + lines.append("{") + lines.append(" return myTimeSignatureGroupSet;") + lines.append("}\n") + lines.append("void TimeChoice::removeTimeSignatureGroup(const TimeSignatureGroupSetIterConst &value)") + lines.append("{") + lines.append(" if (value != myTimeSignatureGroupSet.cend())") + lines.append(" {") + lines.append(" myTimeSignatureGroupSet.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append("void TimeChoice::addTimeSignatureGroup(const TimeSignatureGroupPtr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myTimeSignatureGroupSet.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append("void TimeChoice::clearTimeSignatureGroupSet()") + lines.append("{") + lines.append(" myTimeSignatureGroupSet.clear();") + lines.append(" myTimeSignatureGroupSet.push_back(makeTimeSignatureGroup());") + lines.append("}\n") + lines.append("TimeSignatureGroupPtr TimeChoice::getTimeSignatureGroup(const TimeSignatureGroupSetIterConst &setIterator) const") + lines.append("{") + lines.append(" if (setIterator != myTimeSignatureGroupSet.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(" return TimeSignatureGroupPtr();") + lines.append("}\n") + lines.append("SenzaMisuraPtr TimeChoice::getSenzaMisura() const") + lines.append("{") + lines.append(" return mySenzaMisura;") + lines.append("}\n") + lines.append("void TimeChoice::setSenzaMisura(const SenzaMisuraPtr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" mySenzaMisura = value;") + lines.append(" }") + lines.append("}\n") + lines.append("MX_FROM_XELEMENT_UNUSED(TimeChoice);") + lines.append("") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Inline Choice Generation +# --------------------------------------------------------------------------- + + +def generate_inline_choice_h(elem_name: str, class_name: str, + config: dict, has_attrs: bool, + attrs_name: Optional[str]) -> str: + branches = config["branches"] + enum_start = config.get("enum_start", 1) + + lines = [LICENSE, "#pragma once\n"] + project_includes = ['"mx/core/ElementInterface.h"', '"mx/core/ForwardDeclare.h"'] + if has_attrs and attrs_name: + if attrs_name in CORE_ROOT_ATTRS: + project_includes.append(f'"mx/core/{attrs_name}.h"') + else: + project_includes.append(f'"mx/core/elements/{attrs_name}.h"') + for inc in sorted(project_includes): + lines.append(f"#include {inc}") + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + + if has_attrs and attrs_name: + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + for branch in branches: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({branch['class_name']})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + for i, branch in enumerate(branches): + val = i + enum_start + comma = "," if i < len(branches) - 1 else "" + lines.append(f" {branch['enum_name']} = {val}{comma}") + lines.append(" };") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + if has_attrs and attrs_name: + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + + lines.append(f"\n /* _________ Choice _________ */") + lines.append(f" {class_name}::Choice getChoice() const;") + lines.append(f" void setChoice(const {class_name}::Choice value);") + + for branch in branches: + bc = branch["class_name"] + lines.append(f"\n /* _________ {bc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {bc}Ptr get{bc}() const;") + lines.append(f" void set{bc}(const {bc}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + if has_attrs and attrs_name: + lines.append(f" {attrs_name}Ptr myAttributes;") + for branch in branches: + bc = branch["class_name"] + lines.append(f" {bc}Ptr my{bc};") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_inline_choice_cpp(elem_name: str, class_name: str, + config: dict, has_attrs: bool, + attrs_name: Optional[str], + stream_name: str) -> str: + branches = config["branches"] + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + branch_includes = sorted(set( + f'#include "mx/core/elements/{b["class_name"]}.h"' + for b in branches + )) + for inc in branch_includes: + lines.append(inc) + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + first_branch = branches[0] + init_parts = [f"myChoice(Choice::{first_branch['enum_name']})"] + if has_attrs and attrs_name: + init_parts.append(f"myAttributes(std::make_shared<{attrs_name}>())") + for branch in branches: + bc = branch["class_name"] + init_parts.append(f"my{bc}(make{bc}())") + + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + if has_attrs: + lines.append(" return myAttributes->hasValues();") + else: + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + if has_attrs: + lines.append(" return myAttributes->toStream(os);") + else: + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{stream_name}";') + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" switch (myChoice)") + lines.append(" {") + for branch in branches: + bc = branch["class_name"] + lines.append(f" case Choice::{branch['enum_name']}: {{") + lines.append(" os << std::endl;") + if branch.get("is_group"): + lines.append(f" my{bc}->streamContents(os, indentLevel + 1, isOneLineOnly);") + else: + lines.append(f" my{bc}->toStream(os, indentLevel + 1);") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" }") + lines.append(" break;") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + lines.append(" return os;") + lines.append("}\n") + + if has_attrs and attrs_name: + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}\n") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"{class_name}::Choice {class_name}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}\n") + lines.append(f"void {class_name}::setChoice(const {class_name}::Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}\n") + + for branch in branches: + bc = branch["class_name"] + lines.append(f"{bc}Ptr {class_name}::get{bc}() const") + lines.append("{") + lines.append(f" return my{bc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{bc}(const {bc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{bc} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + if has_attrs: + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);\n") + + lines.append(" auto endIter = xelement.end();") + lines.append(" for (auto it = xelement.begin(); it != endIter; ++it)") + lines.append(" {") + + element_branches = [b for b in branches if not b.get("is_group")] + group_branches = [b for b in branches if b.get("is_group")] + + for eb in element_branches: + bc = eb["class_name"] + lines.append(f' if (it->getName() == "{eb["element_name"]}")') + lines.append(" {") + lines.append(f" myChoice = Choice::{eb['enum_name']};") + lines.append(f" isSuccess &= my{bc}->fromXElement(message, *it);") + lines.append(" }") + + if group_branches: + gb = group_branches[0] + bc = gb["class_name"] + lines.append(f" myChoice = Choice::{gb['enum_name']};") + lines.append(f" isSuccess = my{bc}->fromXElement(message, xelement);") + + lines.append(" }\n") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_enum_value_choice_h(elem_name: str, class_name: str, + value_type: str, has_attrs: bool, + attrs_name: Optional[str]) -> str: + lines = [LICENSE, "#pragma once\n"] + project_includes = ['"mx/core/ElementInterface.h"', '"mx/core/Enums.h"', + '"mx/core/ForwardDeclare.h"'] + if has_attrs and attrs_name: + project_includes.append(f'"mx/core/elements/{attrs_name}.h"') + for inc in sorted(project_includes): + lines.append(f"#include {inc}") + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + if has_attrs and attrs_name: + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append(f"\ninline {class_name}Ptr make{class_name}(const {value_type} &value)") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>(value);") + lines.append("}") + lines.append(f"\ninline {class_name}Ptr make{class_name}({value_type} &&value)") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>(std::move(value));") + lines.append("}") + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}(const {value_type} &value);") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + if has_attrs and attrs_name: + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name} &attributes);") + lines.append(f" {value_type} getValue() const;") + lines.append(f" void setValue(const {value_type} &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {value_type} myValue;") + if has_attrs and attrs_name: + lines.append(f" {attrs_name}Ptr myAttributes;") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_enum_value_choice_cpp(elem_name: str, class_name: str, + value_type: str, enum_type: str, + other_variant: str, other_xml_name: str, + has_attrs: bool, + attrs_name: Optional[str]) -> str: + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_value = "myValue(value)" + init_default = "myValue()" + if has_attrs and attrs_name: + attrs_init = f"myAttributes(std::make_shared<{attrs_name}>())" + init_value += f", {attrs_init}" + init_default += f", {attrs_init}" + + lines.append(f"{class_name}::{class_name}(const {value_type} &value) : {init_value}") + lines.append("{") + lines.append("}\n") + + lines.append(f"{class_name}::{class_name}() : {init_default}") + lines.append("{") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' return os << "{elem_name}";') + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(f" if (myValue.getValue() == {enum_type}::{other_variant})") + lines.append(" {") + lines.append(' indent(os, indentLevel + 1);') + lines.append(' os << "<";') + lines.append(" core::toStream(os, myValue.getValue());") + lines.append(' os << ">";') + lines.append(" os << myValue;") + lines.append(' os << "";') + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(' indent(os, indentLevel + 1);') + lines.append(' os << "<";') + lines.append(" core::toStream(os, myValue.getValue());") + lines.append(' os << "/>";') + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(" return os;") + lines.append("}\n") + + if has_attrs and attrs_name: + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + if has_attrs and attrs_name: + lines.append(" return myAttributes->hasValues();") + else: + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + if has_attrs and attrs_name: + lines.append(" return myAttributes->toStream(os);") + else: + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"{value_type} {class_name}::getValue() const") + lines.append("{") + lines.append(" return myValue;") + lines.append("}\n") + + lines.append(f"void {class_name}::setValue(const {value_type} &value)") + lines.append("{") + lines.append(" myValue = value;") + lines.append("}\n") + + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + if has_attrs and attrs_name: + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + lines.append("") + lines.append(" auto b = xelement.begin();") + lines.append(" auto e = xelement.end();") + lines.append("") + lines.append(" if (b != e)") + lines.append(" {") + lines.append(f' if (b->getName() == "{other_xml_name}")') + lines.append(" {") + lines.append(" myValue.setValue(b->getValue());") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" myValue.setValue(b->getName());") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(f' message << "{class_name}: parsing failed" << std::endl;') + lines.append(" return false;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Tree-Based Generation (for elements with nested choice/sequence structure) +# --------------------------------------------------------------------------- + + +@dataclass +class TreeChoiceBranch: + enum_name: str + class_name: str + is_set: bool + is_group: bool + group_name: str = "" + xml_name: str = "" + is_container: bool = False + # XML element names that trigger this container branch (computed from first + # child of the container sequence). Empty for non-container branches. + trigger_names: list = field(default_factory=list) + + +@dataclass +class ContainerMember: + class_name: str + var_name: str + is_set: bool = False + is_optional: bool = False + use_stream_contents: bool = False + element_name: str = "" + + +@dataclass +class InlineChoice: + choice_class: str = "" + branches: list = field(default_factory=list) + is_set: bool = False + is_optional: bool = False + + +@dataclass +class TreeContainer: + class_name: str + members: list = field(default_factory=list) + + +@dataclass +class TreeGenPlan: + choice_class: str = "" + choice_branches: list = field(default_factory=list) + choice_is_set: bool = False + choice_is_optional: bool = False + groups_to_generate: list = field(default_factory=list) + leading_groups: list = field(default_factory=list) + leading_children: list = field(default_factory=list) + trailing_children: list = field(default_factory=list) + containers_to_generate: list = field(default_factory=list) + nested_choices_to_generate: list = field(default_factory=list) + optional_groups_to_generate: list = field(default_factory=list) + inline_choices: list = field(default_factory=list) + + +def _tree_has_nested_structure(content_tree) -> bool: + if isinstance(content_tree, SequenceNode): + for child in content_tree.children: + if isinstance(child, ChoiceNode): + return True + elif isinstance(content_tree, ChoiceNode): + return True + return False + + +def _get_tree_config(parent_name: str) -> dict: + return TREE_ELEMENT_CONFIG.get(parent_name, {}) + + +def _container_trigger_names(seq_node, model: "XsdModel") -> list: + """Return the XML element names that can appear first in seq_node. + + Used to identify which container branch to activate when iterating the + parent's child elements. Inspects the first child of seq_node: + - ElementRefNode: returns [element_name] + - GroupRefNode: returns the element_names of the group's children + """ + if not seq_node.children: + return [] + first = seq_node.children[0] + if isinstance(first, ElementRefNode): + return [first.element_name] + if isinstance(first, GroupRefNode): + group_children = model.groups.get(first.group_name, []) + names = [] + for gc in group_children: + if hasattr(gc, "element_name") and gc.element_name: + names.append(gc.element_name) + return names + return [] + + +def _derive_container_name(seq_node, parent_name: str, branch_idx: int) -> str: + config = _get_tree_config(parent_name) + container_names = config.get("container_names", {}) + if branch_idx in container_names: + return container_names[branch_idx] + + first = seq_node.children[0] if seq_node.children else None + if isinstance(first, GroupRefNode): + return pascal(first.group_name) + "Sequence" + elif isinstance(first, ElementRefNode): + return pascal(first.element_name) + "Sequence" + return pascal(parent_name) + f"Sequence{branch_idx + 1}" + + +def _derive_nested_choice_name(choice_node) -> str: + parts = [] + for branch in choice_node.branches: + if isinstance(branch, ElementRefNode): + parts.append(pascal(branch.element_name)) + elif isinstance(branch, GroupRefNode): + parts.append(pascal(branch.group_name)) + elif isinstance(branch, SequenceNode) and branch.children: + first = branch.children[0] + if isinstance(first, ElementRefNode): + parts.append(pascal(first.element_name)) + elif isinstance(first, GroupRefNode): + parts.append(pascal(first.group_name)) + return "Or".join(parts) + "Choice" if parts else "NestedChoice" + + +def _derive_optional_group_name(seq_node) -> str: + first = seq_node.children[0] if seq_node.children else None + if isinstance(first, ElementRefNode): + return pascal(first.element_name) + "Group" + elif isinstance(first, GroupRefNode): + return pascal(first.group_name) + "Group" + return "OptionalGroup" + + +def _analyze_sequence_members(seq_node, model: XsdModel, plan: TreeGenPlan, + parent_name: str) -> list: + members = [] + for child in seq_node.children: + if isinstance(child, ElementRefNode): + cls = pascal(child.element_name) + members.append(ContainerMember( + class_name=cls, + var_name=camel(child.element_name), + is_set=child.max_occurs != 1, + is_optional=child.min_occurs == 0, + use_stream_contents=False, + element_name=child.element_name, + )) + elif isinstance(child, GroupRefNode): + if child.group_name in GENERATE_GROUPS: + cls = pascal(child.group_name) + "Group" + vname = camel(child.group_name) + "Group" + else: + cls = pascal(child.group_name) + vname = camel(child.group_name) + group_children = model.groups.get(child.group_name, []) + plan.groups_to_generate.append((cls, group_children)) + members.append(ContainerMember( + class_name=cls, + var_name=vname, + is_set=child.max_occurs != 1, + use_stream_contents=True, + )) + elif isinstance(child, ChoiceNode): + nested_name = _derive_nested_choice_name(child) + nested_branches = [] + for nb in child.branches: + if isinstance(nb, ElementRefNode): + nb_cls = pascal(nb.element_name) + nested_branches.append(TreeChoiceBranch( + enum_name=camel(nb.element_name), + class_name=nb_cls, + is_set=nb.max_occurs != 1, + is_group=False, + xml_name=nb.element_name, + )) + elif isinstance(nb, GroupRefNode): + if nb.group_name in GENERATE_GROUPS: + nb_cls = pascal(nb.group_name) + "Group" + nb_enum = camel(nb.group_name) + "Group" + else: + nb_cls = pascal(nb.group_name) + nb_enum = camel(nb.group_name) + group_children = model.groups.get(nb.group_name, []) + plan.groups_to_generate.append((nb_cls, group_children)) + nested_branches.append(TreeChoiceBranch( + enum_name=nb_enum, + class_name=nb_cls, + is_set=nb.max_occurs != 1, + is_group=True, + group_name=nb.group_name, + )) + plan.nested_choices_to_generate.append( + (nested_name, nested_branches, parent_name)) + members.append(ContainerMember( + class_name=nested_name, + var_name=nested_name[0].lower() + nested_name[1:], + use_stream_contents=True, + )) + elif isinstance(child, SequenceNode) and child.min_occurs == 0: + group_name = _derive_optional_group_name(child) + group_members = [] + for gc in child.children: + if isinstance(gc, ElementRefNode): + gc_cls = pascal(gc.element_name) + group_members.append(XsdChildRef( + element_name=gc.element_name, + min_occurs=1, + max_occurs=1, + )) + plan.optional_groups_to_generate.append( + (group_name, group_members)) + members.append(ContainerMember( + class_name=group_name, + var_name=group_name[0].lower() + group_name[1:], + is_optional=True, + use_stream_contents=True, + )) + return members + + +def analyze_tree(elem_name: str, content_tree, model: XsdModel) -> Optional[TreeGenPlan]: + if not _tree_has_nested_structure(content_tree): + return None + + plan = TreeGenPlan() + tree_config = _get_tree_config(elem_name) + + if isinstance(content_tree, SequenceNode): + root_unbounded = content_tree.max_occurs != 1 + seen_choice = False + choice_index = 0 + inline_choice_configs = tree_config.get("inline_choices", []) + for child in content_tree.children: + if isinstance(child, ChoiceNode): + if inline_choice_configs and choice_index < len(inline_choice_configs): + cc_name = inline_choice_configs[choice_index]["choice_class"] + elif choice_index == 0: + cc_name = tree_config.get("choice_class", pascal(elem_name) + "Choice") + else: + cc_name = pascal(elem_name) + f"Choice{choice_index + 1}" + is_set = (child.max_occurs != 1) or root_unbounded + is_optional = ( + child.min_occurs == 0 and child.max_occurs == 1 and not root_unbounded + ) + branches = [] + for branch in child.branches: + if isinstance(branch, GroupRefNode): + cls = pascal(branch.group_name) + branches.append(TreeChoiceBranch( + enum_name=camel(branch.group_name), + class_name=cls, + is_set=branch.max_occurs != 1, + is_group=True, + group_name=branch.group_name, + )) + group_children = model.groups.get(branch.group_name, []) + plan.groups_to_generate.append((cls, group_children)) + elif isinstance(branch, ElementRefNode): + cls = pascal(branch.element_name) + branches.append(TreeChoiceBranch( + enum_name=camel(branch.element_name), + class_name=cls, + is_set=branch.max_occurs != 1, + is_group=False, + xml_name=branch.element_name, + )) + plan.inline_choices.append(InlineChoice( + choice_class=cc_name, branches=branches, + is_set=is_set, is_optional=is_optional, + )) + if choice_index == 0: + plan.choice_class = cc_name + plan.choice_is_set = is_set + plan.choice_is_optional = is_optional + plan.choice_branches = branches + choice_index += 1 + seen_choice = True + elif isinstance(child, GroupRefNode): + if child.group_name in GENERATE_GROUPS: + cls = pascal(child.group_name) + "Group" + else: + cls = pascal(child.group_name) + group_children = model.groups.get(child.group_name, []) + plan.groups_to_generate.append((cls, group_children)) + plan.leading_groups.append((cls, child)) + elif isinstance(child, ElementRefNode): + ref = XsdChildRef( + element_name=child.element_name, + min_occurs=child.min_occurs, + max_occurs=child.max_occurs, + ) + if seen_choice: + plan.trailing_children.append(ref) + else: + plan.leading_children.append(ref) + + elif isinstance(content_tree, ChoiceNode): + plan.choice_class = tree_config.get("choice_class", pascal(elem_name) + "Choice") + for i, branch in enumerate(content_tree.branches): + if isinstance(branch, SequenceNode) and len(branch.children) > 1: + container_name = _derive_container_name( + branch, elem_name, i) + members = _analyze_sequence_members( + branch, model, plan, elem_name) + plan.containers_to_generate.append( + TreeContainer(class_name=container_name, members=members)) + branch_enum_names = tree_config.get("branch_enum_names", {}) + enum_name = branch_enum_names.get( + i, camel(container_name.replace("Sequence", ""))) + trigger_names = _container_trigger_names(branch, model) + plan.choice_branches.append(TreeChoiceBranch( + enum_name=enum_name, + class_name=container_name, + is_set=branch.max_occurs != 1, + is_group=False, + is_container=True, + trigger_names=trigger_names, + )) + elif isinstance(branch, SequenceNode) and len(branch.children) == 1: + child = branch.children[0] + if isinstance(child, ElementRefNode): + cls = pascal(child.element_name) + plan.choice_branches.append(TreeChoiceBranch( + enum_name=camel(child.element_name), + class_name=cls, + is_set=child.max_occurs != 1, + is_group=False, + )) + elif isinstance(child, GroupRefNode): + if child.group_name in GENERATE_GROUPS: + cls = pascal(child.group_name) + "Group" + else: + cls = pascal(child.group_name) + plan.choice_branches.append(TreeChoiceBranch( + enum_name=camel(child.group_name), + class_name=cls, + is_set=child.max_occurs != 1, + is_group=True, + group_name=child.group_name, + )) + elif isinstance(branch, ElementRefNode): + cls = pascal(branch.element_name) + plan.choice_branches.append(TreeChoiceBranch( + enum_name=camel(branch.element_name), + class_name=cls, + is_set=branch.max_occurs != 1, + is_group=False, + )) + elif isinstance(branch, GroupRefNode): + if branch.group_name in GENERATE_GROUPS: + cls = pascal(branch.group_name) + "Group" + else: + cls = pascal(branch.group_name) + plan.choice_branches.append(TreeChoiceBranch( + enum_name=camel(branch.group_name), + class_name=cls, + is_set=branch.max_occurs != 1, + is_group=True, + group_name=branch.group_name, + )) + + return plan if plan.choice_class else None + + +def generate_tree_group_h(class_name: str, children: list, model: XsdModel) -> str: + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + + child_classes = [child_class_name(c) for c in children] + for cc in sorted(set(child_classes)): + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({cc})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + elif child.min_occurs == 0: + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + else: + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" {cc}Set my{cc}Set;") + else: + lines.append(f" {cc}Ptr my{cc};") + if child.min_occurs == 0: + lines.append(f" bool myHas{cc};") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_tree_group_cpp(class_name: str, children: list, model: XsdModel) -> str: + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + child_includes = sorted(set( + f'#include "mx/core/elements/{child_class_name(c)}.h"' + for c in children + )) + for inc in child_includes: + lines.append(inc) + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [] + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + init_parts.append(f"my{cc}Set()") + else: + init_parts.append(f"my{cc}(make{cc}())") + if child.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + parts = [] + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + parts.append(f"my{cc}Set.size() > 0") + elif child.min_occurs == 0: + parts.append(f"myHas{cc}") + else: + parts.append("true") + if any("true" == p for p in parts): + lines.append(" return true;") + elif parts: + lines.append(f" return {' || '.join(parts)};") + else: + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" isOneLineOnly = false;") + lines.append(" bool isFirst = true;") + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" if (!isFirst)") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel);") + lines.append(" isFirst = false;") + lines.append(" }") + elif child.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" if (!isFirst)") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" isFirst = false;") + lines.append(" }") + else: + lines.append(" if (!isFirst)") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" isFirst = false;") + lines.append(" return os;") + lines.append("}\n") + + for child in children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + elif child.min_occurs == 0: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + else: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({class_name});\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_container_h(container: TreeContainer) -> str: + cn = container.class_name + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + + fwd = [] + for m in container.members: + fwd.append(m.class_name) + for f in sorted(set(fwd)): + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({cn})\n") + + lines.append(f"inline {cn}Ptr make{cn}()") + lines.append("{") + lines.append(f" return std::make_shared<{cn}>();") + lines.append("}") + + lines.append(f"\nclass {cn} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {cn}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + for m in container.members: + cc = m.class_name + if m.is_set: + lines.append(f"\n /* _________ {cc} minOccurs = 1, maxOccurs = unbounded _________ */") + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &setIterator);") + lines.append(f" void clear{cc}Set();") + elif m.is_optional: + lines.append(f"\n /* _________ {cc} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + else: + lines.append(f"\n /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + for m in container.members: + cc = m.class_name + if m.is_set: + lines.append(f" {cc}Set my{cc}Set;") + else: + lines.append(f" {cc}Ptr my{cc};") + if m.is_optional: + lines.append(f" bool myHas{cc};") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_container_cpp(container: TreeContainer) -> str: + cn = container.class_name + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{cn}.h"') + lines.append('#include "mx/core/FromXElement.h"') + child_includes = sorted(set( + f'#include "mx/core/elements/{m.class_name}.h"' + for m in container.members + )) + for inc in child_includes: + lines.append(inc) + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [] + for m in container.members: + cc = m.class_name + if m.is_set: + init_parts.append(f"my{cc}Set()") + else: + init_parts.append(f"my{cc}(make{cc}())") + if m.is_optional: + init_parts.append(f"myHas{cc}(false)") + + _emit_ctor_init(lines, f"{cn}::{cn}()", init_parts) + lines.append("{") + set_inits = [m for m in container.members if m.is_set] + for m in set_inits: + cc = m.class_name + lines.append(f" my{cc}Set.push_back(make{cc}());") + lines.append("}\n") + + lines.append(f"bool {cn}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{cn}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{cn}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"bool {cn}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + lines.append(f"std::ostream &{cn}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + first = True + for m in container.members: + cc = m.class_name + if m.is_set: + lines.append(f" for (auto it = my{cc}Set.cbegin(); it != my{cc}Set.cend(); ++it)") + lines.append(" {") + lines.append(f" if (it != my{cc}Set.cbegin())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(" (*it)->toStream(os, indentLevel);") + lines.append(" }") + first = False + elif m.is_optional: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + if m.use_stream_contents: + lines.append(f" my{cc}->streamContents(os, indentLevel, isOneLineOnly);") + else: + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" }") + first = False + elif m.use_stream_contents: + if not first: + lines.append(" os << std::endl;") + lines.append(f" my{cc}->streamContents(os, indentLevel, isOneLineOnly);") + first = False + else: + if not first: + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + first = False + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}\n") + + for m in container.members: + cc = m.class_name + if m.is_set: + lines.append(f"const {cc}Set &{cn}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {cn}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {cn}::remove{cc}(const {cc}SetIterConst &setIterator)") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" if (my{cc}Set.size() > 1)") + lines.append(" {") + lines.append(f" my{cc}Set.erase(setIterator);") + lines.append(" }") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {cn}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append(f" my{cc}Set.push_back(make{cc}());") + lines.append("}\n") + elif m.is_optional: + lines.append(f"{cc}Ptr {cn}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {cn}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + lines.append(f"bool {cn}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {cn}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + else: + lines.append(f"{cc}Ptr {cn}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {cn}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({cn});\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_tree_choice_h(choice_class: str, branches: list, + parent_name: str) -> str: + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + + for b in branches: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({b.class_name})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({choice_class})\n") + + lines.append(f"inline {choice_class}Ptr make{choice_class}()") + lines.append("{") + lines.append(f" return std::make_shared<{choice_class}>();") + lines.append("}") + + lines.append(f"\nclass {choice_class} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + for i, b in enumerate(branches): + comma = "," if i < len(branches) - 1 else "" + lines.append(f" {b.enum_name} = {i}{comma}") + lines.append(" };") + lines.append(f" {choice_class}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" Choice getChoice() const;") + lines.append(f" void setChoice(const Choice value);") + + for b in branches: + if b.is_set: + lines.append(f"\n const {b.class_name}Set &get{b.class_name}Set() const;") + lines.append(f" void remove{b.class_name}(const {b.class_name}SetIterConst &value);") + lines.append(f" void add{b.class_name}(const {b.class_name}Ptr &value);") + lines.append(f" void clear{b.class_name}Set();") + else: + lines.append(f" {b.class_name}Ptr get{b.class_name}() const;") + lines.append(f" void set{b.class_name}(const {b.class_name}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + for b in branches: + if b.is_set: + lines.append(f" {b.class_name}Set my{b.class_name}Set;") + else: + lines.append(f" {b.class_name}Ptr my{b.class_name};") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_tree_choice_cpp(choice_class: str, branches: list, + parent_name: str) -> str: + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{choice_class}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for b in branches: + lines.append(f'#include "mx/core/elements/{b.class_name}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + first_enum = branches[0].enum_name + init_parts = [f"myChoice(Choice::{first_enum})"] + for b in branches: + if b.is_set: + init_parts.append(f"my{b.class_name}Set()") + else: + init_parts.append(f"my{b.class_name}(make{b.class_name}())") + _emit_ctor_init(lines, f"{choice_class}::{choice_class}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {choice_class}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{choice_class}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{choice_class}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"bool {choice_class}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + lines.append(f"std::ostream &{choice_class}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + for b in branches: + lines.append(f" if (myChoice == Choice::{b.enum_name})") + lines.append(" {") + if b.is_set: + lines.append(f" for (auto it = my{b.class_name}Set.cbegin(); it != my{b.class_name}Set.cend(); ++it)") + lines.append(" {") + lines.append(f" if (it != my{b.class_name}Set.cbegin())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(" (*it)->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" }") + lines.append(f" if (my{b.class_name}Set.size() > 1)") + lines.append(" {") + lines.append(" isOneLineOnly = false;") + lines.append(" }") + else: + if b.is_group: + lines.append(f" if (my{b.class_name})") + lines.append(" {") + lines.append(f" my{b.class_name}->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" }") + elif b.is_container: + lines.append(f" my{b.class_name}->streamContents(os, indentLevel, isOneLineOnly);") + else: + lines.append(f" my{b.class_name}->toStream(os, indentLevel);") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"{choice_class}::Choice {choice_class}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}\n") + + lines.append(f"void {choice_class}::setChoice(const Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}\n") + + for b in branches: + if b.is_set: + lines.append(f"const {b.class_name}Set &{choice_class}::get{b.class_name}Set() const") + lines.append("{") + lines.append(f" return my{b.class_name}Set;") + lines.append("}\n") + lines.append(f"void {choice_class}::remove{b.class_name}(const {b.class_name}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{b.class_name}Set.cend())") + lines.append(" {") + lines.append(f" my{b.class_name}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {choice_class}::add{b.class_name}(const {b.class_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{b.class_name}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {choice_class}::clear{b.class_name}Set()") + lines.append("{") + lines.append(f" my{b.class_name}Set.clear();") + lines.append("}\n") + else: + lines.append(f"{b.class_name}Ptr {choice_class}::get{b.class_name}() const") + lines.append("{") + lines.append(f" return my{b.class_name};") + lines.append("}\n") + lines.append(f"void {choice_class}::set{b.class_name}(const {b.class_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{b.class_name} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({choice_class});\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_tree_parent_h(elem_name: str, class_name: str, + choice_class: str, trailing: list, + has_attrs: bool, attrs_name: Optional[str], + model: XsdModel, + choice_is_set: bool = False, + leading_groups: list = None, + leading_children: list = None, + choice_is_optional: bool = False, + inline_choices: list = None, + choice_branches: list = None) -> str: + if leading_groups is None: + leading_groups = [] + if leading_children is None: + leading_children = [] + if inline_choices is None: + inline_choices = [] + multi_choice = len(inline_choices) > 1 + lines = [LICENSE, "#pragma once\n"] + project_includes = ['"mx/core/ElementInterface.h"', '"mx/core/ForwardDeclare.h"'] + if has_attrs and attrs_name: + if attrs_name in CORE_ROOT_ATTRS: + project_includes.append(f'"mx/core/{attrs_name}.h"') + else: + project_includes.append(f'"mx/core/elements/{attrs_name}.h"') + for inc in sorted(project_includes): + lines.append(f"#include {inc}") + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + if multi_choice: + if not any(ic.is_set for ic in inline_choices): + lines.append("namespace ezxml\n{\nclass XElementIterator;\n}") + lines.append("") + elif not choice_is_set: + lines.append("namespace ezxml\n{\nclass XElementIterator;\n}") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + + if has_attrs and attrs_name: + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + for lg_cls, lg_ref in leading_groups: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({lg_cls})") + for child in leading_children: + cc = child_class_name(child) + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({cc})") + if multi_choice: + for ic in inline_choices: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({ic.choice_class})") + else: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({choice_class})") + for child in trailing: + cc = child_class_name(child) + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({cc})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + if has_attrs and attrs_name: + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + + for lg_cls, lg_ref in leading_groups: + lines.append(f"\n /* _________ {lg_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {lg_cls}Ptr get{lg_cls}() const;") + lines.append(f" void set{lg_cls}(const {lg_cls}Ptr &value);") + + for child in leading_children: + cc = child_class_name(child) + max_o = "unbounded" if child.max_occurs == -1 else str(child.max_occurs) + lines.append(f"\n /* _________ {cc} minOccurs = {child.min_occurs}, maxOccurs = {max_o} _________ */") + if child.max_occurs != 1: + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + elif child.min_occurs == 0: + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + else: + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + + if multi_choice: + for ic in inline_choices: + icc = ic.choice_class + if ic.is_optional and not ic.is_set: + lines.append(f"\n /* _________ {icc} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {icc}Ptr get{icc}() const;") + lines.append(f" void set{icc}(const {icc}Ptr &value);") + lines.append(f" bool getHas{icc}() const;") + lines.append(f" void setHas{icc}(const bool value);") + elif ic.is_set: + lines.append(f"\n /* _________ {icc} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {icc}Set &get{icc}Set() const;") + lines.append(f" void add{icc}(const {icc}Ptr &value);") + lines.append(f" void remove{icc}(const {icc}SetIterConst &value);") + lines.append(f" void clear{icc}Set();") + else: + lines.append(f" {icc}Ptr get{icc}() const;") + lines.append(f" void set{icc}(const {icc}Ptr &value);") + else: + if choice_is_optional and not choice_is_set: + lines.append(f"\n /* _________ {choice_class} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {choice_class}Ptr get{choice_class}() const;") + lines.append(f" void set{choice_class}(const {choice_class}Ptr &value);") + lines.append(f" bool getHas{choice_class}() const;") + lines.append(f" void setHas{choice_class}(const bool value);") + elif choice_is_set: + lines.append(f"\n /* _________ {choice_class} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {choice_class}Set &get{choice_class}Set() const;") + lines.append(f" void add{choice_class}(const {choice_class}Ptr &value);") + lines.append(f" void remove{choice_class}(const {choice_class}SetIterConst &value);") + lines.append(f" void clear{choice_class}Set();") + lines.append(f" {choice_class}Ptr get{choice_class}(const {choice_class}SetIterConst &setIterator) const;") + else: + lines.append(f" {choice_class}Ptr get{choice_class}() const;") + lines.append(f" void set{choice_class}(const {choice_class}Ptr &value);") + + for child in trailing: + cc = child_class_name(child) + max_o = "unbounded" if child.max_occurs == -1 else str(child.max_occurs) + if leading_children: + lines.append(f"\n /* _________ {cc} minOccurs = {child.min_occurs}, maxOccurs = {max_o} _________ */") + if child.max_occurs != 1: + if choice_is_set: + if not leading_children: + lines.append(f"\n /* _________ {cc} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + lines.append(f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;") + else: + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void clear{cc}Set();") + elif child.min_occurs == 0: + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + else: + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + if has_attrs and attrs_name: + lines.append(f" {attrs_name}Ptr myAttributes;") + for lg_cls, lg_ref in leading_groups: + lines.append(f" {lg_cls}Ptr my{lg_cls};") + for child in leading_children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" {cc}Set my{cc}Set;") + else: + lines.append(f" {cc}Ptr my{cc};") + if child.min_occurs == 0: + lines.append(f" bool myHas{cc};") + if multi_choice: + for ic in inline_choices: + icc = ic.choice_class + if ic.is_set: + lines.append(f" {icc}Set my{icc}Set;") + else: + lines.append(f" {icc}Ptr my{icc};") + if ic.is_optional: + lines.append(f" bool myHas{icc};") + else: + if choice_is_set: + lines.append(f" {choice_class}Set my{choice_class}Set;") + else: + lines.append(f" {choice_class}Ptr my{choice_class};") + if choice_is_optional: + lines.append(f" bool myHas{choice_class};") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" {cc}Set my{cc}Set;") + else: + lines.append(f" {cc}Ptr my{cc};") + if child.min_occurs == 0: + lines.append(f" bool myHas{cc};") + + # Private import helpers for parent-imported choice groups (Issues E/F). + if not multi_choice and _get_tree_config(elem_name).get( + "parent_imports_choice_groups", False) and choice_branches: + lines.append("") + for b in choice_branches: + if not b.is_group: + continue + lines.append(f" bool import{b.class_name}(std::ostream &message, ::ezxml::XElementIterator &iter,") + lines.append(f" {' ' * len(b.class_name)}::ezxml::XElementIterator &endIter, bool &isSuccess);") + + # Private import helpers for container branches (is_container=True). + if not multi_choice and choice_branches: + container_branches = [b for b in choice_branches if b.is_container and b.trigger_names] + if container_branches: + lines.append("") + for b in container_branches: + fn_name = f"importContainer{b.class_name}" + lines.append(f" bool {fn_name}(std::ostream &message, ::ezxml::XElementIterator &it,") + lines.append(f" {' ' * (len(fn_name) + 6)}::ezxml::XElementIterator &endIter, bool &isSuccess);") + + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_tree_parent_cpp(elem_name: str, class_name: str, + choice_class: str, trailing: list, + has_attrs: bool, attrs_name: Optional[str], + plan: TreeGenPlan, model: XsdModel) -> str: + choice_is_set = plan.choice_is_set + choice_is_optional = plan.choice_is_optional + leading_groups = plan.leading_groups or [] + leading_children = plan.leading_children or [] + inline_choices = plan.inline_choices or [] + multi_choice = len(inline_choices) > 1 + always_has_contents = _get_tree_config(elem_name).get("always_has_contents", False) + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + + all_includes = set() + if multi_choice: + for ic in inline_choices: + all_includes.add(ic.choice_class) + for b in ic.branches: + all_includes.add(b.class_name) + if b.is_group: + group_children = model.groups.get(b.group_name, []) + for gc in group_children: + all_includes.add(child_class_name(gc)) + else: + for b in plan.choice_branches: + all_includes.add(b.class_name) + if b.is_group: + group_children = model.groups.get(b.group_name, []) + for gc in group_children: + all_includes.add(child_class_name(gc)) + if b.is_container: + # Include headers for all members of the container, and for + # nested choice branch classes so the importContainer method + # can reference them. + container = next( + (c for c in plan.containers_to_generate if c.class_name == b.class_name), + None, + ) + if container: + for m in container.members: + all_includes.add(m.class_name) + if m.use_stream_contents and not m.element_name and not m.is_optional: + # Nested choice: add its branch class names too + nc_entry = next( + ( + (nc_class, nc_branches) + for (nc_class, nc_branches, _) in plan.nested_choices_to_generate + if nc_class == m.class_name + ), + None, + ) + if nc_entry: + _, nc_branches = nc_entry + for nb in nc_branches: + all_includes.add(nb.class_name) + if nb.is_group: + gc_list = model.groups.get(nb.group_name, []) + for gc in gc_list: + all_includes.add(child_class_name(gc)) + all_includes.add(choice_class) + for lg_cls, lg_ref in leading_groups: + all_includes.add(lg_cls) + if isinstance(lg_ref, GroupRefNode): + group_children = model.groups.get(lg_ref.group_name, []) + for gc in group_children: + all_includes.add(child_class_name(gc)) + for child in leading_children: + all_includes.add(child_class_name(child)) + for child in trailing: + all_includes.add(child_class_name(child)) + for inc in sorted(all_includes): + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [] + if has_attrs and attrs_name: + init_parts.append(f"myAttributes(std::make_shared<{attrs_name}>())") + for lg_cls, lg_ref in leading_groups: + init_parts.append(f"my{lg_cls}(make{lg_cls}())") + for child in leading_children: + cc = child_class_name(child) + if child.max_occurs != 1: + init_parts.append(f"my{cc}Set()") + else: + init_parts.append(f"my{cc}(make{cc}())") + if child.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + if multi_choice: + for ic in inline_choices: + icc = ic.choice_class + if ic.is_set: + init_parts.append(f"my{icc}Set()") + else: + init_parts.append(f"my{icc}(make{icc}())") + if ic.is_optional: + init_parts.append(f"myHas{icc}(false)") + else: + if choice_is_set: + init_parts.append(f"my{choice_class}Set()") + else: + init_parts.append(f"my{choice_class}(std::make_shared<{choice_class}>())") + if choice_is_optional: + init_parts.append(f"myHas{choice_class}(false)") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + init_parts.append(f"my{cc}Set()") + else: + init_parts.append(f"my{cc}(make{cc}())") + if child.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + # If the config requests that the choice set be pre-seeded with one default + # member (matching HEAD's pattern for notehead-text), emit the push_back. + if choice_is_set and _get_tree_config(elem_name).get("seed_choice_set", False): + lines.append(f" my{choice_class}Set.push_back(make{choice_class}());") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + if has_attrs: + lines.append(" return myAttributes->hasValues();") + else: + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + if has_attrs: + lines.append(" return myAttributes->toStream(os);") + else: + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{elem_name}";') + lines.append(" return os;") + lines.append("}\n") + + has_contents_parts = [] + for lg_cls, lg_ref in leading_groups: + has_contents_parts.append(f"my{lg_cls}->hasContents()") + for child in leading_children: + cc = child_class_name(child) + if child.max_occurs != 1: + has_contents_parts.append(f"my{cc}Set.size() > 0") + elif child.min_occurs == 0: + has_contents_parts.append(f"myHas{cc}") + else: + has_contents_parts.append("true") + if multi_choice: + for ic in inline_choices: + icc = ic.choice_class + if ic.is_set: + has_contents_parts.append(f"my{icc}Set.size() > 0") + elif ic.is_optional: + has_contents_parts.append(f"myHas{icc}") + else: + has_contents_parts.append(f"my{icc}->hasContents()") + else: + if choice_is_set: + has_contents_parts.append(f"my{choice_class}Set.size() > 0") + elif choice_is_optional: + has_contents_parts.append(f"myHas{choice_class}") + else: + has_contents_parts.append(f"my{choice_class}->hasContents()") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + has_contents_parts.append(f"my{cc}Set.size() > 0") + elif child.min_occurs == 0: + has_contents_parts.append(f"myHas{cc}") + else: + has_contents_parts.append("true") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + if always_has_contents or "true" in has_contents_parts: + lines.append(" return true;") + else: + lines.append(f" return {' || '.join(has_contents_parts)};") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + if multi_choice: + has_required = any( + (child.min_occurs > 0 and child.max_occurs == 1) + for child in list(leading_children) + list(trailing) + ) or any(not ic.is_optional and not ic.is_set for ic in inline_choices) + if not has_required: + lines.append(" if (hasContents())") + lines.append(" {") + indent = " " + else: + indent = " " + for child in leading_children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f"{indent}for (auto x : my{cc}Set)") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} x->toStream(os, indentLevel + 1);") + lines.append(f"{indent}}}") + elif child.min_occurs == 0: + lines.append(f"{indent}if (myHas{cc})") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} my{cc}->toStream(os, indentLevel + 1);") + lines.append(f"{indent}}}") + else: + lines.append(f"{indent}os << std::endl;") + lines.append(f"{indent}my{cc}->toStream(os, indentLevel + 1);") + for ic in inline_choices: + icc = ic.choice_class + if ic.is_optional: + lines.append(f"{indent}if (myHas{icc})") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} my{icc}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(f"{indent}}}") + elif ic.is_set: + lines.append(f"{indent}for (auto x : my{icc}Set)") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(f"{indent}}}") + else: + lines.append(f"{indent}if (my{icc}->hasContents())") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} my{icc}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(f"{indent}}}") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f"{indent}for (auto x : my{cc}Set)") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} x->toStream(os, indentLevel + 1);") + lines.append(f"{indent}}}") + elif child.min_occurs == 0: + lines.append(f"{indent}if (myHas{cc})") + lines.append(f"{indent}{{") + lines.append(f"{indent} os << std::endl;") + lines.append(f"{indent} my{cc}->toStream(os, indentLevel + 1);") + lines.append(f"{indent}}}") + else: + lines.append(f"{indent}os << std::endl;") + lines.append(f"{indent}my{cc}->toStream(os, indentLevel + 1);") + lines.append(f"{indent}os << std::endl;") + lines.append(f"{indent}isOneLineOnly = false;") + if not has_required: + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" isOneLineOnly = true;") + lines.append(" }") + elif choice_is_set: + if leading_groups: + lines.append(" if (hasContents())") + lines.append(" {") + for lg_cls, lg_ref in leading_groups: + lines.append(f" if (my{lg_cls}->hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{lg_cls}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + lines.append(f" for (auto x : my{choice_class}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" isOneLineOnly = true;") + lines.append(" }") + elif always_has_contents and not trailing: + lines.append(f" if (my{choice_class}Set.size() > 0)") + lines.append(" {") + lines.append(f" for (auto x : my{choice_class}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" isOneLineOnly = true;") + lines.append(" }") + else: + for lg_cls, lg_ref in leading_groups: + lines.append(f" if (my{lg_cls}->hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{lg_cls}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + lines.append(f" for (auto x : my{choice_class}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + elif leading_children or choice_is_optional: + for child in leading_children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + elif child.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel + 1);") + lines.append(" }") + else: + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel + 1);") + if choice_is_optional: + lines.append(f" if (myHas{choice_class})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{choice_class}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + else: + lines.append(f" if (my{choice_class}->hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{choice_class}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + elif child.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel + 1);") + lines.append(" }") + else: + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel + 1);") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + else: + lines.append(f" if (my{choice_class}->hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" if (my{choice_class})") + lines.append(" {") + lines.append(f" my{choice_class}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(" }") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" for (auto it = my{cc}Set.cbegin(); it != my{cc}Set.cend(); ++it)") + lines.append(" {") + lines.append(f" if (it == my{cc}Set.cbegin())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(" (*it)->toStream(os, indentLevel + 1);") + lines.append(" os << std::endl;") + lines.append(" }") + elif child.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(f" if (my{choice_class}->hasContents())") + lines.append(" {") + lines.append(" isOneLineOnly = false;") + lines.append(" }") + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f" if (my{cc}Set.size() > 0)") + lines.append(" {") + lines.append(" isOneLineOnly = false;") + lines.append(" }") + lines.append(" return os;") + lines.append("}\n") + + if has_attrs and attrs_name: + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}\n") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}\n") + + for lg_cls, lg_ref in leading_groups: + lines.append(f"{lg_cls}Ptr {class_name}::get{lg_cls}() const") + lines.append("{") + lines.append(f" return my{lg_cls};") + lines.append("}\n") + lines.append(f"void {class_name}::set{lg_cls}(const {lg_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{lg_cls} = value;") + lines.append(" }") + lines.append("}\n") + + for child in leading_children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + else: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + if child.min_occurs == 0: + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + + if multi_choice: + for ic in inline_choices: + icc = ic.choice_class + if ic.is_set: + lines.append(f"const {icc}Set &{class_name}::get{icc}Set() const") + lines.append("{") + lines.append(f" return my{icc}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::add{icc}(const {icc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{icc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::remove{icc}(const {icc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{icc}Set.cend())") + lines.append(" {") + lines.append(f" my{icc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{icc}Set()") + lines.append("{") + lines.append(f" my{icc}Set.clear();") + lines.append("}\n") + else: + lines.append(f"{icc}Ptr {class_name}::get{icc}() const") + lines.append("{") + lines.append(f" return my{icc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{icc}(const {icc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{icc} = value;") + lines.append(" }") + lines.append("}\n") + if ic.is_optional: + lines.append(f"bool {class_name}::getHas{icc}() const") + lines.append("{") + lines.append(f" return myHas{icc};") + lines.append("}\n") + lines.append(f"void {class_name}::setHas{icc}(const bool value)") + lines.append("{") + lines.append(f" myHas{icc} = value;") + lines.append("}\n") + else: + if choice_is_set: + lines.append(f"const {choice_class}Set &{class_name}::get{choice_class}Set() const") + lines.append("{") + lines.append(f" return my{choice_class}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::add{choice_class}(const {choice_class}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{choice_class}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::remove{choice_class}(const {choice_class}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{choice_class}Set.cend())") + lines.append(" {") + lines.append(f" my{choice_class}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{choice_class}Set()") + lines.append("{") + lines.append(f" my{choice_class}Set.clear();") + if _get_tree_config(elem_name).get("seed_choice_set", False): + lines.append(f" my{choice_class}Set.push_back(make{choice_class}());") + lines.append("}\n") + lines.append(f"{choice_class}Ptr {class_name}::get{choice_class}(const {choice_class}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{choice_class}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {choice_class}Ptr();") + lines.append("}\n") + else: + lines.append(f"{choice_class}Ptr {class_name}::get{choice_class}() const") + lines.append("{") + lines.append(f" return my{choice_class};") + lines.append("}\n") + lines.append(f"void {class_name}::set{choice_class}(const {choice_class}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{choice_class} = value;") + lines.append(" }") + lines.append("}\n") + if choice_is_optional: + lines.append(f"bool {class_name}::getHas{choice_class}() const") + lines.append("{") + lines.append(f" return myHas{choice_class};") + lines.append("}\n") + lines.append(f"void {class_name}::setHas{choice_class}(const bool value)") + lines.append("{") + lines.append(f" myHas{choice_class} = value;") + lines.append("}\n") + + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + if choice_is_set: + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + else: + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + elif child.min_occurs == 0: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + else: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + if has_attrs: + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + for child in leading_children: + cc = child_class_name(child) + if child.max_occurs == 1 and child.min_occurs == 1: + lines.append(f" bool is{cc}Found = false;") + lines.append("") + lines.append(" auto endIter = xelement.end();") + lines.append(" for (auto it = xelement.begin(); it != endIter; ++it)") + lines.append(" {") + + for child in leading_children: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f' if (importElementSet(message, it, endIter, isSuccess, "{child.element_name}", my{cc}Set))') + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + elif child.min_occurs == 0: + lines.append(f" if (importElement(message, *it, isSuccess, *my{cc}, myHas{cc}))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + else: + lines.append(f" if (importElement(message, *it, isSuccess, *my{cc}, is{cc}Found))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + + for lg_cls, lg_ref in leading_groups: + if isinstance(lg_ref, GroupRefNode): + group_children = model.groups.get(lg_ref.group_name, []) + elem_names = [gc.element_name for gc in group_children] + cond = " || ".join(f'it->getName() == "{n}"' for n in elem_names) + lines.append(f" if ({cond})") + lines.append(" {") + lines.append(f" importGroup(message, it, endIter, isSuccess, my{lg_cls});") + lines.append(" continue;") + lines.append(" }") + + if multi_choice: + for ic in inline_choices: + icc = ic.choice_class + for b in ic.branches: + if b.is_group: + group_children = model.groups.get(b.group_name, []) + elem_names = [gc.element_name for gc in group_children] + cond = " || ".join(f'it->getName() == "{n}"' for n in elem_names) + lines.append(f" if ({cond})") + lines.append(" {") + lines.append(f" my{icc}->setChoice({icc}::Choice::{b.enum_name});") + lines.append(f" auto groupPtr = my{icc}->get{b.class_name}();") + lines.append(f" importGroup(message, it, endIter, isSuccess, groupPtr);") + if ic.is_optional: + lines.append(f" myHas{icc} = true;") + lines.append(" continue;") + lines.append(" }") + elif b.xml_name: + lines.append(f' if (it->getName() == "{b.xml_name}")') + lines.append(" {") + lines.append(f" my{icc}->setChoice({icc}::Choice::{b.enum_name});") + lines.append(f" isSuccess &= my{icc}->get{b.class_name}()->fromXElement(message, *it);") + if ic.is_optional: + lines.append(f" myHas{icc} = true;") + lines.append(" continue;") + lines.append(" }") + else: + parent_imports_groups = _get_tree_config(elem_name).get( + "parent_imports_choice_groups", False) + for b in plan.choice_branches: + if b.is_group and parent_imports_groups: + # Route group parsing through a private member function on the + # parent class (see Issues E/F). The body is emitted after + # fromXElementImpl below. + lines.append(f" if (import{b.class_name}(message, it, endIter, isSuccess))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + elif b.is_group: + group_children = model.groups.get(b.group_name, []) + elem_names = [gc.element_name for gc in group_children] + cond = " || ".join(f'it->getName() == "{n}"' for n in elem_names) + lines.append(f" if ({cond})") + lines.append(" {") + if choice_is_set: + lines.append(f" auto choice = make{choice_class}();") + lines.append(f" choice->setChoice({choice_class}::Choice::{b.enum_name});") + lines.append(f" auto groupPtr = choice->get{b.class_name}();") + lines.append(f" importGroup(message, it, endIter, isSuccess, groupPtr);") + lines.append(f" my{choice_class}Set.push_back(choice);") + else: + lines.append(f" my{choice_class}->setChoice({choice_class}::Choice::{b.enum_name});") + if b.is_set: + lines.append(f" auto item = make{b.class_name}();") + lines.append(f" importGroup(message, it, endIter, isSuccess, item);") + lines.append(f" my{choice_class}->add{b.class_name}(item);") + else: + lines.append(f" auto groupPtr = my{choice_class}->get{b.class_name}();") + lines.append(f" importGroup(message, it, endIter, isSuccess, groupPtr);") + lines.append(" continue;") + lines.append(" }") + elif not b.is_group and b.xml_name: + xml_name_kebab = b.xml_name + if choice_is_set: + lines.append(f' if (it->getName() == "{xml_name_kebab}")') + lines.append(" {") + lines.append(f" auto choice = make{choice_class}();") + lines.append(f" choice->setChoice({choice_class}::Choice::{b.enum_name});") + lines.append(f" isSuccess &= choice->get{b.class_name}()->fromXElement(message, *it);") + lines.append(f" my{choice_class}Set.push_back(choice);") + lines.append(" continue;") + lines.append(" }") + else: + lines.append(f' if (it->getName() == "{xml_name_kebab}")') + lines.append(" {") + lines.append(f" my{choice_class}->setChoice({choice_class}::Choice::{b.enum_name});") + lines.append(f" isSuccess &= my{choice_class}->get{b.class_name}()->fromXElement(message, *it);") + if choice_is_optional: + lines.append(f" myHas{choice_class} = true;") + lines.append(" continue;") + lines.append(" }") + elif b.is_container and b.trigger_names: + # Dispatch to a private importContainer method when any of + # the container's trigger element names is seen. The method + # handles parsing all container members in order and leaves + # the iterator pointing at the last consumed element. + cond = " || ".join(f'it->getName() == "{n}"' for n in b.trigger_names) + lines.append(f" if ({cond})") + lines.append(" {") + lines.append(f" if (importContainer{b.class_name}(message, it, endIter, isSuccess))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(" }") + + for child in trailing: + cc = child_class_name(child) + if child.max_occurs != 1: + lines.append(f' if (importElementSet(message, it, endIter, isSuccess, "{child.element_name}", my{cc}Set))') + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + elif child.min_occurs == 0: + if leading_children: + lines.append(f" if (importElement(message, *it, isSuccess, *my{cc}, myHas{cc}))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + else: + lines.append(f' importElement(message, "{child.element_name}", isSuccess, *it, myHas{cc}, my{cc});') + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}\n") + + # Emit private member-function bodies for parent-imported choice groups + # (Issues E/F). Routed through here when + # TREE_ELEMENT_CONFIG[elem_name]["parent_imports_choice_groups"] is True. + if not multi_choice and _get_tree_config(elem_name).get( + "parent_imports_choice_groups", False): + for b in plan.choice_branches: + if not b.is_group: + continue + group_children = model.groups.get(b.group_name, []) + elem_names = [gc.element_name for gc in group_children] + mismatch_cond = " && ".join(f'iter->getName() != "{n}"' for n in elem_names) + match_cond = " || ".join(f'iter->getName() == "{n}"' for n in elem_names) + + lines.append(f"bool {class_name}::import{b.class_name}(std::ostream &message, ::ezxml::XElementIterator &iter,") + lines.append(f" {' ' * len(class_name)}::ezxml::XElementIterator &endIter, bool &isSuccess)") + lines.append("{") + lines.append(" if (iter == endIter)") + lines.append(" {") + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(f" if ({mismatch_cond})") + lines.append(" {") + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(" bool isIterIncremented = false;") + if b.is_set: + lines.append(" bool isFirstItemAdded = false;") + lines.append("") + lines.append(f" while (iter != endIter &&") + lines.append(f" ({match_cond}))") + lines.append(" {") + lines.append(f" auto item = make{b.class_name}();") + lines.append(f" const auto &items = my{choice_class}->get{b.class_name}Set();") + lines.append(f" my{choice_class}->setChoice({choice_class}::Choice::{b.enum_name});") + lines.append("") + for gc in group_children: + gcc = child_class_name(gc) + lines.append(f' if (iter != endIter && iter->getName() == "{gc.element_name}")') + lines.append(" {") + if gc.min_occurs == 0: + lines.append(f" item->setHas{gcc}(true);") + lines.append(f" isSuccess &= item->get{gcc}()->fromXElement(message, *iter);") + lines.append(" isIterIncremented = true;") + lines.append(" ++iter;") + lines.append(" }") + lines.append("") + lines.append(" if (!isFirstItemAdded && items.size() == 1)") + lines.append(" {") + lines.append(f" my{choice_class}->add{b.class_name}(item);") + lines.append(f" my{choice_class}->remove{b.class_name}(items.cbegin());") + lines.append(" isFirstItemAdded = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" my{choice_class}->add{b.class_name}(item);") + lines.append(" isFirstItemAdded = true;") + lines.append(" }") + lines.append(" }") + lines.append("") + else: + lines.append("") + for gc in group_children: + gcc = child_class_name(gc) + lines.append(f' if (iter != endIter && iter->getName() == "{gc.element_name}")') + lines.append(" {") + lines.append(f" my{choice_class}->setChoice({choice_class}::Choice::{b.enum_name});") + if gc.min_occurs == 0: + lines.append(f" my{choice_class}->get{b.class_name}()->setHas{gcc}(true);") + lines.append(f" isSuccess &= my{choice_class}->get{b.class_name}()->get{gcc}()->fromXElement(message, *iter);") + lines.append(" isIterIncremented = true;") + lines.append(" ++iter;") + lines.append(" }") + lines.append("") + lines.append(" if (isIterIncremented)") + lines.append(" {") + lines.append(" --iter;") + lines.append(" }") + lines.append("") + lines.append(" return true;") + lines.append("}\n") + + # Emit private member-function bodies for container branches (is_container=True). + # Each container branch gets an importContainer method that parses the + # container's members in order from the shared iterator. + if not multi_choice: + # Build a lookup: nested choice class_name -> list of TreeChoiceBranch + nested_choice_map = {} + for (nc_class, nc_branches, _nc_parent) in plan.nested_choices_to_generate: + nested_choice_map[nc_class] = nc_branches + + for b in plan.choice_branches: + if not b.is_container or not b.trigger_names: + continue + + # Find the container definition + container = next( + (c for c in plan.containers_to_generate if c.class_name == b.class_name), + None, + ) + if container is None: + continue + + mismatch_cond = " && ".join(f'it->getName() != "{n}"' for n in b.trigger_names) + fn_name = f"importContainer{b.class_name}" + indent = " " * (len(class_name) + len(fn_name) + 7) + lines.append(f"bool {class_name}::{fn_name}(std::ostream &message, ::ezxml::XElementIterator &it,") + lines.append(f" {indent}::ezxml::XElementIterator &endIter, bool &isSuccess)") + lines.append("{") + lines.append(" if (it == endIter)") + lines.append(" {") + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(f" if ({mismatch_cond})") + lines.append(" {") + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(f" my{choice_class}->setChoice({choice_class}::Choice::{b.enum_name});") + lines.append(f" auto containerPtr = my{choice_class}->get{b.class_name}();") + lines.append(" bool isIterIncremented = false;") + lines.append("") + + for m in container.members: + cc = m.class_name + if m.is_set and m.element_name: + # Repeating element - use a while loop + lines.append(f' while (it != endIter && it->getName() == "{m.element_name}")') + lines.append(" {") + lines.append(f" auto item = make{cc}();") + lines.append(f" isSuccess &= item->fromXElement(message, *it);") + lines.append(f" const auto &items = containerPtr->get{cc}Set();") + lines.append(" if (items.size() == 1 && !isIterIncremented)") + lines.append(" {") + lines.append(f" containerPtr->add{cc}(item);") + lines.append(f" containerPtr->remove{cc}(items.cbegin());") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" containerPtr->add{cc}(item);") + lines.append(" }") + lines.append(" isIterIncremented = true;") + lines.append(" ++it;") + lines.append(" }") + lines.append("") + elif m.is_optional and m.use_stream_contents and not m.element_name: + # Optional group (e.g. MetronomeRelationGroup): call importGroup + lines.append(f" bool has{cc} = false;") + lines.append(f" auto {camel(cc)} = containerPtr->get{cc}();") + lines.append(f" importGroup(message, it, endIter, isSuccess, {camel(cc)}, has{cc});") + lines.append(f" if (has{cc})") + lines.append(" {") + lines.append(f" containerPtr->setHas{cc}(true);") + lines.append(" isIterIncremented = true;") + lines.append(" ++it;") + lines.append(" }") + lines.append("") + elif m.use_stream_contents and not m.element_name and not m.is_optional: + if cc in nested_choice_map: + # Nested choice: dispatch each branch + nc_branches = nested_choice_map[cc] + lines.append(f" auto {camel(cc)} = containerPtr->get{cc}();") + first_branch = True + for nb in nc_branches: + kw = "if" if first_branch else "else if" + if nb.is_group: + group_children = model.groups.get(nb.group_name, []) + elem_names = [gc.element_name for gc in group_children + if hasattr(gc, "element_name") and gc.element_name] + if not elem_names: + continue + ncond = " || ".join(f'it->getName() == "{n}"' for n in elem_names) + lines.append(f" {kw} (it != endIter && ({ncond}))") + lines.append(" {") + lines.append(f" {camel(cc)}->setChoice({cc}::Choice::{nb.enum_name});") + lines.append(f" auto groupPtr = {camel(cc)}->get{nb.class_name}();") + lines.append(f" importGroup(message, it, endIter, isSuccess, groupPtr);") + lines.append(" isIterIncremented = true;") + lines.append(" ++it;") + lines.append(" }") + first_branch = False + elif nb.xml_name: + lines.append(f' {kw} (it != endIter && it->getName() == "{nb.xml_name}")') + lines.append(" {") + lines.append(f" {camel(cc)}->setChoice({cc}::Choice::{nb.enum_name});") + lines.append(f" isSuccess &= {camel(cc)}->get{nb.class_name}()->fromXElement(message, *it);") + lines.append(" isIterIncremented = true;") + lines.append(" ++it;") + lines.append(" }") + first_branch = False + lines.append("") + else: + # Plain group (use importGroup) + lines.append(f" if (it != endIter)") + lines.append(" {") + lines.append(f" auto groupPtr = containerPtr->get{cc}();") + lines.append(f" importGroup(message, it, endIter, isSuccess, groupPtr);") + lines.append(" isIterIncremented = true;") + lines.append(" ++it;") + lines.append(" }") + lines.append("") + elif not m.is_optional and not m.is_set and m.element_name: + # Required single element + lines.append(f' if (it != endIter && it->getName() == "{m.element_name}")') + lines.append(" {") + lines.append(f" isSuccess &= containerPtr->get{cc}()->fromXElement(message, *it);") + lines.append(" isIterIncremented = true;") + lines.append(" ++it;") + lines.append(" }") + lines.append("") + elif m.is_optional and m.element_name: + # Optional single element + lines.append(f' if (it != endIter && it->getName() == "{m.element_name}")') + lines.append(" {") + lines.append(f" containerPtr->setHas{cc}(true);") + lines.append(f" isSuccess &= containerPtr->get{cc}()->fromXElement(message, *it);") + lines.append(" isIterIncremented = true;") + lines.append(" ++it;") + lines.append(" }") + lines.append("") + + lines.append(" if (isIterIncremented)") + lines.append(" {") + lines.append(" --it;") + lines.append(" }") + lines.append("") + lines.append(" return true;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Bespoke generator: credit +# --------------------------------------------------------------------------- +# +# The credit element has a structure not produced by any other element: +# +# sequence +# credit-type* (leading optional-unbounded) +# link* (leading optional-unbounded) +# bookmark* (leading optional-unbounded) +# choice +# credit-image -- single element branch +# sequence +# credit-words -- always-present singleton +# sequence min=0 max=unbounded +# link* +# bookmark* +# credit-words -- always-present singleton inside the inner pair +# +# The original codegen promoted: +# - the outer choice to CreditChoice (enum branches = creditImage, +# creditWords; the creditWords branch encompasses both the singleton +# credit-words and a CreditWordsGroupSet for the inner repeating sequence). +# - the inner unbounded sequence to CreditWordsGroup (one instance per +# iteration; holds LinkSet, BookmarkSet, and a single CreditWordsPtr). +# +# Names below are derived from the parsed XSD content_tree so that spec +# changes (added/renamed attributes, additional leading children, additional +# group members) propagate automatically. + + +def _extract_credit_structure(ct): + """Walk credit's content_tree and return (leading, single_branch, + pair_first, group_children) where: + - leading: list[ElementRefNode] preceding the choice + - single_branch: ElementRefNode (the credit-image-style branch) + - pair_first: ElementRefNode (the credit-words singleton) + - group_children: list[ElementRefNode] inside the unbounded inner seq + """ + tree = ct.content_tree + assert isinstance(tree, SequenceNode) + leading = [] + choice_node = None + for c in tree.children: + if isinstance(c, ChoiceNode): + choice_node = c + break + if isinstance(c, ElementRefNode): + leading.append(c) + assert choice_node is not None, "credit: expected a choice in content_tree" + + single_branch = None + pair_first = None + group_children = [] + for b in choice_node.branches: + if isinstance(b, ElementRefNode): + single_branch = b + elif isinstance(b, SequenceNode): + # pair branch: first child is the singleton, second is the inner unbounded seq + pair_first = b.children[0] + inner = b.children[1] + assert isinstance(inner, SequenceNode) + group_children = [c for c in inner.children if isinstance(c, ElementRefNode)] + assert single_branch is not None and pair_first is not None + return leading, single_branch, pair_first, group_children + + +def generate_credit_choice_h(choice_class, single_branch, pair_first, group_children): + single_cls = pascal(single_branch.element_name) # CreditImage + pair_cls = pascal(pair_first.element_name) # CreditWords + group_cls = pascal(pair_first.element_name) + "Group" # CreditWordsGroup + + # Forward-declare order matches the original codegen: branch[0] class, + # the synthetic group class for the pair branch, then the pair branch's + # singleton class. + fwds = [single_cls, group_cls, pair_cls] + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for fwd in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({fwd})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({choice_class})\n") + lines.append(f"inline {choice_class}Ptr make{choice_class}()") + lines.append("{") + lines.append(f" return std::make_shared<{choice_class}>();") + lines.append("}") + lines.append(f"\nclass {choice_class} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + lines.append(f" {camel(single_branch.element_name)} = 1,") + lines.append(f" {camel(pair_first.element_name)} = 2") + lines.append(" };") + lines.append(f" {choice_class}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append("") + lines.append(" /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {choice_class}::Choice getChoice() const;") + lines.append(f" void setChoice(const {choice_class}::Choice value);") + lines.append("") + lines.append(f" /* _________ {single_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {single_cls}Ptr get{single_cls}() const;") + lines.append(f" void set{single_cls}(const {single_cls}Ptr &value);") + lines.append("") + lines.append(f" /* _________ {pair_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {pair_cls}Ptr get{pair_cls}() const;") + lines.append(f" void set{pair_cls}(const {pair_cls}Ptr &value);") + lines.append("") + lines.append(f" /* _________ {group_cls} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {group_cls}Set &get{group_cls}Set() const;") + lines.append(f" void add{group_cls}(const {group_cls}Ptr &value);") + lines.append(f" void remove{group_cls}(const {group_cls}SetIterConst &value);") + lines.append(f" void clear{group_cls}Set();") + lines.append(f" {group_cls}Ptr get{group_cls}(const {group_cls}SetIterConst &setIterator) const;") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + lines.append(f" {single_cls}Ptr my{single_cls};") + lines.append(f" {pair_cls}Ptr my{pair_cls};") + lines.append(f" {group_cls}Set my{group_cls}Set;") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_credit_choice_cpp(choice_class, single_branch, pair_first, group_children): + single_cls = pascal(single_branch.element_name) # CreditImage + pair_cls = pascal(pair_first.element_name) # CreditWords + group_cls = pascal(pair_first.element_name) + "Group" # CreditWordsGroup + + # Includes match the original: union of all element classes referenced by + # the choice + its group (incl. transitive Link/Bookmark from the group). + inc_classes = {single_cls, pair_cls, group_cls} + for gc in group_children: + inc_classes.add(pascal(gc.element_name)) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{choice_class}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in sorted(inc_classes): + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + # Default branch = the pair branch (creditWords), matching original codegen. + init_parts = [ + f"myChoice(Choice::{camel(pair_first.element_name)})", + f"my{single_cls}(make{single_cls}())", + f"my{pair_cls}(make{pair_cls}())", + f"my{group_cls}Set()", + ] + _emit_ctor_init(lines, f"{choice_class}::{choice_class}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {choice_class}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + + lines.append(f"std::ostream &{choice_class}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"std::ostream &{choice_class}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"bool {choice_class}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + lines.append(f"std::ostream &{choice_class}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" switch (myChoice)") + lines.append(" {") + lines.append(f" case Choice::{camel(single_branch.element_name)}:") + lines.append(f" my{single_cls}->toStream(os, indentLevel);") + lines.append(" break;") + lines.append(f" case Choice::{camel(pair_first.element_name)}: {{") + lines.append(f" my{pair_cls}->toStream(os, indentLevel);") + lines.append("") + lines.append(f" if (my{group_cls}Set.size() > 0)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append("") + lines.append(f" for (auto x : my{group_cls}Set)") + lines.append(" {") + lines.append(" x->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" }") + lines.append("") + lines.append(" break;") + lines.append(" }") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"{choice_class}::Choice {choice_class}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}\n") + + lines.append(f"void {choice_class}::setChoice(const {choice_class}::Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}\n") + + for cls in (single_cls, pair_cls): + lines.append(f"{cls}Ptr {choice_class}::get{cls}() const") + lines.append("{") + lines.append(f" return my{cls};") + lines.append("}\n") + lines.append(f"void {choice_class}::set{cls}(const {cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cls} = value;") + lines.append(" }") + lines.append("}\n") + + # group set accessors: order is getSet, remove, add, clear, get(setIterator) + lines.append(f"const {group_cls}Set &{choice_class}::get{group_cls}Set() const") + lines.append("{") + lines.append(f" return my{group_cls}Set;") + lines.append("}\n") + lines.append(f"void {choice_class}::remove{group_cls}(const {group_cls}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{group_cls}Set.cend())") + lines.append(" {") + lines.append(f" my{group_cls}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {choice_class}::add{group_cls}(const {group_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{group_cls}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {choice_class}::clear{group_cls}Set()") + lines.append("{") + lines.append(f" my{group_cls}Set.clear();") + lines.append("}\n") + lines.append(f"{group_cls}Ptr {choice_class}::get{group_cls}(const {group_cls}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{group_cls}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {group_cls}Ptr();") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({choice_class});\n") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_credit_words_group_h(group_cls, group_children, singleton_name): + # group_children includes link*, bookmark*, credit-words(single) + # singleton_name is 'credit-words' (the always-present last member) + set_members = [c for c in group_children if c.element_name != singleton_name] + singleton_cls = pascal(singleton_name) + + fwds = sorted({pascal(c.element_name) for c in group_children}) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for fwd in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({fwd})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({group_cls})\n") + lines.append(f"inline {group_cls}Ptr make{group_cls}()") + lines.append("{") + lines.append(f" return std::make_shared<{group_cls}>();") + lines.append("}") + lines.append(f"\nclass {group_cls} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {group_cls}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + # The committed CreditWordsGroup.h has a small inconsistency: its first + # set's comment header is "LinkSet" but the second is "Bookmark". The + # general project convention (matching other group headers like + # Appearance, Barline) uses {cc} without the Set suffix; we use that + # convention. The single residual line is an EXC documented in state.md. + for c in set_members: + cc = pascal(c.element_name) + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + lines.append(f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;") + lines.append("") + lines.append(f" /* _________ {singleton_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {singleton_cls}Ptr get{singleton_cls}() const;") + lines.append(f" void set{singleton_cls}(const {singleton_cls}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + for c in set_members: + cc = pascal(c.element_name) + lines.append(f" {cc}Set my{cc}Set;") + lines.append(f" {singleton_cls}Ptr my{singleton_cls};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_credit_words_group_cpp(group_cls, group_children, singleton_name): + set_members = [c for c in group_children if c.element_name != singleton_name] + singleton_cls = pascal(singleton_name) + inc_classes = sorted({pascal(c.element_name) for c in group_children}) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{group_cls}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [f"my{pascal(c.element_name)}Set()" for c in set_members] + init_parts.append(f"my{singleton_cls}(make{singleton_cls}())") + _emit_ctor_init(lines, f"{group_cls}::{group_cls}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {group_cls}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + lines.append(f"std::ostream &{group_cls}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"std::ostream &{group_cls}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"bool {group_cls}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + # streamContents: bespoke isFirst pattern across sets, then singleton at the end + lines.append(f"std::ostream &{group_cls}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" bool isFirst = true;") + for c in set_members: + cc = pascal(c.element_name) + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" if (!isFirst)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(" x->toStream(os, indentLevel);") + lines.append(" isFirst = false;") + lines.append(" }") + lines.append(" if (!isFirst)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(f" my{singleton_cls}->toStream(os, indentLevel);") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}\n") + + # Per-set accessors: order is getSet, add, remove, clear, get(setIterator). + # This matches the committed CreditWordsGroup.cpp (add-before-remove), an + # outlier vs the rest of the codebase. See iter-33 gotcha for the analogous + # MidiDeviceInstrumentGroupSet outlier. + for c in set_members: + cc = pascal(c.element_name) + lines.append(f"const {cc}Set &{group_cls}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {group_cls}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {group_cls}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {group_cls}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + lines.append(f"{cc}Ptr {group_cls}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + + lines.append(f"{singleton_cls}Ptr {group_cls}::get{singleton_cls}() const") + lines.append("{") + lines.append(f" return my{singleton_cls};") + lines.append("}\n") + lines.append(f"void {group_cls}::set{singleton_cls}(const {singleton_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{singleton_cls} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({group_cls});\n") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_credit_h(class_name, leading, choice_class, attrs_name): + fwds = sorted({pascal(c.element_name) for c in leading} | {choice_class}) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append(f'#include "mx/core/elements/{attrs_name}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace ezxml\n{\nclass XElementIterator;\n}") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + for fwd in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({fwd})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + for c in leading: + cc = pascal(c.element_name) + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = {c.min_occurs}, maxOccurs = unbounded _________ */") + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + lines.append(f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;") + lines.append("") + lines.append(f" /* _________ {choice_class} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {choice_class}Ptr get{choice_class}() const;") + lines.append(f" void set{choice_class}(const {choice_class}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {attrs_name}Ptr myAttributes;") + for c in leading: + cc = pascal(c.element_name) + lines.append(f" {cc}Set my{cc}Set;") + lines.append(f" {choice_class}Ptr my{choice_class};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_credit_cpp(elem_name, class_name, leading, single_branch, pair_first, + group_children, choice_class, group_cls, attrs_name): + single_cls = pascal(single_branch.element_name) + pair_cls = pascal(pair_first.element_name) + singleton_name = pair_first.element_name # credit-words + + # Includes: leading children + single_branch + pair_first + group_cls + choice_class + inc_classes = sorted({pascal(c.element_name) for c in leading} + | {single_cls, pair_cls, group_cls, choice_class}) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [f"myAttributes(std::make_shared<{attrs_name}>())"] + for c in leading: + cc = pascal(c.element_name) + init_parts.append(f"my{cc}Set()") + init_parts.append(f"my{choice_class}(make{choice_class}())") + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return myAttributes->hasValues();") + lines.append("}\n") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return myAttributes->toStream(os);") + lines.append("}\n") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{elem_name}";') + lines.append(" return os;") + lines.append("}\n") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + for c in leading: + cc = pascal(c.element_name) + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(f" my{choice_class}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}\n") + + # Attributes accessors + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}\n") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}\n") + + # Set accessors for leading children: getSet, remove, add, clear, get + for c in leading: + cc = pascal(c.element_name) + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + + # Choice accessors + lines.append(f"{choice_class}Ptr {class_name}::get{choice_class}() const") + lines.append("{") + lines.append(f" return my{choice_class};") + lines.append("}\n") + lines.append(f"void {class_name}::set{choice_class}(const {choice_class}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{choice_class} = value;") + lines.append(" }") + lines.append("}\n") + + # MX_CREDIT_RETURN_IF_END macro and fromXElementImpl + # All names here are derived from the parsed children so spec changes + # (e.g. a renamed choice element) will propagate. + single_name = single_branch.element_name # e.g. credit-image + macro_name = "MX_CREDIT_RETURN_IF_END" + found_flag = f"is{single_cls}Or{pair_cls}Found" + err_msg = (f"\"{class_name}: neither '{singleton_name}' nor '{single_name}' was present" + f" - one of these is required\"") + lines.append(f"#ifndef {macro_name}") + lines.append(f"#define {macro_name} \\") + lines.append(" if (i == endIter) \\") + lines.append(" { \\") + lines.append(f" if (!{found_flag}) \\") + lines.append(" { \\") + lines.append(f" message << {err_msg} \\") + lines.append(" << std::endl; \\") + lines.append(" isSuccess = false; \\") + lines.append(" } \\") + lines.append(" return isSuccess; \\") + lines.append(" }") + lines.append("#endif") + lines.append("") + + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + lines.append(f" bool {found_flag} = false;") + lines.append("") + lines.append(" ::ezxml::XElementIterator i = xelement.begin();") + lines.append(" ::ezxml::XElementIterator endIter = xelement.end();") + lines.append(f" {macro_name};") + lines.append("") + for c in leading: + cc = pascal(c.element_name) + cname = c.element_name + lines.append(f' if (i->getName() == "{cname}")') + lines.append(" {") + lines.append(f' while (i != endIter && i->getName() == "{cname}")') + lines.append(" {") + lines.append(f" auto item = make{cc}();") + lines.append(" isSuccess &= item->fromXElement(message, *i);") + lines.append(f" add{cc}(item);") + lines.append(" ++i;") + lines.append(" }") + lines.append(" }") + lines.append(f" {macro_name};") + lines.append("") + lines.append(f' if (i->getName() == "{single_name}" || i->getName() == "{singleton_name}")') + lines.append(" {") + lines.append(f" {found_flag} = true;") + lines.append(f' if (i->getName() == "{single_name}")') + lines.append(" {") + lines.append(f" my{choice_class}->setChoice({choice_class}::Choice::{camel(single_name)});") + lines.append(f" isSuccess &= my{choice_class}->get{single_cls}()->fromXElement(message, *i);") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append(" }") + lines.append("") + lines.append(f' if (i->getName() == "{singleton_name}")') + lines.append(" {") + lines.append(f" my{choice_class}->setChoice({choice_class}::Choice::{camel(singleton_name)});") + lines.append(f" isSuccess &= my{choice_class}->get{pair_cls}()->fromXElement(message, *i);") + lines.append(" ++i;") + lines.append(f" {macro_name};") + lines.append("") + set_members = [c for c in group_children if c.element_name != singleton_name] + set_names_or = " || ".join(f'(i->getName() == "{c.element_name}")' for c in set_members) + set_names_or += f' || (i->getName() == "{singleton_name}")' + lines.append(f" auto creditWordsGroup = make{group_cls}();") + lines.append(f" while (i != endIter &&") + lines.append(f" ({set_names_or}))") + lines.append(" {") + for c in set_members: + cc = pascal(c.element_name) + cname = c.element_name + local = camel(c.element_name) + lines.append(f' while (i != endIter && i->getName() == "{cname}")') + lines.append(" {") + lines.append(f" auto {local} = make{cc}();") + lines.append(f" isSuccess &= {local}->fromXElement(message, xelement);") + lines.append(f" creditWordsGroup->add{cc}({local});") + lines.append(" ++i;") + lines.append(" }") + lines.append(f" {macro_name};") + lines.append("") + lines.append(f' if (i->getName() == "{singleton_name}")') + lines.append(" {") + lines.append(f" isSuccess &= creditWordsGroup->get{pair_cls}()->fromXElement(message, *i);") + lines.append(f" my{choice_class}->add{group_cls}(creditWordsGroup);") + lines.append(f" creditWordsGroup = make{group_cls}();") + lines.append(" ++i;") + lines.append(" }") + lines.append(f" {macro_name};") + lines.append(" }") + lines.append(" }") + lines.append(" }") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _emit_credit_family(elem_name, elem, ct, model, generated_attrs, stats): + leading, single_branch, pair_first, group_children = _extract_credit_structure(ct) + + class_name = element_class_name(elem_name) + choice_class = pascal(elem_name) + "Choice" # CreditChoice + group_cls = pascal(pair_first.element_name) + "Group" # CreditWordsGroup + type_name = elem.type_name or "" + + # 1. Attrs struct via the standard generator + if ct.attributes: + attrs_name = resolve_attrs_name(elem_name, type_name, model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(attrs_name, ct.attributes, model) + c = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), c) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + else: + attrs_name = None + + # 2. CreditChoice + h = generate_credit_choice_h(choice_class, single_branch, pair_first, group_children) + c = generate_credit_choice_cpp(choice_class, single_branch, pair_first, group_children) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.cpp"), c) + + # 3. CreditWordsGroup + h = generate_credit_words_group_h(group_cls, group_children, pair_first.element_name) + c = generate_credit_words_group_cpp(group_cls, group_children, pair_first.element_name) + write_file(os.path.join(ELEM_DIR, f"{group_cls}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{group_cls}.cpp"), c) + + # 4. Credit + h = generate_credit_h(class_name, leading, choice_class, attrs_name) + c = generate_credit_cpp(elem_name, class_name, leading, single_branch, pair_first, + group_children, choice_class, group_cls, attrs_name) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), c) + + stats["elem_written"] += 1 + stats["bespoke_written"] = stats.get("bespoke_written", 0) + 1 + + +# --------------------------------------------------------------------------- +# Lyric (bespoke, schema-driven) +# +# Lyric's complex type is structured as: +# sequence +# choice: +# sequence: syllabic? text (sequence)*[unbounded] extend? +# inner sequence: (sequence?: elision syllabic?) text +# extend +# laughing +# humming +# end-line? +# end-paragraph? +# group ref editorial +# +# This produces four hand-written-shaped classes in the original codegen: +# - LyricTextChoice (the outer required choice) +# - SyllabicTextGroup (the sequence branch promoted to a group) +# - ElisionSyllabicTextGroup(the unbounded inner sequence promoted to a group) +# - ElisionSyllabicGroup (the optional inner sub-sequence promoted to a group) +# +# All names that come from the XSD (element refs, attribute refs, etc.) are +# read from the parsed model so that spec changes propagate automatically. +# The class names of the synthetic groups are derived from the names of the +# first two children of each synthetic group plus a "Group" suffix. + + +def _synth_group_name_from_children(children): + """Compose a synthetic group name from the first two children, then + append 'Group'. For an ElementRefNode child the stem is + pascal(element_name); for an already-named synthetic group child whose + name ends with 'Group' the stem is the name with 'Group' stripped. + """ + stems = [] + for c in children[:2]: + if isinstance(c, ElementRefNode): + stems.append(pascal(c.element_name)) + elif isinstance(c, str): + stems.append(c[:-5] if c.endswith("Group") else c) + else: + raise AssertionError(f"unexpected child for group naming: {c!r}") + return "".join(stems) + "Group" + + +def _extract_lyric_structure(ct): + """Walk lyric's content_tree and return a dict describing the structure. + + Returns a dict with these keys (all element references are + ElementRefNode pulled directly from the parsed XSD): + - choice: ChoiceNode (the required outer choice) + - seq_branch_pre: elements in the sequence branch before the + inner unbounded sequence (e.g. syllabic?, text) + - seq_branch_post: elements after the inner unbounded sequence + (e.g. extend?) + - inner_seq: the unbounded SequenceNode inside the seq branch + - inner_text: ElementRefNode for the text inside inner_seq + - inner_optional_seq: the optional SequenceNode inside inner_seq + - inner_optional_elems: elements inside the optional sub-sequence + (e.g. elision, syllabic?) + - singleton_branches: other branches of the choice (extend, laughing, + humming) as a list of ElementRefNode + - trailing_elems: ElementRefNodes after the choice (end-line?, + end-paragraph?) + - editorial_group: GroupRefNode for the trailing group ref + """ + tree = ct.content_tree + assert isinstance(tree, SequenceNode) + + choice = None + trailing_elems = [] + editorial_group = None + for c in tree.children: + if isinstance(c, ChoiceNode): + assert choice is None + choice = c + elif isinstance(c, ElementRefNode): + trailing_elems.append(c) + elif isinstance(c, GroupRefNode): + editorial_group = c + else: + raise AssertionError(f"unexpected top-level child: {type(c).__name__}") + assert choice is not None + + seq_branch = None + singleton_branches = [] + for b in choice.branches: + if isinstance(b, SequenceNode): + assert seq_branch is None + seq_branch = b + elif isinstance(b, ElementRefNode): + singleton_branches.append(b) + else: + raise AssertionError(f"unexpected choice branch: {type(b).__name__}") + assert seq_branch is not None + + seq_branch_pre = [] + inner_seq = None + seq_branch_post = [] + for c in seq_branch.children: + if isinstance(c, SequenceNode): + assert inner_seq is None + inner_seq = c + elif isinstance(c, ElementRefNode): + if inner_seq is None: + seq_branch_pre.append(c) + else: + seq_branch_post.append(c) + else: + raise AssertionError(f"unexpected seq branch child: {type(c).__name__}") + assert inner_seq is not None + + inner_optional_seq = None + inner_text = None + for c in inner_seq.children: + if isinstance(c, SequenceNode): + assert inner_optional_seq is None + inner_optional_seq = c + elif isinstance(c, ElementRefNode): + assert inner_text is None + inner_text = c + else: + raise AssertionError(f"unexpected inner seq child: {type(c).__name__}") + assert inner_optional_seq is not None and inner_text is not None + + inner_optional_elems = [] + for c in inner_optional_seq.children: + assert isinstance(c, ElementRefNode) + inner_optional_elems.append(c) + + return { + "choice": choice, + "seq_branch_pre": seq_branch_pre, + "seq_branch_post": seq_branch_post, + "inner_seq": inner_seq, + "inner_text": inner_text, + "inner_optional_seq": inner_optional_seq, + "inner_optional_elems": inner_optional_elems, + "singleton_branches": singleton_branches, + "trailing_elems": trailing_elems, + "editorial_group": editorial_group, + } + + +def generate_elision_syllabic_group_h(group_cls, elems): + """elems = [required_elision, optional_syllabic]""" + fwds = sorted({pascal(e.element_name) for e in elems}) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for f in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({group_cls})\n") + lines.append(f"inline {group_cls}Ptr make{group_cls}()") + lines.append("{") + lines.append(f" return std::make_shared<{group_cls}>();") + lines.append("}") + lines.append(f"\nclass {group_cls} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {group_cls}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + for e in elems: + cc = pascal(e.element_name) + optional = e.min_occurs == 0 + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = {e.min_occurs}, maxOccurs = {e.max_occurs} _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + if optional: + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + for e in elems: + cc = pascal(e.element_name) + optional = e.min_occurs == 0 + lines.append(f" {cc}Ptr my{cc};") + if optional: + lines.append(f" bool myHas{cc};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_elision_syllabic_group_cpp(group_cls, elems): + inc_classes = sorted({pascal(e.element_name) for e in elems}) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{group_cls}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [] + for e in elems: + cc = pascal(e.element_name) + init_parts.append(f"my{cc}(make{cc}())") + if e.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + _emit_ctor_init(lines, f"{group_cls}::{group_cls}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {group_cls}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + lines.append(f"std::ostream &{group_cls}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"std::ostream &{group_cls}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"bool {group_cls}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + # streamContents: required elements emitted plainly; optional elements + # emitted only when their HasX flag is true, and each optional emission + # flips isOneLineOnly to false and emits a leading newline. + lines.append(f"std::ostream &{group_cls}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" isOneLineOnly = true;") + for e in elems: + cc = pascal(e.element_name) + if e.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" }") + else: + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" return os;") + lines.append("}\n") + + for e in elems: + cc = pascal(e.element_name) + lines.append(f"{cc}Ptr {group_cls}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {group_cls}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + if e.min_occurs == 0: + lines.append(f"bool {group_cls}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {group_cls}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({group_cls});\n") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_elision_syllabic_text_group_h(group_cls, inner_group_cls, text_elem): + """ElisionSyllabicTextGroup has optional inner group + required text.""" + text_cls = pascal(text_elem.element_name) + fwds = sorted({inner_group_cls, text_cls}) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for f in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({group_cls})\n") + lines.append(f"inline {group_cls}Ptr make{group_cls}()") + lines.append("{") + lines.append(f" return std::make_shared<{group_cls}>();") + lines.append("}") + lines.append(f"\nclass {group_cls} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {group_cls}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append("") + lines.append(f" /* _________ {inner_group_cls} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {inner_group_cls}Ptr get{inner_group_cls}() const;") + lines.append(f" void set{inner_group_cls}(const {inner_group_cls}Ptr &value);") + lines.append(f" bool getHas{inner_group_cls}() const;") + lines.append(f" void setHas{inner_group_cls}(const bool value);") + lines.append("") + lines.append(f" /* _________ {text_cls} minOccurs = {text_elem.min_occurs}, maxOccurs = {text_elem.max_occurs} _________ */") + lines.append(f" {text_cls}Ptr get{text_cls}() const;") + lines.append(f" void set{text_cls}(const {text_cls}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {inner_group_cls}Ptr my{inner_group_cls};") + lines.append(f" bool myHas{inner_group_cls};") + lines.append(f" {text_cls}Ptr my{text_cls};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_elision_syllabic_text_group_cpp(group_cls, inner_group_cls, text_elem): + text_cls = pascal(text_elem.element_name) + inc_classes = sorted({inner_group_cls, text_cls}) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{group_cls}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [ + f"my{inner_group_cls}(make{inner_group_cls}())", + f"myHas{inner_group_cls}(false)", + f"my{text_cls}(make{text_cls}())", + ] + _emit_ctor_init(lines, f"{group_cls}::{group_cls}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {group_cls}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + lines.append(f"std::ostream &{group_cls}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"std::ostream &{group_cls}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"bool {group_cls}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + # streamContents: optional inner group, then required text. + # The committed file streams the inner group via streamContents (not + # toStream) because the inner group represents a flattened sub-sequence, + # not a self-contained XML element. + lines.append(f"std::ostream &{group_cls}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" isOneLineOnly = true;") + lines.append(f" if (myHas{inner_group_cls})") + lines.append(" {") + lines.append(f" my{inner_group_cls}->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(f" my{text_cls}->toStream(os, indentLevel);") + lines.append(" return os;") + lines.append("}\n") + + # Accessors: inner group (optional), then text (required) + lines.append(f"{inner_group_cls}Ptr {group_cls}::get{inner_group_cls}() const") + lines.append("{") + lines.append(f" return my{inner_group_cls};") + lines.append("}\n") + lines.append(f"void {group_cls}::set{inner_group_cls}(const {inner_group_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{inner_group_cls} = value;") + lines.append(" }") + lines.append("}\n") + lines.append(f"bool {group_cls}::getHas{inner_group_cls}() const") + lines.append("{") + lines.append(f" return myHas{inner_group_cls};") + lines.append("}\n") + lines.append(f"void {group_cls}::setHas{inner_group_cls}(const bool value)") + lines.append("{") + lines.append(f" myHas{inner_group_cls} = value;") + lines.append("}\n") + lines.append(f"{text_cls}Ptr {group_cls}::get{text_cls}() const") + lines.append("{") + lines.append(f" return my{text_cls};") + lines.append("}\n") + lines.append(f"void {group_cls}::set{text_cls}(const {text_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{text_cls} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({group_cls});\n") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_syllabic_text_group_h(group_cls, pre_elems, inner_group_cls, + inner_set_name, post_elems): + """SyllabicTextGroup. pre_elems = [syllabic?, text]; inner_group_cls = + 'ElisionSyllabicTextGroup' (used as a set); post_elems = [extend?].""" + elem_classes = {pascal(e.element_name) for e in pre_elems + post_elems} + fwds = sorted(elem_classes | {inner_group_cls}) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for f in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({group_cls})\n") + lines.append(f"inline {group_cls}Ptr make{group_cls}()") + lines.append("{") + lines.append(f" return std::make_shared<{group_cls}>();") + lines.append("}") + lines.append(f"\nclass {group_cls} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {group_cls}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + + for e in pre_elems: + cc = pascal(e.element_name) + optional = e.min_occurs == 0 + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = {e.min_occurs}, maxOccurs = {e.max_occurs} _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + if optional: + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + + # ElisionSyllabicTextGroup* (unbounded set) + lines.append("") + lines.append(f" /* _________ {inner_group_cls} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {inner_group_cls}Set &get{inner_group_cls}Set() const;") + lines.append(f" void add{inner_group_cls}(const {inner_group_cls}Ptr &value);") + lines.append(f" void remove{inner_group_cls}(const {inner_group_cls}SetIterConst &value);") + lines.append(f" void clear{inner_group_cls}Set();") + lines.append(f" {inner_group_cls}Ptr get{inner_group_cls}(") + lines.append(f" const {inner_group_cls}SetIterConst &setIterator) const;") + + for e in post_elems: + cc = pascal(e.element_name) + optional = e.min_occurs == 0 + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = {e.min_occurs}, maxOccurs = {e.max_occurs} _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + if optional: + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + for e in pre_elems: + cc = pascal(e.element_name) + lines.append(f" {cc}Ptr my{cc};") + if e.min_occurs == 0: + lines.append(f" bool myHas{cc};") + lines.append(f" {inner_group_cls}Set my{inner_group_cls}Set;") + for e in post_elems: + cc = pascal(e.element_name) + lines.append(f" {cc}Ptr my{cc};") + if e.min_occurs == 0: + lines.append(f" bool myHas{cc};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_syllabic_text_group_cpp(group_cls, pre_elems, inner_group_cls, + post_elems): + elem_classes = sorted({pascal(e.element_name) for e in pre_elems + post_elems} + | {inner_group_cls}) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{group_cls}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in elem_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [] + for e in pre_elems: + cc = pascal(e.element_name) + init_parts.append(f"my{cc}(make{cc}())") + if e.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + init_parts.append(f"my{inner_group_cls}Set()") + for e in post_elems: + cc = pascal(e.element_name) + init_parts.append(f"my{cc}(make{cc}())") + if e.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + _emit_ctor_init(lines, f"{group_cls}::{group_cls}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {group_cls}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + lines.append(f"std::ostream &{group_cls}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"std::ostream &{group_cls}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"bool {group_cls}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + # streamContents: pre_elems (in order; optional ones emit endl AFTER if + # required text follows; the committed pattern emits the optional + # syllabic first then endl, then text). For maximal fidelity we model: + # if optional: emit elem, endl + # if required: emit elem (no surrounding endl) + # The set iterates with leading endl per element, calling streamContents. + # Trailing optional elements emit leading endl then elem. + lines.append(f"std::ostream &{group_cls}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" isOneLineOnly = true;") + for e in pre_elems: + cc = pascal(e.element_name) + if e.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" os << std::endl;") + lines.append(" }") + else: + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(f" for (auto x : my{inner_group_cls}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" }") + for e in post_elems: + cc = pascal(e.element_name) + if e.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" }") + else: + lines.append(f" my{cc}->toStream(os, indentLevel);") + + # final isOneLineOnly: true only if NO optional pre/post and NO set entries + cond_parts = [] + for e in pre_elems: + if e.min_occurs == 0: + cond_parts.append(f"myHas{pascal(e.element_name)}") + cond_parts.append(f"my{inner_group_cls}Set.size() > 0") + for e in post_elems: + if e.min_occurs == 0: + cond_parts.append(f"myHas{pascal(e.element_name)}") + lines.append(f" isOneLineOnly = !({' || '.join(cond_parts)});") + lines.append(" return os;") + lines.append("}\n") + + # Accessor blocks, in declaration order. For each element: get, set, + # then if optional also getHas, setHas. For the set: getSet, add, remove, + # clear, get-by-iter (add-before-remove pattern, matching + # CreditWordsGroup / MidiDeviceInstrumentGroup). + def emit_single_accessors(e): + cc = pascal(e.element_name) + optional = e.min_occurs == 0 + lines.append(f"{cc}Ptr {group_cls}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {group_cls}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + if optional: + lines.append(f"bool {group_cls}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {group_cls}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + + for e in pre_elems: + emit_single_accessors(e) + + cc = inner_group_cls + lines.append(f"const {cc}Set &{group_cls}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {group_cls}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {group_cls}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {group_cls}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + lines.append(f"{cc}Ptr {group_cls}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + + for e in post_elems: + emit_single_accessors(e) + + lines.append(f"MX_FROM_XELEMENT_UNUSED({group_cls});\n") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_lyric_text_choice_h(choice_class, seq_group_cls, singleton_branches): + """choice_class = 'LyricTextChoice'; seq_group_cls = 'SyllabicTextGroup'; + singleton_branches = list of ElementRefNode (extend, laughing, humming).""" + classes = [seq_group_cls] + [pascal(b.element_name) for b in singleton_branches] + fwds = sorted(classes) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for f in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({choice_class})\n") + lines.append(f"inline {choice_class}Ptr make{choice_class}()") + lines.append("{") + lines.append(f" return std::make_shared<{choice_class}>();") + lines.append("}") + lines.append(f"\nclass {choice_class} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + # First enum: the sequence-branch synthetic group. Then singletons in branch order. + seq_group_camel = camel(seq_group_cls[:1].lower() + seq_group_cls[1:]) + # the seq group's identifier is just camel of the class name's first segment + # but committed uses 'syllabicTextGroup' = camelCase of the class name. + seq_group_choice_enum = seq_group_cls[:1].lower() + seq_group_cls[1:] + enum_lines = [f" {seq_group_choice_enum} = 1"] + for i, b in enumerate(singleton_branches, start=2): + enum_lines.append(f" {camel(b.element_name)} = {i}") + lines.append(",\n".join(enum_lines)) + lines.append(" };") + lines.append(f" {choice_class}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {choice_class}::Choice getChoice() const;") + lines.append(f" void setChoice(const {choice_class}::Choice value);") + lines.append("") + lines.append(f" /* _________ {seq_group_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {seq_group_cls}Ptr get{seq_group_cls}() const;") + lines.append(f" void set{seq_group_cls}(const {seq_group_cls}Ptr &value);") + for b in singleton_branches: + cc = pascal(b.element_name) + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + lines.append(f" {seq_group_cls}Ptr my{seq_group_cls};") + for b in singleton_branches: + cc = pascal(b.element_name) + lines.append(f" {cc}Ptr my{cc};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_lyric_text_choice_cpp(choice_class, seq_group_cls, singleton_branches): + inc_classes = sorted({seq_group_cls} | {pascal(b.element_name) for b in singleton_branches}) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{choice_class}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + seq_group_choice_enum = seq_group_cls[:1].lower() + seq_group_cls[1:] + init_parts = [ + f"myChoice(Choice::{seq_group_choice_enum})", + f"my{seq_group_cls}(make{seq_group_cls}())", + ] + for b in singleton_branches: + cc = pascal(b.element_name) + init_parts.append(f"my{cc}(make{cc}())") + _emit_ctor_init(lines, f"{choice_class}::{choice_class}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {choice_class}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + lines.append(f"std::ostream &{choice_class}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"std::ostream &{choice_class}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + # hasContents: returns true ONLY when choice is the sequence branch. + # This is a committed quirk: the singletons emit via toStream which + # write their own XML wrapping, so the parent treats the choice as + # "contentless" in those cases. See state.md gotcha. + lines.append(f"bool {choice_class}::hasContents() const") + lines.append("{") + lines.append(f" return myChoice == Choice::{seq_group_choice_enum};") + lines.append("}\n") + + # streamContents: switch on choice; sequence branch uses streamContents, + # singletons use toStream. + lines.append(f"std::ostream &{choice_class}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" switch (myChoice)") + lines.append(" {") + lines.append(f" case Choice::{seq_group_choice_enum}: {{") + lines.append(f" my{seq_group_cls}->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" }") + lines.append(" break;") + for b in singleton_branches: + cc = pascal(b.element_name) + lines.append(f" case Choice::{camel(b.element_name)}: {{") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" }") + lines.append(" break;") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + lines.append(" return os;") + lines.append("}\n") + + lines.append(f"{choice_class}::Choice {choice_class}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}\n") + lines.append(f"void {choice_class}::setChoice(const {choice_class}::Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}\n") + + # Accessors per member, in declaration order. For each: get, set (no + # 'has' since each member is always present in the choice). + members = [seq_group_cls] + [pascal(b.element_name) for b in singleton_branches] + for cc in members: + lines.append(f"{cc}Ptr {choice_class}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {choice_class}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({choice_class});\n") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_lyric_h(class_name, attrs_name, choice_class, trailing_elems, + editorial_group_cls): + fwds = sorted({choice_class, editorial_group_cls} + | {pascal(e.element_name) for e in trailing_elems}) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append(f'#include "mx/core/elements/{attrs_name}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + for f in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + lines.append("") + lines.append(f" /* _________ {choice_class} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {choice_class}Ptr get{choice_class}() const;") + lines.append(f" void set{choice_class}(const {choice_class}Ptr &value);") + for e in trailing_elems: + cc = pascal(e.element_name) + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = {e.min_occurs}, maxOccurs = {e.max_occurs} _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + if e.min_occurs == 0: + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + lines.append("") + lines.append(f" /* _________ {editorial_group_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {editorial_group_cls}Ptr get{editorial_group_cls}() const;") + lines.append(f" void set{editorial_group_cls}(const {editorial_group_cls}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {attrs_name}Ptr myAttributes;") + lines.append(f" {choice_class}Ptr my{choice_class};") + for e in trailing_elems: + cc = pascal(e.element_name) + lines.append(f" {cc}Ptr my{cc};") + if e.min_occurs == 0: + lines.append(f" bool myHas{cc};") + lines.append(f" {editorial_group_cls}Ptr my{editorial_group_cls};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_lyric_cpp(elem_name, class_name, attrs_name, choice_class, + seq_group_cls, singleton_branches, trailing_elems, + editorial_group_cls): + # Includes: choice class, editorial group, trailing elems, plus the + # singletons + seq-group because fromXElement uses them. + inc_classes = sorted({choice_class, editorial_group_cls, seq_group_cls} + | {pascal(b.element_name) for b in singleton_branches} + | {pascal(e.element_name) for e in trailing_elems}) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [ + f"myAttributes(std::make_shared<{attrs_name}>())", + f"my{choice_class}(make{choice_class}())", + ] + for e in trailing_elems: + cc = pascal(e.element_name) + init_parts.append(f"my{cc}(make{cc}())") + if e.min_occurs == 0: + init_parts.append(f"myHas{cc}(false)") + init_parts.append(f"my{editorial_group_cls}(make{editorial_group_cls}())") + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return myAttributes->hasValues();") + lines.append("}\n") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" if (myAttributes->hasValues())") + lines.append(" {") + lines.append(" myAttributes->toStream(os);") + lines.append(" }") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' return os << "{elem_name}";') + lines.append("}\n") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + # Attribute accessors before streamContents (matches committed Lyric.cpp). + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}\n") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}\n") + + # streamContents: leading endl, choice streamContents, trailing optional + # elements (each guarded by has-flag), then editorial group (with leading + # endl only when it has contents), then trailing endl. + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" os << std::endl;") + lines.append(f" my{choice_class}->streamContents(os, indentLevel + 1, isOneLineOnly);") + for e in trailing_elems: + cc = pascal(e.element_name) + if e.min_occurs == 0: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel + 1);") + lines.append(" }") + else: + lines.append(f" my{cc}->toStream(os, indentLevel + 1);") + lines.append(f" if (my{editorial_group_cls}->hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(f" my{editorial_group_cls}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}\n") + + # Choice accessors + lines.append(f"{choice_class}Ptr {class_name}::get{choice_class}() const") + lines.append("{") + lines.append(f" return my{choice_class};") + lines.append("}\n") + lines.append(f"void {class_name}::set{choice_class}(const {choice_class}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{choice_class} = value;") + lines.append(" }") + lines.append("}\n") + + for e in trailing_elems: + cc = pascal(e.element_name) + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + if e.min_occurs == 0: + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}\n") + + lines.append(f"{editorial_group_cls}Ptr {class_name}::get{editorial_group_cls}() const") + lines.append("{") + lines.append(f" return my{editorial_group_cls};") + lines.append("}\n") + lines.append(f"void {class_name}::set{editorial_group_cls}(const {editorial_group_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{editorial_group_cls} = value;") + lines.append(" }") + lines.append("}\n") + + # fromXElementImpl: iterate xelement; for each child: + # - try checkSetChoiceMember for each singleton branch + # - else: set choice to seq-group, get the seq-group ptr, importGroup + # - then importElement for each trailing optional + # - then importGroup for editorial + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + lines.append("") + lines.append(" auto endIter = xelement.end();") + lines.append(" for (auto it = xelement.begin(); it != endIter; ++it)") + lines.append(" {") + # The committed Lyric.cpp has deliberate blank lines around each + # checkSetChoiceMember block (after the opening brace, before/after + # continue, and between the if/else-if branches). `make fmt` preserves + # those blank lines, so we emit them explicitly. + lines.append("") + seq_group_enum = seq_group_cls[:1].lower() + seq_group_cls[1:] + for i, b in enumerate(singleton_branches): + cc = pascal(b.element_name) + cname = b.element_name + guard = "if" if i == 0 else "else if" + lines.append(f' {guard} (checkSetChoiceMember(message, *it, isSuccess, my{choice_class}, "{cname}",') + lines.append(f' &{choice_class}::get{cc},') + lines.append(f' static_cast({choice_class}::Choice::{camel(cname)})))') + lines.append(" {") + lines.append("") + lines.append(" continue;") + lines.append(" }") + lines.append("") + lines.append(" else") + lines.append(" {") + lines.append(f" my{choice_class}->setChoice({choice_class}::Choice::{seq_group_enum});") + lines.append(f" {seq_group_cls}Ptr ptr = my{choice_class}->get{seq_group_cls}();") + lines.append(f" importGroup(message, it, endIter, isSuccess, ptr);") + lines.append(" }") + for e in trailing_elems: + cc = pascal(e.element_name) + if e.min_occurs == 0: + lines.append(f" if (importElement(message, *it, isSuccess, *my{cc}, myHas{cc}))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + else: + lines.append(f" importElement(message, *it, isSuccess, *my{cc});") + lines.append(f" importGroup(message, it, endIter, isSuccess, my{editorial_group_cls});") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _emit_lyric_family(elem_name, elem, ct, model, generated_attrs, stats): + s = _extract_lyric_structure(ct) + + class_name = element_class_name(elem_name) # Lyric + # The choice class name is bespoke: 'LyricTextChoice'. It is not + # pascal(elem_name) + 'Choice' (which would be 'LyricChoice'). The + # original codegen named it after the "text" sense of its branches. + choice_class = pascal(elem_name) + "TextChoice" + + # Synthetic group class names are derived bottom-up from the first two + # children of each synthetic group plus a 'Group' suffix. + inner_optional_group = _synth_group_name_from_children(s["inner_optional_elems"]) + inner_seq_group = _synth_group_name_from_children([inner_optional_group, s["inner_text"]]) + seq_branch_group = _synth_group_name_from_children(s["seq_branch_pre"]) + + type_name = elem.type_name or "" + + # 1. Attrs struct + if ct.attributes: + attrs_name = resolve_attrs_name(elem_name, type_name, model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(attrs_name, ct.attributes, model) + c = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), c) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + else: + attrs_name = None + + # 2. Innermost group: ElisionSyllabicGroup + h = generate_elision_syllabic_group_h(inner_optional_group, s["inner_optional_elems"]) + c = generate_elision_syllabic_group_cpp(inner_optional_group, s["inner_optional_elems"]) + write_file(os.path.join(ELEM_DIR, f"{inner_optional_group}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{inner_optional_group}.cpp"), c) + + # 3. Middle group: ElisionSyllabicTextGroup + h = generate_elision_syllabic_text_group_h(inner_seq_group, inner_optional_group, s["inner_text"]) + c = generate_elision_syllabic_text_group_cpp(inner_seq_group, inner_optional_group, s["inner_text"]) + write_file(os.path.join(ELEM_DIR, f"{inner_seq_group}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{inner_seq_group}.cpp"), c) + + # 4. Outer sequence-branch group: SyllabicTextGroup + h = generate_syllabic_text_group_h(seq_branch_group, s["seq_branch_pre"], + inner_seq_group, None, s["seq_branch_post"]) + c = generate_syllabic_text_group_cpp(seq_branch_group, s["seq_branch_pre"], + inner_seq_group, s["seq_branch_post"]) + write_file(os.path.join(ELEM_DIR, f"{seq_branch_group}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{seq_branch_group}.cpp"), c) + + # 5. LyricTextChoice + h = generate_lyric_text_choice_h(choice_class, seq_branch_group, s["singleton_branches"]) + c = generate_lyric_text_choice_cpp(choice_class, seq_branch_group, s["singleton_branches"]) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.cpp"), c) + + # 6. Editorial group class name (already generated elsewhere via GENERATE_GROUPS) + editorial_group_cls = pascal(s["editorial_group"].group_name) + "Group" + + # 7. Lyric + h = generate_lyric_h(class_name, attrs_name, choice_class, s["trailing_elems"], + editorial_group_cls) + c = generate_lyric_cpp(elem_name, class_name, attrs_name, choice_class, + seq_branch_group, s["singleton_branches"], s["trailing_elems"], + editorial_group_cls) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), c) + + stats["elem_written"] += 1 + stats["bespoke_written"] = stats.get("bespoke_written", 0) + 1 + + +# --------------------------------------------------------------------------- +# PartList (bespoke, schema-driven) +# +# part-list's complex type is: +# sequence: +# group ref part-group (0..unbounded) +# group ref score-part (1..1) +# choice (0..unbounded): +# group ref part-group +# group ref score-part +# +# In MusicXML each xs:group of this form wraps a same-named element, so the +# group references resolve to element references with identical names. +# Two classes are produced: +# - PartGroupOrScorePart: the (trailing) choice promoted to a class. It +# is a simple 2-branch choice (each branch is a single element ref), +# with no attributes and no fromXElement logic (MX_FROM_XELEMENT_UNUSED). +# - PartList: with myPartGroupSet, myScorePart, myPartGroupOrScorePartSet, +# and a custom fromXElementImpl that uses a 'isFirstScorePartEncountered' +# state machine to split incoming children before/after the first +# score-part. +# +# All element names come from the parsed XSD model so that spec changes +# (renamed elements, etc.) propagate. The bespoke choice class name +# 'PartGroupOrScorePart' is composed from the two branch element names: +# pascal(branch[0]) + 'Or' + pascal(branch[1]). + + +def pascal_to_camel(pascal_name: str) -> str: + """Convert a PascalCase identifier to camelCase by lowercasing only + the first character. Used for variable names derived from class names + that have no hyphen/underscore separators.""" + if not pascal_name: + return pascal_name + return pascal_name[0].lower() + pascal_name[1:] + + +def _extract_part_list_structure(ct): + """Walk part-list's content_tree and return a dict describing the structure. + + Returns a dict with these keys (all element names come from the parsed XSD): + - leading_unbounded: element name of the leading unbounded group-ref + (e.g. 'part-group') + - singleton: element name of the middle required group-ref + (e.g. 'score-part') + - choice_branches: list of element names of the trailing choice's + branches (e.g. ['part-group', 'score-part']) + """ + tree = ct.content_tree + assert isinstance(tree, SequenceNode) + assert len(tree.children) == 3, ( + f"part-list: expected 3 top-level children, got {len(tree.children)}") + + leading_node = tree.children[0] + middle_node = tree.children[1] + choice_node = tree.children[2] + + def _grouplike_name(n): + if isinstance(n, GroupRefNode): + return n.group_name + if isinstance(n, ElementRefNode): + return n.element_name + raise AssertionError(f"unexpected node: {type(n).__name__}") + + assert isinstance(leading_node, GroupRefNode) and leading_node.max_occurs == -1, ( + "part-list: expected leading unbounded group ref") + assert isinstance(middle_node, GroupRefNode) and middle_node.max_occurs == 1, ( + "part-list: expected middle singleton group ref") + assert isinstance(choice_node, ChoiceNode) and choice_node.max_occurs == -1, ( + "part-list: expected trailing unbounded choice") + + return { + "leading_unbounded": _grouplike_name(leading_node), + "singleton": _grouplike_name(middle_node), + "choice_branches": [_grouplike_name(b) for b in choice_node.branches], + } + + +def generate_part_group_or_score_part_h(class_name, branches): + """branches = list of element names (e.g. ['part-group', 'score-part']). + Emits the simple 2-branch element-ref choice class header. + """ + branch_cls = [pascal(n) for n in branches] + fwds = sorted(set(branch_cls)) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for fwd in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({fwd})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + for i, n in enumerate(branches): + comma = "" if i == len(branches) - 1 else "," + lines.append(f" {camel(n)} = {i + 1}{comma}") + lines.append(" };") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append("") + lines.append(" /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {class_name}::Choice getChoice() const;") + lines.append(f" void setChoice(const {class_name}::Choice value);") + # Bespoke quirk to match committed: the PartGroup accessor block uses + # 'maxPartGroupOccurs' in its comment header rather than 'maxOccurs'. + # See state.md gotcha. + for i, n in enumerate(branches): + cc = pascal(n) + lines.append("") + if i == 0: + lines.append(f" /* _________ {cc} minOccurs = 1, max{cc}Occurs = 1 _________ */") + else: + lines.append(f" /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + for n in branches: + cc = pascal(n) + lines.append(f" {cc}Ptr my{cc};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_part_group_or_score_part_cpp(class_name, branches): + branch_cls = [pascal(n) for n in branches] + inc_classes = sorted(set(branch_cls)) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + # Constructor: default choice is the first branch. + init_parts = [f"myChoice(Choice::{camel(branches[0])})"] + for n in branches: + cc = pascal(n) + init_parts.append(f"my{cc}(make{cc}())") + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + + # hasContents: any one of the chosen branches has contents + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + cond_parts = [] + for n in branches: + cc = pascal(n) + cond_parts.append(f"(myChoice == Choice::{camel(n)} && my{cc}->hasContents())") + lines.append(" return " + (" ||\n ".join(cond_parts)) + ";") + lines.append("}\n") + + # streamContents: switch on the choice, call toStream on the selected branch + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" switch (myChoice)") + lines.append(" {") + for n in branches: + cc = pascal(n) + lines.append(f" case Choice::{camel(n)}:") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" break;") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}\n") + + # Choice accessors + lines.append(f"{class_name}::Choice {class_name}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}\n") + lines.append(f"void {class_name}::setChoice(const {class_name}::Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}\n") + + # Per-branch accessors: getter then setter + for n in branches: + cc = pascal(n) + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}\n") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}\n") + + lines.append(f"MX_FROM_XELEMENT_UNUSED({class_name})\n") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_part_list_h(class_name, leading_name, singleton_name, choice_class_name): + leading_cls = pascal(leading_name) + singleton_cls = pascal(singleton_name) + fwds = sorted({leading_cls, choice_class_name, singleton_cls}) + [class_name] + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for fwd in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({fwd})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append("") + lines.append(f" /* _________ {leading_cls} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {leading_cls}Set &get{leading_cls}Set() const;") + lines.append(f" void add{leading_cls}(const {leading_cls}Ptr &value);") + lines.append(f" void remove{leading_cls}(const {leading_cls}SetIterConst &value);") + lines.append(f" void clear{leading_cls}Set();") + lines.append(f" {leading_cls}Ptr get{leading_cls}(const {leading_cls}SetIterConst &setIterator) const;") + lines.append("") + lines.append(f" /* _________ {singleton_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {singleton_cls}Ptr get{singleton_cls}() const;") + lines.append(f" void set{singleton_cls}(const {singleton_cls}Ptr &value);") + lines.append("") + lines.append(f" /* _________ {choice_class_name} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {choice_class_name}Set &get{choice_class_name}Set() const;") + lines.append(f" void add{choice_class_name}(const {choice_class_name}Ptr &value);") + lines.append(f" void remove{choice_class_name}(const {choice_class_name}SetIterConst &value);") + lines.append(f" void clear{choice_class_name}Set();") + lines.append(f" {choice_class_name}Ptr get{choice_class_name}(const {choice_class_name}SetIterConst &setIterator) const;") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {leading_cls}Set my{leading_cls}Set;") + lines.append(f" {singleton_cls}Ptr my{singleton_cls};") + lines.append(f" {choice_class_name}Set my{choice_class_name}Set;") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_part_list_cpp(elem_name, class_name, leading_name, singleton_name, + choice_class_name, choice_branches): + leading_cls = pascal(leading_name) + singleton_cls = pascal(singleton_name) + inc_classes = sorted({leading_cls, choice_class_name, singleton_cls}) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_classes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include \n") + lines.append("namespace mx\n{\nnamespace core\n{") + + init_parts = [ + f"my{leading_cls}Set()", + f"my{singleton_cls}(make{singleton_cls}())", + f"my{choice_class_name}Set()", + ] + _emit_ctor_init(lines, f"{class_name}::{class_name}()", init_parts) + lines.append("{") + lines.append("}\n") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}\n") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}\n") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{elem_name}";') + lines.append(" return os;") + lines.append("}\n") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}\n") + + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(f" for (auto x : my{leading_cls}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(f" my{singleton_cls}->toStream(os, indentLevel + 1);") + lines.append(f" for (auto x : my{choice_class_name}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(" return os;") + lines.append("}\n") + + # leading set accessors (order: getSet, add, remove, clear, get(setIterator)) + lines.append(f"const {leading_cls}Set &{class_name}::get{leading_cls}Set() const") + lines.append("{") + lines.append(f" return my{leading_cls}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::add{leading_cls}(const {leading_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{leading_cls}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::remove{leading_cls}(const {leading_cls}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{leading_cls}Set.cend())") + lines.append(" {") + lines.append(f" my{leading_cls}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{leading_cls}Set()") + lines.append("{") + lines.append(f" my{leading_cls}Set.clear();") + lines.append("}\n") + lines.append(f"{leading_cls}Ptr {class_name}::get{leading_cls}(const {leading_cls}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{leading_cls}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {leading_cls}Ptr();") + lines.append("}\n") + + # Singleton accessors + lines.append(f"{singleton_cls}Ptr {class_name}::get{singleton_cls}() const") + lines.append("{") + lines.append(f" return my{singleton_cls};") + lines.append("}\n") + lines.append(f"void {class_name}::set{singleton_cls}(const {singleton_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{singleton_cls} = value;") + lines.append(" }") + lines.append("}\n") + + # Trailing choice-set accessors (order: getSet, add, remove, clear, get) + cc = choice_class_name + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}\n") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}\n") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}\n") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}\n") + + # Custom fromXElementImpl with isFirstScorePartEncountered state machine. + # leading_name e.g. 'part-group', singleton_name e.g. 'score-part'. + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(f" bool isFirst{singleton_cls}Encountered = false;") + lines.append("") + lines.append(" for (auto it = xelement.begin(); it != xelement.end(); ++it)") + lines.append(" {") + lines.append(" const std::string elementName = it->getName();") + lines.append(f' if (elementName != "{leading_name}" && elementName != "{singleton_name}")') + lines.append(" {") + lines.append(f' message << "{class_name}: fromXElement encountered unexpected element \'" << elementName << "\'" << std::endl;') + lines.append(" }") + lines.append(f" if (!isFirst{singleton_cls}Encountered)") + lines.append(" {") + lines.append(f' if (elementName == "{leading_name}")') + lines.append(" {") + lines.append(f" auto {camel(leading_name)} = make{leading_cls}();") + lines.append(f" isSuccess &= {camel(leading_name)}->fromXElement(message, *it);") + lines.append(f" my{leading_cls}Set.push_back({camel(leading_name)});") + lines.append(" }") + lines.append(f' else if (elementName == "{singleton_name}")') + lines.append(" {") + lines.append(f" isFirst{singleton_cls}Encountered = true;") + lines.append(f" isSuccess &= my{singleton_cls}->fromXElement(message, *it);") + lines.append(" }") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" auto {pascal_to_camel(choice_class_name)} = make{choice_class_name}();") + lines.append("") + cc_local = pascal_to_camel(choice_class_name) + for i, n in enumerate(choice_branches): + cc_b = pascal(n) + prefix = "if" if i == 0 else "else if" + lines.append(f' {prefix} (elementName == "{n}")') + lines.append(" {") + lines.append(f" {cc_local}->setChoice({choice_class_name}::Choice::{camel(n)});") + lines.append(f" {cc_local}->get{cc_b}()->fromXElement(message, *it);") + lines.append(" }") + lines.append("") + lines.append(f" my{choice_class_name}Set.push_back({cc_local});") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(f" if (!isFirst{singleton_cls}Encountered)") + lines.append(" {") + lines.append(f' message << "{class_name}: at least one \'{singleton_name}\' elements are required but none were found" << std::endl;') + lines.append(" isSuccess = false;") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}\n") + + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Harmony (bespoke, schema-driven) +# +# harmony's complex type: +# sequence: +# group ref harmony-chord (1..unbounded) -> emits HarmonyChordGroup +# element frame (0..1) +# element offset (0..1) +# group ref editorial (1..1) +# group ref staff (0..1) +# plus attribute groups -> HarmonyAttributes +# +# The harmony-chord xs:group internal structure (not exposed via +# model.groups because the inner xs:choice is dropped by _parse_groups, +# and not exposed via complex_types because it's a group, not a type): +# sequence: +# choice: +# element root +# element function (type style-text) +# element kind +# element inversion (0..1) +# element bass (0..1) +# element degree (0..unbounded) +# +# The bespoke handler walks model.root directly to recover the harmony-chord +# inner choice. All names (group, choice branches, elements) come from the +# parsed XSD. +# +# Output classes: +# - HarmonyChordGroup: synthetic group with internal Choice (root|function), +# required kind, optional inversion/bass, unbounded degree set. The +# class is invoked via streamContents (not toStream) from the parent, +# and is MX_FROM_XELEMENT_UNUSED -- the parent Harmony's fromXElementImpl +# state-machine builds the chord groups itself. +# - Harmony: with the HarmonyChordGroup set (always has >=1 entry, ctor +# pushes one), optional frame/offset, required EditorialGroup, +# optional Staff, plus the HarmonyAttributes. Includes a custom +# fromXElementImpl that consumes incoming root/function as the start of +# a new chord group, then required kind, then optional inversion/bass, +# then unbounded degree, then falls through to import frame/offset/ +# editorial/staff. A private addGroup helper replaces the default- +# constructed first entry on first add, then appends subsequently. + + +def _extract_harmony_chord_group_structure(model): + """Walk model.root to find the harmony-chord xs:group and recover its + full structure (the inner xs:choice that model.groups['harmony-chord'] + drops). + + Returns a dict: + - choice_branches: list[str] of element names (e.g. ['root', 'function']) + - kind: str (the required singleton element name) + - optionals: list[str] (optional singleton element names, in order) + - unbounded: str (the trailing unbounded element name) + """ + grp = None + for g in model.root.iter(f"{XS}group"): + if g.get("name") == "harmony-chord": + grp = g + break + assert grp is not None, "harmony-chord group not found in XSD" + + seq = grp.find(f"{XS}sequence") + assert seq is not None, "harmony-chord: expected sequence" + + choice_branches = [] + singletons = [] # (name, min_occurs) + unbounded = None + + for child in seq: + if child.tag == f"{XS}choice": + for b in child: + if b.tag == f"{XS}element": + nm = b.get("ref") or b.get("name") + if nm: + choice_branches.append(nm) + elif child.tag == f"{XS}element": + nm = child.get("ref") or child.get("name") + if not nm: + continue + min_o = int(child.get("minOccurs", "1")) + max_o = child.get("maxOccurs", "1") + max_o = -1 if max_o == "unbounded" else int(max_o) + if max_o == -1: + unbounded = nm + else: + singletons.append((nm, min_o)) + + assert choice_branches, "harmony-chord: expected at least one choice branch" + assert singletons, "harmony-chord: expected at least the required kind element" + kind = singletons[0][0] + assert singletons[0][1] == 1, "harmony-chord: first singleton must be required" + optionals = [n for (n, mo) in singletons[1:] if mo == 0] + assert unbounded is not None, "harmony-chord: expected an unbounded trailing element" + + return { + "choice_branches": choice_branches, + "kind": kind, + "optionals": optionals, + "unbounded": unbounded, + } + + +def _extract_harmony_structure(ct, model): + """Walk harmony's content_tree to identify the leading harmony-chord + group ref, the optional frame/offset elements, the editorial group ref, + and the optional staff group ref. Returns a dict.""" + tree = ct.content_tree + assert isinstance(tree, SequenceNode) + + chord_group = None + optional_elems = [] # list[ElementRefNode] - frame, offset + editorial_group = None + staff_group = None + + for c in tree.children: + if isinstance(c, GroupRefNode) and c.max_occurs == -1: + chord_group = c + elif isinstance(c, ElementRefNode) and c.min_occurs == 0: + optional_elems.append(c) + elif isinstance(c, GroupRefNode) and c.group_name == "staff": + staff_group = c + elif isinstance(c, GroupRefNode): + editorial_group = c + + assert chord_group is not None, "harmony: expected leading unbounded chord group ref" + assert editorial_group is not None, "harmony: expected editorial group ref" + + inner = _extract_harmony_chord_group_structure(model) + + return { + "chord_group_name": chord_group.group_name, # 'harmony-chord' + "optional_elems": [c.element_name for c in optional_elems], + "editorial_group": editorial_group.group_name, # 'editorial' + "staff_group": staff_group.group_name if staff_group else None, + "inner": inner, + } + + +def generate_harmony_chord_group_h(class_name, inner): + """class_name e.g. 'HarmonyChordGroup'. inner = dict from + _extract_harmony_chord_group_structure.""" + branches = inner["choice_branches"] + kind = inner["kind"] + optionals = inner["optionals"] + unbounded = inner["unbounded"] + + branch_cls = [pascal(n) for n in branches] + kind_cls = pascal(kind) + optional_cls = [pascal(n) for n in optionals] + unbounded_cls = pascal(unbounded) + + fwds = sorted(set(branch_cls + [kind_cls] + optional_cls + [unbounded_cls])) + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for fwd in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({fwd})") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})\n") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append(f"\nclass {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + for i, n in enumerate(branches): + comma = "" if i == len(branches) - 1 else "," + lines.append(f" {camel(n)} = {i + 1}{comma}") + lines.append(" };") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {class_name}::Choice getChoice() const;") + lines.append(f" void setChoice(const {class_name}::Choice value);") + # branch accessors + for n in branches: + cc = pascal(n) + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + # kind (required singleton) + lines.append("") + lines.append(f" /* _________ {kind_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {kind_cls}Ptr get{kind_cls}() const;") + lines.append(f" void set{kind_cls}(const {kind_cls}Ptr &value);") + # optionals + for n in optionals: + cc = pascal(n) + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + # unbounded set + cc = unbounded_cls + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + lines.append(f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + for n in branches: + cc = pascal(n) + lines.append(f" {cc}Ptr my{cc};") + lines.append(f" {kind_cls}Ptr my{kind_cls};") + for n in optionals: + cc = pascal(n) + lines.append(f" {cc}Ptr my{cc};") + lines.append(f" bool myHas{cc};") + lines.append(f" {unbounded_cls}Set my{unbounded_cls}Set;") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_harmony_chord_group_cpp(class_name, inner): + branches = inner["choice_branches"] + kind = inner["kind"] + optionals = inner["optionals"] + unbounded = inner["unbounded"] + + branch_cls = [pascal(n) for n in branches] + kind_cls = pascal(kind) + optional_cls = [pascal(n) for n in optionals] + unbounded_cls = pascal(unbounded) + + includes = sorted(set(branch_cls + [kind_cls] + optional_cls + [unbounded_cls])) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in includes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{") + + # Ctor + init_parts = [] + init_parts.append(f"myChoice(Choice::{camel(branches[0])})") + for n in branches: + cc = pascal(n) + init_parts.append(f"my{cc}(make{cc}())") + init_parts.append(f"my{kind_cls}(make{kind_cls}())") + for n in optionals: + cc = pascal(n) + init_parts.append(f"my{cc}(make{cc}())") + init_parts.append(f"myHas{cc}(false)") + init_parts.append(f"my{unbounded_cls}Set()") + lines.append(f"{class_name}::{class_name}()") + lines.append(" : " + ", ".join(init_parts)) + lines.append("{") + lines.append("}") + lines.append("") + + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + # streamContents + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" switch (myChoice)") + lines.append(" {") + for n in branches: + cc = pascal(n) + lines.append(f" case Choice::{camel(n)}:") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" break;") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(f" my{kind_cls}->toStream(os, indentLevel);") + for n in optionals: + cc = pascal(n) + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + lines.append(" }") + lines.append(f" for (auto x : my{unbounded_cls}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel);") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}") + lines.append("") + + # getChoice / setChoice + lines.append(f"{class_name}::Choice {class_name}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setChoice(const {class_name}::Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}") + lines.append("") + + # branch accessors (get/set) + for n in branches: + cc = pascal(n) + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + + # kind accessors + cc = kind_cls + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + + # optional accessors + for n in optionals: + cc = pascal(n) + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}") + lines.append("") + + # unbounded set accessors + cc = unbounded_cls + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}") + lines.append("") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}") + lines.append("") + lines.append(f"MX_FROM_XELEMENT_UNUSED({class_name});") + lines.append("") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_harmony_h(class_name, attrs_name, chord_group_cls, optional_elems, + editorial_group_cls, staff_cls): + """Emit Harmony.h. optional_elems is a list of element names (frame, offset).""" + optional_cls = [pascal(n) for n in optional_elems] + + fwds = [f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})"] + elem_fwds = [editorial_group_cls] + optional_cls + [chord_group_cls] + if staff_cls: + elem_fwds.append(staff_cls) + # sort alphabetically (this matches committed Harmony.h ordering of fwd decls) + for fwd in sorted(set(elem_fwds)): + fwds.append(f"MX_FORWARD_DECLARE_ELEMENT({fwd})") + fwds.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})") + + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append(f'#include "mx/core/elements/{attrs_name}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + lines.extend(fwds) + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + lines.append("") + # chord group set + cc = chord_group_cls + lines.append(f" /* _________ {cc} minOccurs = 1, maxOccurs = unbounded _________ */") + lines.append(f" const {cc}Set &get{cc}Set() const;") + lines.append(f" void add{cc}(const {cc}Ptr &value);") + lines.append(f" void remove{cc}(const {cc}SetIterConst &value);") + lines.append(f" void clear{cc}Set();") + lines.append(f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;") + # optional elements + for n in optional_elems: + cc = pascal(n) + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + # editorial group (required) + cc = editorial_group_cls + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + # optional staff + if staff_cls: + cc = staff_cls + lines.append("") + lines.append(f" /* _________ {cc} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {cc}Ptr get{cc}() const;") + lines.append(f" void set{cc}(const {cc}Ptr &value);") + lines.append(f" bool getHas{cc}() const;") + lines.append(f" void setHas{cc}(const bool value);") + + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {attrs_name}Ptr myAttributes;") + lines.append(f" {chord_group_cls}Set my{chord_group_cls}Set;") + for n in optional_elems: + cc = pascal(n) + lines.append(f" {cc}Ptr my{cc};") + lines.append(f" bool myHas{cc};") + lines.append(f" {editorial_group_cls}Ptr my{editorial_group_cls};") + if staff_cls: + lines.append(f" {staff_cls}Ptr my{staff_cls};") + lines.append(f" bool myHas{staff_cls};") + lines.append("") + lines.append(f" void addGroup({chord_group_cls}Ptr &grp, bool &isFirst);") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_harmony_cpp(elem_name, class_name, attrs_name, chord_group_cls, + inner, optional_elems, editorial_group_cls, staff_cls): + """Emit Harmony.cpp including the custom fromXElementImpl state machine.""" + branches = inner["choice_branches"] # ['root', 'function'] + kind = inner["kind"] # 'kind' + inner_optionals = inner["optionals"] # ['inversion', 'bass'] + unbounded = inner["unbounded"] # 'degree' + + branch_cls = [pascal(n) for n in branches] + kind_cls = pascal(kind) + inner_optional_cls = [pascal(n) for n in inner_optionals] + unbounded_cls = pascal(unbounded) + optional_cls = [pascal(n) for n in optional_elems] + + # Include set: parent attrs, chord-group's members (so makeXxx() calls + # resolve), the chord group class itself, the optional elements, the + # editorial group, the staff. Match the committed include layout, which + # lists every element class touched in fromXElementImpl plus the chord + # group itself. + includes = sorted(set( + branch_cls + [kind_cls] + inner_optional_cls + [unbounded_cls] + + [editorial_group_cls] + optional_cls + [chord_group_cls] + + ([staff_cls] if staff_cls else []) + )) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in includes: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{") + + # Constructor + init_parts = [ + f"myAttributes(std::make_shared<{attrs_name}>())", + f"my{chord_group_cls}Set()", + ] + for n in optional_elems: + cc = pascal(n) + init_parts.append(f"my{cc}(make{cc}())") + init_parts.append(f"myHas{cc}(false)") + init_parts.append(f"my{editorial_group_cls}(make{editorial_group_cls}())") + if staff_cls: + init_parts.append(f"my{staff_cls}(make{staff_cls}())") + init_parts.append(f"myHas{staff_cls}(false)") + + lines.append(f"{class_name}::{class_name}()") + lines.append(" : " + ", ".join(init_parts)) + lines.append("{") + lines.append(f" my{chord_group_cls}Set.push_back(make{chord_group_cls}());") + lines.append("}") + lines.append("") + + # hasAttributes / streamAttributes / streamName + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return myAttributes->hasValues();") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return myAttributes->toStream(os);") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{elem_name}";') + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + + # streamContents + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(f" for (auto x : my{chord_group_cls}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + for n in optional_elems: + cc = pascal(n) + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(f" if (my{editorial_group_cls}->hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{editorial_group_cls}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + if staff_cls: + lines.append(f" if (myHas{staff_cls})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{staff_cls}->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}") + lines.append("") + + # Attribute accessors + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}") + lines.append("") + + # Chord group set accessors. Note: removeXxx for a "must have at least 1" + # set guards against removing the last element. clearXxxSet resets to a + # single default entry. + cc = chord_group_cls + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" if (my{cc}Set.size() > 1)") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append(f" my{cc}Set.push_back(make{cc}());") + lines.append("}") + lines.append("") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}") + lines.append("") + + # Optional element accessors (frame/offset) + for n in optional_elems: + cc = pascal(n) + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}") + lines.append("") + + # Editorial group accessor + cc = editorial_group_cls + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + + # Staff accessors (optional) + if staff_cls: + cc = staff_cls + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}") + lines.append("") + + # fromXElementImpl. The state machine: on each pass, if we see a choice + # branch (root|function), we start consuming the chord-group elements + # in fixed order (kind, optional inversion, optional bass, then any + # number of degree). On any other element name, we try importElement + # for the optional frame/offset, then importGroup for editorial, + # then importElement for staff. + branch_check = ' || '.join(f'elementName == "{n}"' for n in branches) + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + lines.append(" bool isChoiceFound = false;") + lines.append(f" bool isFirst{chord_group_cls}Added = false;") + lines.append("") + lines.append(" auto endIter = xelement.end();") + lines.append(" for (auto it = xelement.begin(); it != endIter; ++it)") + lines.append(" {") + lines.append(" const std::string elementName = it->getName();") + lines.append(f" if ({branch_check})") + lines.append(" {") + lines.append(" bool decrementIter = false;") + lines.append(" isChoiceFound = true;") + lines.append(f" auto item = make{chord_group_cls}();") + + # Per-branch dispatch + for i, n in enumerate(branches): + cc = pascal(n) + prefix = "if" if i == 0 else "else" + if i == 0: + lines.append(f' if (elementName == "{n}")') + elif i == len(branches) - 1: + lines.append(" else") + else: + lines.append(f' else if (elementName == "{n}")') + lines.append(" {") + lines.append(f" item->setChoice({chord_group_cls}::Choice::{camel(n)});") + lines.append(f" isSuccess &= item->get{cc}()->fromXElement(message, *it);") + lines.append(" }") + lines.append(" decrementIter = true;") + lines.append(" ++it;") + # required kind + lines.append(" if (it == endIter || it->getName() != \"" + kind + "\")") + lines.append(" {") + lines.append(f' message << "{class_name}: \'{kind}\' is a required element and is missing aborting" << std::endl;') + lines.append(" return false;") + lines.append(" }") + lines.append(f" isSuccess &= item->get{kind_cls}()->fromXElement(message, *it);") + lines.append(" decrementIter = true;") + lines.append(" ++it;") + lines.append(" if (it == endIter)") + lines.append(" {") + lines.append(f" addGroup(item, isFirst{chord_group_cls}Added);") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append(" }") + # optionals + for n in inner_optionals: + cc = pascal(n) + lines.append(f' if (it->getName() == "{n}")') + lines.append(" {") + lines.append(f" isSuccess &= item->get{cc}()->fromXElement(message, *it);") + lines.append(f" item->setHas{cc}(true);") + lines.append(" decrementIter = true;") + lines.append(" ++it;") + lines.append(" }") + lines.append(" if (it == endIter)") + lines.append(" {") + lines.append(f" addGroup(item, isFirst{chord_group_cls}Added);") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append(" }") + # unbounded (degree) + lines.append("") + lines.append(f' while (it != endIter && it->getName() == "{unbounded}")') + lines.append(" {") + lines.append(f" auto {camel(unbounded)} = make{unbounded_cls}();") + lines.append(f" isSuccess &= {camel(unbounded)}->fromXElement(message, *it);") + lines.append(f" item->add{unbounded_cls}({camel(unbounded)});") + lines.append(" decrementIter = true;") + lines.append(" ++it;") + lines.append(" }") + lines.append("") + lines.append(f" addGroup(item, isFirst{chord_group_cls}Added);") + lines.append("") + lines.append(" if (decrementIter)") + lines.append(" {") + lines.append(" --it;") + lines.append(" }") + lines.append(" continue;") + lines.append(" }") + lines.append("") + # frame/offset + for n in optional_elems: + cc = pascal(n) + lines.append(f" if (importElement(message, *it, isSuccess, *my{cc}, myHas{cc}))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + # editorial group + lines.append(f" importGroup(message, it, endIter, isSuccess, my{editorial_group_cls});") + # staff + if staff_cls: + lines.append(f" if (importElement(message, *it, isSuccess, *my{staff_cls}, myHas{staff_cls}))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(" if (!isChoiceFound)") + lines.append(" {") + # Quoted branch names: emit ' as a backslash-escape only when the + # surrounding C++ string is wrapped in single quotes; here it's a + # double-quoted C++ string so plain ' is correct. + branch_names_quoted = " or ".join(f"'{n}'" for n in branches) + lines.append(f' message << "{class_name}: either {branch_names_quoted} is required but neither was present" << std::endl;') + lines.append(" isSuccess = false;") + lines.append(" }") + lines.append("") + lines.append(" return isSuccess;") + lines.append("}") + lines.append("") + + # addGroup helper + lines.append(f"void {class_name}::addGroup({chord_group_cls}Ptr &grp, bool &isFirst)") + lines.append("{") + lines.append(f" if (!isFirst && my{chord_group_cls}Set.size() == 1)") + lines.append(" {") + lines.append(f" *my{chord_group_cls}Set.begin() = grp;") + lines.append(" isFirst = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" my{chord_group_cls}Set.push_back(grp);") + lines.append(" isFirst = true;") + lines.append(" }") + lines.append("}") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _emit_harmony_family(elem_name, elem, ct, model, generated_attrs, stats): + s = _extract_harmony_structure(ct, model) + + class_name = element_class_name(elem_name) # Harmony + chord_group_cls = pascal(s["chord_group_name"]) + "Group" # HarmonyChordGroup + editorial_group_cls = pascal(s["editorial_group"]) + "Group" # EditorialGroup + staff_cls = pascal(s["staff_group"]) if s["staff_group"] else None # Staff + + # 1. Attrs struct + type_name = elem.type_name or "" + attrs_name = None + if ct.attributes: + attrs_name = resolve_attrs_name(elem_name, type_name, model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(attrs_name, ct.attributes, model) + c = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), c) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + + # 2. HarmonyChordGroup + h = generate_harmony_chord_group_h(chord_group_cls, s["inner"]) + c = generate_harmony_chord_group_cpp(chord_group_cls, s["inner"]) + write_file(os.path.join(ELEM_DIR, f"{chord_group_cls}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{chord_group_cls}.cpp"), c) + + # 3. Harmony + h = generate_harmony_h(class_name, attrs_name, chord_group_cls, + s["optional_elems"], editorial_group_cls, staff_cls) + c = generate_harmony_cpp(elem_name, class_name, attrs_name, chord_group_cls, + s["inner"], s["optional_elems"], + editorial_group_cls, staff_cls) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), c) + + stats["elem_written"] += 1 + stats["bespoke_written"] = stats.get("bespoke_written", 0) + 1 + + +def _emit_part_list_family(elem_name, elem, ct, model, generated_attrs, stats): + s = _extract_part_list_structure(ct) + + class_name = element_class_name(elem_name) # PartList + branches = s["choice_branches"] # ['part-group', 'score-part'] + # Choice class name composed from the two branch names: + # pascal(b[0]) + 'Or' + pascal(b[1]). For part-list this is + # 'PartGroupOrScorePart', matching the committed codegen. + assert len(branches) == 2, "part-list: expected exactly 2 choice branches" + choice_class_name = pascal(branches[0]) + "Or" + pascal(branches[1]) + + # No attributes on part-list. + if ct.attributes: + attrs_name = resolve_attrs_name(elem_name, elem.type_name or "", model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(attrs_name, ct.attributes, model) + c = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), c) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + + # 1. PartGroupOrScorePart + h = generate_part_group_or_score_part_h(choice_class_name, branches) + c = generate_part_group_or_score_part_cpp(choice_class_name, branches) + write_file(os.path.join(ELEM_DIR, f"{choice_class_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{choice_class_name}.cpp"), c) + + # 2. PartList + h = generate_part_list_h(class_name, s["leading_unbounded"], s["singleton"], choice_class_name) + c = generate_part_list_cpp(elem_name, class_name, s["leading_unbounded"], s["singleton"], + choice_class_name, branches) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), c) + + stats["elem_written"] += 1 + stats["bespoke_written"] = stats.get("bespoke_written", 0) + 1 + + +# --------------------------------------------------------------------------- +# score-partwise / score-timewise family (bespoke, schema-driven) +# +# Both top-level wrappers live as inline anonymous complex types inside +# top-level xs:element declarations, with the same three-level shape: +# (or "score-timewise") +# +# +# +# ("measure" for timewise) +# +# +# ("part" for timewise) +# +# +# (or part-attributes) +# +# +# +# (or measure-attributes) +# +# +# +# +# +# +# +# The standard ``_parse_elements`` path captures the outer level but the +# inner XML name collisions (``part`` and ``measure`` recur across both +# wrappers) and the direct ``xs:group ref`` at CT level (no wrapping +# sequence) cause partial data. The bespoke handler walks ``model.root`` +# directly to recover the full nested structure. +# +# Structural roles (consistent across both flavors): +# outer -> ScorePartwise / ScoreTimewise +# set_holder -> PartwisePart / TimewiseMeasure (mid level; holds +# a set of music_data_holder children) +# music_data_holder-> PartwiseMeasure / TimewisePart (deepest; holds +# a MusicDataGroup with the choice classes) +# +# The hand-written original code applies a small number of per-flavor +# quirks (extra includes, JIT vs eager attrs, message wording, presence of +# MX_DEBUG_THROW_ON_PARSE_ISSUE, post-loop required-child check, ...). All +# such knobs are captured in SCORE_WRAPPER_FLAVOR_CONFIG below so the same +# shared handler can emit either family. +# --------------------------------------------------------------------------- + + +# Per-flavor knobs that capture hand-written variations between the partwise +# and timewise families. Keys are the outer XSD element names. +SCORE_WRAPPER_FLAVOR_CONFIG = { + "score-partwise": { + # ScorePartwise.cpp + "outer_extra_includes": [], + "outer_loop_uses_end_var": False, + # PartwiseMeasure (music-data holder) + "music_data_holder_attrs_jit": True, + "music_data_holder_debug_throw": True, + # PartwisePart (set holder) + "set_holder_clear_repushes_default": True, + "set_holder_remove_has_size_guard": True, + "set_holder_post_loop_required": False, + "set_holder_first_flag_name": "isFirstAdded", + "set_holder_use_return_macro": True, + # Loop body style differences (partwise variant). + "set_holder_loop_uses_element_name_var": False, + "set_holder_unexpected_order": "message_first", # message << ...; isSuccess = false; + "set_holder_unexpected_msg": "encountered_quoted", # "...: encountered an unexpected element '...'" + "set_holder_begin_deref_parens": False, # *mySet.begin() vs *(mySet.begin()) + "set_holder_from_x_before_first_check": True, + "set_holder_blank_after_first_decl": False, + "set_holder_blank_inside_else": False, + "set_holder_child_var_source": "xml_name", # "xml_name" => camel(xml); "class_name" => pascal_to_camel(cls) + }, + "score-timewise": { + # ScoreTimewise.cpp + "outer_extra_includes": [ + "ezxml/XElement.h", + "ezxml/XElementIterator.h", + ], + "outer_loop_uses_end_var": True, + # TimewisePart (music-data holder) + "music_data_holder_attrs_jit": False, + "music_data_holder_debug_throw": False, + # TimewiseMeasure (set holder) + "set_holder_clear_repushes_default": False, + "set_holder_remove_has_size_guard": False, + "set_holder_post_loop_required": True, + "set_holder_first_flag_name": "isFirstTimewisePartFound", + "set_holder_use_return_macro": False, + # Loop body style differences (timewise variant). + "set_holder_loop_uses_element_name_var": True, + "set_holder_unexpected_order": "issuccess_first", # isSuccess = false; message << ...; + "set_holder_unexpected_msg": "trailing_encountered", # "...: unexpected element '...' encountered" + "set_holder_begin_deref_parens": True, + "set_holder_from_x_before_first_check": True, + "set_holder_blank_after_first_decl": True, + "set_holder_blank_inside_else": True, + "set_holder_child_var_source": "class_name", + }, +} + + +def _extract_score_wrapper_structure(model, outer_name): + """Walk model.root to recover the full nested structure of a top-level + score wrapper. Returns a dict with role-based keys; no XML name is + hardcoded here -- every name flows from the parsed XSD.""" + outer_el = None + for el in model.root.iter(f"{XS}element"): + if el.get("name") == outer_name: + outer_el = el + break + if outer_el is None: + raise RuntimeError( + f"score wrapper '{outer_name}' not found in XSD") + + outer_ct = outer_el.find(f"{XS}complexType") + outer_seq = outer_ct.find(f"{XS}sequence") + outer_attr_group = outer_ct.find(f"{XS}attributeGroup").get("ref") + + header_group = outer_seq.find(f"{XS}group").get("ref") + + inner_el = outer_seq.find(f"{XS}element") + inner_name = inner_el.get("name") + inner_ct = inner_el.find(f"{XS}complexType") + inner_attr_group = inner_ct.find(f"{XS}attributeGroup").get("ref") + inner_seq = inner_ct.find(f"{XS}sequence") + + deepest_el = inner_seq.find(f"{XS}element") + deepest_name = deepest_el.get("name") + deepest_ct = deepest_el.find(f"{XS}complexType") + deepest_content_group = deepest_ct.find(f"{XS}group").get("ref") + deepest_attr_group = deepest_ct.find(f"{XS}attributeGroup").get("ref") + + return { + "outer_name": outer_name, + "outer_attr_group": outer_attr_group, # document-attributes + "header_group": header_group, # score-header + "inner_name": inner_name, # part + "inner_attr_group": inner_attr_group, # part-attributes + "deepest_name": deepest_name, # measure + "deepest_content_group": deepest_content_group, # music-data + "deepest_attr_group": deepest_attr_group, # measure-attributes + } + + +def _score_flavor_prefix(outer_name): + """Derive the mx/core flavor prefix from outer XSD element name. + 'score-partwise' -> 'Partwise', 'score-timewise' -> 'Timewise'.""" + if not outer_name.startswith("score-"): + raise RuntimeError( + f"_score_flavor_prefix expects 'score-X', got '{outer_name}'") + return pascal(outer_name[len("score-"):]) + + +def _generate_score_outer_h(class_name, attrs_name, header_group_cls, + child_cls, fwd_order): + """Emit ScorePartwise.h (or ScoreTimewise.h with appropriate inputs).""" + lines = [] + lines.append("// MusicXML Class Library") + lines.append("// Copyright (c) by Matthew James Briggs") + lines.append("// Distributed under the MIT License") + lines.append("") + lines.append("#pragma once") + lines.append("") + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append(f'#include "mx/core/elements/{attrs_name}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx") + lines.append("{") + lines.append("namespace core") + lines.append("{") + lines.append("") + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + for n in fwd_order: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({n})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + lines.append("") + lines.append(f" /* _________ {header_group_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {header_group_cls}Ptr get{header_group_cls}() const;") + lines.append(f" void set{header_group_cls}(const {header_group_cls}Ptr &value);") + lines.append("") + lines.append(f" /* _________ {child_cls} minOccurs = 1, maxOccurs = unbounded _________ */") + lines.append(f" const {child_cls}Set &get{child_cls}Set() const;") + lines.append(f" void add{child_cls}(const {child_cls}Ptr &value);") + lines.append(f" void remove{child_cls}(const {child_cls}SetIterConst &value);") + lines.append(f" void clear{child_cls}Set();") + lines.append(f" {child_cls}Ptr get{child_cls}(const {child_cls}SetIterConst &setIterator) const;") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {attrs_name}Ptr myAttributes;") + lines.append(f" {header_group_cls}Ptr my{header_group_cls};") + lines.append(f" {child_cls}Set my{child_cls}Set;") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _generate_score_outer_cpp(outer_xml_name, class_name, attrs_name, + header_group_cls, child_cls, inner_xml_name, + header_skip_names, flavor_cfg): + """Emit ScorePartwise.cpp or ScoreTimewise.cpp. + + header_skip_names: list of XML element names (from score-header group) that + the loop silently ``continue``s past because ScoreHeaderGroup already + consumed them. Schema-driven from model.groups['score-header']. + flavor_cfg: per-flavor knobs from SCORE_WRAPPER_FLAVOR_CONFIG. + """ + is_first_var = "isFirst" + pascal(inner_xml_name) + "Added" + lines = [] + lines.append("// MusicXML Class Library") + lines.append("// Copyright (c) by Matthew James Briggs") + lines.append("// Distributed under the MIT License") + lines.append("") + lines.append(f'#include "mx/core/elements/{class_name}.h"') + for inc in flavor_cfg.get("outer_extra_includes", []): + lines.append(f'#include "{inc}"') + lines.append('#include "mx/core/FromXElement.h"') + lines.append(f'#include "mx/core/elements/{child_cls}.h"') + lines.append(f'#include "mx/core/elements/{header_group_cls}.h"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx") + lines.append("{") + lines.append("namespace core") + lines.append("{") + lines.append(f"{class_name}::{class_name}()") + lines.append(f" : myAttributes(std::make_shared<{attrs_name}>()), my{header_group_cls}(make{header_group_cls}()),") + lines.append(f" my{child_cls}Set()") + lines.append("{") + lines.append(f" my{child_cls}Set.push_back(make{child_cls}());") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return myAttributes->hasValues();") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return myAttributes->toStream(os);") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{outer_xml_name}";') + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" os << std::endl;") + lines.append(f" my{header_group_cls}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(f" for (auto x : my{child_cls}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"{header_group_cls}Ptr {class_name}::get{header_group_cls}() const") + lines.append("{") + lines.append(f" return my{header_group_cls};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{header_group_cls}(const {header_group_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{header_group_cls} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"const {child_cls}Set &{class_name}::get{child_cls}Set() const") + lines.append("{") + lines.append(f" return my{child_cls}Set;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::remove{child_cls}(const {child_cls}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{child_cls}Set.cend())") + lines.append(" {") + lines.append(f" if (my{child_cls}Set.size() > 1)") + lines.append(" {") + lines.append(f" my{child_cls}Set.erase(value);") + lines.append(" }") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::add{child_cls}(const {child_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{child_cls}Set.push_back(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::clear{child_cls}Set()") + lines.append("{") + lines.append(f" my{child_cls}Set.clear();") + lines.append(f" my{child_cls}Set.push_back(make{child_cls}());") + lines.append("}") + lines.append("") + lines.append(f"{child_cls}Ptr {class_name}::get{child_cls}(const {child_cls}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{child_cls}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {child_cls}Ptr();") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(f" bool {is_first_var} = false;") + lines.append("") + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + lines.append(f" isSuccess &= my{header_group_cls}->fromXElement(message, xelement);") + lines.append("") + lines.append(" auto it = xelement.begin();") + lines.append(" auto end = xelement.end();") + lines.append("") + if flavor_cfg.get("outer_loop_uses_end_var", False): + lines.append(" for (; it != end; ++it)") + else: + lines.append(" for (; it != xelement.end(); ++it)") + lines.append(" {") + lines.append(" const std::string elementName = it->getName();") + lines.append("") + skip_check = " || ".join(f'elementName == "{n}"' for n in header_skip_names) + lines.append(f" if ({skip_check})") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(f' else if (elementName == "{inner_xml_name}")') + lines.append(" {") + lines.append(f" auto {camel(inner_xml_name)} = make{child_cls}();") + lines.append("") + lines.append(f" if (!{is_first_var} && my{child_cls}Set.size() == 1)") + lines.append(" {") + lines.append(f" *(my{child_cls}Set.begin()) = {camel(inner_xml_name)};") + lines.append(f" {is_first_var} = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" my{child_cls}Set.push_back({camel(inner_xml_name)});") + lines.append(f" {is_first_var} = true;") + lines.append(" }") + lines.append("") + lines.append(f" isSuccess &= {camel(inner_xml_name)}->fromXElement(message, *it);") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}") + lines.append("") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" +def _generate_partwise_part_h(class_name, attrs_name, child_cls): + """Header for PartwisePart: wraps an unbounded set of PartwiseMeasure.""" + lines = [] + lines.append("// MusicXML Class Library") + lines.append("// Copyright (c) by Matthew James Briggs") + lines.append("// Distributed under the MIT License") + lines.append("") + lines.append("#pragma once") + lines.append("") + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append(f'#include "mx/core/elements/{attrs_name}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx") + lines.append("{") + lines.append("namespace core") + lines.append("{") + lines.append("") + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + # Forward declares ordered: PartwiseMeasure < PartwisePart. + for n in sorted([child_cls, class_name]): + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({n})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + lines.append("") + lines.append(f" /* _________ {child_cls} minOccurs = 0, maxOccurs = unbounded _________ */") + lines.append(f" const {child_cls}Set &get{child_cls}Set() const;") + lines.append(f" void add{child_cls}(const {child_cls}Ptr &value);") + lines.append(f" void remove{child_cls}(const {child_cls}SetIterConst &value);") + lines.append(f" void clear{child_cls}Set();") + lines.append(f" {child_cls}Ptr get{child_cls}(const {child_cls}SetIterConst &setIterator) const;") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {attrs_name}Ptr myAttributes;") + lines.append(f" {child_cls}Set my{child_cls}Set;") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _generate_partwise_part_cpp(outer_xml_name, class_name, attrs_name, + child_cls, child_xml_name, flavor_cfg): + """Cpp for the set holder (PartwisePart / TimewiseMeasure). + + outer_xml_name is the XSD name (typically 'part' for partwise, + 'measure' for timewise) used in streamName. + flavor_cfg: per-flavor knobs from SCORE_WRAPPER_FLAVOR_CONFIG. + """ + is_first_var = flavor_cfg.get("set_holder_first_flag_name", "isFirstAdded") + if flavor_cfg.get("set_holder_child_var_source", "xml_name") == "class_name": + child_var = pascal_to_camel(child_cls) + else: + child_var = camel(child_xml_name) + lines = [] + lines.append("// MusicXML Class Library") + lines.append("// Copyright (c) by Matthew James Briggs") + lines.append("// Distributed under the MIT License") + lines.append("") + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + lines.append(f'#include "mx/core/elements/{child_cls}.h"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx") + lines.append("{") + lines.append("namespace core") + lines.append("{") + lines.append(f"{class_name}::{class_name}() : myAttributes(std::make_shared<{attrs_name}>()), my{child_cls}Set()") + lines.append("{") + lines.append(f" my{child_cls}Set.push_back(make{child_cls}());") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return myAttributes->hasValues();") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return myAttributes->toStream(os);") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{outer_xml_name}";') + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" if (hasContents())") + lines.append(" {") + lines.append(f" for (auto x : my{child_cls}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" isOneLineOnly = true;") + lines.append(" }") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"const {child_cls}Set &{class_name}::get{child_cls}Set() const") + lines.append("{") + lines.append(f" return my{child_cls}Set;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::add{child_cls}(const {child_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{child_cls}Set.push_back(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::remove{child_cls}(const {child_cls}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{child_cls}Set.cend())") + lines.append(" {") + if flavor_cfg.get("set_holder_remove_has_size_guard", True): + lines.append(f" if (my{child_cls}Set.size() > 1)") + lines.append(" {") + lines.append(f" my{child_cls}Set.erase(value);") + lines.append(" }") + else: + lines.append(f" my{child_cls}Set.erase(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::clear{child_cls}Set()") + lines.append("{") + lines.append(f" my{child_cls}Set.clear();") + if flavor_cfg.get("set_holder_clear_repushes_default", True): + lines.append(f" my{child_cls}Set.push_back(make{child_cls}());") + lines.append("}") + lines.append("") + lines.append(f"{child_cls}Ptr {class_name}::get{child_cls}(const {child_cls}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{child_cls}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {child_cls}Ptr();") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + if flavor_cfg.get("set_holder_blank_after_first_decl", False): + lines.append("") + lines.append(f" bool {is_first_var} = false;") + lines.append("") + lines.append(" for (auto it = xelement.begin(); it != xelement.end(); ++it)") + lines.append(" {") + if flavor_cfg.get("set_holder_loop_uses_element_name_var", False): + lines.append(" const std::string elementName = it->getName();") + lines.append("") + name_expr = "elementName" + else: + name_expr = "it->getName()" + lines.append(f' if ({name_expr} != "{child_xml_name}")') + lines.append(" {") + unexpected_msg = flavor_cfg.get("set_holder_unexpected_msg", "encountered_quoted") + if unexpected_msg == "trailing_encountered": + msg_line = (f' message << "{class_name}: unexpected element \'"' + f' << {name_expr} << "\' encountered" << std::endl;') + else: # "encountered_quoted" + msg_line = (f' message << "{class_name}: encountered an unexpected element \'"' + f' << {name_expr} << "\'" << std::endl;') + if flavor_cfg.get("set_holder_unexpected_order", "message_first") == "issuccess_first": + lines.append(" isSuccess = false;") + lines.append(msg_line) + else: + lines.append(msg_line) + lines.append(" isSuccess = false;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" auto {child_var} = make{child_cls}();") + if flavor_cfg.get("set_holder_blank_inside_else", False): + lines.append(f" isSuccess &= {child_var}->fromXElement(message, *it);") + lines.append("") + else: + lines.append(f" isSuccess &= {child_var}->fromXElement(message, *it);") + lines.append(f" if (!{is_first_var} && my{child_cls}Set.size() == 1)") + lines.append(" {") + if flavor_cfg.get("set_holder_begin_deref_parens", False): + lines.append(f" *(my{child_cls}Set.begin()) = {child_var};") + else: + lines.append(f" *my{child_cls}Set.begin() = {child_var};") + lines.append(f" {is_first_var} = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" my{child_cls}Set.push_back({child_var});") + lines.append(f" {is_first_var} = true;") + lines.append(" }") + lines.append(" }") + lines.append(" }") + lines.append("") + if flavor_cfg.get("set_holder_post_loop_required", False): + lines.append(f" if (!{is_first_var})") + lines.append(" {") + lines.append(f' message << "{class_name}: no \'{child_xml_name}\' elements were found";') + lines.append(" isSuccess = false;") + lines.append(" }") + lines.append("") + if flavor_cfg.get("set_holder_use_return_macro", True): + lines.append(" MX_RETURN_IS_SUCCESS;") + else: + lines.append(" return isSuccess;") + lines.append("}") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" +def _generate_partwise_measure_h(class_name, attrs_name, music_data_group_cls, + flavor_cfg): + """Header for the music-data holder (PartwiseMeasure / TimewisePart). + + flavor_cfg.music_data_holder_attrs_jit controls whether the attrs member + is JIT-allocated under MX_MUTEX (partwise) or eagerly held (timewise). + """ + lines = [] + lines.append("// MusicXML Class Library") + lines.append("// Copyright (c) by Matthew James Briggs") + lines.append("// Distributed under the MIT License") + lines.append("") + lines.append("#pragma once") + lines.append("") + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append(f'#include "mx/core/elements/{attrs_name}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx") + lines.append("{") + lines.append("namespace core") + lines.append("{") + lines.append("") + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + for n in sorted([music_data_group_cls, class_name]): + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({n})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + lines.append("") + lines.append(f" /* _________ {music_data_group_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {music_data_group_cls}Ptr get{music_data_group_cls}() const;") + lines.append(f" void set{music_data_group_cls}(const {music_data_group_cls}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + if flavor_cfg.get("music_data_holder_attrs_jit", True): + lines.append(" MX_MUTEX") + lines.append(f" mutable {attrs_name}Ptr myAttributes;") + else: + lines.append(f" {attrs_name}Ptr myAttributes;") + lines.append(f" {music_data_group_cls}Ptr my{music_data_group_cls};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _generate_partwise_measure_cpp(outer_xml_name, class_name, attrs_name, + music_data_group_cls, music_data_choices, + flavor_cfg): + """Cpp for the music-data holder (PartwiseMeasure / TimewisePart). + + music_data_choices: list of dicts each with: + xml_name - XML element name ('note', 'attributes', ...) + class_name - C++ class name ('Note', 'Properties', ...) + enum_name - Choice enum name ('note', 'properties', ...) + flavor_cfg: per-flavor knobs from SCORE_WRAPPER_FLAVOR_CONFIG. + """ + attrs_jit = flavor_cfg.get("music_data_holder_attrs_jit", True) + debug_throw = flavor_cfg.get("music_data_holder_debug_throw", True) + # JIT flavor (partwise) refers to attrs via getAttributes() everywhere + # to trigger the lazy allocation; eager flavor (timewise) accesses + # myAttributes directly because it is initialized in the ctor. + attrs_ref = "getAttributes()" if attrs_jit else "myAttributes" + # Includes: alphabetical list of music-data choice classes, plus + # MusicDataChoice and MusicDataGroup. + choice_class_includes = sorted({c["class_name"] for c in music_data_choices}) + extra = sorted({"MusicDataChoice", music_data_group_cls}) + + lines = [] + lines.append("// MusicXML Class Library") + lines.append("// Copyright (c) by Matthew James Briggs") + lines.append("// Distributed under the MIT License") + lines.append("") + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + # The committed PartwiseMeasure.cpp has #includes sorted alphabetically + # for each choice class then MusicDataChoice then MusicDataGroup then Note + # (because Note alphabetically falls between MusicDataGroup and Print). + # We compose the full sorted set: + all_includes = sorted(set(choice_class_includes) | set(extra)) + for n in all_includes: + lines.append(f'#include "mx/core/elements/{n}.h"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx") + lines.append("{") + lines.append("namespace core") + lines.append("{") + if attrs_jit: + ctor_init = f"myAttributes(nullptr)" + else: + ctor_init = f"myAttributes(std::make_shared<{attrs_name}>())" + lines.append(f"{class_name}::{class_name}() : {ctor_init}, my{music_data_group_cls}(make{music_data_group_cls}())") + lines.append("{") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(f" return {attrs_ref}->hasValues();") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(f" return {attrs_ref}->toStream(os);") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(f' os << "{outer_xml_name}";') + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(f" return my{music_data_group_cls}->hasContents();") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" if (hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" my{music_data_group_cls}->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" isOneLineOnly = true;") + lines.append(" }") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + if attrs_jit: + lines.append(" MX_LOCK") + lines.append(f" MX_JIT_ALLOCATE_ATTRIBUTES({attrs_name});") + lines.append(" return myAttributes;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"{music_data_group_cls}Ptr {class_name}::get{music_data_group_cls}() const") + lines.append("{") + lines.append(f" return my{music_data_group_cls};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{music_data_group_cls}(const {music_data_group_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{music_data_group_cls} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(" bool isFirstMusicDataChoiceAdded = false;") + lines.append(f" isSuccess &= {attrs_ref}->fromXElement(message, xelement);") + lines.append("") + lines.append(" for (auto it = xelement.begin(); it != xelement.end(); ++it)") + lines.append(" {") + lines.append(" const std::string elementName = it->getName();") + lines.append("") + lines.append(" auto choiceObject = makeMusicDataChoice();") + lines.append(" bool choiceObjectShouldBeAdded = true;") + lines.append("") + for i, c in enumerate(music_data_choices): + prefix = "if" if i == 0 else "else if" + lines.append(f' {prefix} (elementName == "{c["xml_name"]}")') + lines.append(" {") + lines.append(f" choiceObject->setChoice(MusicDataChoice::Choice::{c['enum_name']});") + lines.append(f" isSuccess &= choiceObject->get{c['class_name']}()->fromXElement(message, *it);") + if debug_throw: + lines.append(" MX_DEBUG_THROW_ON_PARSE_ISSUE;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" choiceObjectShouldBeAdded = false;") + lines.append(" }") + lines.append("") + lines.append(" if (choiceObjectShouldBeAdded)") + lines.append(" {") + lines.append(f" if (!isFirstMusicDataChoiceAdded && my{music_data_group_cls}->getMusicDataChoiceSet().size() == 1)") + lines.append(" {") + lines.append(f" my{music_data_group_cls}->addMusicDataChoice(choiceObject);") + lines.append(f" my{music_data_group_cls}->removeMusicDataChoice(my{music_data_group_cls}->getMusicDataChoiceSet().cbegin());") + lines.append(" isFirstMusicDataChoiceAdded = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" my{music_data_group_cls}->addMusicDataChoice(choiceObject);") + lines.append(" isFirstMusicDataChoiceAdded = true;") + lines.append(" }") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}") + lines.append("") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def _emit_score_wrapper_family(elem_name, elem, ct, model, generated_attrs, stats): + """Shared bespoke handler for the score-partwise / score-timewise family. + + Emits the three classes that make up the wrapper (outer, set holder, + music-data holder) plus their attribute structs from a single + schema-driven structural walk of the XSD. Per-flavor hand-written + variations are pulled from SCORE_WRAPPER_FLAVOR_CONFIG[elem_name]. + """ + flavor_cfg = SCORE_WRAPPER_FLAVOR_CONFIG.get(elem_name, {}) + s = _extract_score_wrapper_structure(model, elem_name) + + flavor = _score_flavor_prefix(elem_name) # "Partwise" / "Timewise" + outer_class = element_class_name(elem_name) # "ScorePartwise" / "ScoreTimewise" + inner_class = flavor + pascal(s["inner_name"]) # "PartwisePart" / "TimewiseMeasure" + deepest_class = flavor + pascal(s["deepest_name"]) # "PartwiseMeasure" / "TimewisePart" + header_group_cls = pascal(s["header_group"]) + "Group" # "ScoreHeaderGroup" + music_data_group_cls = pascal(s["deepest_content_group"]) + "Group" # "MusicDataGroup" + + # Attribute struct names follow the standard convention based on the + # underlying element XML name; resolve_attrs_name returns: + # "score-partwise" -> "ScorePartwiseAttributes" + # "part" -> "PartAttributes" + # "measure" -> "MeasureAttributes" + outer_attrs_name = resolve_attrs_name(elem_name, "", model) + inner_attrs_name = resolve_attrs_name(s["inner_name"], "", model) + deepest_attrs_name = resolve_attrs_name(s["deepest_name"], "", model) + + # 1. Outer attrs struct (document-attributes -> ScorePartwiseAttributes). + if ct.attributes: + if outer_attrs_name not in generated_attrs and outer_attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(outer_attrs_name, ct.attributes, model) + c = generate_attrs_cpp(outer_attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{outer_attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{outer_attrs_name}.cpp"), c) + generated_attrs.add(outer_attrs_name) + stats["attrs_written"] += 1 + + # 2. Inner attrs struct (part-attributes -> PartAttributes). + inner_attrs_list = model.attribute_groups.get(s["inner_attr_group"], []) + if inner_attrs_list: + if inner_attrs_name not in generated_attrs and inner_attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(inner_attrs_name, inner_attrs_list, model) + c = generate_attrs_cpp(inner_attrs_name, inner_attrs_list, model) + write_file(os.path.join(ELEM_DIR, f"{inner_attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{inner_attrs_name}.cpp"), c) + generated_attrs.add(inner_attrs_name) + stats["attrs_written"] += 1 + + # 3. Deepest attrs struct (measure-attributes -> MeasureAttributes). + deepest_attrs_list = model.attribute_groups.get(s["deepest_attr_group"], []) + if deepest_attrs_list: + if deepest_attrs_name not in generated_attrs and deepest_attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(deepest_attrs_name, deepest_attrs_list, model) + c = generate_attrs_cpp(deepest_attrs_name, deepest_attrs_list, model) + write_file(os.path.join(ELEM_DIR, f"{deepest_attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{deepest_attrs_name}.cpp"), c) + generated_attrs.add(deepest_attrs_name) + stats["attrs_written"] += 1 + + # 4. ScorePartwise.h / .cpp + # Forward declares in ScorePartwise.h come out as + # [PartwisePart, ScoreHeaderGroup, ScorePartwise] + # which is sorted lexicographically. + fwd_order = sorted([inner_class, header_group_cls, outer_class]) + # The score-header group's child element XML names are the children that + # the outer fromXElementImpl should silently 'continue' past (they were + # already consumed by ScoreHeaderGroup). Schema-driven from model.groups. + header_skip_names = [c.element_name for c in model.groups.get(s["header_group"], [])] + h = _generate_score_outer_h(outer_class, outer_attrs_name, + header_group_cls, inner_class, fwd_order) + c = _generate_score_outer_cpp(elem_name, outer_class, outer_attrs_name, + header_group_cls, inner_class, + s["inner_name"], header_skip_names, + flavor_cfg) + write_file(os.path.join(ELEM_DIR, f"{outer_class}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{outer_class}.cpp"), c) + + # 5. PartwisePart.h / .cpp + h = _generate_partwise_part_h(inner_class, inner_attrs_name, deepest_class) + c = _generate_partwise_part_cpp(s["inner_name"], inner_class, + inner_attrs_name, deepest_class, + s["deepest_name"], flavor_cfg) + write_file(os.path.join(ELEM_DIR, f"{inner_class}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{inner_class}.cpp"), c) + + # 6. PartwiseMeasure.h / .cpp + # Build the MusicDataChoice schema-driven from model.groups["music-data"]. + # The music-data group is an xs:choice; _parse_groups would store the + # choice children directly. Walk model.root to be safe. + md_group = s["deepest_content_group"] # "music-data" + music_data_choices = [] + for group_node in model.root.iter(f"{XS}group"): + if group_node.get("name") == md_group: + choice_node = None + seq_node = group_node.find(f"{XS}sequence") + if seq_node is not None: + choice_node = seq_node.find(f"{XS}choice") + if choice_node is None: + choice_node = group_node.find(f"{XS}choice") + if choice_node is None: + raise RuntimeError(f"group '{md_group}' has no xs:choice") + for el in choice_node.findall(f"{XS}element"): + xml_n = el.get("name") or el.get("ref") + cls = element_class_name(xml_n) + enum_n = pascal_to_camel(cls) + music_data_choices.append({ + "xml_name": xml_n, + "class_name": cls, + "enum_name": enum_n, + }) + break + + h = _generate_partwise_measure_h(deepest_class, deepest_attrs_name, + music_data_group_cls, flavor_cfg) + c = _generate_partwise_measure_cpp(s["deepest_name"], deepest_class, + deepest_attrs_name, + music_data_group_cls, + music_data_choices, + flavor_cfg) + write_file(os.path.join(ELEM_DIR, f"{deepest_class}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{deepest_class}.cpp"), c) + + stats["elem_written"] += 1 + stats["bespoke_written"] = stats.get("bespoke_written", 0) + 1 + + +# --------------------------------------------------------------------------- +# Note (bespoke, schema-driven) +# +# note's complex type is structurally unique. It has: +# +# sequence: +# choice: -> NoteChoice (synthetic) +# sequence(grace, full-note group, tie*) -> GraceNoteGroup (synthetic) +# sequence(cue, full-note group, duration) -> CueNoteGroup (synthetic) +# sequence(full-note group, duration, tie*)-> NormalNoteGroup(synthetic) +# <16 trailing children: instrument? editorial-voice group type? dot* ...> +# +# The full-note group is itself complex: +# +# sequence: +# chord? +# choice(pitch | unpitched | rest) -> FullNoteTypeChoice (synth) +# +# Note also has a 19-attribute NoteAttributes struct. +# +# The committed code uses MX_MUTEX / MX_LOCK / MX_JIT_ALLOCATE in Note (the +# only top-level element with lazy allocation) and in FullNoteGroup + +# FullNoteTypeChoice. The branch groups (Grace/Cue/Normal) eagerly construct +# all members. +# +# Note::fromXElementImpl is a hand-written state machine with three private +# helpers: parseNoteChoice, parseFullNoteGroup, parseEditorialVoiceGroup. All +# six synthetic helper classes use MX_FROM_XELEMENT_UNUSED -- Note owns the +# parse entirely. +# +# Schema-driven inputs (everything that should propagate when the XSD changes): +# - The 3 outer-choice branches and their member sequences (model.elements, +# content_tree). +# - The trailing children (instrument, type, dot, ..., play). +# - The full-note group's inner choice members (pitch/unpitched/rest) and +# pre-choice element (chord). +# - The editorial-voice group's members (footnote/level/voice). +# - The duration group's single element name. +# - The staff group's single element name. +# - The note attributes (model.complex_types['note'].attributes). +# +# Hand-naming decisions (not in the XSD): +# - The choice class name is 'NoteChoice'. +# - The third (no-leading-element) branch's group class is 'NormalNoteGroup' +# and its enum variant is 'normal'. Stored as NOTE_THIRD_BRANCH_*. +# - The full-note inner choice class is 'FullNoteTypeChoice'. + + +# Naming decision for the third outer-choice branch (the one without a +# leading singleton element ref). The XSD does not give this branch a name; +# the original codegen called it 'Normal'. +NOTE_THIRD_BRANCH_CLASS = "NormalNoteGroup" +NOTE_THIRD_BRANCH_ENUM = "normal" + + +def _extract_note_structure(ct, model): + """Return a structured dict describing note's content tree. + + Returns: + { + "branches": [BranchInfo, BranchInfo, BranchInfo], + "trailing": [(kind, name, min, max), ...], # outer-sequence trailing + "full_note": { + "pre": ["chord"], # element refs before the inner choice + "choice_branches": ["pitch", "unpitched", "rest"], + }, + "editorial_voice": ["footnote", "level", "voice"], + "duration": "duration", + "staff": "staff", + } + + BranchInfo is a dict: + { + "class_name": "GraceNoteGroup", + "enum_name": "grace", + "leading_element": "grace" or None, # the singleton element name + "members": [(kind, name, min, max), ...] # all members in order + "tie_max": int or 0, + "has_full_note": bool, + "has_duration": bool, + } + """ + seq = ct.content_tree + if not isinstance(seq, SequenceNode): + raise RuntimeError(f"note: expected outer SequenceNode, got {type(seq).__name__}") + if not seq.children or not isinstance(seq.children[0], ChoiceNode): + raise RuntimeError("note: first child of outer sequence must be a ChoiceNode") + + outer_choice = seq.children[0] + + branches = [] + for branch in outer_choice.branches: + if not isinstance(branch, SequenceNode): + raise RuntimeError( + f"note: outer choice branch must be SequenceNode, got {type(branch).__name__}") + members = [] + leading_element = None + tie_max = 0 + has_full_note = False + has_duration = False + for c in branch.children: + if isinstance(c, ElementRefNode): + members.append(("element", c.element_name, c.min_occurs, c.max_occurs)) + if leading_element is None and not members[:-1]: + leading_element = c.element_name + if c.element_name == "tie": + tie_max = c.max_occurs + elif isinstance(c, GroupRefNode): + members.append(("group", c.group_name, c.min_occurs, c.max_occurs)) + if c.group_name == "full-note": + has_full_note = True + elif c.group_name == "duration": + has_duration = True + + if leading_element == "grace": + cls = "GraceNoteGroup" + enum_n = "grace" + elif leading_element == "cue": + cls = "CueNoteGroup" + enum_n = "cue" + else: + cls = NOTE_THIRD_BRANCH_CLASS + enum_n = NOTE_THIRD_BRANCH_ENUM + branches.append({ + "class_name": cls, + "enum_name": enum_n, + "leading_element": leading_element, + "members": members, + "tie_max": tie_max, + "has_full_note": has_full_note, + "has_duration": has_duration, + }) + + trailing = [] + for c in seq.children[1:]: + if isinstance(c, ElementRefNode): + trailing.append(("element", c.element_name, c.min_occurs, c.max_occurs)) + elif isinstance(c, GroupRefNode): + trailing.append(("group", c.group_name, c.min_occurs, c.max_occurs)) + + # full-note group: walk model.root because _parse_groups drops the inner choice. + fn_pre = [] + fn_choice = [] + for g in model.root.iter(f"{XS}group"): + if g.get("name") != "full-note": + continue + sq = g.find(f"{XS}sequence") + if sq is None: + raise RuntimeError("full-note group missing xs:sequence") + for child in sq: + tag = child.tag.split("}")[-1] + if tag == "element": + fn_pre.append(child.get("name") or child.get("ref")) + elif tag == "choice": + for el in child.findall(f"{XS}element"): + fn_choice.append(el.get("name") or el.get("ref")) + break + + ev = [c.element_name for c in model.groups.get("editorial-voice", [])] + dur_elems = [c.element_name for c in model.groups.get("duration", [])] + staff_elems = [c.element_name for c in model.groups.get("staff", [])] + + return { + "branches": branches, + "trailing": trailing, + "full_note": {"pre": fn_pre, "choice_branches": fn_choice}, + "editorial_voice": ev, + "duration": dur_elems[0] if dur_elems else "duration", + "staff": staff_elems[0] if staff_elems else "staff", + } + + +# --------------------------------------------------------------------------- +# FullNoteTypeChoice (synthetic choice over pitch | unpitched | rest) +# --------------------------------------------------------------------------- + +def generate_full_note_type_choice_h(class_name, branch_names): + """branch_names: ordered list of XSD element names, e.g. ['pitch','unpitched','rest']""" + branch_classes = [pascal(b) for b in branch_names] + fwds = sorted(set(branch_classes)) + [class_name] + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for f in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + for i, b in enumerate(branch_names): + comma = "," if i < len(branch_names) - 1 else "" + lines.append(f" {camel(b)} = {i + 1}{comma}") + lines.append(" };") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append("") + lines.append(" /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {class_name}::Choice getChoice() const;") + lines.append(f" void setChoice(const {class_name}::Choice value);") + for b, bc in zip(branch_names, branch_classes): + lines.append("") + lines.append(f" /* _________ {bc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {bc}Ptr get{bc}() const;") + lines.append(f" void set{bc}(const {bc}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" MX_MUTEX") + lines.append(" Choice myChoice;") + for bc in branch_classes: + lines.append(f" mutable {bc}Ptr my{bc};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_full_note_type_choice_cpp(class_name, branch_names): + branch_classes = [pascal(b) for b in branch_names] + includes = sorted(set([f"mx/core/elements/{bc}.h" for bc in branch_classes])) + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in includes: + lines.append(f'#include "{inc}"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{") + # Constructor + inits = [f"myChoice(Choice::{camel(branch_names[0])})"] + for bc in branch_classes: + inits.append(f"my{bc}(make{bc}())") + lines.append(f"{class_name}::{class_name}()") + lines.append(f" : {', '.join(inits)}") + lines.append("{") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append("") + lines.append(" MX_UNUSED(indentLevel);") + lines.append(" MX_UNUSED(isOneLineOnly);") + lines.append("") + lines.append(" switch (myChoice)") + lines.append(" {") + for b, bc in zip(branch_names, branch_classes): + lines.append(f" case Choice::{camel(b)}:") + lines.append(f" get{bc}()->toStream(os, indentLevel);") + lines.append(" break;") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"{class_name}::Choice {class_name}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setChoice(const {class_name}::Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}") + for bc in branch_classes: + lines.append("") + lines.append(f"{bc}Ptr {class_name}::get{bc}() const") + lines.append("{") + lines.append(" MX_LOCK") + lines.append(f" MX_JIT_ALLOCATE({bc});") + lines.append(f" return my{bc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{bc}(const {bc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{bc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"MX_FROM_XELEMENT_UNUSED({class_name});") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# FullNoteGroup (chord? + FullNoteTypeChoice) +# --------------------------------------------------------------------------- + +def generate_full_note_group_h(class_name, pre_elems, inner_choice_cls): + """pre_elems: list of element names before the inner choice (e.g. ['chord'])""" + fwds = sorted(set([pascal(e) for e in pre_elems] + [inner_choice_cls])) + [class_name] + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for f in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + for e in pre_elems: + ec = pascal(e) + lines.append("") + lines.append(f" /* _________ {ec} minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(f" {ec}Ptr get{ec}() const;") + lines.append(f" void set{ec}(const {ec}Ptr &value);") + lines.append(f" bool getHas{ec}() const;") + lines.append(f" void setHas{ec}(const bool value);") + lines.append("") + lines.append(f" /* _________ {inner_choice_cls} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {inner_choice_cls}Ptr get{inner_choice_cls}() const;") + lines.append(f" void set{inner_choice_cls}(const {inner_choice_cls}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" MX_MUTEX") + for e in pre_elems: + ec = pascal(e) + lines.append(f" {ec}Ptr my{ec};") + lines.append(f" bool myHas{ec};") + lines.append(f" mutable {inner_choice_cls}Ptr my{inner_choice_cls};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_full_note_group_cpp(class_name, pre_elems, inner_choice_cls): + includes = sorted(set([f"mx/core/elements/{pascal(e)}.h" for e in pre_elems] + + [f"mx/core/elements/{inner_choice_cls}.h"])) + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in includes: + lines.append(f'#include "{inc}"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{") + inits = [] + for e in pre_elems: + ec = pascal(e) + inits.append(f"my{ec}(make{ec}())") + inits.append(f"myHas{ec}(false)") + inits.append(f"my{inner_choice_cls}(nullptr)") + lines.append(f"{class_name}::{class_name}() : {', '.join(inits)}") + lines.append("{") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + for e in pre_elems: + ec = pascal(e) + lines.append(f" if (myHas{ec})") + lines.append(" {") + lines.append(f" my{ec}->toStream(os, indentLevel);") + lines.append(" os << std::endl;") + lines.append(" }") + lines.append(f" get{inner_choice_cls}()->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}") + for e in pre_elems: + ec = pascal(e) + lines.append("") + lines.append(f"{ec}Ptr {class_name}::get{ec}() const") + lines.append("{") + lines.append(f" return my{ec};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{ec}(const {ec}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{ec} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::getHas{ec}() const") + lines.append("{") + lines.append(f" return myHas{ec};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setHas{ec}(const bool value)") + lines.append("{") + lines.append(f" myHas{ec} = value;") + lines.append("}") + lines.append("") + lines.append(f"{inner_choice_cls}Ptr {class_name}::get{inner_choice_cls}() const") + lines.append("{") + lines.append(" MX_LOCK") + lines.append(f" MX_JIT_ALLOCATE({inner_choice_cls});") + lines.append(f" return my{inner_choice_cls};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{inner_choice_cls}(const {inner_choice_cls}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{inner_choice_cls} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"MX_FROM_XELEMENT_UNUSED({class_name});") + lines.append("") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Outer-choice branch group (Grace/Cue/Normal) +# Each branch is a sequence of element/group refs. The committed branch +# classes share a uniform shape: each member is either a singleton element +# (chord-like: get/set/has) or a set (Tie, max=2 with size cap). +# --------------------------------------------------------------------------- + +def generate_outer_branch_group_h(class_name, members, full_note_cls): + """members: list of (kind, name, min, max). + 'group' members with name 'full-note' refer to FullNoteGroup; + 'group' members with name 'duration' refer to a single Duration element.""" + fwds = set() + decls = [] # tuple-of-decl-lines for ordered member emission + privates = [] + for kind, name, mn, mx in members: + if kind == "group" and name == "full-note": + fwds.add(full_note_cls) + decls.append([ + "", + f" /* _________ {full_note_cls} minOccurs = 1, maxOccurs = 1 _________ */", + f" {full_note_cls}Ptr get{full_note_cls}() const;", + f" void set{full_note_cls}(const {full_note_cls}Ptr &value);", + ]) + privates.append(f" {full_note_cls}Ptr my{full_note_cls};") + elif kind == "group" and name == "duration": + cc = "Duration" + fwds.add(cc) + decls.append([ + "", + f" /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */", + f" {cc}Ptr get{cc}() const;", + f" void set{cc}(const {cc}Ptr &value);", + ]) + privates.append(f" {cc}Ptr my{cc};") + elif kind == "element": + cc = pascal(name) + fwds.add(cc) + if mx != 1: + # Set member (e.g. tie maxOccurs=2) + mx_str = "unbounded" if mx == -1 else str(mx) + decls.append([ + "", + f" /* _________ {cc} minOccurs = {mn}, maxOccurs = {mx_str} _________ */", + f" const {cc}Set &get{cc}Set() const;", + f" void add{cc}(const {cc}Ptr &value);", + f" void remove{cc}(const {cc}SetIterConst &value);", + f" void clear{cc}Set();", + f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;", + ]) + privates.append(f" {cc}Set my{cc}Set;") + elif mn == 0: + decls.append([ + "", + f" /* _________ {cc} minOccurs = 0, maxOccurs = 1 _________ */", + f" {cc}Ptr get{cc}() const;", + f" void set{cc}(const {cc}Ptr &value);", + f" bool getHas{cc}() const;", + f" void setHas{cc}(const bool value);", + ]) + privates.append(f" {cc}Ptr my{cc};") + privates.append(f" bool myHas{cc};") + else: + decls.append([ + "", + f" /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */", + f" {cc}Ptr get{cc}() const;", + f" void set{cc}(const {cc}Ptr &value);", + ]) + privates.append(f" {cc}Ptr my{cc};") + + fwd_list = sorted(fwds) + [class_name] + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for f in fwd_list: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + for d in decls: + for ln in d: + lines.append(ln) + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + for p in privates: + lines.append(p) + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_outer_branch_group_cpp(class_name, members, full_note_cls): + """Stream contents: walk members in order. Singletons stream directly + (no os<") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{") + + # Constructor inits + inits = [] + for kind, name, mn, mx in members: + if kind == "group" and name == "full-note": + inits.append(f"my{full_note_cls}(make{full_note_cls}())") + elif kind == "group" and name == "duration": + inits.append("myDuration(makeDuration())") + elif kind == "element": + cc = pascal(name) + if mx != 1: + inits.append(f"my{cc}Set()") + elif mn == 0: + inits.append(f"my{cc}(make{cc}())") + inits.append(f"myHas{cc}(false)") + else: + inits.append(f"my{cc}(make{cc}())") + lines.append(f"{class_name}::{class_name}() : {', '.join(inits)}") + lines.append("{") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + # Walk members in order, producing the committed streamContents pattern. + has_set = False + for i, (kind, name, mn, mx) in enumerate(members): + is_first = (i == 0) + if kind == "group" and name == "full-note": + # FullNoteGroup -> streamContents (not toStream) because it's a synthetic group + if not is_first: + lines.append(" os << std::endl;") + lines.append(f" my{full_note_cls}->streamContents(os, indentLevel, isOneLineOnly);") + else: + lines.append(f" my{full_note_cls}->streamContents(os, indentLevel, isOneLineOnly);") + elif kind == "group" and name == "duration": + if not is_first: + lines.append(" os << std::endl;") + lines.append(" myDuration->toStream(os, indentLevel);") + elif kind == "element": + cc = pascal(name) + if mx != 1: + # Set + has_set = True + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel);") + lines.append(" }") + else: + if not is_first: + lines.append(" os << std::endl;") + lines.append(f" my{cc}->toStream(os, indentLevel);") + # The committed Grace/Cue cases emit os< 0 else None + lines.append("") + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + if cap is not None: + lines.append(f" if (my{cc}Set.size() < {cap})") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + else: + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}") + lines.append("") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}") + elif mn == 0: + lines.append("") + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}") + else: + lines.append("") + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + + lines.append("") + lines.append(f"MX_FROM_XELEMENT_UNUSED({class_name});") + lines.append("") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# NoteChoice (3-branch choice over the synthetic outer-branch groups) +# Members are eagerly constructed (not JIT). Mirrors committed. +# --------------------------------------------------------------------------- + +def generate_note_choice_h(class_name, branches): + """branches: list of dicts (from _extract_note_structure) with class_name + enum_name.""" + branch_classes = [b["class_name"] for b in branches] + fwds = sorted(set(branch_classes)) + [class_name] + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + for f in fwds: + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(" enum class Choice") + lines.append(" {") + for i, b in enumerate(branches): + comma = "," if i < len(branches) - 1 else "" + lines.append(f" {b['enum_name']} = {i + 1}{comma}") + lines.append(" };") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append("") + lines.append(" /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {class_name}::Choice getChoice() const;") + lines.append(f" void setChoice(const {class_name}::Choice value);") + for b in branches: + bc = b["class_name"] + lines.append("") + lines.append(f" /* _________ {bc} minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(f" {bc}Ptr get{bc}() const;") + lines.append(f" void set{bc}(const {bc}Ptr &value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" Choice myChoice;") + for bc in branch_classes: + lines.append(f" {bc}Ptr my{bc};") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_note_choice_cpp(class_name, branches): + branch_classes = [b["class_name"] for b in branches] + incs = sorted(set(branch_classes)) + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in incs: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{") + # Default choice is the LAST branch (the no-leading-element branch). + # The committed code defaults to Choice::normal which is branch index 2. + default_branch = branches[-1] + inits = [f"myChoice(Choice::{default_branch['enum_name']})"] + for bc in branch_classes: + inits.append(f"my{bc}(make{bc}())") + lines.append(f"{class_name}::{class_name}()") + lines.append(f" : {', '.join(inits)}") + lines.append("{") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return false;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" switch (myChoice)") + lines.append(" {") + for b in branches: + lines.append(f" case Choice::{b['enum_name']}:") + lines.append(f" my{b['class_name']}->streamContents(os, indentLevel, isOneLineOnly);") + lines.append(" break;") + lines.append(" default:") + lines.append(" break;") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"{class_name}::Choice {class_name}::getChoice() const") + lines.append("{") + lines.append(" return myChoice;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setChoice(const {class_name}::Choice value)") + lines.append("{") + lines.append(" myChoice = value;") + lines.append("}") + for bc in branch_classes: + lines.append("") + lines.append(f"{bc}Ptr {class_name}::get{bc}() const") + lines.append("{") + lines.append(f" return my{bc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{bc}(const {bc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{bc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"MX_FROM_XELEMENT_UNUSED({class_name});") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Note.h / Note.cpp +# +# Note is a TREE-style flat container, but with: +# - MX_MUTEX + JIT-allocated singleton members (only top-level element with this) +# - A custom fromXElementImpl that dispatches by element name into three +# private parse helpers: parseNoteChoice, parseFullNoteGroup, +# parseEditorialVoiceGroup. +# --------------------------------------------------------------------------- + +def _note_member_info(kind, name, mn, mx): + """Return (display_cls, is_set, is_optional, is_singleton_required, member_name). + + For 'group' members the class is the synthetic group class + (EditorialVoiceGroup, Staff has just 'staff' element so it uses the + Staff element class directly via group_ref mapping). Returns None for + members that should be skipped. + """ + if kind == "element": + cc = pascal(name) + is_set = (mx != 1) + is_optional = (mn == 0 and mx == 1) + return (cc, is_set, is_optional, False, name) + elif kind == "group": + if name == "editorial-voice": + return ("EditorialVoiceGroup", False, False, False, name) + elif name == "staff": + # staff group contains a single 'staff' element. Committed Note + # treats Staff as a singleton optional element directly. + return ("Staff", False, mn == 0, False, "staff") + return None + + +def generate_note_h(class_name, attrs_name, choice_class, trailing, model): + """Generate Note.h. attrs_name is e.g. 'NoteAttributes'. choice_class is + 'NoteChoice'. trailing is the list of (kind,name,mn,mx) from the outer + sequence after the outer choice.""" + fwds = set([attrs_name, choice_class, "FullNoteGroup"]) + member_decls = [] + member_privates = [] + + # First member is always the NoteChoice + member_decls.append([ + "", + f" /* _________ {choice_class} minOccurs = 1, maxOccurs = 1 _________ */", + f" {choice_class}Ptr get{choice_class}() const;", + f" void set{choice_class}(const {choice_class}Ptr &value);", + ]) + member_privates.append([f"mutable {choice_class}Ptr my{choice_class};"]) + + for kind, name, mn, mx in trailing: + info = _note_member_info(kind, name, mn, mx) + if info is None: + continue + cc, is_set, is_optional, _, _ = info + fwds.add(cc) + if is_set: + mx_str = "unbounded" if mx == -1 else str(mx) + member_decls.append([ + "", + f" /* _________ {cc} minOccurs = {mn}, maxOccurs = {mx_str} _________ */", + f" const {cc}Set &get{cc}Set() const;", + f" void add{cc}(const {cc}Ptr &value);", + f" void remove{cc}(const {cc}SetIterConst &value);", + f" void clear{cc}Set();", + f" {cc}Ptr get{cc}(const {cc}SetIterConst &setIterator) const;", + ]) + member_privates.append([f"{cc}Set my{cc}Set;"]) + elif kind == "group" and name == "editorial-voice": + # Required group (treated as min=1 max=1) + member_decls.append([ + "", + f" /* _________ {cc} minOccurs = 1, maxOccurs = 1 _________ */", + f" {cc}Ptr get{cc}() const;", + f" void set{cc}(const {cc}Ptr &value);", + ]) + member_privates.append([f"mutable {cc}Ptr my{cc};"]) + elif is_optional: + member_decls.append([ + "", + f" /* _________ {cc} minOccurs = 0, maxOccurs = 1 _________ */", + f" {cc}Ptr get{cc}() const;", + f" void set{cc}(const {cc}Ptr &value);", + f" bool getHas{cc}() const;", + f" void setHas{cc}(const bool value);", + ]) + member_privates.append([ + f"mutable {cc}Ptr my{cc};", + f"bool myHas{cc};", + ]) + + fwd_list = sorted(fwds) + [class_name] + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append(f'#include "mx/core/elements/{attrs_name}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace ezxml") + lines.append("{") + lines.append("class XElementIterator;") + lines.append("}") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + # Attributes forward declare is a struct, not element + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + for f in fwd_list: + if f == attrs_name: + continue + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({f})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + for d in member_decls: + for ln in d: + lines.append(ln) + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(" MX_MUTEX") + lines.append(f" mutable {attrs_name}Ptr myAttributes;") + for grp in member_privates: + for ln in grp: + lines.append(f" {ln}") + lines.append("") + # The three private parse helpers + lines.append(f" bool parseNoteChoice(std::ostream &message, ::ezxml::XElement ¬eElement, ::ezxml::XElementIterator &iter);") + lines.append("") + lines.append(f" bool parseFullNoteGroup(std::ostream &message, ::ezxml::XElement ¬eElement, ::ezxml::XElementIterator &iter,") + lines.append(" FullNoteGroupPtr &outFullNoteGroup);") + lines.append("") + lines.append(f" bool parseEditorialVoiceGroup(std::ostream &message, ::ezxml::XElement ¬eElement,") + lines.append(" ::ezxml::XElementIterator &iter);") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +def generate_note_cpp(class_name, attrs_name, choice_class, trailing, + full_note_pre, full_note_choice, editorial_voice_members, + branches, duration_elem, model): + """Big one. Generates: + - Includes + - Constructor with init list + - hasAttributes/streamAttributes/streamName/hasContents + - streamContents (walk trailing members in order) + - All getters/setters/etc. + - fromXElementImpl: dispatch by element name (schema-driven) + - parseNoteChoice helper + - parseFullNoteGroup helper + - parseEditorialVoiceGroup helper + """ + + # ---- Compute member info table for trailing ---- + members = [] # ordered list of dicts + for kind, name, mn, mx in trailing: + info = _note_member_info(kind, name, mn, mx) + if info is None: + continue + cc, is_set, is_optional, _, xml_name = info + is_required_group = (kind == "group" and name == "editorial-voice") + members.append({ + "cls": cc, + "xml_name": xml_name, + "is_set": is_set, + "is_optional": is_optional, + "is_group": is_required_group, + "is_unbounded": (mx == -1), + "max_occurs": mx, + "kind": kind, + "group_name": name if kind == "group" else None, + }) + + # ---- Includes ---- + incs = set([attrs_name, choice_class, "FullNoteGroup", "FullNoteTypeChoice"]) + incs |= set([b["class_name"] for b in branches]) + # Include all member class headers + for m in members: + incs.add(m["cls"]) + # Note's fromXElementImpl references the inner-choice element classes + # via the FullNoteGroup parse. Include them so the .cpp can call e.g. + # outFullNoteGroup->getFullNoteTypeChoice()->getPitch()->fromXElement. + for fc in full_note_choice: + incs.add(pascal(fc)) + # FullNoteGroup's pre-elements (chord) also referenced for setHasChord. + for pe in full_note_pre: + incs.add(pascal(pe)) + # Tie element class for the parseNoteChoice helper. + incs.add("Tie") + # Cue / Grace element classes referenced by parseNoteChoice. + for b in branches: + if b["leading_element"]: + incs.add(pascal(b["leading_element"])) + # Duration class referenced in parseNoteChoice helper. + incs.add(pascal(duration_elem)) + # Editorial-voice group's element classes referenced in parseEditorialVoiceGroup. + for evn in editorial_voice_members: + incs.add(pascal(evn)) + + inc_list = sorted(incs) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + for inc in inc_list: + lines.append(f'#include "mx/core/elements/{inc}.h"') + lines.append("#include ") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{") + + # ---- Constructor ---- + # The committed Note ctor inits myAttributes(nullptr), myNoteChoice(nullptr), + # then for each trailing member: ptr(nullptr) for singletons (with myHasX(false) + # if optional), or default ctor for sets, or required-group ptr(nullptr). + inits = ["myAttributes(nullptr)", f"my{choice_class}(nullptr)"] + for m in members: + cc = m["cls"] + if m["is_set"]: + inits.append(f"my{cc}Set()") + elif m["is_group"]: + inits.append(f"my{cc}(nullptr)") + elif m["is_optional"]: + inits.append(f"my{cc}(nullptr)") + inits.append(f"myHas{cc}(false)") + + lines.append(f"{class_name}::{class_name}()") + # Wrap inits across multiple lines like committed (we'll let make fmt + # normalize the line breaking). + lines.append(f" : {', '.join(inits)}") + lines.append("{") + lines.append("}") + lines.append("") + + # hasAttributes / streamAttributes + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return getAttributes()->hasValues();") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return getAttributes()->toStream(os);") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(' os << "note";') + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + + # streamContents -- the committed pattern: + # os << std::endl; (always at start) + # getNoteChoice()->streamContents(...); + # for each trailing member in order: + # - singleton optional: if (myHasX) { os<toStream(os, indentLevel+1); } + # - required group: if (getX()->hasContents()) { os<streamContents(os, indentLevel+1, isOneLineOnly); } + # - set: for (auto x : myXSet) { os<toStream(os, indentLevel+1); } + # isOneLineOnly = false; os << std::endl; return os; + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" os << std::endl;") + lines.append(f" get{choice_class}()->streamContents(os, indentLevel + 1, isOneLineOnly);") + for m in members: + cc = m["cls"] + if m["is_set"]: + lines.append(f" for (auto x : my{cc}Set)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + elif m["is_group"]: + lines.append(f" if (get{cc}()->hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" get{cc}()->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + elif m["is_optional"]: + lines.append(f" if (myHas{cc})") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(f" get{cc}()->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" isOneLineOnly = false;") + lines.append(" os << std::endl;") + lines.append(" return os;") + lines.append("}") + lines.append("") + + # ---- Attribute getters/setters ---- + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" MX_LOCK;") + lines.append(f" MX_JIT_ALLOCATE_ATTRIBUTES({attrs_name});") + lines.append(" return myAttributes;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}") + lines.append("") + + # ---- NoteChoice getter/setter ---- + lines.append(f"{choice_class}Ptr {class_name}::get{choice_class}() const") + lines.append("{") + lines.append(" MX_LOCK;") + lines.append(f" MX_JIT_ALLOCATE({choice_class});") + lines.append(f" return my{choice_class};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{choice_class}(const {choice_class}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{choice_class} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + + # ---- Trailing member getters/setters ---- + for m in members: + cc = m["cls"] + if m["is_set"]: + mx = m["max_occurs"] + lines.append(f"const {cc}Set &{class_name}::get{cc}Set() const") + lines.append("{") + lines.append(f" return my{cc}Set;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::remove{cc}(const {cc}SetIterConst &value)") + lines.append("{") + lines.append(f" if (value != my{cc}Set.cend())") + lines.append(" {") + lines.append(f" my{cc}Set.erase(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::add{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + if mx > 0: + lines.append(f" if (my{cc}Set.size() < {mx})") + lines.append(" {") + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + else: + lines.append(f" my{cc}Set.push_back(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::clear{cc}Set()") + lines.append("{") + lines.append(f" my{cc}Set.clear();") + lines.append("}") + lines.append("") + lines.append(f"{cc}Ptr {class_name}::get{cc}(const {cc}SetIterConst &setIterator) const") + lines.append("{") + lines.append(f" if (setIterator != my{cc}Set.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(f" return {cc}Ptr();") + lines.append("}") + lines.append("") + elif m["is_group"]: + # JIT-allocated group + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(" MX_LOCK;") + lines.append(f" MX_JIT_ALLOCATE({cc});") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + elif m["is_optional"]: + lines.append(f"{cc}Ptr {class_name}::get{cc}() const") + lines.append("{") + lines.append(" MX_LOCK;") + lines.append(f" MX_JIT_ALLOCATE({cc});") + lines.append(f" return my{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cc}(const {cc}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" my{cc} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::getHas{cc}() const") + lines.append("{") + lines.append(f" return myHas{cc};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setHas{cc}(const bool value)") + lines.append("{") + lines.append(f" myHas{cc} = value;") + lines.append("}") + lines.append("") + + # ============================================================ + # fromXElementImpl + # ============================================================ + # Build the dispatch list -- schema-driven from: + # - the outer choice branches' "starter" element names (the elements + # that signal "this is a NoteChoice" iteration): the leading element + # of each branch (grace/cue) PLUS the elements that introduce the + # full-note group (chord + full-note-choice members) -- collectively + # the set of "note-choice elements". + # - editorial-voice group member names + # - trailing element names (instrument, type, dot, ...) + # + # The dispatch ordering matches the committed code's hand-written order. + # In committed: pitch | unpitched | rest | chord | grace | cue (note-choice) + # Then trailing in declared XSD order: + # instrument, editorial-voice, type, dot, accidental, time-modification, + # stem, notehead, notehead-text, staff, beam, notations, lyric, play. + + note_choice_elements = [] # element XML names that trigger parseNoteChoice + note_choice_elements.extend(full_note_choice) # pitch, unpitched, rest + note_choice_elements.extend(full_note_pre) # chord + for b in branches: + if b["leading_element"]: + note_choice_elements.append(b["leading_element"]) + + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(" bool isNoteChoiceFound = false;") + # For each set member with maxOccurs != 1, we need a "first added" flag + # that handles the "default-constructed singleton already in the set" + # idiom that the committed code uses. The set is initialized empty + # (myDotSet()) so the size==1 branch actually never fires here. But + # the committed code does have that pattern -- emit it for parity. + first_added_flags = [] + for m in members: + if m["is_set"]: + flag = f"isFirst{m['cls']}Added" + first_added_flags.append((flag, m["cls"])) + lines.append(f" bool {flag} = false;") + lines.append("") + lines.append(" isSuccess &= getAttributes()->fromXElement(message, xelement);") + lines.append("") + lines.append(" for (auto it = xelement.begin(); it != xelement.end(); ++it)") + lines.append(" {") + lines.append(" const std::string elementName = it->getName();") + lines.append("") + + # First branch: note-choice elements (handled by parseNoteChoice with --it). + nc_cond = " || ".join([f'elementName == "{n}"' for n in note_choice_elements]) + lines.append(f" if ({nc_cond})") + lines.append(" {") + lines.append(" isNoteChoiceFound = true;") + lines.append(" isSuccess &= parseNoteChoice(message, xelement, it);") + lines.append(" --it;") + lines.append(" }") + + # Then walk trailing members in order, emitting if/else if branches. + for m in members: + cls = m["cls"] + if m["is_set"]: + xml_n = m["xml_name"] + lines.append(f' else if (elementName == "{xml_n}")') + lines.append(" {") + lines.append(f" auto {camel(xml_n)} = make{cls}();") + lines.append(f" isSuccess &= {camel(xml_n)}->fromXElement(message, *it);") + lines.append("") + lines.append(f" if (!isFirst{cls}Added && my{cls}Set.size() == 1)") + lines.append(" {") + lines.append(f" *(my{cls}Set.begin()) = {camel(xml_n)};") + lines.append(f" isFirst{cls}Added = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" my{cls}Set.push_back({camel(xml_n)});") + lines.append(f" isFirst{cls}Added = true;") + lines.append(" }") + lines.append(" }") + elif m["is_group"]: + # editorial-voice: dispatch to parseEditorialVoiceGroup + ev_cond = " || ".join([f'elementName == "{n}"' for n in editorial_voice_members]) + lines.append(f" else if ({ev_cond})") + lines.append(" {") + lines.append(" isSuccess &= parseEditorialVoiceGroup(message, xelement, it);") + lines.append(" }") + elif m["is_optional"]: + xml_n = m["xml_name"] + lines.append(f' else if (elementName == "{xml_n}")') + lines.append(" {") + lines.append(f" myHas{cls} = true;") + lines.append(f" isSuccess &= get{cls}()->fromXElement(message, *it);") + lines.append(" }") + + lines.append(" else") + lines.append(" {") + lines.append(" isSuccess = false;") + lines.append(f' message << "Note: unexpected element \'" << elementName << "\'" << std::endl;') + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(" if (!isNoteChoiceFound)") + lines.append(" {") + lines.append(" isSuccess = false;") + lines.append(f' message << "Note: \'note-choice\' elements were required but not found" << std::endl;') + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}") + lines.append("") + + # ============================================================ + # parseNoteChoice + # ============================================================ + # Dispatch on the first child element name: + # - "grace" -> setChoice(grace); parse Grace; fullNoteGroup = getGrace().getFullNoteGroup; ++iter + # - "cue" -> setChoice(cue); parse Cue; fullNoteGroup = getCue().getFullNoteGroup; ++iter + # - else -> setChoice(normal); fullNoteGroup = getNormal().getFullNoteGroup + # Then parseFullNoteGroup(message, noteElement, iter, fullNoteGroup). + # Then if NOT grace, expect 'duration'; assign via the right branch. + # Then if NOT cue, parse tie* (assigning via the right branch). + lines.append(f"bool {class_name}::parseNoteChoice(std::ostream &message, ::ezxml::XElement ¬eElement, ::ezxml::XElementIterator &iter)") + lines.append("{") + lines.append(" if (iter == noteElement.end())") + lines.append(" {") + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(" bool isSuccess = true;") + lines.append("") + lines.append(" const std::string elementName = iter->getName();") + lines.append(" FullNoteGroupPtr fullNoteGroup = nullptr;") + lines.append("") + + # Find normal branch (no leading element) and the named branches. + named_branches = [b for b in branches if b["leading_element"]] + normal_branch = next((b for b in branches if not b["leading_element"]), None) + if normal_branch is None: + raise RuntimeError("Note: no third (no-leading-element) branch found") + + for i, b in enumerate(named_branches): + le = b["leading_element"] + le_cls = pascal(le) + bg = b["class_name"] + prefix = "if" if i == 0 else "else if" + lines.append(f' {prefix} (elementName == "{le}")') + lines.append(" {") + lines.append(f" get{choice_class}()->setChoice({choice_class}::Choice::{b['enum_name']});") + lines.append(f" isSuccess &= get{choice_class}()->get{bg}()->get{le_cls}()->fromXElement(message, *iter);") + lines.append(f" fullNoteGroup = get{choice_class}()->get{bg}()->getFullNoteGroup();") + lines.append(" ++iter;") + lines.append(" }") + + nbg = normal_branch["class_name"] + lines.append(" else") + lines.append(" {") + lines.append(f" get{choice_class}()->setChoice({choice_class}::Choice::{normal_branch['enum_name']});") + lines.append(f" fullNoteGroup = get{choice_class}()->get{nbg}()->getFullNoteGroup();") + lines.append(" }") + lines.append("") + lines.append(" // we should now be pointing at the full note group") + lines.append(" isSuccess &= parseFullNoteGroup(message, noteElement, iter, fullNoteGroup);") + lines.append("") + + # Branches that require duration after the full-note group: any branch + # whose members include a duration group ref (i.e. branches "cue" and + # "normal" in MusicXML 3.0). + duration_branches = [b for b in branches if b["has_duration"]] + # Filter named branches that have duration -> "cue" + named_dur_branches = [b for b in duration_branches if b["leading_element"]] + # The committed code does: + # if (elementName != "grace") -> the only branch that has NO duration. + # We invert by listing the leading-element name that is duration-FREE. + no_dur_branches = [b for b in branches if not b["has_duration"]] + # Expecting exactly one of these (grace). + if len(no_dur_branches) == 1 and no_dur_branches[0]["leading_element"]: + no_dur_name = no_dur_branches[0]["leading_element"] + lines.append(f" // {pascal(no_dur_branches[0]['class_name'])}s do not have a duration element") + lines.append(f' if (elementName != "{no_dur_name}")') + else: + # fallback: enumerate + cond = " || ".join([f'elementName == "{b["leading_element"]}"' for b in duration_branches if b["leading_element"]]) + if not duration_branches[-1]["leading_element"]: + # Normal branch (no leading element) is reached when elementName + # is none of the named leading elements. + named_le = " && ".join([f'elementName != "{b["leading_element"]}"' for b in named_branches]) + cond = f"({cond}) || ({named_le})" + lines.append(f" if ({cond})") + lines.append(" {") + lines.append(f' if (iter == noteElement.end() || iter->getName() != "{duration_elem}")') + lines.append(" {") + lines.append(f' message << "Note: parseNoteChoice - a \'{duration_elem}\' element was required but not found" << std::endl;') + lines.append(" return false;") + lines.append(" }") + lines.append("") + # For each branch that has duration, emit: + # if (getNoteChoice()->getChoice() == NoteChoice::Choice::normal) { ... } + # else if (...cue) { ... } + for j, b in enumerate(duration_branches): + prefix = "if" if j == 0 else "else if" + bg = b["class_name"] + lines.append(f" {prefix} (get{choice_class}()->getChoice() == {choice_class}::Choice::{b['enum_name']})") + lines.append(" {") + lines.append(f" get{choice_class}()->get{bg}()->get{pascal(duration_elem)}()->fromXElement(message, *iter);") + lines.append(" }") + lines.append(" ++iter;") + lines.append(" }") + lines.append("") + lines.append(" // additional stuff is optional so we may be at the end iter") + lines.append(" if (iter == noteElement.end())") + lines.append(" {") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append(" }") + lines.append("") + + # Tie parsing: only branches whose tie_max > 0 + tie_branches = [b for b in branches if b["tie_max"] > 0] + # Branches WITHOUT tie support -> early-return. + no_tie_branches = [b for b in branches if b["tie_max"] == 0] + + tie_cond = " || ".join( + [f"get{choice_class}()->getChoice() == {choice_class}::Choice::{b['enum_name']}" for b in tie_branches]) + lines.append(f" // now we may be pointing at tie elements, but only if the choice supports them") + lines.append(f" if ({tie_cond})") + lines.append(" {") + lines.append(" std::string possibleTieElementName = iter->getName();") + lines.append(' while (iter != noteElement.end() && iter->getName() == "tie")') + lines.append(" {") + lines.append(" auto tie = makeTie();") + lines.append(" isSuccess &= tie->fromXElement(message, *iter);") + for k, b in enumerate(tie_branches): + prefix = "if" if k == 0 else "else if" + bg = b["class_name"] + lines.append(f" {prefix} (get{choice_class}()->getChoice() == {choice_class}::Choice::{b['enum_name']})") + lines.append(" {") + lines.append(f" get{choice_class}()->get{bg}()->addTie(tie);") + lines.append(" }") + lines.append(" ++iter;") + lines.append(" }") + lines.append(" }") + for b in no_tie_branches: + lines.append(f" else if (get{choice_class}()->getChoice() == {choice_class}::Choice::{b['enum_name']})") + lines.append(" {") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}") + lines.append("") + + # ============================================================ + # parseFullNoteGroup + # ============================================================ + lines.append(f"bool {class_name}::parseFullNoteGroup(std::ostream &message, ::ezxml::XElement ¬eElement, ::ezxml::XElementIterator &iter,") + lines.append(" FullNoteGroupPtr &outFullNoteGroup)") + lines.append("{") + lines.append(" if (iter == noteElement.end())") + lines.append(" {") + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(" bool isSuccess = true;") + lines.append("") + # chord pre-element: if first iter name == "chord", set has chord and advance. + for pe in full_note_pre: + pe_cls = pascal(pe) + lines.append(f' if (iter->getName() == "{pe}")') + lines.append(" {") + lines.append(f" outFullNoteGroup->setHas{pe_cls}(true);") + lines.append(" ++iter;") + lines.append(" }") + lines.append("") + lines.append(" // we should now be pointing at the FullNoteTypeChoice") + lines.append(" if (iter == noteElement.end())") + lines.append(" {") + lines.append(' message << "Note: parseFullNoteGroup did not find the FullNoteChoice" << std::endl;') + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(" const std::string noteChoiceElementName = iter->getName();") + lines.append("") + # full_note_choice members: pitch, unpitched, rest + for k, fc in enumerate(full_note_choice): + prefix = "if" if k == 0 else "else if" + fc_cls = pascal(fc) + lines.append(f' {prefix} (noteChoiceElementName == "{fc}")') + lines.append(" {") + lines.append(f" outFullNoteGroup->getFullNoteTypeChoice()->setChoice(FullNoteTypeChoice::Choice::{camel(fc)});") + lines.append(f" isSuccess &= outFullNoteGroup->getFullNoteTypeChoice()->get{fc_cls}()->fromXElement(message, *iter);") + lines.append(" ++iter;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(' message << "Note: parseFullNoteGroup encountered unexpected element \'" << noteChoiceElementName << "\'"') + lines.append(" << std::endl;") + lines.append(" isSuccess = false;") + lines.append(" ++iter;") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}") + lines.append("") + + # ============================================================ + # parseEditorialVoiceGroup + # ============================================================ + lines.append(f"bool {class_name}::parseEditorialVoiceGroup(std::ostream &message, ::ezxml::XElement ¬eElement,") + lines.append(" ::ezxml::XElementIterator &iter)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(" bool isIterIncremented = false;") + ev_cond = " || ".join([f'iter->getName() == "{n}"' for n in editorial_voice_members]) + lines.append(f" while (iter != noteElement.end() &&") + lines.append(f" ({ev_cond}))") + lines.append(" {") + for j, evn in enumerate(editorial_voice_members): + prefix = "if" if j == 0 else "else if" + ev_cls = pascal(evn) + lines.append(f' {prefix} (iter->getName() == "{evn}")') + lines.append(" {") + lines.append(f" getEditorialVoiceGroup()->setHas{ev_cls}(true);") + lines.append(f" isSuccess &= getEditorialVoiceGroup()->get{ev_cls}()->fromXElement(message, *iter);") + lines.append(" }") + lines.append("") + lines.append(" ++iter;") + lines.append(" isIterIncremented = true;") + lines.append(" }") + lines.append("") + lines.append(" if (isIterIncremented)") + lines.append(" {") + lines.append(" --iter;") + lines.append(" }") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}") + lines.append("} // namespace core") + lines.append("} // namespace mx") + return "\n".join(lines) + "\n" + + +# --------------------------------------------------------------------------- +# Orchestrator +# --------------------------------------------------------------------------- + +def _emit_note_family(elem_name, elem, ct, model, generated_attrs, stats): + """Emit Note + NoteAttributes + NoteChoice + 5 helper classes. + + File set (8 .h + 8 .cpp): + NoteAttributes, FullNoteTypeChoice, FullNoteGroup, + GraceNoteGroup, CueNoteGroup, NormalNoteGroup, NoteChoice, Note. + """ + s = _extract_note_structure(ct, model) + + class_name = element_class_name(elem_name) # "Note" + choice_class = pascal(elem_name) + "Choice" # "NoteChoice" + full_note_cls = pascal("full-note") + "Group" # "FullNoteGroup" + full_note_type_choice_cls = pascal("full-note") + "TypeChoice" # "FullNoteTypeChoice" + + type_name = elem.type_name or "" + attrs_name = resolve_attrs_name(elem_name, type_name, model) # "NoteAttributes" + + # 1. NoteAttributes -- standard attrs path + if ct.attributes and attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(attrs_name, ct.attributes, model) + c = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), c) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + + # 2. FullNoteTypeChoice (inner pitch/unpitched/rest choice) + h = generate_full_note_type_choice_h(full_note_type_choice_cls, s["full_note"]["choice_branches"]) + c = generate_full_note_type_choice_cpp(full_note_type_choice_cls, s["full_note"]["choice_branches"]) + write_file(os.path.join(ELEM_DIR, f"{full_note_type_choice_cls}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{full_note_type_choice_cls}.cpp"), c) + + # 3. FullNoteGroup + h = generate_full_note_group_h(full_note_cls, s["full_note"]["pre"], full_note_type_choice_cls) + c = generate_full_note_group_cpp(full_note_cls, s["full_note"]["pre"], full_note_type_choice_cls) + write_file(os.path.join(ELEM_DIR, f"{full_note_cls}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{full_note_cls}.cpp"), c) + + # 4. The three outer branch groups (Grace/Cue/Normal) + for b in s["branches"]: + h = generate_outer_branch_group_h(b["class_name"], b["members"], full_note_cls) + c = generate_outer_branch_group_cpp(b["class_name"], b["members"], full_note_cls) + write_file(os.path.join(ELEM_DIR, f"{b['class_name']}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{b['class_name']}.cpp"), c) + + # 5. NoteChoice + h = generate_note_choice_h(choice_class, s["branches"]) + c = generate_note_choice_cpp(choice_class, s["branches"]) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.cpp"), c) + + # 6. Note + h = generate_note_h(class_name, attrs_name, choice_class, s["trailing"], model) + c = generate_note_cpp(class_name, attrs_name, choice_class, s["trailing"], + s["full_note"]["pre"], s["full_note"]["choice_branches"], + s["editorial_voice"], s["branches"], s["duration"], model) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), c) + + stats["elem_written"] += 1 + stats["bespoke_written"] = stats.get("bespoke_written", 0) + 1 + + +# --------------------------------------------------------------------------- +# Direction (bespoke, schema-driven) +# +# Direction's child contains a choice whose branches are +# read from the parsed XSD (each branch is an ElementRefNode with +# max_occurs == -1 indicating a "multi" branch). Two anonymous-namespace +# arrays in Direction.cpp -- directions[] (every branch name) and +# multiDirections[] (only the unbounded ones) -- are derived directly from +# those branches so MusicXML 4.0 changes propagate automatically. +# +# The bulk of the Direction.cpp body is a hand-shaped parser that promotes +# the first parsed direction-type into the default-seeded slot and then +# appends. For each "multi" branch (rehearsal, segno, words, coda, +# dynamics, percussion) the parser also swaps the DirectionType's first +# default-seeded sub-element using addX + removeX. The shape of that +# parser cannot be expressed by the shared rule-based path, so it lives +# here. + + +def _emit_direction_family(elem_name, elem, ct, model, generated_attrs, stats): + assert elem_name == "direction" + + # Read the direction-type choice branches from the parsed model. These + # drive the two anonymous-namespace arrays in Direction.cpp and the + # per-branch dispatch in createDirectionType(). + dt_ct = model.complex_types["direction-type"] + dt_branches = list(dt_ct.content_tree.branches) + branch_names = [b.element_name for b in dt_branches] + multi_branch_names = [b.element_name for b in dt_branches if b.max_occurs == -1] + + class_name = element_class_name(elem_name) # "Direction" + type_name = elem.type_name or "" + + # 1. Attrs struct via the standard generator. + attrs_name = None + if ct.attributes: + attrs_name = resolve_attrs_name(elem_name, type_name, model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h = generate_attrs_h(attrs_name, ct.attributes, model) + c = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), c) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + + # 2. Direction.h + h_content = _generate_direction_h(class_name, attrs_name) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h_content) + + # 3. Direction.cpp + c_content = _generate_direction_cpp(class_name, attrs_name, branch_names, multi_branch_names) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), c_content) + + stats["elem_written"] += 1 + stats["bespoke_written"] = stats.get("bespoke_written", 0) + 1 + + +def _generate_direction_h(class_name, attrs_name): + lines = [LICENSE, "#pragma once\n"] + lines.append('#include "mx/core/ElementInterface.h"') + lines.append('#include "mx/core/ForwardDeclare.h"') + lines.append(f'#include "mx/core/elements/{attrs_name}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace ezxml\n{\nclass XElementIterator;\n}") + lines.append("") + lines.append("namespace mx\n{\nnamespace core\n{\n") + lines.append(f"MX_FORWARD_DECLARE_ATTRIBUTES({attrs_name})") + lines.append("MX_FORWARD_DECLARE_ELEMENT(DirectionType)") + lines.append("MX_FORWARD_DECLARE_ELEMENT(EditorialVoiceDirectionGroup)") + lines.append("MX_FORWARD_DECLARE_ELEMENT(Offset)") + lines.append("MX_FORWARD_DECLARE_ELEMENT(Sound)") + lines.append("MX_FORWARD_DECLARE_ELEMENT(Staff)") + lines.append(f"MX_FORWARD_DECLARE_ELEMENT({class_name})") + lines.append("") + lines.append(f"inline {class_name}Ptr make{class_name}()") + lines.append("{") + lines.append(f" return std::make_shared<{class_name}>();") + lines.append("}") + lines.append("") + lines.append(f"class {class_name} : public ElementInterface") + lines.append("{") + lines.append(" public:") + lines.append(f" {class_name}();") + lines.append("") + lines.append(" virtual bool hasAttributes() const;") + lines.append(" virtual std::ostream &streamAttributes(std::ostream &os) const;") + lines.append(" virtual std::ostream &streamName(std::ostream &os) const;") + lines.append(" virtual bool hasContents() const;") + lines.append(" virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const;") + lines.append(f" {attrs_name}Ptr getAttributes() const;") + lines.append(f" void setAttributes(const {attrs_name}Ptr &value);") + lines.append("") + lines.append(" /* _________ DirectionType minOccurs = 1, maxOccurs = unbounded _________ */") + lines.append(" const DirectionTypeSet &getDirectionTypeSet() const;") + lines.append(" void addDirectionType(const DirectionTypePtr &value);") + lines.append(" void removeDirectionType(const DirectionTypeSetIterConst &value);") + lines.append(" void clearDirectionTypeSet();") + lines.append(" DirectionTypePtr getDirectionType(const DirectionTypeSetIterConst &setIterator) const;") + lines.append("") + lines.append(" /* _________ Offset minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(" OffsetPtr getOffset() const;") + lines.append(" void setOffset(const OffsetPtr &value);") + lines.append(" bool getHasOffset() const;") + lines.append(" void setHasOffset(const bool value);") + lines.append("") + lines.append(" /* _________ EditorialVoiceDirectionGroup minOccurs = 1, maxOccurs = 1 _________ */") + lines.append(" EditorialVoiceDirectionGroupPtr getEditorialVoiceDirectionGroup() const;") + lines.append(" void setEditorialVoiceDirectionGroup(const EditorialVoiceDirectionGroupPtr &value);") + lines.append("") + lines.append(" /* _________ Staff minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(" StaffPtr getStaff() const;") + lines.append(" void setStaff(const StaffPtr &value);") + lines.append(" bool getHasStaff() const;") + lines.append(" void setHasStaff(const bool value);") + lines.append("") + lines.append(" /* _________ Sound minOccurs = 0, maxOccurs = 1 _________ */") + lines.append(" SoundPtr getSound() const;") + lines.append(" void setSound(const SoundPtr &value);") + lines.append(" bool getHasSound() const;") + lines.append(" void setHasSound(const bool value);") + lines.append("") + lines.append(" private:") + lines.append(" virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement);") + lines.append("") + lines.append(" private:") + lines.append(f" {attrs_name}Ptr myAttributes;") + lines.append(" DirectionTypeSet myDirectionTypeSet;") + lines.append(" OffsetPtr myOffset;") + lines.append(" bool myHasOffset;") + lines.append(" EditorialVoiceDirectionGroupPtr myEditorialVoiceDirectionGroup;") + lines.append(" StaffPtr myStaff;") + lines.append(" bool myHasStaff;") + lines.append(" SoundPtr mySound;") + lines.append(" bool myHasSound;") + lines.append("") + lines.append(" bool importDirectionTypeSet(std::ostream &message, ::ezxml::XElementIterator &iter,") + lines.append(" ::ezxml::XElementIterator &endIter, bool &isSuccess, bool &isFound);") + lines.append(" DirectionTypePtr createDirectionType(std::ostream &message, ::ezxml::XElementIterator &iter,") + lines.append(" ::ezxml::XElementIterator &endIter, bool &isSuccess);") + lines.append("") + lines.append(" bool isDirectionType(const std::string &elementName) const;") + lines.append(" bool isMultiDirectionType(const std::string &elementName) const;") + lines.append("};") + lines.append("} // namespace core") + lines.append("} // namespace mx") + lines.append("") + return "\n".join(lines) + + +def _generate_direction_cpp(class_name, attrs_name, branch_names, multi_branch_names): + # Build the anonymous-namespace arrays from XSD-derived branch names. + directions_init = ", ".join(f'"{n}"' for n in branch_names) + multi_init = ", ".join(f'"{n}"' for n in multi_branch_names) + + lines = [LICENSE] + lines.append(f'#include "mx/core/elements/{class_name}.h"') + lines.append('#include "mx/core/FromXElement.h"') + lines.append('#include "mx/core/elements/DirectionType.h"') + lines.append('#include "mx/core/elements/EditorialVoiceDirectionGroup.h"') + lines.append('#include "mx/core/elements/Footnote.h"') + lines.append('#include "mx/core/elements/Level.h"') + lines.append('#include "mx/core/elements/Offset.h"') + lines.append('#include "mx/core/elements/Sound.h"') + lines.append('#include "mx/core/elements/Staff.h"') + lines.append('#include "mx/core/elements/Voice.h"') + lines.append("") + # Per-branch direction-type element includes, sorted to match HEAD + # (alphabetical by C++ class name). + sorted_branches = sorted(branch_names, key=lambda n: pascal(n)) + for n in sorted_branches: + lines.append(f'#include "mx/core/elements/{pascal(n)}.h"') + lines.append("") + lines.append("#include ") + lines.append("#include ") + lines.append("#include ") + lines.append("") + lines.append("namespace") + lines.append("{") + lines.append("") + lines.append(f"constexpr const size_t directionsSize = {len(branch_names)};") + lines.append(f"constexpr const char *const directions[] = {{{directions_init}}};") + lines.append("") + lines.append(f"constexpr const size_t multiDirectionsSize = {len(multi_branch_names)};") + lines.append(f"constexpr const char *const multiDirections[] = {{{multi_init}}};") + lines.append("} // namespace") + lines.append("") + lines.append("namespace mx") + lines.append("{") + lines.append("namespace core") + lines.append("{") + lines.append(f"{class_name}::{class_name}()") + lines.append(f" : myAttributes(std::make_shared<{attrs_name}>()), myDirectionTypeSet(), myOffset(makeOffset()),") + lines.append(" myHasOffset(false), myEditorialVoiceDirectionGroup(makeEditorialVoiceDirectionGroup()), myStaff(makeStaff()),") + lines.append(" myHasStaff(false), mySound(makeSound()), myHasSound(false)") + lines.append("{") + lines.append(" myDirectionTypeSet.push_back(makeDirectionType());") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasAttributes() const") + lines.append("{") + lines.append(" return myAttributes->hasValues();") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamAttributes(std::ostream &os) const") + lines.append("{") + lines.append(" return myAttributes->toStream(os);") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamName(std::ostream &os) const") + lines.append("{") + lines.append(' os << "direction";') + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"bool {class_name}::hasContents() const") + lines.append("{") + lines.append(" return true;") + lines.append("}") + lines.append("") + lines.append(f"std::ostream &{class_name}::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const") + lines.append("{") + lines.append(" for (auto x : myDirectionTypeSet)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" x->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" if (myHasOffset)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" myOffset->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" if (myEditorialVoiceDirectionGroup->hasContents())") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" myEditorialVoiceDirectionGroup->streamContents(os, indentLevel + 1, isOneLineOnly);") + lines.append(" }") + lines.append(" if (myHasStaff)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" myStaff->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" if (myHasSound)") + lines.append(" {") + lines.append(" os << std::endl;") + lines.append(" mySound->toStream(os, indentLevel + 1);") + lines.append(" }") + lines.append(" os << std::endl;") + lines.append(" isOneLineOnly = false;") + lines.append(" return os;") + lines.append("}") + lines.append("") + lines.append(f"{attrs_name}Ptr {class_name}::getAttributes() const") + lines.append("{") + lines.append(" return myAttributes;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setAttributes(const {attrs_name}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myAttributes = value;") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"const DirectionTypeSet &{class_name}::getDirectionTypeSet() const") + lines.append("{") + lines.append(" return myDirectionTypeSet;") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::addDirectionType(const DirectionTypePtr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(" myDirectionTypeSet.push_back(value);") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::removeDirectionType(const DirectionTypeSetIterConst &value)") + lines.append("{") + lines.append(" if (value != myDirectionTypeSet.cend())") + lines.append(" {") + lines.append(" if (myDirectionTypeSet.size() > 1)") + lines.append(" {") + lines.append(" myDirectionTypeSet.erase(value);") + lines.append(" }") + lines.append(" }") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::clearDirectionTypeSet()") + lines.append("{") + lines.append(" myDirectionTypeSet.clear();") + lines.append(" myDirectionTypeSet.push_back(makeDirectionType());") + lines.append("}") + lines.append("") + lines.append(f"DirectionTypePtr {class_name}::getDirectionType(const DirectionTypeSetIterConst &setIterator) const") + lines.append("{") + lines.append(" if (setIterator != myDirectionTypeSet.cend())") + lines.append(" {") + lines.append(" return *setIterator;") + lines.append(" }") + lines.append(" return DirectionTypePtr();") + lines.append("}") + lines.append("") + # Plain single-occurrence accessors: offset, editorial-voice-direction, staff, sound + for (cap_field, lower_field, with_has) in [ + ("Offset", "myOffset", True), + ("EditorialVoiceDirectionGroup", "myEditorialVoiceDirectionGroup", False), + ("Staff", "myStaff", True), + ("Sound", "mySound", True), + ]: + lines.append(f"{cap_field}Ptr {class_name}::get{cap_field}() const") + lines.append("{") + lines.append(f" return {lower_field};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::set{cap_field}(const {cap_field}Ptr &value)") + lines.append("{") + lines.append(" if (value)") + lines.append(" {") + lines.append(f" {lower_field} = value;") + lines.append(" }") + lines.append("}") + lines.append("") + if with_has: + lines.append(f"bool {class_name}::getHas{cap_field}() const") + lines.append("{") + lines.append(f" return myHas{cap_field};") + lines.append("}") + lines.append("") + lines.append(f"void {class_name}::setHas{cap_field}(const bool value)") + lines.append("{") + lines.append(f" myHas{cap_field} = value;") + lines.append("}") + lines.append("") + + # fromXElementImpl + lines.append(f"bool {class_name}::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement)") + lines.append("{") + lines.append(" bool isSuccess = true;") + lines.append(" bool isDirectionTypeFound = false;") + lines.append(" isSuccess &= myAttributes->fromXElement(message, xelement);") + lines.append("") + lines.append(" auto endIter = xelement.end();") + lines.append(" for (auto it = xelement.begin(); it != endIter; ++it)") + lines.append(" {") + lines.append(" if (importDirectionTypeSet(message, it, endIter, isSuccess, isDirectionTypeFound))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(" if (importElement(message, *it, isSuccess, *myOffset, myHasOffset))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(" importGroup(message, it, endIter, isSuccess, myEditorialVoiceDirectionGroup);") + lines.append(" if (importElement(message, *it, isSuccess, *myStaff, myHasStaff))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(" if (importElement(message, *it, isSuccess, *mySound, myHasSound))") + lines.append(" {") + lines.append(" continue;") + lines.append(" }") + lines.append(" }") + lines.append("") + lines.append(" MX_RETURN_IS_SUCCESS;") + lines.append("}") + lines.append("") + + # importDirectionTypeSet + lines.append(f"bool {class_name}::importDirectionTypeSet(std::ostream &message, ::ezxml::XElementIterator &iter,") + lines.append(" ::ezxml::XElementIterator &endIter, bool &isSuccess, bool &isFound)") + lines.append("{") + lines.append(" if (iter == endIter)") + lines.append(" {") + lines.append(" isFound = false;") + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(' if (iter->getName() != "direction-type")') + lines.append(" {") + lines.append(" isFound = false;") + lines.append(" return false;") + lines.append(" }") + lines.append("") + lines.append(" isFound = true;") + lines.append(" bool isIterIncremented = false;") + lines.append(" bool isFirstDirectionTypeAdded = false;") + lines.append("") + lines.append(' while ((iter != endIter) && (iter->getName() == "direction-type"))') + lines.append(" {") + lines.append(" auto subiter = iter->begin();") + lines.append(" auto subiterEnd = iter->end();") + lines.append("") + lines.append(" auto directionType = createDirectionType(message, subiter, subiterEnd, isSuccess);") + lines.append("") + lines.append(" if (!isFirstDirectionTypeAdded && myDirectionTypeSet.size() == 1)") + lines.append(" {") + lines.append(" *myDirectionTypeSet.begin() = directionType;") + lines.append(" isFirstDirectionTypeAdded = true;") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(" myDirectionTypeSet.push_back(directionType);") + lines.append(" isFirstDirectionTypeAdded = true;") + lines.append(" }") + lines.append("") + lines.append(" isIterIncremented = true;") + lines.append(" ++iter;") + lines.append(" }") + lines.append("") + lines.append(" if (isIterIncremented)") + lines.append(" {") + lines.append(" --iter;") + lines.append(" }") + lines.append(" return isFirstDirectionTypeAdded;") + lines.append("}") + lines.append("") + + # isDirectionType + lines.append(f"bool {class_name}::isDirectionType(const std::string &elementName) const") + lines.append("{") + lines.append(" for (size_t i = 0; i < directionsSize; ++i)") + lines.append(" {") + lines.append(" if (strcmp(directions[i], elementName.c_str()) == 0)") + lines.append(" {") + lines.append(" return true;") + lines.append(" }") + lines.append(" }") + lines.append(" return false;") + lines.append("}") + lines.append("") + + # isMultiDirectionType + lines.append(f"bool {class_name}::isMultiDirectionType(const std::string &elementName) const") + lines.append("{") + lines.append(" for (size_t i = 0; i < multiDirectionsSize; ++i)") + lines.append(" {") + lines.append(" if (strcmp(multiDirections[i], elementName.c_str()) == 0)") + lines.append(" {") + lines.append(" return true;") + lines.append(" }") + lines.append(" }") + lines.append(" return false;") + lines.append("}") + lines.append("") + + # createDirectionType + lines.append(f"DirectionTypePtr {class_name}::createDirectionType(std::ostream &message, ::ezxml::XElementIterator &subIter,") + lines.append(" ::ezxml::XElementIterator &subIterEnd, bool &isSuccess)") + lines.append("{") + lines.append(" auto directionType = makeDirectionType();") + lines.append("") + lines.append(" if (subIter == subIterEnd)") + lines.append(" {") + lines.append(' message << "Direction: well thats weird - should not get here" << std::endl;') + lines.append(" isSuccess = false;") + lines.append(" return directionType;") + lines.append(" }") + lines.append("") + + # Single-occurrence branches (non-multi): emit if-block with setChoice + fromXElement. + # HEAD emits these BEFORE the multi branches. + multi_set = set(multi_branch_names) + for n in branch_names: + if n in multi_set: + continue + cls = pascal(n) # e.g. OctaveShift + getter = f"get{cls}" # e.g. getOctaveShift + choice_enum = camel(n) # e.g. octaveShift + lines.append(f' if (subIter->getName() == "{n}")') + lines.append(" {") + lines.append(f" directionType->setChoice(DirectionType::Choice::{choice_enum});") + lines.append(f" isSuccess &= directionType->{getter}()->fromXElement(message, *subIter);") + lines.append(" return directionType;") + lines.append(" }") + lines.append("") + + # Multi branches: HEAD orders them rehearsal, segno, words, coda, dynamics, + # percussion -- which is the XSD order filtered to multi. + for idx, n in enumerate(multi_branch_names): + cls = pascal(n) + choice_enum = camel(n) + if idx == 0: + lines.append(f' std::string name = "{n}";') + else: + lines.append(f' name = "{n}";') + lines.append(" if (subIter->getName() == name)") + lines.append(" {") + lines.append(f" directionType->setChoice(DirectionType::Choice::{choice_enum});") + lines.append(" bool isFirstSubItemAdded = false;") + lines.append("") + lines.append(" while (subIter != subIterEnd)") + lines.append(" {") + lines.append(" if (subIter->getName() != name)") + lines.append(" {") + lines.append(f' message << "Direction: createDirectionType encountered an unexpected element \'" << subIter->getName()') + lines.append(f' << "\' while parsing a collection of \'" << name << "\' elements" << std::endl;') + lines.append(" isSuccess = false;") + lines.append(" return directionType;") + lines.append(" }") + lines.append(f" auto itemToAdd = make{cls}();") + lines.append(" isSuccess &= itemToAdd->fromXElement(message, *subIter);") + lines.append(f" if (!isFirstSubItemAdded && directionType->get{cls}Set().size() == 1)") + lines.append(" {") + lines.append(f" directionType->add{cls}(itemToAdd);") + lines.append(f" directionType->remove{cls}(directionType->get{cls}Set().cbegin());") + lines.append(" }") + lines.append(" else") + lines.append(" {") + lines.append(f" directionType->add{cls}(itemToAdd);") + lines.append(" }") + lines.append(" isFirstSubItemAdded = true;") + lines.append(" ++subIter;") + lines.append(" } // end loop") + lines.append(" return directionType;") + lines.append(f" }} // end {n}") + lines.append("") + + lines.append(" return directionType;") + lines.append("}") + lines.append("") + lines.append("} // namespace core") + lines.append("} // namespace mx") + lines.append("") + return "\n".join(lines) + + +BESPOKE_ELEMENTS = { + "credit": _emit_credit_family, + "lyric": _emit_lyric_family, + "note": _emit_note_family, + "part-list": _emit_part_list_family, + "harmony": _emit_harmony_family, + "score-partwise": _emit_score_wrapper_family, + "score-timewise": _emit_score_wrapper_family, + "direction": _emit_direction_family, +} + + +def main(): + model = XsdModel(XSD_PATH) + + stats = {"attrs_written": 0, "elem_written": 0, "elem_skipped": 0} + categories = {} + + generated_attrs = set() + + for elem_name, elem in model.elements.items(): + if elem_name in SKIP_ELEMENTS or elem_name in DYNAMICS_MARKS: + continue + # Elements claimed by a family-handler (e.g. score-partwise emits + # PartwisePart and PartwiseMeasure for "part" and "measure") must be + # silently skipped here so the default path doesn't double-write or + # produce broken files. + if elem_name in BESPOKE_FAMILY_OWNED: + continue + + cat = classify_element(elem, model) + categories.setdefault(cat, []).append(elem_name) + + ct = model.complex_types.get(elem.type_name) if elem.type_name else None + if ct is None and elem.anonymous_type is not None: + ct = elem.anonymous_type + + # Bespoke per-element generators (last-resort path for elements that + # don't fit any reusable mechanism). Each handler is schema-driven + # (reads names from the parsed XSD). + bespoke = BESPOKE_ELEMENTS.get(elem_name) + if bespoke and ct: + bespoke(elem_name, elem, ct, model, generated_attrs, stats) + continue + + # Tree-based generation for elements with nested choice/sequence + if elem_name in TREE_ELEMENTS and ct and ct.content_tree: + if True: + plan = analyze_tree(elem_name, ct.content_tree, model) + if plan: + class_name = element_class_name(elem_name) + type_name = elem.type_name or "" + has_attrs = bool(ct.attributes) + attrs_name = None + + if has_attrs: + attrs_name = resolve_attrs_name(elem_name, type_name, model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h_content = generate_attrs_h(attrs_name, ct.attributes, model) + cpp_content = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h_content) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), cpp_content) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + + for group_class, group_children in plan.groups_to_generate: + gh = generate_tree_group_h(group_class, group_children, model) + gc = generate_tree_group_cpp(group_class, group_children, model) + write_file(os.path.join(ELEM_DIR, f"{group_class}.h"), gh) + write_file(os.path.join(ELEM_DIR, f"{group_class}.cpp"), gc) + + for og_class, og_children in plan.optional_groups_to_generate: + gh = generate_tree_group_h(og_class, og_children, model) + gc = generate_tree_group_cpp(og_class, og_children, model) + write_file(os.path.join(ELEM_DIR, f"{og_class}.h"), gh) + write_file(os.path.join(ELEM_DIR, f"{og_class}.cpp"), gc) + + for nc_class, nc_branches, nc_parent in plan.nested_choices_to_generate: + nch = generate_tree_choice_h(nc_class, nc_branches, nc_parent) + ncc = generate_tree_choice_cpp(nc_class, nc_branches, nc_parent) + write_file(os.path.join(ELEM_DIR, f"{nc_class}.h"), nch) + write_file(os.path.join(ELEM_DIR, f"{nc_class}.cpp"), ncc) + + for container in plan.containers_to_generate: + cth = generate_container_h(container) + ctc = generate_container_cpp(container) + write_file(os.path.join(ELEM_DIR, f"{container.class_name}.h"), cth) + write_file(os.path.join(ELEM_DIR, f"{container.class_name}.cpp"), ctc) + + tree_config = _get_tree_config(elem_name) + if tree_config.get("inlined_choice"): + inline_cfg = { + "branches": [ + { + "enum_name": b.enum_name, + "class_name": b.class_name, + "is_group": b.is_group, + "element_name": b.xml_name or b.class_name, + } + for b in plan.choice_branches + ], + "enum_start": 1, + } + ph = generate_inline_choice_h(elem_name, class_name, inline_cfg, + has_attrs, attrs_name) + pc = generate_inline_choice_cpp(elem_name, class_name, inline_cfg, + has_attrs, attrs_name, elem_name) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), ph) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), pc) + stats["elem_written"] += 1 + stats["tree_written"] = stats.get("tree_written", 0) + 1 + continue + + if len(plan.inline_choices) > 1: + for ic in plan.inline_choices: + ch = generate_tree_choice_h(ic.choice_class, ic.branches, elem_name) + cc = generate_tree_choice_cpp(ic.choice_class, ic.branches, elem_name) + write_file(os.path.join(ELEM_DIR, f"{ic.choice_class}.h"), ch) + write_file(os.path.join(ELEM_DIR, f"{ic.choice_class}.cpp"), cc) + else: + ch = generate_tree_choice_h(plan.choice_class, plan.choice_branches, elem_name) + cc = generate_tree_choice_cpp(plan.choice_class, plan.choice_branches, elem_name) + write_file(os.path.join(ELEM_DIR, f"{plan.choice_class}.h"), ch) + write_file(os.path.join(ELEM_DIR, f"{plan.choice_class}.cpp"), cc) + + ph = generate_tree_parent_h(elem_name, class_name, plan.choice_class, + plan.trailing_children, has_attrs, attrs_name, model, + choice_is_set=plan.choice_is_set, + leading_groups=plan.leading_groups, + leading_children=plan.leading_children, + choice_is_optional=plan.choice_is_optional, + inline_choices=plan.inline_choices, + choice_branches=plan.choice_branches) + pc = generate_tree_parent_cpp(elem_name, class_name, plan.choice_class, + plan.trailing_children, has_attrs, attrs_name, + plan, model) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), ph) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), pc) + stats["elem_written"] += 1 + stats["tree_written"] = stats.get("tree_written", 0) + 1 + continue + + if cat == "choice" and elem_name not in CHOICE_SKIP: + enum_val_cfg = ENUM_VALUE_CHOICE_CONFIG.get(elem_name) + inline_cfg = INLINE_CHOICE_CONFIG.get(elem_name) + if enum_val_cfg and ct: + class_name = element_class_name(elem_name) + has_attrs = bool(ct.attributes) + attrs_name = None + type_name = elem.type_name or "" + if has_attrs: + attrs_name = resolve_attrs_name(elem_name, type_name, model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h_content = generate_attrs_h(attrs_name, ct.attributes, model) + cpp_content = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h_content) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), cpp_content) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + h_content = generate_enum_value_choice_h( + elem_name, class_name, enum_val_cfg["value_type"], + has_attrs, attrs_name) + cpp_content = generate_enum_value_choice_cpp( + elem_name, class_name, enum_val_cfg["value_type"], + enum_val_cfg["enum_type"], enum_val_cfg["other_variant"], + enum_val_cfg["other_xml_name"], has_attrs, attrs_name) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h_content) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), cpp_content) + stats["elem_written"] += 1 + elif inline_cfg and ct: + class_name = element_class_name(elem_name) + stream_name = elem_name + type_name = elem.type_name or "" + has_attrs = bool(ct.attributes) + attrs_name = None + + if has_attrs: + attrs_name = resolve_attrs_name(elem_name, type_name, model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h_content = generate_attrs_h(attrs_name, ct.attributes, model) + cpp_content = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h_content) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), cpp_content) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + + for branch in inline_cfg["branches"]: + if branch.get("is_group"): + group_children = [ + XsdChildRef( + element_name=c["name"], + min_occurs=c["min"], + max_occurs=c["max"], + ) + for c in branch["children"] + ] + group_class = branch["class_name"] + group_base = group_class.replace("Group", "").lower() + gh = generate_group_h(group_base, group_children, model) + gc = generate_group_cpp(group_base, group_children, model) + write_file(os.path.join(ELEM_DIR, f"{group_class}.h"), gh) + write_file(os.path.join(ELEM_DIR, f"{group_class}.cpp"), gc) + + ph = generate_inline_choice_h(elem_name, class_name, inline_cfg, + has_attrs, attrs_name) + pc = generate_inline_choice_cpp(elem_name, class_name, inline_cfg, + has_attrs, attrs_name, stream_name) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), ph) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), pc) + stats["elem_written"] += 1 + stats["inline_choice_written"] = stats.get("inline_choice_written", 0) + 1 + + else: + config = CHOICE_ELEMENT_CONFIG.get(elem_name) + if config and ct and ct.choice_children: + choice_class = config["choice_class"] + is_set = config["is_set"] + enum_start = config["enum_start"] + class_name = element_class_name(elem_name) + stream_name = elem_name + type_name = elem.type_name or "" + has_attrs = bool(ct.attributes) + attrs_name = None + + if has_attrs: + attrs_name = resolve_attrs_name(elem_name, type_name, model) + if attrs_name not in generated_attrs and attrs_name not in CORE_ROOT_ATTRS: + h_content = generate_attrs_h(attrs_name, ct.attributes, model) + cpp_content = generate_attrs_cpp(attrs_name, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.h"), h_content) + write_file(os.path.join(ELEM_DIR, f"{attrs_name}.cpp"), cpp_content) + generated_attrs.add(attrs_name) + stats["attrs_written"] += 1 + + choice_children = list(ct.choice_children) + extra = config.get("extra_children", []) + if extra: + after_name = config.get("extra_children_after") + if after_name: + insert_idx = next((i for i, c in enumerate(choice_children) + if c.element_name == after_name), len(choice_children) - 1) + 1 + else: + insert_idx = len(choice_children) - 1 + for ec in extra: + choice_children.insert(insert_idx, type('obj', (), { + 'element_name': ec, 'min_occurs': 1, 'max_occurs': 1})()) + insert_idx += 1 + + if config.get("bespoke_choice"): + ch = generate_time_choice_h() + cc = generate_time_choice_cpp() + write_file(os.path.join(ELEM_DIR, f"{choice_class}.h"), ch) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.cpp"), cc) + gh = generate_group_h("time-signature", + TIME_SIGNATURE_GROUP_CHILDREN, model) + gc = generate_group_cpp("time-signature", + TIME_SIGNATURE_GROUP_CHILDREN, model) + write_file(os.path.join(ELEM_DIR, "TimeSignatureGroup.h"), gh) + write_file(os.path.join(ELEM_DIR, "TimeSignatureGroup.cpp"), gc) + else: + choice_is_set = config.get("choice_is_set", is_set) + ch = generate_choice_class_h(choice_class, choice_children, + choice_is_set, enum_start, elem_name, model) + cc = generate_choice_class_cpp(choice_class, choice_children, + choice_is_set, enum_start, elem_name, + model, config) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.h"), ch) + write_file(os.path.join(ELEM_DIR, f"{choice_class}.cpp"), cc) + + if not config.get("skip_parent"): + ph = generate_choice_parent_h(elem_name, class_name, choice_class, + is_set, has_attrs, attrs_name, model, + config) + pc = generate_choice_parent_cpp(elem_name, class_name, choice_class, + is_set, has_attrs, attrs_name, + stream_name, model, config, + choice_children) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), ph) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), pc) + stats["elem_written"] += 1 + stats["choice_written"] = stats.get("choice_written", 0) + 1 + else: + stats["elem_skipped"] += 1 + + elif cat in ("empty-with-attrs", "text-with-attrs", "complex-with-attrs", + "complex", "text-value", "empty", "simple-value"): + + class_name = element_class_name(elem_name) + stream_name = elem_name + type_name = elem.type_name or "" + + if ct and ct.attributes: + sname = resolve_attrs_name(elem_name, type_name, model) + if sname not in generated_attrs and sname not in CORE_ROOT_ATTRS: + h_content = generate_attrs_h(sname, ct.attributes, model) + cpp_content = generate_attrs_cpp(sname, ct.attributes, model) + write_file(os.path.join(ELEM_DIR, f"{sname}.h"), h_content) + write_file(os.path.join(ELEM_DIR, f"{sname}.cpp"), cpp_content) + generated_attrs.add(sname) + stats["attrs_written"] += 1 + + if cat == "simple-value": + value_type = resolve_cpp_type(elem.type_name, model) + fake_ct = XsdComplexType(name=elem.type_name) + fake_ct.has_simple_content = True + fake_ct.simple_content_base = elem.type_name + h_content = generate_element_h(elem_name, class_name, stream_name, cat, fake_ct, model, type_name) + cpp_content = generate_element_cpp(elem_name, class_name, stream_name, cat, fake_ct, model, type_name) + else: + h_content = generate_element_h(elem_name, class_name, stream_name, cat, ct, model, type_name) + cpp_content = generate_element_cpp(elem_name, class_name, stream_name, cat, ct, model, type_name) + + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h_content) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), cpp_content) + stats["elem_written"] += 1 + else: + stats["elem_skipped"] += 1 + + # Generate Group wrapper classes + groups_written = 0 + for group_name, group_children in model.groups.items(): + if group_name not in GENERATE_GROUPS: + continue + h_content = generate_group_h(group_name, group_children, model) + cpp_content = generate_group_cpp(group_name, group_children, model) + class_name = group_class_name(group_name) + write_file(os.path.join(ELEM_DIR, f"{class_name}.h"), h_content) + write_file(os.path.join(ELEM_DIR, f"{class_name}.cpp"), cpp_content) + groups_written += 1 + + print("=== Generation Stats ===") + print(f"Attributes structs written: {stats['attrs_written']}") + print(f"Element classes written: {stats['elem_written']}") + print(f"Choice classes written: {stats.get('choice_written', 0)}") + print(f"Inline choice written: {stats.get('inline_choice_written', 0)}") + print(f"Tree-based written: {stats.get('tree_written', 0)}") + print(f"Group classes written: {groups_written}") + print(f"Element classes skipped: {stats['elem_skipped']}") + print() + print("=== Element Categories ===") + for cat, names in sorted(categories.items()): + print(f" {cat}: {len(names)}") + print() + print("=== Skipped Elements ===") + for cat in ["choice", "sequence-with-choice", "anonymous", "unknown"]: + if cat in categories: + for n in categories[cat][:10]: + print(f" [{cat}] {n}") + + +def write_file(path, content): + with open(path, "w") as f: + f.write(content) + + +if __name__ == "__main__": + main() From 279a10a292fa670a2464b792f058dd07f5882005 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Fri, 22 May 2026 11:35:28 +0200 Subject: [PATCH 3/4] src: revert MusicXML 4.0 backports and fix typos exposed by revgen Non-generated consuming code that has to agree with what the revgen generator emits from the 3.x XSD in docs/musicxml.xsd. Classified per docs/ai/projects/gen/m2/triage.md. ArpeggiateFunctions.cpp / NotationsWriter.cpp: Revert a MusicXML 4.0 backport that had bolted a `none` value onto core::UpDown by way of a hand-rolled core::UpDownNone enum used for the arpeggiate `direction` attribute. The 3.x XSD only defines up-down (up | down), so the generator emits ArpeggiateAttributes with a core::UpDown direction; the impl layer must match. The api::MarkType::arpeggiate (undirected) case now sets hasDirection = false without assigning a value, and TODOs are left in place noting that the 'none' value returns as first-class in MusicXML 4.0 and should be restored under Milestone 5 (mxml4-types). ArpeggiateTest.cpp: Matching UpDownNone::up -> UpDown::up change in the core-level test fixture. Test semantics are unchanged; tests remain an invariant per the project's cardinal rules. MetronomeTest.cpp: Fix three `setPerMinuteOtBeatUnitChoice` typos to `setPerMinuteOrBeatUnitChoice` (Ot -> Or). The generator produces the correctly-spelled setter name on BeatUnitPer, so the existing test was relying on a typo that the regenerated header no longer carries. Part of Milestone 2 (fix-gen) in docs/ai/projects/gen/plan.md. --- src/private/mx/impl/ArpeggiateFunctions.cpp | 9 +++------ src/private/mx/impl/NotationsWriter.cpp | 6 +++--- src/private/mxtest/core/ArpeggiateTest.cpp | 2 +- src/private/mxtest/core/MetronomeTest.cpp | 6 +++--- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/private/mx/impl/ArpeggiateFunctions.cpp b/src/private/mx/impl/ArpeggiateFunctions.cpp index 5eaf47c6..cf3ec82b 100644 --- a/src/private/mx/impl/ArpeggiateFunctions.cpp +++ b/src/private/mx/impl/ArpeggiateFunctions.cpp @@ -25,17 +25,14 @@ api::MarkData ArpeggiateFunctions::parseArpeggiate() const switch (attr->direction) { - case core::UpDownNone::up: + case core::UpDown::up: markType = api::MarkType::arpeggiateUp; break; - case core::UpDownNone::down: + case core::UpDown::down: markType = api::MarkType::arpeggiateDown; break; - - case core::UpDownNone::none: // MusicXML 4.0 Backport - markType = api::MarkType::arpeggiate; - break; + // TODO: fixme - MusicXML 4.0 adds UpDownNone with 'none' value } } api::MarkData markData{markType}; diff --git a/src/private/mx/impl/NotationsWriter.cpp b/src/private/mx/impl/NotationsWriter.cpp index cae8f4fd..ecaee451 100644 --- a/src/private/mx/impl/NotationsWriter.cpp +++ b/src/private/mx/impl/NotationsWriter.cpp @@ -395,17 +395,17 @@ core::NotationsPtr NotationsWriter::getNotations() const if (mark.markType == api::MarkType::arpeggiate) { - attr.direction = core::UpDownNone::none; // MusicXML 4.0 Backport + // TODO: fixme - MusicXML 4.0 adds UpDownNone with 'none' value attr.hasDirection = false; } else if (mark.markType == api::MarkType::arpeggiateUp) { - attr.direction = core::UpDownNone::up; + attr.direction = core::UpDown::up; attr.hasDirection = true; } else if (mark.markType == api::MarkType::arpeggiateDown) { - attr.direction = core::UpDownNone::down; + attr.direction = core::UpDown::down; attr.hasDirection = true; } } diff --git a/src/private/mxtest/core/ArpeggiateTest.cpp b/src/private/mxtest/core/ArpeggiateTest.cpp index 03f46f76..4529c310 100644 --- a/src/private/mxtest/core/ArpeggiateTest.cpp +++ b/src/private/mxtest/core/ArpeggiateTest.cpp @@ -22,7 +22,7 @@ TEST(Test01, Arpeggiate) attributes1->hasDefaultX = true; attributes1->defaultX = TenthsValue{0.1}; attributes1->hasDirection = true; - attributes1->direction = UpDownNone::up; + attributes1->direction = UpDown::up; attributes1->hasNumber = true; attributes1->number = NumberLevel{2}; diff --git a/src/private/mxtest/core/MetronomeTest.cpp b/src/private/mxtest/core/MetronomeTest.cpp index 71ce3603..9b6eb14d 100644 --- a/src/private/mxtest/core/MetronomeTest.cpp +++ b/src/private/mxtest/core/MetronomeTest.cpp @@ -300,17 +300,17 @@ BeatUnitPerPtr tgenBeatUnitPer(TestMode v) { case TestMode::one: { o->setBeatUnitGroup(tgenBeatUnitGroup(TestMode::two)); - o->setPerMinuteOtBeatUnitChoice(tgenPerMinuteOrBeatUnitChoice(TestMode::three)); + o->setPerMinuteOrBeatUnitChoice(tgenPerMinuteOrBeatUnitChoice(TestMode::three)); } break; case TestMode::two: { o->setBeatUnitGroup(tgenBeatUnitGroup(TestMode::three)); - o->setPerMinuteOtBeatUnitChoice(tgenPerMinuteOrBeatUnitChoice(TestMode::one)); + o->setPerMinuteOrBeatUnitChoice(tgenPerMinuteOrBeatUnitChoice(TestMode::one)); } break; case TestMode::three: { o->setBeatUnitGroup(tgenBeatUnitGroup(TestMode::one)); - o->setPerMinuteOtBeatUnitChoice(tgenPerMinuteOrBeatUnitChoice(TestMode::two)); + o->setPerMinuteOrBeatUnitChoice(tgenPerMinuteOrBeatUnitChoice(TestMode::two)); } break; default: From cf0c22d2bf783d8aae03d4de586bfd0a983a10e3 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Fri, 22 May 2026 11:49:07 +0200 Subject: [PATCH 4/4] core: regenerate mx/core/elements/ from revgen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Output of `python3 gen/generate.py && make fmt` against the hand-written src/private/mx/core/elements/ tree, run on top of the revgen + M2 fix-gen work tracked in docs/ai/projects/gen/. These files are *generated*, not hand-edited. The generator (gen/generate.py) was reverse-engineered from the existing mx/core sources and docs/musicxml.xsd as Milestone 1 of the gen project, then iterated on through Milestone 2 (fix-gen) until make test-all reports 0 failures with the generated tree in place (final baseline: docs/ai/projects/gen/m2/test-all-d4-candidate.log, 2678 test cases, 9914 assertions). The project policy (plan.md, state.md) is normally to *not* check in regenerated code - the loop is `generate → fmt → test-all → reset` and the canonical mx/core stays hand-written. This commit deliberately breaks that policy to snapshot the generator output on the tip of the M2 work so the residual hand-written vs. generated diff is reviewable in git history. Scope: src/private/mx/core/elements/ only. 454 files changed (+2890 / -2500). No new files; no removed files; SKIP_ELEMENTS and CHOICE_SKIP in the generator are both empty. The remaining diff is the "unexplained residual" the gen project's eval.py measures, and is the surface area Milestone 3+ will continue to drive down. Per docs/ai/projects/gen/state.md, the next session should NOT treat this commit as canonical mx/core. To return to the hand-written tree, revert this commit (or `git checkout -- src/private/mx/core/ && git clean -fd src/private/mx/core/`). --- src/private/mx/core/elements/Accidental.cpp | 11 +- .../mx/core/elements/AccidentalAttributes.cpp | 18 +- .../mx/core/elements/AccidentalAttributes.h | 3 + .../elements/AccidentalMarkAttributes.cpp | 15 +- .../core/elements/AccidentalMarkAttributes.h | 3 + .../mx/core/elements/AccidentalText.cpp | 2 +- .../elements/AccidentalTextAttributes.cpp | 42 +-- .../core/elements/AccidentalTextAttributes.h | 7 + src/private/mx/core/elements/Accord.cpp | 8 - src/private/mx/core/elements/Accord.h | 2 +- .../mx/core/elements/AccordAttributes.cpp | 8 +- .../mx/core/elements/AccordAttributes.h | 2 +- .../core/elements/AccordionRegistration.cpp | 8 +- .../mx/core/elements/AccordionRegistration.h | 2 +- .../AccordionRegistrationAttributes.cpp | 4 +- src/private/mx/core/elements/Appearance.cpp | 9 +- .../mx/core/elements/ArpeggiateAttributes.cpp | 16 +- .../mx/core/elements/ArpeggiateAttributes.h | 5 +- src/private/mx/core/elements/Arrow.cpp | 2 - src/private/mx/core/elements/ArrowGroup.cpp | 17 +- .../mx/core/elements/Articulations.cpp | 3 +- src/private/mx/core/elements/Backup.cpp | 6 +- src/private/mx/core/elements/Barline.cpp | 19 +- src/private/mx/core/elements/Barline.h | 4 +- .../mx/core/elements/BarlineAttributes.cpp | 3 +- .../mx/core/elements/BarreAttributes.cpp | 13 +- .../mx/core/elements/BarreAttributes.h | 3 + src/private/mx/core/elements/Bass.cpp | 6 +- .../mx/core/elements/BassAlterAttributes.cpp | 16 +- .../mx/core/elements/BassAlterAttributes.h | 3 + .../mx/core/elements/BassStepAttributes.cpp | 11 +- .../mx/core/elements/BassStepAttributes.h | 4 + .../mx/core/elements/BeamAttributes.cpp | 13 +- src/private/mx/core/elements/BeamAttributes.h | 3 + src/private/mx/core/elements/BeatRepeat.cpp | 20 +- src/private/mx/core/elements/BeatRepeat.h | 3 +- .../mx/core/elements/BeatRepeatAttributes.cpp | 4 +- .../mx/core/elements/BeatUnitGroup.cpp | 67 ++--- src/private/mx/core/elements/BeatUnitGroup.h | 3 +- src/private/mx/core/elements/BeatUnitPer.cpp | 19 +- src/private/mx/core/elements/BeatUnitPer.h | 6 +- .../BeatUnitPerOrNoteRelationNoteChoice.cpp | 25 +- .../BeatUnitPerOrNoteRelationNoteChoice.h | 4 +- src/private/mx/core/elements/Bend.cpp | 21 +- src/private/mx/core/elements/Bend.h | 5 + src/private/mx/core/elements/BendChoice.cpp | 15 +- src/private/mx/core/elements/BendChoice.h | 12 +- .../mx/core/elements/BookmarkAttributes.cpp | 2 +- .../mx/core/elements/BookmarkAttributes.h | 1 + .../mx/core/elements/BracketAttributes.cpp | 51 ++-- .../mx/core/elements/BracketAttributes.h | 10 - .../mx/core/elements/BreathMarkAttributes.cpp | 13 +- .../mx/core/elements/BreathMarkAttributes.h | 4 +- src/private/mx/core/elements/Clef.cpp | 4 - src/private/mx/core/elements/Clef.h | 2 +- .../mx/core/elements/ClefAttributes.cpp | 4 +- src/private/mx/core/elements/Coda.h | 1 + src/private/mx/core/elements/Creator.cpp | 2 +- .../core/elements/CreditImageAttributes.cpp | 18 +- .../mx/core/elements/CreditImageAttributes.h | 2 + src/private/mx/core/elements/CreditType.cpp | 1 + .../core/elements/CreditWordsAttributes.cpp | 22 +- .../mx/core/elements/CreditWordsAttributes.h | 2 + .../mx/core/elements/CreditWordsGroup.h | 2 +- src/private/mx/core/elements/Damp.h | 1 + src/private/mx/core/elements/DampAll.h | 1 + .../mx/core/elements/DashesAttributes.cpp | 13 +- .../mx/core/elements/DashesAttributes.h | 3 + src/private/mx/core/elements/Degree.cpp | 16 +- src/private/mx/core/elements/Degree.h | 2 +- .../core/elements/DegreeAlterAttributes.cpp | 11 +- .../mx/core/elements/DegreeAlterAttributes.h | 3 + .../mx/core/elements/DegreeTypeAttributes.cpp | 11 +- .../mx/core/elements/DegreeTypeAttributes.h | 4 + .../core/elements/DegreeValueAttributes.cpp | 11 +- .../mx/core/elements/DegreeValueAttributes.h | 4 + .../DelayedInvertedTurnAttributes.cpp | 23 +- .../elements/DelayedInvertedTurnAttributes.h | 3 + .../core/elements/DelayedTurnAttributes.cpp | 21 +- .../mx/core/elements/DelayedTurnAttributes.h | 3 + .../mx/core/elements/DirectionType.cpp | 131 +++++----- src/private/mx/core/elements/DirectionType.h | 2 + src/private/mx/core/elements/Directive.cpp | 23 +- src/private/mx/core/elements/Directive.h | 6 +- .../mx/core/elements/DirectiveAttributes.cpp | 13 +- .../mx/core/elements/DirectiveAttributes.h | 3 + .../core/elements/DisplayStepOctaveGroup.cpp | 49 +--- .../core/elements/DisplayTextAttributes.cpp | 42 +-- .../mx/core/elements/DisplayTextAttributes.h | 7 + .../elements/DisplayTextOrAccidentalText.cpp | 41 +-- .../elements/DisplayTextOrAccidentalText.h | 10 +- .../mx/core/elements/DistanceAttributes.cpp | 2 +- src/private/mx/core/elements/Divisions.cpp | 1 + .../mx/core/elements/DynamicsAttributes.cpp | 11 +- .../mx/core/elements/EditorialGroup.cpp | 14 +- .../elements/EditorialVoiceDirectionGroup.cpp | 2 + .../mx/core/elements/EditorialVoiceGroup.cpp | 2 + .../mx/core/elements/ElisionAttributes.cpp | 24 +- .../mx/core/elements/ElisionAttributes.h | 5 + .../mx/core/elements/EmptyFontAttributes.cpp | 2 +- .../mx/core/elements/EmptyLineAttributes.cpp | 20 +- .../mx/core/elements/EmptyLineAttributes.h | 3 + .../elements/EmptyPlacementAttributes.cpp | 13 +- .../core/elements/EmptyPlacementAttributes.h | 3 + .../elements/EmptyTrillSoundAttributes.cpp | 21 +- .../core/elements/EmptyTrillSoundAttributes.h | 3 + src/private/mx/core/elements/Encoder.cpp | 2 +- .../mx/core/elements/EncoderAttributes.cpp | 2 +- src/private/mx/core/elements/EncodingChoice.h | 2 +- .../mx/core/elements/EndingAttributes.cpp | 23 +- .../mx/core/elements/EndingAttributes.h | 3 + .../mx/core/elements/ExtendAttributes.cpp | 17 +- .../mx/core/elements/ExtendAttributes.h | 3 + src/private/mx/core/elements/Eyeglasses.h | 1 + .../mx/core/elements/FermataAttributes.cpp | 8 +- src/private/mx/core/elements/Fifths.cpp | 2 +- src/private/mx/core/elements/Figure.cpp | 4 +- src/private/mx/core/elements/FigureNumber.cpp | 2 +- .../core/elements/FigureNumberAttributes.cpp | 13 +- .../mx/core/elements/FigureNumberAttributes.h | 3 + src/private/mx/core/elements/FiguredBass.cpp | 9 +- src/private/mx/core/elements/FiguredBass.h | 2 +- src/private/mx/core/elements/Fingering.cpp | 2 +- .../mx/core/elements/FingeringAttributes.cpp | 13 +- .../mx/core/elements/FingeringAttributes.h | 3 + .../mx/core/elements/FirstFretAttributes.h | 2 - src/private/mx/core/elements/Footnote.h | 1 + .../mx/core/elements/FootnoteAttributes.cpp | 42 +-- .../mx/core/elements/FootnoteAttributes.h | 7 + src/private/mx/core/elements/Forward.cpp | 2 +- src/private/mx/core/elements/Forward.h | 3 +- src/private/mx/core/elements/Frame.cpp | 8 - src/private/mx/core/elements/Frame.h | 2 +- .../mx/core/elements/FrameAttributes.cpp | 7 +- src/private/mx/core/elements/FrameNote.cpp | 8 - .../mx/core/elements/FretAttributes.cpp | 12 +- src/private/mx/core/elements/FretAttributes.h | 3 + .../mx/core/elements/FunctionAttributes.cpp | 11 +- .../mx/core/elements/FunctionAttributes.h | 3 + .../mx/core/elements/GlissandoAttributes.cpp | 22 +- .../mx/core/elements/GlissandoAttributes.h | 3 + .../mx/core/elements/GraceAttributes.cpp | 2 +- .../elements/GroupAbbreviationAttributes.cpp | 11 +- .../elements/GroupAbbreviationAttributes.h | 3 + .../elements/GroupAbbreviationDisplay.cpp | 49 ++-- .../core/elements/GroupAbbreviationDisplay.h | 10 +- .../core/elements/GroupBarlineAttributes.cpp | 2 +- .../mx/core/elements/GroupNameAttributes.cpp | 11 +- .../mx/core/elements/GroupNameAttributes.h | 3 + .../mx/core/elements/GroupNameDisplay.cpp | 48 ++-- .../mx/core/elements/GroupNameDisplay.h | 5 +- .../elements/GroupNameDisplayAttributes.cpp | 2 +- .../core/elements/GroupSymbolAttributes.cpp | 11 +- .../mx/core/elements/GroupSymbolAttributes.h | 3 + src/private/mx/core/elements/Grouping.h | 2 +- .../mx/core/elements/GroupingAttributes.cpp | 2 +- .../mx/core/elements/HammerOnAttributes.cpp | 19 +- .../mx/core/elements/HammerOnAttributes.h | 3 + .../mx/core/elements/HandbellAttributes.cpp | 13 +- .../mx/core/elements/HandbellAttributes.h | 3 + src/private/mx/core/elements/Harmonic.cpp | 62 +++-- src/private/mx/core/elements/Harmonic.h | 7 +- .../mx/core/elements/HarmonicAttributes.cpp | 2 +- .../mx/core/elements/HarmonicInfoChoice.cpp | 18 +- .../mx/core/elements/HarmonicInfoChoice.h | 20 +- .../mx/core/elements/HarmonicTypeChoice.cpp | 13 +- .../mx/core/elements/HarmonicTypeChoice.h | 16 +- src/private/mx/core/elements/HarpPedals.h | 2 +- .../mx/core/elements/HarpPedalsAttributes.cpp | 7 +- src/private/mx/core/elements/Heel.cpp | 8 +- src/private/mx/core/elements/Heel.h | 2 +- src/private/mx/core/elements/Hole.cpp | 6 +- src/private/mx/core/elements/Hole.h | 2 +- src/private/mx/core/elements/HoleClosed.cpp | 9 +- .../mx/core/elements/HoleClosedAttributes.cpp | 2 +- .../mx/core/elements/Identification.cpp | 2 +- .../mx/core/elements/ImageAttributes.cpp | 20 +- .../mx/core/elements/ImageAttributes.h | 2 + .../mx/core/elements/InstrumentAttributes.cpp | 2 +- .../mx/core/elements/InstrumentSound.cpp | 3 +- .../mx/core/elements/Interchangeable.cpp | 11 - .../mx/core/elements/Interchangeable.h | 2 +- src/private/mx/core/elements/Inversion.cpp | 2 +- .../mx/core/elements/InversionAttributes.cpp | 13 +- .../mx/core/elements/InversionAttributes.h | 3 + .../mx/core/elements/InvertedMordent.cpp | 9 +- .../mx/core/elements/InvertedMordent.h | 2 +- .../elements/InvertedMordentAttributes.cpp | 14 +- .../core/elements/InvertedTurnAttributes.cpp | 23 +- .../mx/core/elements/InvertedTurnAttributes.h | 3 + .../mx/core/elements/KeyAttributes.cpp | 2 +- src/private/mx/core/elements/KeyChoice.cpp | 5 +- src/private/mx/core/elements/KeyChoice.h | 2 +- src/private/mx/core/elements/KeyOctave.cpp | 2 +- .../mx/core/elements/KeyOctaveAttributes.cpp | 3 +- src/private/mx/core/elements/KeyStep.cpp | 1 - src/private/mx/core/elements/Kind.cpp | 2 +- .../mx/core/elements/KindAttributes.cpp | 21 +- src/private/mx/core/elements/KindAttributes.h | 6 + src/private/mx/core/elements/LayoutGroup.cpp | 70 ++--- src/private/mx/core/elements/LeftDivider.h | 1 + .../mx/core/elements/LevelAttributes.cpp | 4 +- src/private/mx/core/elements/LineWidth.cpp | 2 +- .../mx/core/elements/LineWidthAttributes.cpp | 4 +- src/private/mx/core/elements/Link.cpp | 2 +- .../mx/core/elements/LinkAttributes.cpp | 27 +- src/private/mx/core/elements/LinkAttributes.h | 2 +- .../core/elements/LyricLanguageAttributes.cpp | 6 +- .../mx/core/elements/MeasureAttributes.cpp | 3 +- .../mx/core/elements/MeasureAttributes.h | 1 + .../mx/core/elements/MeasureLayout.cpp | 7 +- .../elements/MeasureNumberingAttributes.cpp | 19 +- .../elements/MeasureNumberingAttributes.h | 5 + .../mx/core/elements/MeasureRepeat.cpp | 2 +- .../core/elements/MeasureRepeatAttributes.cpp | 2 +- .../mx/core/elements/MeasureStyleChoice.cpp | 3 +- .../mx/core/elements/MeasureStyleChoice.h | 8 + src/private/mx/core/elements/Membrane.cpp | 1 - src/private/mx/core/elements/Metronome.cpp | 243 +++++++++--------- src/private/mx/core/elements/Metronome.h | 10 + .../mx/core/elements/MetronomeAttributes.cpp | 4 +- .../core/elements/MetronomeBeamAttributes.cpp | 2 +- .../mx/core/elements/MetronomeNote.cpp | 5 - .../mx/core/elements/MetronomeRelation.cpp | 3 +- .../mx/core/elements/MetronomeRelation.h | 10 + .../core/elements/MetronomeRelationGroup.cpp | 8 +- .../mx/core/elements/MetronomeRelationGroup.h | 4 - .../mx/core/elements/MetronomeTuplet.cpp | 14 +- .../mx/core/elements/MetronomeTuplet.h | 2 +- .../elements/MetronomeTupletAttributes.cpp | 7 +- .../elements/MidiDeviceInstrumentGroup.cpp | 29 +-- .../mx/core/elements/MidiInstrument.cpp | 3 +- src/private/mx/core/elements/MidiInstrument.h | 2 +- .../elements/MidiInstrumentAttributes.cpp | 2 +- src/private/mx/core/elements/MidiName.h | 1 + .../mx/core/elements/Miscellaneous.cpp | 2 +- .../elements/MiscellaneousFieldAttributes.cpp | 2 +- src/private/mx/core/elements/Mode.cpp | 3 +- src/private/mx/core/elements/Mordent.cpp | 8 +- src/private/mx/core/elements/Mordent.h | 2 +- .../mx/core/elements/MordentAttributes.cpp | 14 +- .../core/elements/MultipleRestAttributes.cpp | 2 +- .../core/elements/NonArpeggiateAttributes.cpp | 16 +- .../core/elements/NonArpeggiateAttributes.h | 3 + .../mx/core/elements/NonTraditionalKey.cpp | 63 ++--- .../mx/core/elements/NormalNoteGroup.cpp | 2 + .../elements/NormalTypeNormalDotGroup.cpp | 19 +- src/private/mx/core/elements/Notations.cpp | 72 +++--- .../mx/core/elements/NotationsChoice.cpp | 75 +++--- .../mx/core/elements/NotationsChoice.h | 80 ++---- src/private/mx/core/elements/Note.cpp | 26 +- src/private/mx/core/elements/NoteAttributes.h | 1 + .../mx/core/elements/NoteRelationNote.cpp | 14 +- .../mx/core/elements/NoteSizeAttributes.cpp | 2 +- src/private/mx/core/elements/Notehead.cpp | 2 +- .../mx/core/elements/NoteheadAttributes.cpp | 10 +- src/private/mx/core/elements/NoteheadText.cpp | 84 +++--- src/private/mx/core/elements/NoteheadText.h | 4 +- .../mx/core/elements/NoteheadTextChoice.cpp | 37 +-- .../mx/core/elements/NoteheadTextChoice.h | 12 +- src/private/mx/core/elements/OctaveChange.cpp | 1 + .../core/elements/OctaveShiftAttributes.cpp | 21 +- .../mx/core/elements/OctaveShiftAttributes.h | 3 + .../mx/core/elements/OffsetAttributes.cpp | 2 +- .../mx/core/elements/OpusAttributes.cpp | 23 +- src/private/mx/core/elements/OpusAttributes.h | 2 +- src/private/mx/core/elements/Ornaments.cpp | 165 +++++++++--- src/private/mx/core/elements/Ornaments.h | 4 +- .../mx/core/elements/OrnamentsChoice.cpp | 140 ++-------- .../mx/core/elements/OrnamentsChoice.h | 74 ++---- .../elements/OtherAppearanceAttributes.cpp | 2 +- .../elements/OtherArticulationAttributes.cpp | 13 +- .../elements/OtherArticulationAttributes.h | 3 + .../elements/OtherDirectionAttributes.cpp | 21 +- .../core/elements/OtherDirectionAttributes.h | 5 + .../core/elements/OtherNotationAttributes.cpp | 19 +- .../core/elements/OtherNotationAttributes.h | 3 + .../core/elements/OtherOrnamentAttributes.cpp | 13 +- .../core/elements/OtherOrnamentAttributes.h | 3 + src/private/mx/core/elements/OtherPlay.cpp | 2 +- .../mx/core/elements/OtherPlayAttributes.cpp | 4 +- .../mx/core/elements/OtherPlayAttributes.h | 2 +- .../elements/OtherTechnicalAttributes.cpp | 15 +- .../core/elements/OtherTechnicalAttributes.h | 3 + src/private/mx/core/elements/PageLayout.cpp | 15 +- src/private/mx/core/elements/PageLayout.h | 2 +- src/private/mx/core/elements/PageMargins.cpp | 18 -- src/private/mx/core/elements/PageMargins.h | 2 +- .../core/elements/PageMarginsAttributes.cpp | 2 +- .../mx/core/elements/PartAbbreviation.cpp | 2 +- .../elements/PartAbbreviationAttributes.cpp | 13 +- .../elements/PartAbbreviationAttributes.h | 3 + .../core/elements/PartAbbreviationDisplay.cpp | 52 ++-- .../core/elements/PartAbbreviationDisplay.h | 5 +- src/private/mx/core/elements/PartGroup.cpp | 3 +- src/private/mx/core/elements/PartGroup.h | 2 +- .../mx/core/elements/PartGroupAttributes.cpp | 2 +- .../mx/core/elements/PartNameAttributes.cpp | 10 +- .../mx/core/elements/PartNameDisplay.cpp | 36 ++- .../mx/core/elements/PartNameDisplay.h | 5 +- .../mx/core/elements/PartSymbolAttributes.cpp | 12 +- .../mx/core/elements/PartSymbolAttributes.h | 3 + .../mx/core/elements/PedalAttributes.cpp | 24 +- .../mx/core/elements/PedalAttributes.h | 5 + src/private/mx/core/elements/PedalTuning.cpp | 8 - .../mx/core/elements/PerMinuteAttributes.cpp | 5 +- .../elements/PerMinuteOrBeatUnitChoice.cpp | 25 +- .../core/elements/PerMinuteOrBeatUnitChoice.h | 6 +- .../mx/core/elements/PercussionAttributes.cpp | 8 +- .../mx/core/elements/PercussionChoice.cpp | 2 +- .../mx/core/elements/PercussionChoice.h | 2 + src/private/mx/core/elements/Pitch.cpp | 12 +- src/private/mx/core/elements/Pitched.cpp | 1 - src/private/mx/core/elements/Play.cpp | 63 +++-- src/private/mx/core/elements/Play.h | 6 +- src/private/mx/core/elements/PlayAttributes.h | 1 - src/private/mx/core/elements/Pluck.cpp | 2 +- .../mx/core/elements/PluckAttributes.cpp | 13 +- .../mx/core/elements/PluckAttributes.h | 3 + .../mx/core/elements/PrefixAttributes.cpp | 13 +- .../mx/core/elements/PrefixAttributes.h | 3 + .../elements/PrincipalVoiceAttributes.cpp | 25 +- .../core/elements/PrincipalVoiceAttributes.h | 5 + src/private/mx/core/elements/Print.cpp | 2 +- src/private/mx/core/elements/Print.h | 2 +- .../mx/core/elements/PrintAttributes.h | 2 +- src/private/mx/core/elements/Properties.cpp | 181 ++++++------- .../mx/core/elements/PullOffAttributes.cpp | 19 +- .../mx/core/elements/PullOffAttributes.h | 3 + .../mx/core/elements/RehearsalAttributes.cpp | 42 +-- .../mx/core/elements/RehearsalAttributes.h | 7 + .../mx/core/elements/RelationAttributes.cpp | 2 +- .../mx/core/elements/RelationAttributes.h | 2 +- .../mx/core/elements/RepeatAttributes.cpp | 5 +- src/private/mx/core/elements/Rest.cpp | 10 +- src/private/mx/core/elements/Rest.h | 2 +- src/private/mx/core/elements/RightDivider.h | 1 + .../mx/core/elements/RightsAttributes.h | 2 +- src/private/mx/core/elements/Root.cpp | 6 +- .../mx/core/elements/RootAlterAttributes.cpp | 18 +- .../mx/core/elements/RootAlterAttributes.h | 3 + .../mx/core/elements/RootStepAttributes.cpp | 13 +- .../mx/core/elements/RootStepAttributes.h | 4 + src/private/mx/core/elements/Scaling.cpp | 10 +- .../mx/core/elements/ScoreHeaderGroup.cpp | 29 +-- .../mx/core/elements/ScoreInstrument.cpp | 24 +- .../mx/core/elements/ScoreInstrument.h | 19 +- .../elements/ScoreInstrumentAttributes.cpp | 2 +- .../core/elements/ScoreInstrumentAttributes.h | 1 - src/private/mx/core/elements/ScorePart.cpp | 56 ++-- src/private/mx/core/elements/ScorePart.h | 2 +- .../mx/core/elements/ScorePartwise.cpp | 3 +- .../core/elements/ScorePartwiseAttributes.cpp | 3 - .../core/elements/ScorePartwiseAttributes.h | 1 + .../mx/core/elements/ScoreTimewise.cpp | 3 +- src/private/mx/core/elements/ScoreTimewise.h | 3 +- .../core/elements/ScoreTimewiseAttributes.cpp | 2 - .../core/elements/ScoreTimewiseAttributes.h | 1 + src/private/mx/core/elements/Segno.h | 1 + src/private/mx/core/elements/Slash.cpp | 20 +- src/private/mx/core/elements/Slash.h | 3 +- .../mx/core/elements/SlashAttributes.cpp | 4 +- .../mx/core/elements/SlideAttributes.cpp | 22 +- .../mx/core/elements/SlideAttributes.h | 3 + .../mx/core/elements/SlurAttributes.cpp | 10 +- src/private/mx/core/elements/SlurAttributes.h | 23 -- src/private/mx/core/elements/Solo.cpp | 2 +- .../mx/core/elements/SoloOrEnsembleChoice.cpp | 15 +- .../mx/core/elements/SoloOrEnsembleChoice.h | 16 +- src/private/mx/core/elements/Sound.cpp | 42 ++- src/private/mx/core/elements/Sound.h | 11 +- .../mx/core/elements/SoundAttributes.h | 2 +- src/private/mx/core/elements/StaffDetails.cpp | 17 +- src/private/mx/core/elements/StaffDetails.h | 5 +- .../core/elements/StaffDetailsAttributes.cpp | 6 +- src/private/mx/core/elements/StaffLayout.cpp | 8 +- src/private/mx/core/elements/StaffLayout.h | 2 +- .../core/elements/StaffLayoutAttributes.cpp | 2 +- src/private/mx/core/elements/StaffTuning.cpp | 9 - src/private/mx/core/elements/StaffTuning.h | 2 +- .../core/elements/StaffTuningAttributes.cpp | 2 +- src/private/mx/core/elements/StaffType.cpp | 1 - .../mx/core/elements/StemAttributes.cpp | 11 +- src/private/mx/core/elements/StemAttributes.h | 3 + src/private/mx/core/elements/Stick.cpp | 8 - src/private/mx/core/elements/Stick.h | 2 +- src/private/mx/core/elements/String.cpp | 12 +- src/private/mx/core/elements/String.h | 4 +- .../mx/core/elements/StringMuteAttributes.cpp | 21 +- .../mx/core/elements/StringMuteAttributes.h | 5 + src/private/mx/core/elements/StrongAccent.cpp | 8 +- src/private/mx/core/elements/StrongAccent.h | 2 +- .../core/elements/StrongAccentAttributes.cpp | 8 +- .../mx/core/elements/SuffixAttributes.cpp | 11 +- .../mx/core/elements/SuffixAttributes.h | 3 + .../mx/core/elements/SupportsAttributes.cpp | 7 +- .../mx/core/elements/SupportsAttributes.h | 1 - src/private/mx/core/elements/Syllabic.cpp | 1 - .../mx/core/elements/SystemDistance.cpp | 1 + .../mx/core/elements/SystemDividers.cpp | 10 - src/private/mx/core/elements/SystemLayout.cpp | 8 +- src/private/mx/core/elements/SystemLayout.h | 2 + .../mx/core/elements/SystemMargins.cpp | 12 +- .../mx/core/elements/TapAttributes.cpp | 13 +- src/private/mx/core/elements/TapAttributes.h | 3 + .../mx/core/elements/TextAttributes.cpp | 22 +- src/private/mx/core/elements/TextAttributes.h | 5 + .../mx/core/elements/TieAttributes.cpp | 4 +- src/private/mx/core/elements/TieAttributes.h | 1 - .../mx/core/elements/TiedAttributes.cpp | 8 +- src/private/mx/core/elements/Time.cpp | 5 +- .../mx/core/elements/TimeAttributes.cpp | 2 +- .../mx/core/elements/TimeModification.cpp | 21 +- .../TimeModificationNormalTypeNormalDot.cpp | 53 +++- .../mx/core/elements/TimeSignatureGroup.cpp | 15 +- .../mx/core/elements/TimeSignatureGroup.h | 6 + .../mx/core/elements/TimewiseMeasure.cpp | 1 - .../mx/core/elements/TimewiseMeasure.h | 4 +- src/private/mx/core/elements/TimewisePart.cpp | 1 - src/private/mx/core/elements/Toe.cpp | 8 +- src/private/mx/core/elements/Toe.h | 2 +- .../mx/core/elements/TraditionalKey.cpp | 58 ++--- src/private/mx/core/elements/TraditionalKey.h | 2 +- src/private/mx/core/elements/Transpose.cpp | 4 - src/private/mx/core/elements/Transpose.h | 2 +- .../mx/core/elements/TremoloAttributes.cpp | 11 +- .../mx/core/elements/TremoloAttributes.h | 3 + src/private/mx/core/elements/Tuplet.cpp | 10 +- src/private/mx/core/elements/Tuplet.h | 2 +- src/private/mx/core/elements/TupletActual.cpp | 2 +- .../mx/core/elements/TupletAttributes.cpp | 10 +- .../mx/core/elements/TupletDotAttributes.cpp | 9 +- .../mx/core/elements/TupletDotAttributes.h | 3 + src/private/mx/core/elements/TupletNormal.cpp | 2 +- .../core/elements/TupletNumberAttributes.cpp | 14 +- .../mx/core/elements/TupletNumberAttributes.h | 3 + .../mx/core/elements/TupletTypeAttributes.cpp | 12 +- .../mx/core/elements/TupletTypeAttributes.h | 3 + .../mx/core/elements/TurnAttributes.cpp | 21 +- src/private/mx/core/elements/TurnAttributes.h | 3 + .../mx/core/elements/TypeAttributes.cpp | 4 +- src/private/mx/core/elements/Unpitched.cpp | 10 +- .../mx/core/elements/VirtualInstrument.cpp | 8 +- src/private/mx/core/elements/Volume.cpp | 1 + .../mx/core/elements/WavyLineAttributes.cpp | 18 +- .../mx/core/elements/WavyLineAttributes.h | 3 + .../mx/core/elements/WedgeAttributes.cpp | 27 +- .../mx/core/elements/WedgeAttributes.h | 7 +- .../mx/core/elements/WithBarAttributes.cpp | 15 +- .../mx/core/elements/WithBarAttributes.h | 3 + src/private/mx/core/elements/Wood.cpp | 1 - .../mx/core/elements/WordsAttributes.cpp | 40 +-- .../mx/core/elements/WordsAttributes.h | 7 + src/private/mx/core/elements/Work.cpp | 8 +- 454 files changed, 2890 insertions(+), 2500 deletions(-) diff --git a/src/private/mx/core/elements/Accidental.cpp b/src/private/mx/core/elements/Accidental.cpp index 815d8dd4..4dbb30e2 100644 --- a/src/private/mx/core/elements/Accidental.cpp +++ b/src/private/mx/core/elements/Accidental.cpp @@ -79,16 +79,7 @@ bool Accidental::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xele { bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); - if (xelement.getValue() == "double-flat") - { - message << "Accidental: 'double-flat' is not a valid MusicXML 'accidental' value - converting it to 'flat-flat'" - << std::endl; - myValue = parseAccidentalValue("flat-flat"); - } - else - { - myValue = parseAccidentalValue(xelement.getValue()); - } + myValue = parseAccidentalValue(xelement.getValue()); MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/AccidentalAttributes.cpp b/src/private/mx/core/elements/AccidentalAttributes.cpp index 7ea8d68c..6330a19a 100644 --- a/src/private/mx/core/elements/AccidentalAttributes.cpp +++ b/src/private/mx/core/elements/AccidentalAttributes.cpp @@ -11,18 +11,19 @@ namespace mx namespace core { AccidentalAttributes::AccidentalAttributes() - : cautionary(YesNo::no), editorial(YesNo::no), parentheses(YesNo::no), bracket(), size(), defaultX(), defaultY(), - relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), hasCautionary(false), hasEditorial(false), hasParentheses(false), - hasBracket(false), hasSize(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false) + : cautionary(YesNo::no), editorial(YesNo::no), parentheses(YesNo::no), bracket(YesNo::no), size(SymbolSize::full), + defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasCautionary(false), hasEditorial(false), + hasParentheses(false), hasBracket(false), hasSize(false), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), + hasFontWeight(false), hasColor(false) { } bool AccidentalAttributes::hasValues() const { return hasCautionary || hasEditorial || hasParentheses || hasBracket || hasSize || hasDefaultX || hasDefaultY || - hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight; + hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &AccidentalAttributes::toStream(std::ostream &os) const @@ -42,6 +43,7 @@ std::ostream &AccidentalAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -109,6 +111,10 @@ bool AccidentalAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/AccidentalAttributes.h b/src/private/mx/core/elements/AccidentalAttributes.h index 88071b67..5cf087f3 100644 --- a/src/private/mx/core/elements/AccidentalAttributes.h +++ b/src/private/mx/core/elements/AccidentalAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -41,6 +42,7 @@ struct AccidentalAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasCautionary; bool hasEditorial; bool hasParentheses; @@ -54,6 +56,7 @@ struct AccidentalAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/AccidentalMarkAttributes.cpp b/src/private/mx/core/elements/AccidentalMarkAttributes.cpp index dda6ddd5..830495cf 100644 --- a/src/private/mx/core/elements/AccidentalMarkAttributes.cpp +++ b/src/private/mx/core/elements/AccidentalMarkAttributes.cpp @@ -12,16 +12,16 @@ namespace core { AccidentalMarkAttributes::AccidentalMarkAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool AccidentalMarkAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &AccidentalMarkAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &AccidentalMarkAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,13 +85,17 @@ bool AccidentalMarkAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/AccidentalMarkAttributes.h b/src/private/mx/core/elements/AccidentalMarkAttributes.h index 43fda2a5..77f3d6df 100644 --- a/src/private/mx/core/elements/AccidentalMarkAttributes.h +++ b/src/private/mx/core/elements/AccidentalMarkAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct AccidentalMarkAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct AccidentalMarkAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/AccidentalText.cpp b/src/private/mx/core/elements/AccidentalText.cpp index 773d686f..52071f2b 100644 --- a/src/private/mx/core/elements/AccidentalText.cpp +++ b/src/private/mx/core/elements/AccidentalText.cpp @@ -81,7 +81,7 @@ bool AccidentalText::fromXElementImpl(std::ostream &message, ::ezxml::XElement & bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue = parseAccidentalValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/AccidentalTextAttributes.cpp b/src/private/mx/core/elements/AccidentalTextAttributes.cpp index 74f8c9ea..6839af9c 100644 --- a/src/private/mx/core/elements/AccidentalTextAttributes.cpp +++ b/src/private/mx/core/elements/AccidentalTextAttributes.cpp @@ -11,22 +11,23 @@ namespace mx namespace core { AccidentalTextAttributes::AccidentalTextAttributes() - : justify(LeftCenterRight::center), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), halign(), - underline(), overline(), lineThrough(), rotation(), letterSpacing(), lineHeight(), lang("it"), - space(XmlSpace::default_), enclosure(EnclosureShape::rectangle), hasJustify(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasHalign(false), hasUnderline(false), hasOverline(false), - hasLineThrough(false), hasRotation(false), hasLetterSpacing(false), hasLineHeight(false), hasLang(false), - hasSpace(false), hasEnclosure(false) + : justify(LeftCenterRight::left), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + halign(LeftCenterRight::left), valign(Valign::bottom), underline(), overline(), lineThrough(), rotation(), + letterSpacing(), lineHeight(), lang("it"), space(), dir(), enclosure(EnclosureShape::none), hasJustify(false), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), + hasValign(false), hasUnderline(false), hasOverline(false), hasLineThrough(false), hasRotation(false), + hasLetterSpacing(false), hasLineHeight(false), hasLang(false), hasSpace(false), hasDir(false), hasEnclosure(false) { } bool AccidentalTextAttributes::hasValues() const { return hasJustify || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight || hasHalign || hasUnderline || hasOverline || hasLineThrough || hasRotation || - hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasEnclosure; + hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign || hasUnderline || hasOverline || + hasLineThrough || hasRotation || hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasDir || + hasEnclosure; } std::ostream &AccidentalTextAttributes::toStream(std::ostream &os) const @@ -42,7 +43,9 @@ std::ostream &AccidentalTextAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); streamAttribute(os, underline, "underline", hasUnderline); streamAttribute(os, overline, "overline", hasOverline); streamAttribute(os, lineThrough, "line-through", hasLineThrough); @@ -51,6 +54,7 @@ std::ostream &AccidentalTextAttributes::toStream(std::ostream &os) const streamAttribute(os, lineHeight, "line-height", hasLineHeight); streamAttribute(os, lang, "xml:lang", hasLang); streamAttribute(os, space, "xml:space", hasSpace); + streamAttribute(os, dir, "dir", hasDir); streamAttribute(os, enclosure, "enclosure", hasEnclosure); } return os; @@ -103,10 +107,18 @@ bool AccidentalTextAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, underline, hasUnderline, "underline")) { continue; @@ -131,19 +143,15 @@ bool AccidentalTextAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) - { - continue; - } if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, dir, hasDir, "dir", &parseTextDirection)) { continue; } @@ -154,7 +162,7 @@ bool AccidentalTextAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/AccidentalTextAttributes.h b/src/private/mx/core/elements/AccidentalTextAttributes.h index 596d8778..6ad50e08 100644 --- a/src/private/mx/core/elements/AccidentalTextAttributes.h +++ b/src/private/mx/core/elements/AccidentalTextAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -40,7 +41,9 @@ struct AccidentalTextAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; NumberOfLines underline; NumberOfLines overline; NumberOfLines lineThrough; @@ -49,6 +52,7 @@ struct AccidentalTextAttributes : public AttributesInterface NumberOrNormal lineHeight; XmlLang lang; XmlSpace space; + TextDirection dir; EnclosureShape enclosure; bool hasJustify; bool hasDefaultX; @@ -59,7 +63,9 @@ struct AccidentalTextAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; bool hasUnderline; bool hasOverline; bool hasLineThrough; @@ -68,6 +74,7 @@ struct AccidentalTextAttributes : public AttributesInterface bool hasLineHeight; bool hasLang; bool hasSpace; + bool hasDir; bool hasEnclosure; private: diff --git a/src/private/mx/core/elements/Accord.cpp b/src/private/mx/core/elements/Accord.cpp index 0269b87d..d039e307 100644 --- a/src/private/mx/core/elements/Accord.cpp +++ b/src/private/mx/core/elements/Accord.cpp @@ -142,14 +142,6 @@ bool Accord::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement } } - if (!isTuningStepFound) - { - message << "Accord: '" << myTuningStep->getElementName() << "' is required but was not found" << std::endl; - } - if (!isTuningOctaveFound) - { - message << "Accord: '" << myTuningOctave->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Accord.h b/src/private/mx/core/elements/Accord.h index b31b0b58..89b03894 100644 --- a/src/private/mx/core/elements/Accord.h +++ b/src/private/mx/core/elements/Accord.h @@ -39,7 +39,7 @@ class Accord : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; AccordAttributesPtr getAttributes() const; - void setAttributes(const AccordAttributesPtr &value); + void setAttributes(const AccordAttributesPtr &attributes); /* _________ TuningStep minOccurs = 1, maxOccurs = 1 _________ */ TuningStepPtr getTuningStep() const; diff --git a/src/private/mx/core/elements/AccordAttributes.cpp b/src/private/mx/core/elements/AccordAttributes.cpp index 3ebc2beb..3f54a159 100644 --- a/src/private/mx/core/elements/AccordAttributes.cpp +++ b/src/private/mx/core/elements/AccordAttributes.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -AccordAttributes::AccordAttributes() : string(), hasString(false) +AccordAttributes::AccordAttributes() : string_(), hasString(false) { } @@ -23,7 +23,7 @@ std::ostream &AccordAttributes::toStream(std::ostream &os) const { if (hasValues()) { - streamAttribute(os, string, "string", hasString); + streamAttribute(os, string_, "string", hasString); } return os; } @@ -38,13 +38,13 @@ bool AccordAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement for (; it != endIter; ++it) { - if (parseAttribute(message, it, className, isSuccess, string, hasString, "string")) + if (parseAttribute(message, it, className, isSuccess, string_, hasString, "string")) { continue; } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/AccordAttributes.h b/src/private/mx/core/elements/AccordAttributes.h index ff0e32ca..2a30fa8e 100644 --- a/src/private/mx/core/elements/AccordAttributes.h +++ b/src/private/mx/core/elements/AccordAttributes.h @@ -25,7 +25,7 @@ struct AccordAttributes : public AttributesInterface AccordAttributes(); virtual bool hasValues() const; virtual std::ostream &toStream(std::ostream &os) const; - StringNumber string; + StringNumber string_; bool hasString; private: diff --git a/src/private/mx/core/elements/AccordionRegistration.cpp b/src/private/mx/core/elements/AccordionRegistration.cpp index 1813ec06..0fb20347 100644 --- a/src/private/mx/core/elements/AccordionRegistration.cpp +++ b/src/private/mx/core/elements/AccordionRegistration.cpp @@ -43,7 +43,6 @@ bool AccordionRegistration::hasContents() const std::ostream &AccordionRegistration::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - isOneLineOnly = !hasContents(); if (myHasAccordionHigh) { os << std::endl; @@ -59,10 +58,15 @@ std::ostream &AccordionRegistration::streamContents(std::ostream &os, const int os << std::endl; myAccordionLow->toStream(os, indentLevel + 1); } - if (hasContents()) + if (myHasAccordionHigh || myHasAccordionMiddle || myHasAccordionLow) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; } diff --git a/src/private/mx/core/elements/AccordionRegistration.h b/src/private/mx/core/elements/AccordionRegistration.h index ef245326..cca4490f 100644 --- a/src/private/mx/core/elements/AccordionRegistration.h +++ b/src/private/mx/core/elements/AccordionRegistration.h @@ -39,7 +39,7 @@ class AccordionRegistration : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; AccordionRegistrationAttributesPtr getAttributes() const; - void setAttributes(const AccordionRegistrationAttributesPtr &value); + void setAttributes(const AccordionRegistrationAttributesPtr &attributes); /* _________ AccordionHigh minOccurs = 0, maxOccurs = 1 _________ */ AccordionHighPtr getAccordionHigh() const; diff --git a/src/private/mx/core/elements/AccordionRegistrationAttributes.cpp b/src/private/mx/core/elements/AccordionRegistrationAttributes.cpp index 06be0566..ccc1e7d7 100644 --- a/src/private/mx/core/elements/AccordionRegistrationAttributes.cpp +++ b/src/private/mx/core/elements/AccordionRegistrationAttributes.cpp @@ -12,8 +12,8 @@ namespace core { AccordionRegistrationAttributes::AccordionRegistrationAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::center), - valign(Valign::baseline), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::left), + valign(Valign::bottom), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false) { diff --git a/src/private/mx/core/elements/Appearance.cpp b/src/private/mx/core/elements/Appearance.cpp index 74b1caf0..1f91ced3 100644 --- a/src/private/mx/core/elements/Appearance.cpp +++ b/src/private/mx/core/elements/Appearance.cpp @@ -62,11 +62,16 @@ std::ostream &Appearance::streamContents(std::ostream &os, const int indentLevel os << std::endl; x->toStream(os, indentLevel + 1); } - isOneLineOnly = !hasContents(); - if (!isOneLineOnly) + if (myLineWidthSet.size() > 0 || myNoteSizeSet.size() > 0 || myDistanceSet.size() > 0 || + myOtherAppearanceSet.size() > 0) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; } diff --git a/src/private/mx/core/elements/ArpeggiateAttributes.cpp b/src/private/mx/core/elements/ArpeggiateAttributes.cpp index 0e219f9b..9f309494 100644 --- a/src/private/mx/core/elements/ArpeggiateAttributes.cpp +++ b/src/private/mx/core/elements/ArpeggiateAttributes.cpp @@ -11,15 +11,16 @@ namespace mx namespace core { ArpeggiateAttributes::ArpeggiateAttributes() - : number(), direction(), defaultX(), defaultY(), relativeX(), relativeY(), placement(AboveBelow::below), + : number(), direction(), defaultX(), defaultY(), relativeX(), relativeY(), placement(AboveBelow::below), color(), hasNumber(false), hasDirection(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasPlacement(false) + hasRelativeY(false), hasPlacement(false), hasColor(false) { } bool ArpeggiateAttributes::hasValues() const { - return hasNumber || hasDirection || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasPlacement; + return hasNumber || hasDirection || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasPlacement || + hasColor; } std::ostream &ArpeggiateAttributes::toStream(std::ostream &os) const @@ -33,6 +34,7 @@ std::ostream &ArpeggiateAttributes::toStream(std::ostream &os) const streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); streamAttribute(os, placement, "placement", hasPlacement); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -51,7 +53,7 @@ bool ArpeggiateAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle { continue; } - if (parseAttribute(message, it, className, isSuccess, direction, hasDirection, "direction", &parseUpDownNone)) + if (parseAttribute(message, it, className, isSuccess, direction, hasDirection, "direction", &parseUpDown)) { continue; } @@ -75,9 +77,13 @@ bool ArpeggiateAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/ArpeggiateAttributes.h b/src/private/mx/core/elements/ArpeggiateAttributes.h index ea59a569..34cbda6d 100644 --- a/src/private/mx/core/elements/ArpeggiateAttributes.h +++ b/src/private/mx/core/elements/ArpeggiateAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" @@ -28,12 +29,13 @@ struct ArpeggiateAttributes : public AttributesInterface virtual bool hasValues() const; virtual std::ostream &toStream(std::ostream &os) const; NumberLevel number; - UpDownNone direction; // MusicXML 4.0 Backport: was UpDown in 3.x (no "none" value) + UpDown direction; TenthsValue defaultX; TenthsValue defaultY; TenthsValue relativeX; TenthsValue relativeY; AboveBelow placement; + Color color; bool hasNumber; bool hasDirection; bool hasDefaultX; @@ -41,6 +43,7 @@ struct ArpeggiateAttributes : public AttributesInterface bool hasRelativeX; bool hasRelativeY; bool hasPlacement; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Arrow.cpp b/src/private/mx/core/elements/Arrow.cpp index 30e7e2ab..c6c494ca 100644 --- a/src/private/mx/core/elements/Arrow.cpp +++ b/src/private/mx/core/elements/Arrow.cpp @@ -124,11 +124,9 @@ bool Arrow::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { myChoice = Choice::circularArrow; isSuccess &= myCircularArrow->fromXElement(message, *it); - // this is a mistake I think MX_RETURN_IS_SUCCESS; } myChoice = Choice::arrowGroup; isSuccess = myArrowGroup->fromXElement(message, xelement); - // this is a mistake I think MX_RETURN_IS_SUCCESS; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/ArrowGroup.cpp b/src/private/mx/core/elements/ArrowGroup.cpp index 0456937c..bdb2a082 100644 --- a/src/private/mx/core/elements/ArrowGroup.cpp +++ b/src/private/mx/core/elements/ArrowGroup.cpp @@ -39,13 +39,19 @@ bool ArrowGroup::hasContents() const std::ostream &ArrowGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + bool isFirst = true; + if (!isFirst) + os << std::endl; myArrowDirection->toStream(os, indentLevel); + isFirst = false; if (myHasArrowStyle) { - os << std::endl; + if (!isFirst) + os << std::endl; myArrowStyle->toStream(os, indentLevel); + isFirst = false; } - isOneLineOnly = false; + isOneLineOnly = !hasContents(); return os; } @@ -89,7 +95,6 @@ bool ArrowGroup::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xele { bool isSuccess = true; bool isArrowDirectionFound = false; - for (auto it = xelement.begin(); it != xelement.end(); ++it) { const std::string elementName = it->getName(); @@ -108,12 +113,12 @@ bool ArrowGroup::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xele { if (!isArrowDirectionFound) { - isSuccess = false; - message << "ArrowGroup: 'arrow-direction' element is required but was not found" << std::endl; + message << "ArrowGroup: a 'arrow-direction' element is required but was not found" << std::endl; + return false; } - break; } } + MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Articulations.cpp b/src/private/mx/core/elements/Articulations.cpp index 37334068..c9246175 100644 --- a/src/private/mx/core/elements/Articulations.cpp +++ b/src/private/mx/core/elements/Articulations.cpp @@ -27,7 +27,8 @@ std::ostream &Articulations::streamAttributes(std::ostream &os) const std::ostream &Articulations::streamName(std::ostream &os) const { - return os << "articulations"; + os << "articulations"; + return os; } bool Articulations::hasContents() const diff --git a/src/private/mx/core/elements/Backup.cpp b/src/private/mx/core/elements/Backup.cpp index c3959c15..21c96856 100644 --- a/src/private/mx/core/elements/Backup.cpp +++ b/src/private/mx/core/elements/Backup.cpp @@ -39,15 +39,15 @@ bool Backup::hasContents() const std::ostream &Backup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myDuration->toStream(os, indentLevel + 1); - os << std::endl; - myEditorialGroup->streamContents(os, indentLevel + 1, isOneLineOnly); if (myEditorialGroup->hasContents()) { os << std::endl; + myEditorialGroup->streamContents(os, indentLevel + 1, isOneLineOnly); } - isOneLineOnly = false; + os << std::endl; return os; } diff --git a/src/private/mx/core/elements/Barline.cpp b/src/private/mx/core/elements/Barline.cpp index 7d51aed0..79d6b0fa 100644 --- a/src/private/mx/core/elements/Barline.cpp +++ b/src/private/mx/core/elements/Barline.cpp @@ -90,11 +90,16 @@ std::ostream &Barline::streamContents(std::ostream &os, const int indentLevel, b os << std::endl; myRepeat->toStream(os, indentLevel + 1); } - isOneLineOnly = !hasContents(); - if (!isOneLineOnly) + if (myHasBarStyle || true || myHasWavyLine || myHasSegno || myHasCoda || myFermataSet.size() > 0 || myHasEnding || + myHasRepeat) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; } @@ -225,10 +230,7 @@ void Barline::removeFermata(const FermataSetIterConst &value) { if (value != myFermataSet.cend()) { - if (myFermataSet.size() > 0) - { - myFermataSet.erase(value); - } + myFermataSet.erase(value); } } @@ -236,10 +238,7 @@ void Barline::addFermata(const FermataPtr &value) { if (value) { - if (myFermataSet.size() < 2) - { - myFermataSet.push_back(value); - } + myFermataSet.push_back(value); } } diff --git a/src/private/mx/core/elements/Barline.h b/src/private/mx/core/elements/Barline.h index 6b9a072e..6bd8f88d 100644 --- a/src/private/mx/core/elements/Barline.h +++ b/src/private/mx/core/elements/Barline.h @@ -44,7 +44,7 @@ class Barline : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; BarlineAttributesPtr getAttributes() const; - void setAttributes(const BarlineAttributesPtr &value); + void setAttributes(const BarlineAttributesPtr &attributes); /* _________ BarStyle minOccurs = 0, maxOccurs = 1 _________ */ BarStylePtr getBarStyle() const; @@ -74,7 +74,7 @@ class Barline : public ElementInterface bool getHasCoda() const; void setHasCoda(const bool value); - /* _________ Fermata minOccurs = 0, maxOccurs = 2 _________ */ + /* _________ Fermata minOccurs = 0, maxOccurs = unbounded _________ */ const FermataSet &getFermataSet() const; void addFermata(const FermataPtr &value); void removeFermata(const FermataSetIterConst &value); diff --git a/src/private/mx/core/elements/BarlineAttributes.cpp b/src/private/mx/core/elements/BarlineAttributes.cpp index f85a79a3..de0d491f 100644 --- a/src/private/mx/core/elements/BarlineAttributes.cpp +++ b/src/private/mx/core/elements/BarlineAttributes.cpp @@ -11,8 +11,7 @@ namespace mx namespace core { BarlineAttributes::BarlineAttributes() - : location(RightLeftMiddle::right), segno(), coda(), divisions(), hasLocation(false), hasSegno(false), - hasCoda(false), hasDivisions(false) + : location(), segno(), coda(), divisions(), hasLocation(false), hasSegno(false), hasCoda(false), hasDivisions(false) { } diff --git a/src/private/mx/core/elements/BarreAttributes.cpp b/src/private/mx/core/elements/BarreAttributes.cpp index a03675b5..09d51a3f 100644 --- a/src/private/mx/core/elements/BarreAttributes.cpp +++ b/src/private/mx/core/elements/BarreAttributes.cpp @@ -10,13 +10,13 @@ namespace mx { namespace core { -BarreAttributes::BarreAttributes() : type(StartStop::start), hasType(true) +BarreAttributes::BarreAttributes() : type(StartStop::start), color(), hasType(true), hasColor(false) { } bool BarreAttributes::hasValues() const { - return hasType; + return hasType || hasColor; } std::ostream &BarreAttributes::toStream(std::ostream &os) const @@ -24,6 +24,7 @@ std::ostream &BarreAttributes::toStream(std::ostream &os) const if (hasValues()) { streamAttribute(os, type, "type", hasType); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -43,15 +44,19 @@ bool BarreAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/BarreAttributes.h b/src/private/mx/core/elements/BarreAttributes.h index 85cdbda5..da477624 100644 --- a/src/private/mx/core/elements/BarreAttributes.h +++ b/src/private/mx/core/elements/BarreAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" @@ -26,7 +27,9 @@ struct BarreAttributes : public AttributesInterface virtual bool hasValues() const; virtual std::ostream &toStream(std::ostream &os) const; StartStop type; + Color color; const bool hasType; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Bass.cpp b/src/private/mx/core/elements/Bass.cpp index 8b1088ed..757e8b75 100644 --- a/src/private/mx/core/elements/Bass.cpp +++ b/src/private/mx/core/elements/Bass.cpp @@ -39,6 +39,7 @@ bool Bass::hasContents() const std::ostream &Bass::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myBassStep->toStream(os, indentLevel + 1); if (myHasBassAlter) @@ -47,7 +48,6 @@ std::ostream &Bass::streamContents(std::ostream &os, const int indentLevel, bool myBassAlter->toStream(os, indentLevel + 1); } os << std::endl; - isOneLineOnly = false; return os; } @@ -105,10 +105,6 @@ bool Bass::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) } } - if (!isBassStepFound) - { - message << "Bass: '" << myBassStep->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/BassAlterAttributes.cpp b/src/private/mx/core/elements/BassAlterAttributes.cpp index 5a6d81ca..44030e13 100644 --- a/src/private/mx/core/elements/BassAlterAttributes.cpp +++ b/src/private/mx/core/elements/BassAlterAttributes.cpp @@ -11,17 +11,18 @@ namespace mx namespace core { BassAlterAttributes::BassAlterAttributes() - : printObject(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), location(), hasPrintObject(false), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasLocation(false) + : printObject(YesNo::no), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), location(), + hasPrintObject(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), + hasLocation(false) { } bool BassAlterAttributes::hasValues() const { return hasPrintObject || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || - hasFontStyle || hasFontSize || hasFontWeight || hasLocation; + hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasLocation; } std::ostream &BassAlterAttributes::toStream(std::ostream &os) const @@ -37,6 +38,7 @@ std::ostream &BassAlterAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, location, "location", hasLocation); } return os; @@ -89,6 +91,10 @@ bool BassAlterAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, location, hasLocation, "location", &parseLeftRight)) { continue; diff --git a/src/private/mx/core/elements/BassAlterAttributes.h b/src/private/mx/core/elements/BassAlterAttributes.h index b935792f..d6905561 100644 --- a/src/private/mx/core/elements/BassAlterAttributes.h +++ b/src/private/mx/core/elements/BassAlterAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -37,6 +38,7 @@ struct BassAlterAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftRight location; bool hasPrintObject; bool hasDefaultX; @@ -47,6 +49,7 @@ struct BassAlterAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasLocation; private: diff --git a/src/private/mx/core/elements/BassStepAttributes.cpp b/src/private/mx/core/elements/BassStepAttributes.cpp index 4202e691..27ece259 100644 --- a/src/private/mx/core/elements/BassStepAttributes.cpp +++ b/src/private/mx/core/elements/BassStepAttributes.cpp @@ -12,16 +12,16 @@ namespace core { BassStepAttributes::BassStepAttributes() : text(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasText(false), hasDefaultX(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasText(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false) + hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool BassStepAttributes::hasValues() const { return hasText || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight; + hasFontSize || hasFontWeight || hasColor; } std::ostream &BassStepAttributes::toStream(std::ostream &os) const @@ -37,6 +37,7 @@ std::ostream &BassStepAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -88,6 +89,10 @@ bool BassStepAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/BassStepAttributes.h b/src/private/mx/core/elements/BassStepAttributes.h index 9a18c04e..29b9dd2d 100644 --- a/src/private/mx/core/elements/BassStepAttributes.h +++ b/src/private/mx/core/elements/BassStepAttributes.h @@ -5,11 +5,13 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsToken.h" #include #include @@ -37,6 +39,7 @@ struct BassStepAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasText; bool hasDefaultX; bool hasDefaultY; @@ -46,6 +49,7 @@ struct BassStepAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/BeamAttributes.cpp b/src/private/mx/core/elements/BeamAttributes.cpp index 967c0c67..6206a743 100644 --- a/src/private/mx/core/elements/BeamAttributes.cpp +++ b/src/private/mx/core/elements/BeamAttributes.cpp @@ -10,13 +10,15 @@ namespace mx { namespace core { -BeamAttributes::BeamAttributes() : number(1), repeater(), fan(), hasNumber(false), hasRepeater(false), hasFan(false) +BeamAttributes::BeamAttributes() + : number(), repeater(YesNo::no), fan(), color(), hasNumber(false), hasRepeater(false), hasFan(false), + hasColor(false) { } bool BeamAttributes::hasValues() const { - return hasNumber || hasRepeater || hasFan; + return hasNumber || hasRepeater || hasFan || hasColor; } std::ostream &BeamAttributes::toStream(std::ostream &os) const @@ -26,6 +28,7 @@ std::ostream &BeamAttributes::toStream(std::ostream &os) const streamAttribute(os, number, "number", hasNumber); streamAttribute(os, repeater, "repeater", hasRepeater); streamAttribute(os, fan, "fan", hasFan); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -52,9 +55,13 @@ bool BeamAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/BeamAttributes.h b/src/private/mx/core/elements/BeamAttributes.h index c3eb198e..411220fe 100644 --- a/src/private/mx/core/elements/BeamAttributes.h +++ b/src/private/mx/core/elements/BeamAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" #include "mx/core/Integers.h" @@ -29,9 +30,11 @@ struct BeamAttributes : public AttributesInterface BeamLevel number; YesNo repeater; Fan fan; + Color color; bool hasNumber; bool hasRepeater; bool hasFan; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/BeatRepeat.cpp b/src/private/mx/core/elements/BeatRepeat.cpp index 67c6f2d9..aaf604cf 100644 --- a/src/private/mx/core/elements/BeatRepeat.cpp +++ b/src/private/mx/core/elements/BeatRepeat.cpp @@ -45,11 +45,8 @@ std::ostream &BeatRepeat::streamContents(std::ostream &os, const int indentLevel mySlashType->toStream(os, indentLevel + 1); for (auto x : mySlashDotSet) { - if (x) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } + os << std::endl; + x->toStream(os, indentLevel + 1); } os << std::endl; return os; @@ -107,6 +104,15 @@ void BeatRepeat::clearSlashDotSet() mySlashDotSet.clear(); } +SlashDotPtr BeatRepeat::getSlashDot(const SlashDotSetIterConst &setIterator) const +{ + if (setIterator != mySlashDotSet.cend()) + { + return *setIterator; + } + return SlashDotPtr(); +} + bool BeatRepeat::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { bool isSuccess = true; @@ -123,10 +129,6 @@ bool BeatRepeat::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xele importElementSet(message, it, endIter, isSuccess, "slash-dot", mySlashDotSet); } - if (!isSlashTypeFound) - { - message << "BeatRepeat: '" << mySlashType->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/BeatRepeat.h b/src/private/mx/core/elements/BeatRepeat.h index b24c8833..6e4da332 100644 --- a/src/private/mx/core/elements/BeatRepeat.h +++ b/src/private/mx/core/elements/BeatRepeat.h @@ -38,7 +38,7 @@ class BeatRepeat : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; BeatRepeatAttributesPtr getAttributes() const; - void setAttributes(const BeatRepeatAttributesPtr &value); + void setAttributes(const BeatRepeatAttributesPtr &attributes); /* _________ SlashType minOccurs = 1, maxOccurs = 1 _________ */ SlashTypePtr getSlashType() const; @@ -49,6 +49,7 @@ class BeatRepeat : public ElementInterface void addSlashDot(const SlashDotPtr &value); void removeSlashDot(const SlashDotSetIterConst &value); void clearSlashDotSet(); + SlashDotPtr getSlashDot(const SlashDotSetIterConst &setIterator) const; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/BeatRepeatAttributes.cpp b/src/private/mx/core/elements/BeatRepeatAttributes.cpp index 2a136e04..f30d57b7 100644 --- a/src/private/mx/core/elements/BeatRepeatAttributes.cpp +++ b/src/private/mx/core/elements/BeatRepeatAttributes.cpp @@ -11,7 +11,7 @@ namespace mx namespace core { BeatRepeatAttributes::BeatRepeatAttributes() - : type(StartStop::start), slashes(1), useDots(YesNo::no), hasType(true), hasSlashes(false), hasUseDots(false) + : type(StartStop::start), slashes(), useDots(YesNo::no), hasType(true), hasSlashes(false), hasUseDots(false) { } @@ -59,7 +59,7 @@ bool BeatRepeatAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/BeatUnitGroup.cpp b/src/private/mx/core/elements/BeatUnitGroup.cpp index 19e7faa8..5c7eb5a1 100644 --- a/src/private/mx/core/elements/BeatUnitGroup.cpp +++ b/src/private/mx/core/elements/BeatUnitGroup.cpp @@ -38,13 +38,19 @@ bool BeatUnitGroup::hasContents() const std::ostream &BeatUnitGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - isOneLineOnly = false; + bool isFirst = true; + if (!isFirst) + os << std::endl; myBeatUnit->toStream(os, indentLevel); + isFirst = false; for (auto x : myBeatUnitDotSet) { - os << std::endl; + if (!isFirst) + os << std::endl; x->toStream(os, indentLevel); + isFirst = false; } + isOneLineOnly = !hasContents(); return os; } @@ -66,19 +72,19 @@ const BeatUnitDotSet &BeatUnitGroup::getBeatUnitDotSet() const return myBeatUnitDotSet; } -void BeatUnitGroup::addBeatUnitDot(const BeatUnitDotPtr &value) +void BeatUnitGroup::removeBeatUnitDot(const BeatUnitDotSetIterConst &value) { - if (value) + if (value != myBeatUnitDotSet.cend()) { - myBeatUnitDotSet.push_back(value); + myBeatUnitDotSet.erase(value); } } -void BeatUnitGroup::removeBeatUnitDot(const BeatUnitDotSetIterConst &setIterator) +void BeatUnitGroup::addBeatUnitDot(const BeatUnitDotPtr &value) { - if (setIterator != myBeatUnitDotSet.cend()) + if (value) { - myBeatUnitDotSet.erase(setIterator); + myBeatUnitDotSet.push_back(value); } } @@ -87,49 +93,16 @@ void BeatUnitGroup::clearBeatUnitDotSet() myBeatUnitDotSet.clear(); } -bool BeatUnitGroup::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) +BeatUnitDotPtr BeatUnitGroup::getBeatUnitDot(const BeatUnitDotSetIterConst &setIterator) const { - bool isSuccess = true; - bool isBeatUnitFound = false; - bool isFirstBeatUnitDotAdded = false; - - for (auto it = xelement.begin(); it != xelement.end(); ++it) + if (setIterator != myBeatUnitDotSet.cend()) { - const std::string elementName = it->getName(); - - if (elementName == "beat-unit") - { - isBeatUnitFound = true; - isSuccess &= myBeatUnit->fromXElement(message, *it); - } - else if (elementName == "beat-unit-dot") - { - auto beatUnitDot = makeBeatUnitDot(); - isSuccess &= beatUnitDot->fromXElement(message, *it); - - if (!isFirstBeatUnitDotAdded && myBeatUnitDotSet.size() == 1) - { - *(myBeatUnitDotSet.begin()) = beatUnitDot; - isFirstBeatUnitDotAdded = true; - } - else - { - myBeatUnitDotSet.push_back(beatUnitDot); - isFirstBeatUnitDotAdded = true; - } - } - else - { - if (!isBeatUnitFound) - { - isSuccess = false; - message << "BeatUnitGroup: 'beat-unit' element is required but was not found" << std::endl; - } - break; - } + return *setIterator; } - MX_RETURN_IS_SUCCESS; + return BeatUnitDotPtr(); } +MX_FROM_XELEMENT_UNUSED(BeatUnitGroup); + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/BeatUnitGroup.h b/src/private/mx/core/elements/BeatUnitGroup.h index 952893f9..5c522ff5 100644 --- a/src/private/mx/core/elements/BeatUnitGroup.h +++ b/src/private/mx/core/elements/BeatUnitGroup.h @@ -43,8 +43,9 @@ class BeatUnitGroup : public ElementInterface /* _________ BeatUnitDot minOccurs = 0, maxOccurs = unbounded _________ */ const BeatUnitDotSet &getBeatUnitDotSet() const; void addBeatUnitDot(const BeatUnitDotPtr &value); - void removeBeatUnitDot(const BeatUnitDotSetIterConst &setIterator); + void removeBeatUnitDot(const BeatUnitDotSetIterConst &value); void clearBeatUnitDotSet(); + BeatUnitDotPtr getBeatUnitDot(const BeatUnitDotSetIterConst &setIterator) const; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/BeatUnitPer.cpp b/src/private/mx/core/elements/BeatUnitPer.cpp index a113d0c0..315586a0 100644 --- a/src/private/mx/core/elements/BeatUnitPer.cpp +++ b/src/private/mx/core/elements/BeatUnitPer.cpp @@ -64,7 +64,7 @@ PerMinuteOrBeatUnitChoicePtr BeatUnitPer::getPerMinuteOrBeatUnitChoice() const return myPerMinuteOrBeatUnitChoice; } -void BeatUnitPer::setPerMinuteOtBeatUnitChoice(const PerMinuteOrBeatUnitChoicePtr &value) +void BeatUnitPer::setPerMinuteOrBeatUnitChoice(const PerMinuteOrBeatUnitChoicePtr &value) { if (value) { @@ -72,22 +72,7 @@ void BeatUnitPer::setPerMinuteOtBeatUnitChoice(const PerMinuteOrBeatUnitChoicePt } } -bool BeatUnitPer::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - bool isSuccess = true; - - auto endIter = xelement.end(); - for (auto it = xelement.begin(); it != endIter; ++it) - { - importGroup(message, it, endIter, isSuccess, myBeatUnitGroup); - if (it->getName() == "beat-unit-per" || it->getName() == "note-relation-note") - { - isSuccess &= myPerMinuteOrBeatUnitChoice->fromXElement(message, *it); - } - } - - MX_RETURN_IS_SUCCESS; -} +MX_FROM_XELEMENT_UNUSED(BeatUnitPer); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/BeatUnitPer.h b/src/private/mx/core/elements/BeatUnitPer.h index 6d283b83..b932c8ae 100644 --- a/src/private/mx/core/elements/BeatUnitPer.h +++ b/src/private/mx/core/elements/BeatUnitPer.h @@ -35,10 +35,14 @@ class BeatUnitPer : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; + + /* _________ BeatUnitGroup minOccurs = 1, maxOccurs = 1 _________ */ BeatUnitGroupPtr getBeatUnitGroup() const; void setBeatUnitGroup(const BeatUnitGroupPtr &value); + + /* _________ PerMinuteOrBeatUnitChoice minOccurs = 1, maxOccurs = 1 _________ */ PerMinuteOrBeatUnitChoicePtr getPerMinuteOrBeatUnitChoice() const; - void setPerMinuteOtBeatUnitChoice(const PerMinuteOrBeatUnitChoicePtr &value); + void setPerMinuteOrBeatUnitChoice(const PerMinuteOrBeatUnitChoicePtr &value); private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.cpp b/src/private/mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.cpp index b97c3c72..843d2ad0 100644 --- a/src/private/mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.cpp +++ b/src/private/mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.cpp @@ -40,19 +40,14 @@ bool BeatUnitPerOrNoteRelationNoteChoice::hasContents() const std::ostream &BeatUnitPerOrNoteRelationNoteChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - switch (myChoice) + if (myChoice == Choice::beatUnitPer) { - case Choice::beatUnitPer: { myBeatUnitPer->streamContents(os, indentLevel, isOneLineOnly); } - break; - case Choice::noteRelationNote: { + if (myChoice == Choice::noteRelationNote) + { myNoteRelationNote->streamContents(os, indentLevel, isOneLineOnly); } - break; - default: - break; - } isOneLineOnly = false; return os; } @@ -62,7 +57,7 @@ BeatUnitPerOrNoteRelationNoteChoice::Choice BeatUnitPerOrNoteRelationNoteChoice: return myChoice; } -void BeatUnitPerOrNoteRelationNoteChoice::setChoice(const BeatUnitPerOrNoteRelationNoteChoice::Choice value) +void BeatUnitPerOrNoteRelationNoteChoice::setChoice(const Choice value) { myChoice = value; } @@ -87,15 +82,13 @@ NoteRelationNotePtr BeatUnitPerOrNoteRelationNoteChoice::getNoteRelationNote() c void BeatUnitPerOrNoteRelationNoteChoice::setNoteRelationNote(const NoteRelationNotePtr &value) { - myNoteRelationNote = value; + if (value) + { + myNoteRelationNote = value; + } } -bool BeatUnitPerOrNoteRelationNoteChoice::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - MX_CHOICE_IF(beatUnitPer, "beat-unit-per", BeatUnitPer); - MX_CHOICE_IF(noteRelationNote, "note-relation-note", NoteRelationNote); - MX_BAD_ELEMENT_FAILURE(BeatUnitPerOrNoteRelationNoteChoice); -} +MX_FROM_XELEMENT_UNUSED(BeatUnitPerOrNoteRelationNoteChoice); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.h b/src/private/mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.h index 922c3636..09e2646c 100644 --- a/src/private/mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.h +++ b/src/private/mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.h @@ -40,8 +40,8 @@ class BeatUnitPerOrNoteRelationNoteChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - BeatUnitPerOrNoteRelationNoteChoice::Choice getChoice() const; - void setChoice(const BeatUnitPerOrNoteRelationNoteChoice::Choice value); + Choice getChoice() const; + void setChoice(const Choice value); BeatUnitPerPtr getBeatUnitPer() const; void setBeatUnitPer(const BeatUnitPerPtr &value); NoteRelationNotePtr getNoteRelationNote() const; diff --git a/src/private/mx/core/elements/Bend.cpp b/src/private/mx/core/elements/Bend.cpp index 0f9b7cff..36679b51 100644 --- a/src/private/mx/core/elements/Bend.cpp +++ b/src/private/mx/core/elements/Bend.cpp @@ -16,8 +16,9 @@ namespace mx namespace core { Bend::Bend() - : myAttributes(std::make_shared()), myBendAlter(makeBendAlter()), myBendChoice(makeBendChoice()), - myHasBendChoice(false), myWithBar(makeWithBar()), myHasWithBar(false) + : myAttributes(std::make_shared()), myBendAlter(makeBendAlter()), + myBendChoice(std::make_shared()), myHasBendChoice(false), myWithBar(makeWithBar()), + myHasWithBar(false) { } @@ -46,19 +47,16 @@ std::ostream &Bend::streamContents(std::ostream &os, const int indentLevel, bool { os << std::endl; myBendAlter->toStream(os, indentLevel + 1); - if (myHasBendChoice) { os << std::endl; myBendChoice->streamContents(os, indentLevel + 1, isOneLineOnly); } - if (myHasWithBar) { os << std::endl; myWithBar->toStream(os, indentLevel + 1); } - os << std::endl; isOneLineOnly = false; return os; @@ -149,21 +147,20 @@ bool Bend::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { continue; } - - if (checkSetChoiceMember(message, *it, isSuccess, myBendChoice, "pre-bend", &BendChoice::getPreBend, - static_cast(BendChoice::Choice::preBend))) + if (it->getName() == "pre-bend") { + myBendChoice->setChoice(BendChoice::Choice::preBend); + isSuccess &= myBendChoice->getPreBend()->fromXElement(message, *it); myHasBendChoice = true; continue; } - - if (checkSetChoiceMember(message, *it, isSuccess, myBendChoice, "release", &BendChoice::getRelease, - static_cast(BendChoice::Choice::release))) + if (it->getName() == "release") { + myBendChoice->setChoice(BendChoice::Choice::release); + isSuccess &= myBendChoice->getRelease()->fromXElement(message, *it); myHasBendChoice = true; continue; } - if (importElement(message, *it, isSuccess, *myWithBar, myHasWithBar)) { continue; diff --git a/src/private/mx/core/elements/Bend.h b/src/private/mx/core/elements/Bend.h index 14c05ecc..a5b85442 100644 --- a/src/private/mx/core/elements/Bend.h +++ b/src/private/mx/core/elements/Bend.h @@ -12,6 +12,11 @@ #include #include +namespace ezxml +{ +class XElementIterator; +} + namespace mx { namespace core diff --git a/src/private/mx/core/elements/BendChoice.cpp b/src/private/mx/core/elements/BendChoice.cpp index 315dcb57..c7b91ac0 100644 --- a/src/private/mx/core/elements/BendChoice.cpp +++ b/src/private/mx/core/elements/BendChoice.cpp @@ -38,20 +38,15 @@ bool BendChoice::hasContents() const std::ostream &BendChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - MX_UNUSED(isOneLineOnly); - switch (myChoice) + if (myChoice == Choice::preBend) { - case Choice::preBend: { myPreBend->toStream(os, indentLevel); } - break; - case Choice::release: { + if (myChoice == Choice::release) + { myRelease->toStream(os, indentLevel); } - break; - default: - break; - } + isOneLineOnly = false; return os; } @@ -60,7 +55,7 @@ BendChoice::Choice BendChoice::getChoice() const return myChoice; } -void BendChoice::setChoice(BendChoice::Choice value) +void BendChoice::setChoice(const Choice value) { myChoice = value; } diff --git a/src/private/mx/core/elements/BendChoice.h b/src/private/mx/core/elements/BendChoice.h index a492ced4..3c5fdd9a 100644 --- a/src/private/mx/core/elements/BendChoice.h +++ b/src/private/mx/core/elements/BendChoice.h @@ -30,8 +30,8 @@ class BendChoice : public ElementInterface public: enum class Choice { - preBend = 1, - release = 2 + preBend = 0, + release = 1 }; BendChoice(); @@ -40,14 +40,10 @@ class BendChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - BendChoice::Choice getChoice() const; - void setChoice(BendChoice::Choice value); - - /* _________ PreBend minOccurs = 1, maxOccurs = 1 _________ */ + Choice getChoice() const; + void setChoice(const Choice value); PreBendPtr getPreBend() const; void setPreBend(const PreBendPtr &value); - - /* _________ Release minOccurs = 1, maxOccurs = 1 _________ */ ReleasePtr getRelease() const; void setRelease(const ReleasePtr &value); diff --git a/src/private/mx/core/elements/BookmarkAttributes.cpp b/src/private/mx/core/elements/BookmarkAttributes.cpp index 9dbe224a..0afad07b 100644 --- a/src/private/mx/core/elements/BookmarkAttributes.cpp +++ b/src/private/mx/core/elements/BookmarkAttributes.cpp @@ -64,7 +64,7 @@ bool BookmarkAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme if (!isIdFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'id' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/BookmarkAttributes.h b/src/private/mx/core/elements/BookmarkAttributes.h index 34dc4a72..81b27312 100644 --- a/src/private/mx/core/elements/BookmarkAttributes.h +++ b/src/private/mx/core/elements/BookmarkAttributes.h @@ -9,6 +9,7 @@ #include "mx/core/Integers.h" #include "mx/core/XsID.h" #include "mx/core/XsNMToken.h" +#include "mx/core/XsToken.h" #include #include diff --git a/src/private/mx/core/elements/BracketAttributes.cpp b/src/private/mx/core/elements/BracketAttributes.cpp index 701fc949..57422070 100644 --- a/src/private/mx/core/elements/BracketAttributes.cpp +++ b/src/private/mx/core/elements/BracketAttributes.cpp @@ -11,36 +11,37 @@ namespace mx namespace core { BracketAttributes::BracketAttributes() - : type(), number(), lineEnd(LineEnd::down), endLength(), lineType(), dashLength(), spaceLength(), defaultX(), - defaultY(), relativeX(), relativeY(), color(), hasType(true), hasNumber(false), hasLineEnd(true), - hasEndLength(false), hasLineType(false), hasDashLength(false), hasSpaceLength(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasColor(false) + : type(StartStopContinue::start), number(), lineEnd(LineEnd::down), endLength(), lineType(LineType::solid), + dashLength(), spaceLength(), defaultX(), defaultY(), relativeX(), relativeY(), color(), hasType(true), + hasNumber(false), hasLineEnd(true), hasEndLength(false), hasLineType(false), hasDashLength(false), + hasSpaceLength(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasColor(false) { } bool BracketAttributes::hasValues() const { - return hasType || hasLineEnd; + return hasType || hasNumber || hasLineEnd || hasEndLength || hasLineType || hasDashLength || hasSpaceLength || + hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasColor; } std::ostream &BracketAttributes::toStream(std::ostream &os) const { - if (!hasValues()) + if (hasValues()) { - return os; + streamAttribute(os, type, "type", hasType); + streamAttribute(os, number, "number", hasNumber); + streamAttribute(os, lineEnd, "line-end", hasLineEnd); + streamAttribute(os, endLength, "end-length", hasEndLength); + streamAttribute(os, lineType, "line-type", hasLineType); + streamAttribute(os, dashLength, "dash-length", hasDashLength); + streamAttribute(os, spaceLength, "space-length", hasSpaceLength); + streamAttribute(os, defaultX, "default-x", hasDefaultX); + streamAttribute(os, defaultY, "default-y", hasDefaultY); + streamAttribute(os, relativeX, "relative-x", hasRelativeX); + streamAttribute(os, relativeY, "relative-y", hasRelativeY); + streamAttribute(os, color, "color", hasColor); } - streamAttribute(os, type, "type", hasType); - streamAttribute(os, number, "number", hasNumber); - streamAttribute(os, lineEnd, "line-end", hasLineEnd); - streamAttribute(os, endLength, "end-length", hasEndLength); - streamAttribute(os, lineType, "line-type", hasLineType); - streamAttribute(os, dashLength, "dash-length", hasDashLength); - streamAttribute(os, spaceLength, "space-length", hasSpaceLength); - streamAttribute(os, defaultX, "default-x", hasDefaultX); - streamAttribute(os, defaultY, "default-y", hasDefaultY); - streamAttribute(os, relativeX, "relative-x", hasRelativeX); - streamAttribute(os, relativeY, "relative-y", hasRelativeY); - streamAttribute(os, color, "color", hasColor); return os; } @@ -106,6 +107,18 @@ bool BracketAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen } } + if (!isTypeFound) + { + isSuccess = false; + message << className << ": 'type' is a required attribute but was not found" << std::endl; + } + + if (!isLineEndFound) + { + isSuccess = false; + message << className << ": 'line-end' is a required attribute but was not found" << std::endl; + } + MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/BracketAttributes.h b/src/private/mx/core/elements/BracketAttributes.h index 32ea6e10..aa4d0434 100644 --- a/src/private/mx/core/elements/BracketAttributes.h +++ b/src/private/mx/core/elements/BracketAttributes.h @@ -22,16 +22,6 @@ namespace core MX_FORWARD_DECLARE_ATTRIBUTES(BracketAttributes) -/* - - - - - - - - - */ struct BracketAttributes : public AttributesInterface { public: diff --git a/src/private/mx/core/elements/BreathMarkAttributes.cpp b/src/private/mx/core/elements/BreathMarkAttributes.cpp index 42c89e88..d25a91fb 100644 --- a/src/private/mx/core/elements/BreathMarkAttributes.cpp +++ b/src/private/mx/core/elements/BreathMarkAttributes.cpp @@ -12,16 +12,16 @@ namespace core { BreathMarkAttributes::BreathMarkAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool BreathMarkAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &BreathMarkAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &BreathMarkAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,6 +85,10 @@ bool BreathMarkAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/BreathMarkAttributes.h b/src/private/mx/core/elements/BreathMarkAttributes.h index fec92d1b..c1dec368 100644 --- a/src/private/mx/core/elements/BreathMarkAttributes.h +++ b/src/private/mx/core/elements/BreathMarkAttributes.h @@ -5,12 +5,12 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" #include "mx/core/ForwardDeclare.h" -#include "mx/core/XsString.h" #include #include @@ -37,6 +37,7 @@ struct BreathMarkAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -46,6 +47,7 @@ struct BreathMarkAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/Clef.cpp b/src/private/mx/core/elements/Clef.cpp index 526eceb2..6c92f962 100644 --- a/src/private/mx/core/elements/Clef.cpp +++ b/src/private/mx/core/elements/Clef.cpp @@ -154,10 +154,6 @@ bool Clef::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) } } - if (!isSignFound) - { - message << "Clef: '" << mySign->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Clef.h b/src/private/mx/core/elements/Clef.h index 89894700..3945a3d8 100644 --- a/src/private/mx/core/elements/Clef.h +++ b/src/private/mx/core/elements/Clef.h @@ -39,7 +39,7 @@ class Clef : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; ClefAttributesPtr getAttributes() const; - void setAttributes(const ClefAttributesPtr &value); + void setAttributes(const ClefAttributesPtr &attributes); /* _________ Sign minOccurs = 1, maxOccurs = 1 _________ */ SignPtr getSign() const; diff --git a/src/private/mx/core/elements/ClefAttributes.cpp b/src/private/mx/core/elements/ClefAttributes.cpp index 7cf6e9b1..12dd2852 100644 --- a/src/private/mx/core/elements/ClefAttributes.cpp +++ b/src/private/mx/core/elements/ClefAttributes.cpp @@ -11,8 +11,8 @@ namespace mx namespace core { ClefAttributes::ClefAttributes() - : number(), additional(YesNo::no), size(), afterBarline(YesNo::no), defaultX(), defaultY(), relativeX(), - relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), + : number(), additional(YesNo::no), size(SymbolSize::full), afterBarline(YesNo::no), defaultX(), defaultY(), + relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), printObject(YesNo::no), hasNumber(false), hasAdditional(false), hasSize(false), hasAfterBarline(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), diff --git a/src/private/mx/core/elements/Coda.h b/src/private/mx/core/elements/Coda.h index bec81c93..70a7a3a6 100644 --- a/src/private/mx/core/elements/Coda.h +++ b/src/private/mx/core/elements/Coda.h @@ -17,6 +17,7 @@ namespace mx namespace core { +MX_FORWARD_DECLARE_ATTRIBUTES(EmptyPrintObjectStyleAlignAttributes) MX_FORWARD_DECLARE_ELEMENT(Coda) inline CodaPtr makeCoda() diff --git a/src/private/mx/core/elements/Creator.cpp b/src/private/mx/core/elements/Creator.cpp index 498dbbc3..3f690850 100644 --- a/src/private/mx/core/elements/Creator.cpp +++ b/src/private/mx/core/elements/Creator.cpp @@ -79,7 +79,7 @@ bool Creator::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelemen bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.setValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/CreditImageAttributes.cpp b/src/private/mx/core/elements/CreditImageAttributes.cpp index 0a32873f..d326ac67 100644 --- a/src/private/mx/core/elements/CreditImageAttributes.cpp +++ b/src/private/mx/core/elements/CreditImageAttributes.cpp @@ -11,15 +11,15 @@ namespace mx namespace core { CreditImageAttributes::CreditImageAttributes() - : source(), type(), defaultX(), defaultY(), relativeX(), relativeY(), halign(LeftCenterRight::center), - hasSource(true), hasType(true), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasHalign(false) + : source(), type(), defaultX(), defaultY(), relativeX(), relativeY(), halign(LeftCenterRight::left), + valign(ValignImage::bottom), hasSource(true), hasType(true), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasHalign(false), hasValign(false) { } bool CreditImageAttributes::hasValues() const { - return hasSource || hasType || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasHalign; + return hasSource || hasType || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasHalign || hasValign; } std::ostream &CreditImageAttributes::toStream(std::ostream &os) const @@ -33,6 +33,7 @@ std::ostream &CreditImageAttributes::toStream(std::ostream &os) const streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); } return os; } @@ -77,17 +78,22 @@ bool CreditImageAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValignImage)) + { + continue; + } } if (!isSourceFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'source' is a required attribute but was not found" << std::endl; } + if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/CreditImageAttributes.h b/src/private/mx/core/elements/CreditImageAttributes.h index b05c895a..7e429474 100644 --- a/src/private/mx/core/elements/CreditImageAttributes.h +++ b/src/private/mx/core/elements/CreditImageAttributes.h @@ -35,6 +35,7 @@ struct CreditImageAttributes : public AttributesInterface TenthsValue relativeX; TenthsValue relativeY; LeftCenterRight halign; + ValignImage valign; const bool hasSource; const bool hasType; bool hasDefaultX; @@ -42,6 +43,7 @@ struct CreditImageAttributes : public AttributesInterface bool hasRelativeX; bool hasRelativeY; bool hasHalign; + bool hasValign; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/CreditType.cpp b/src/private/mx/core/elements/CreditType.cpp index 850cb344..4cfb45b7 100644 --- a/src/private/mx/core/elements/CreditType.cpp +++ b/src/private/mx/core/elements/CreditType.cpp @@ -60,6 +60,7 @@ void CreditType::setValue(const XsString &value) bool CreditType::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); + MX_UNUSED(xelement); myValue.setValue(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/CreditWordsAttributes.cpp b/src/private/mx/core/elements/CreditWordsAttributes.cpp index d82013f1..731d5624 100644 --- a/src/private/mx/core/elements/CreditWordsAttributes.cpp +++ b/src/private/mx/core/elements/CreditWordsAttributes.cpp @@ -11,14 +11,14 @@ namespace mx namespace core { CreditWordsAttributes::CreditWordsAttributes() - : justify(LeftCenterRight::center), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), color(), - halign(), valign(), underline(), overline(), lineThrough(), rotation(), letterSpacing(), lineHeight(), - lang(XmlLang{"it"}), space(XmlSpace::default_), enclosure(EnclosureShape::rectangle), hasJustify(false), + : justify(LeftCenterRight::left), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + halign(LeftCenterRight::left), valign(Valign::bottom), underline(), overline(), lineThrough(), rotation(), + letterSpacing(), lineHeight(), lang("it"), space(), dir(), enclosure(EnclosureShape::none), hasJustify(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false), hasUnderline(false), hasOverline(false), hasLineThrough(false), hasRotation(false), - hasLetterSpacing(false), hasLineHeight(false), hasLang(false), hasSpace(false), hasEnclosure(false) + hasLetterSpacing(false), hasLineHeight(false), hasLang(false), hasSpace(false), hasDir(false), hasEnclosure(false) { } @@ -26,7 +26,8 @@ bool CreditWordsAttributes::hasValues() const { return hasJustify || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign || hasUnderline || hasOverline || - hasLineThrough || hasRotation || hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasEnclosure; + hasLineThrough || hasRotation || hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasDir || + hasEnclosure; } std::ostream &CreditWordsAttributes::toStream(std::ostream &os) const @@ -53,6 +54,7 @@ std::ostream &CreditWordsAttributes::toStream(std::ostream &os) const streamAttribute(os, lineHeight, "line-height", hasLineHeight); streamAttribute(os, lang, "xml:lang", hasLang); streamAttribute(os, space, "xml:space", hasSpace); + streamAttribute(os, dir, "dir", hasDir); streamAttribute(os, enclosure, "enclosure", hasEnclosure); } return os; @@ -141,19 +143,15 @@ bool CreditWordsAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) - { - continue; - } if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, dir, hasDir, "dir", &parseTextDirection)) { continue; } diff --git a/src/private/mx/core/elements/CreditWordsAttributes.h b/src/private/mx/core/elements/CreditWordsAttributes.h index 88c878eb..eb28e84d 100644 --- a/src/private/mx/core/elements/CreditWordsAttributes.h +++ b/src/private/mx/core/elements/CreditWordsAttributes.h @@ -52,6 +52,7 @@ struct CreditWordsAttributes : public AttributesInterface NumberOrNormal lineHeight; XmlLang lang; XmlSpace space; + TextDirection dir; EnclosureShape enclosure; bool hasJustify; bool hasDefaultX; @@ -73,6 +74,7 @@ struct CreditWordsAttributes : public AttributesInterface bool hasLineHeight; bool hasLang; bool hasSpace; + bool hasDir; bool hasEnclosure; private: diff --git a/src/private/mx/core/elements/CreditWordsGroup.h b/src/private/mx/core/elements/CreditWordsGroup.h index c4e0d380..7abe65cd 100644 --- a/src/private/mx/core/elements/CreditWordsGroup.h +++ b/src/private/mx/core/elements/CreditWordsGroup.h @@ -37,7 +37,7 @@ class CreditWordsGroup : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - /* _________ LinkSet minOccurs = 0, maxOccurs = unbounded _________ */ + /* _________ Link minOccurs = 0, maxOccurs = unbounded _________ */ const LinkSet &getLinkSet() const; void addLink(const LinkPtr &value); void removeLink(const LinkSetIterConst &value); diff --git a/src/private/mx/core/elements/Damp.h b/src/private/mx/core/elements/Damp.h index 2bd1130a..e1cb596e 100644 --- a/src/private/mx/core/elements/Damp.h +++ b/src/private/mx/core/elements/Damp.h @@ -17,6 +17,7 @@ namespace mx namespace core { +MX_FORWARD_DECLARE_ATTRIBUTES(EmptyPrintObjectStyleAlignAttributes) MX_FORWARD_DECLARE_ELEMENT(Damp) inline DampPtr makeDamp() diff --git a/src/private/mx/core/elements/DampAll.h b/src/private/mx/core/elements/DampAll.h index 55652b57..9256a25e 100644 --- a/src/private/mx/core/elements/DampAll.h +++ b/src/private/mx/core/elements/DampAll.h @@ -17,6 +17,7 @@ namespace mx namespace core { +MX_FORWARD_DECLARE_ATTRIBUTES(EmptyPrintObjectStyleAlignAttributes) MX_FORWARD_DECLARE_ELEMENT(DampAll) inline DampAllPtr makeDampAll() diff --git a/src/private/mx/core/elements/DashesAttributes.cpp b/src/private/mx/core/elements/DashesAttributes.cpp index c4d22451..3b28af32 100644 --- a/src/private/mx/core/elements/DashesAttributes.cpp +++ b/src/private/mx/core/elements/DashesAttributes.cpp @@ -12,15 +12,15 @@ namespace core { DashesAttributes::DashesAttributes() : type(StartStopContinue::start), number(), dashLength(), spaceLength(), defaultX(), defaultY(), relativeX(), - relativeY(), hasType(true), hasNumber(false), hasDashLength(false), hasSpaceLength(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false) + relativeY(), color(), hasType(true), hasNumber(false), hasDashLength(false), hasSpaceLength(false), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasColor(false) { } bool DashesAttributes::hasValues() const { return hasType || hasNumber || hasDashLength || hasSpaceLength || hasDefaultX || hasDefaultY || hasRelativeX || - hasRelativeY; + hasRelativeY || hasColor; } std::ostream &DashesAttributes::toStream(std::ostream &os) const @@ -35,6 +35,7 @@ std::ostream &DashesAttributes::toStream(std::ostream &os) const streamAttribute(os, defaultY, "default-y", hasDefaultY); streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -82,12 +83,16 @@ bool DashesAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/DashesAttributes.h b/src/private/mx/core/elements/DashesAttributes.h index 2e4f6f3f..07e82ac8 100644 --- a/src/private/mx/core/elements/DashesAttributes.h +++ b/src/private/mx/core/elements/DashesAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" @@ -35,6 +36,7 @@ struct DashesAttributes : public AttributesInterface TenthsValue defaultY; TenthsValue relativeX; TenthsValue relativeY; + Color color; const bool hasType; bool hasNumber; bool hasDashLength; @@ -43,6 +45,7 @@ struct DashesAttributes : public AttributesInterface bool hasDefaultY; bool hasRelativeX; bool hasRelativeY; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Degree.cpp b/src/private/mx/core/elements/Degree.cpp index 8adc9af3..a32d052d 100644 --- a/src/private/mx/core/elements/Degree.cpp +++ b/src/private/mx/core/elements/Degree.cpp @@ -15,7 +15,7 @@ namespace core { Degree::Degree() : myAttributes(std::make_shared()), myDegreeValue(makeDegreeValue()), - myDegreeAlter(makeDegreeAlter()), myDegreeType(makeDegreeType(DegreeTypeValue::add)) + myDegreeAlter(makeDegreeAlter()), myDegreeType(makeDegreeType()) { } @@ -42,6 +42,7 @@ bool Degree::hasContents() const std::ostream &Degree::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myDegreeValue->toStream(os, indentLevel + 1); os << std::endl; @@ -49,7 +50,6 @@ std::ostream &Degree::streamContents(std::ostream &os, const int indentLevel, bo os << std::endl; myDegreeType->toStream(os, indentLevel + 1); os << std::endl; - isOneLineOnly = false; return os; } @@ -130,18 +130,6 @@ bool Degree::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement } } - if (!isDegreeValueFound) - { - message << "Degree: '" << myDegreeValue->getElementName() << "' is required but was not found" << std::endl; - } - if (!isDegreeAlterFound) - { - message << "Degree: '" << myDegreeAlter->getElementName() << "' is required but was not found" << std::endl; - } - if (!isDegreeTypeFound) - { - message << "Degree: '" << myDegreeType->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Degree.h b/src/private/mx/core/elements/Degree.h index edb88b83..c6ea0954 100644 --- a/src/private/mx/core/elements/Degree.h +++ b/src/private/mx/core/elements/Degree.h @@ -39,7 +39,7 @@ class Degree : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; DegreeAttributesPtr getAttributes() const; - void setAttributes(const DegreeAttributesPtr &value); + void setAttributes(const DegreeAttributesPtr &attributes); /* _________ DegreeValue minOccurs = 1, maxOccurs = 1 _________ */ DegreeValuePtr getDegreeValue() const; diff --git a/src/private/mx/core/elements/DegreeAlterAttributes.cpp b/src/private/mx/core/elements/DegreeAlterAttributes.cpp index 348d6d7a..5725c6bf 100644 --- a/src/private/mx/core/elements/DegreeAlterAttributes.cpp +++ b/src/private/mx/core/elements/DegreeAlterAttributes.cpp @@ -12,16 +12,16 @@ namespace core { DegreeAlterAttributes::DegreeAlterAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), plusMinus(YesNo::no), hasDefaultX(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), plusMinus(YesNo::no), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlusMinus(false) + hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlusMinus(false) { } bool DegreeAlterAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlusMinus; + hasFontWeight || hasColor || hasPlusMinus; } std::ostream &DegreeAlterAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &DegreeAlterAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, plusMinus, "plus-minus", hasPlusMinus); } return os; @@ -84,6 +85,10 @@ bool DegreeAlterAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, plusMinus, hasPlusMinus, "plus-minus", &parseYesNo)) { continue; diff --git a/src/private/mx/core/elements/DegreeAlterAttributes.h b/src/private/mx/core/elements/DegreeAlterAttributes.h index d508270e..64072f4a 100644 --- a/src/private/mx/core/elements/DegreeAlterAttributes.h +++ b/src/private/mx/core/elements/DegreeAlterAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct DegreeAlterAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; YesNo plusMinus; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct DegreeAlterAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlusMinus; private: diff --git a/src/private/mx/core/elements/DegreeTypeAttributes.cpp b/src/private/mx/core/elements/DegreeTypeAttributes.cpp index c6cf5437..1faf64a7 100644 --- a/src/private/mx/core/elements/DegreeTypeAttributes.cpp +++ b/src/private/mx/core/elements/DegreeTypeAttributes.cpp @@ -12,16 +12,16 @@ namespace core { DegreeTypeAttributes::DegreeTypeAttributes() : text(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasText(false), hasDefaultX(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasText(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false) + hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool DegreeTypeAttributes::hasValues() const { return hasText || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight; + hasFontSize || hasFontWeight || hasColor; } std::ostream &DegreeTypeAttributes::toStream(std::ostream &os) const @@ -37,6 +37,7 @@ std::ostream &DegreeTypeAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -88,6 +89,10 @@ bool DegreeTypeAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/DegreeTypeAttributes.h b/src/private/mx/core/elements/DegreeTypeAttributes.h index 9393737c..ea568255 100644 --- a/src/private/mx/core/elements/DegreeTypeAttributes.h +++ b/src/private/mx/core/elements/DegreeTypeAttributes.h @@ -5,11 +5,13 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsToken.h" #include #include @@ -37,6 +39,7 @@ struct DegreeTypeAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasText; bool hasDefaultX; bool hasDefaultY; @@ -46,6 +49,7 @@ struct DegreeTypeAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/DegreeValueAttributes.cpp b/src/private/mx/core/elements/DegreeValueAttributes.cpp index 1d1b65a0..101e8760 100644 --- a/src/private/mx/core/elements/DegreeValueAttributes.cpp +++ b/src/private/mx/core/elements/DegreeValueAttributes.cpp @@ -12,16 +12,16 @@ namespace core { DegreeValueAttributes::DegreeValueAttributes() : symbol(), text(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasSymbol(false), hasText(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasSymbol(false), hasText(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false) + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool DegreeValueAttributes::hasValues() const { return hasSymbol || hasText || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || - hasFontStyle || hasFontSize || hasFontWeight; + hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &DegreeValueAttributes::toStream(std::ostream &os) const @@ -38,6 +38,7 @@ std::ostream &DegreeValueAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -93,6 +94,10 @@ bool DegreeValueAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/DegreeValueAttributes.h b/src/private/mx/core/elements/DegreeValueAttributes.h index a6ca67ed..af261ada 100644 --- a/src/private/mx/core/elements/DegreeValueAttributes.h +++ b/src/private/mx/core/elements/DegreeValueAttributes.h @@ -5,11 +5,13 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsToken.h" #include #include @@ -38,6 +40,7 @@ struct DegreeValueAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasSymbol; bool hasText; bool hasDefaultX; @@ -48,6 +51,7 @@ struct DegreeValueAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/DelayedInvertedTurnAttributes.cpp b/src/private/mx/core/elements/DelayedInvertedTurnAttributes.cpp index e182aa67..0e6d00db 100644 --- a/src/private/mx/core/elements/DelayedInvertedTurnAttributes.cpp +++ b/src/private/mx/core/elements/DelayedInvertedTurnAttributes.cpp @@ -12,20 +12,20 @@ namespace core { DelayedInvertedTurnAttributes::DelayedInvertedTurnAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), placement(AboveBelow::below), - startNote(StartNote::main), trillStep(), twoNoteTurn(TwoNoteTurn::none), accelerate(YesNo::no), beats(), - secondBeat(), lastBeat(), slash(YesNo::no), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasPlacement(false), hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), - hasBeats(false), hasSecondBeat(false), hasLastBeat(false), hasSlash(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), startNote(), + trillStep(), twoNoteTurn(), accelerate(YesNo::no), beats(), secondBeat(), lastBeat(), slash(YesNo::no), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false), + hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), + hasSecondBeat(false), hasLastBeat(false), hasSlash(false) { } bool DelayedInvertedTurnAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || hasAccelerate || - hasBeats || hasSecondBeat || hasLastBeat || hasSlash; + hasFontWeight || hasColor || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || + hasAccelerate || hasBeats || hasSecondBeat || hasLastBeat || hasSlash; } std::ostream &DelayedInvertedTurnAttributes::toStream(std::ostream &os) const @@ -40,6 +40,7 @@ std::ostream &DelayedInvertedTurnAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); streamAttribute(os, startNote, "start-note", hasStartNote); streamAttribute(os, trillStep, "trill-step", hasTrillStep); @@ -96,6 +97,10 @@ bool DelayedInvertedTurnAttributes::fromXElementImpl(std::ostream &message, ::ez { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; @@ -135,7 +140,7 @@ bool DelayedInvertedTurnAttributes::fromXElementImpl(std::ostream &message, ::ez } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/DelayedInvertedTurnAttributes.h b/src/private/mx/core/elements/DelayedInvertedTurnAttributes.h index 4bf5da46..cb46e8cf 100644 --- a/src/private/mx/core/elements/DelayedInvertedTurnAttributes.h +++ b/src/private/mx/core/elements/DelayedInvertedTurnAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct DelayedInvertedTurnAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; StartNote startNote; TrillStep trillStep; @@ -53,6 +55,7 @@ struct DelayedInvertedTurnAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; bool hasStartNote; bool hasTrillStep; diff --git a/src/private/mx/core/elements/DelayedTurnAttributes.cpp b/src/private/mx/core/elements/DelayedTurnAttributes.cpp index 30fee6c5..8cc0efda 100644 --- a/src/private/mx/core/elements/DelayedTurnAttributes.cpp +++ b/src/private/mx/core/elements/DelayedTurnAttributes.cpp @@ -12,20 +12,20 @@ namespace core { DelayedTurnAttributes::DelayedTurnAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), placement(AboveBelow::below), - startNote(StartNote::main), trillStep(), twoNoteTurn(TwoNoteTurn::none), accelerate(YesNo::no), beats(), - secondBeat(), lastBeat(), slash(YesNo::no), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasPlacement(false), hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), - hasBeats(false), hasSecondBeat(false), hasLastBeat(false), hasSlash(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), startNote(), + trillStep(), twoNoteTurn(), accelerate(YesNo::no), beats(), secondBeat(), lastBeat(), slash(YesNo::no), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false), + hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), + hasSecondBeat(false), hasLastBeat(false), hasSlash(false) { } bool DelayedTurnAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || hasAccelerate || - hasBeats || hasSecondBeat || hasLastBeat || hasSlash; + hasFontWeight || hasColor || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || + hasAccelerate || hasBeats || hasSecondBeat || hasLastBeat || hasSlash; } std::ostream &DelayedTurnAttributes::toStream(std::ostream &os) const @@ -40,6 +40,7 @@ std::ostream &DelayedTurnAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); streamAttribute(os, startNote, "start-note", hasStartNote); streamAttribute(os, trillStep, "trill-step", hasTrillStep); @@ -96,6 +97,10 @@ bool DelayedTurnAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/DelayedTurnAttributes.h b/src/private/mx/core/elements/DelayedTurnAttributes.h index e09c4709..bb4ae8d3 100644 --- a/src/private/mx/core/elements/DelayedTurnAttributes.h +++ b/src/private/mx/core/elements/DelayedTurnAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct DelayedTurnAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; StartNote startNote; TrillStep trillStep; @@ -53,6 +55,7 @@ struct DelayedTurnAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; bool hasStartNote; bool hasTrillStep; diff --git a/src/private/mx/core/elements/DirectionType.cpp b/src/private/mx/core/elements/DirectionType.cpp index e83fdbb9..f5c394d6 100644 --- a/src/private/mx/core/elements/DirectionType.cpp +++ b/src/private/mx/core/elements/DirectionType.cpp @@ -40,12 +40,30 @@ DirectionType::DirectionType() myScordatura(makeScordatura()), myImage(makeImage()), myPrincipalVoice(makePrincipalVoice()), myAccordionRegistration(makeAccordionRegistration()), myPercussionSet(), myOtherDirection(makeOtherDirection()) { - myRehearsalSet.push_back(makeRehearsal()); - mySegnoSet.push_back(makeSegno()); - myWordsSet.push_back(makeWords()); - myCodaSet.push_back(makeCoda()); - myDynamicsSet.push_back(makeDynamics()); - myPercussionSet.push_back(makePercussion()); + while (myRehearsalSet.size() < 1) + { + myRehearsalSet.push_back(makeRehearsal()); + } + while (mySegnoSet.size() < 1) + { + mySegnoSet.push_back(makeSegno()); + } + while (myWordsSet.size() < 1) + { + myWordsSet.push_back(makeWords()); + } + while (myCodaSet.size() < 1) + { + myCodaSet.push_back(makeCoda()); + } + while (myDynamicsSet.size() < 1) + { + myDynamicsSet.push_back(makeDynamics()); + } + while (myPercussionSet.size() < 1) + { + myPercussionSet.push_back(makePercussion()); + } } bool DirectionType::hasAttributes() const @@ -72,8 +90,6 @@ bool DirectionType::hasContents() const std::ostream &DirectionType::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { isOneLineOnly = false; - // streamOpenTag( os ); - // os << std::endl; switch (myChoice) { case Choice::rehearsal: { @@ -207,8 +223,6 @@ std::ostream &DirectionType::streamContents(std::ostream &os, const int indentLe default: break; } - // os << std::endl; - // streamCloseTag( os ); os << std::endl; isOneLineOnly = false; return os; @@ -229,6 +243,14 @@ const RehearsalSet &DirectionType::getRehearsalSet() const return myRehearsalSet; } +void DirectionType::addRehearsal(const RehearsalPtr &value) +{ + if (value) + { + myRehearsalSet.push_back(value); + } +} + void DirectionType::removeRehearsal(const RehearsalSetIterConst &value) { if (value != myRehearsalSet.cend()) @@ -240,14 +262,6 @@ void DirectionType::removeRehearsal(const RehearsalSetIterConst &value) } } -void DirectionType::addRehearsal(const RehearsalPtr &value) -{ - if (value) - { - myRehearsalSet.push_back(value); - } -} - void DirectionType::clearRehearsalSet() { myRehearsalSet.clear(); @@ -271,6 +285,14 @@ const SegnoSet &DirectionType::getSegnoSet() const return mySegnoSet; } +void DirectionType::addSegno(const SegnoPtr &value) +{ + if (value) + { + mySegnoSet.push_back(value); + } +} + void DirectionType::removeSegno(const SegnoSetIterConst &value) { if (value != mySegnoSet.cend()) @@ -282,14 +304,6 @@ void DirectionType::removeSegno(const SegnoSetIterConst &value) } } -void DirectionType::addSegno(const SegnoPtr &value) -{ - if (value) - { - mySegnoSet.push_back(value); - } -} - void DirectionType::clearSegnoSet() { mySegnoSet.clear(); @@ -313,6 +327,14 @@ const WordsSet &DirectionType::getWordsSet() const return myWordsSet; } +void DirectionType::addWords(const WordsPtr &value) +{ + if (value) + { + myWordsSet.push_back(value); + } +} + void DirectionType::removeWords(const WordsSetIterConst &value) { if (value != myWordsSet.cend()) @@ -324,14 +346,6 @@ void DirectionType::removeWords(const WordsSetIterConst &value) } } -void DirectionType::addWords(const WordsPtr &value) -{ - if (value) - { - myWordsSet.push_back(value); - } -} - void DirectionType::clearWordsSet() { myWordsSet.clear(); @@ -355,6 +369,14 @@ const CodaSet &DirectionType::getCodaSet() const return myCodaSet; } +void DirectionType::addCoda(const CodaPtr &value) +{ + if (value) + { + myCodaSet.push_back(value); + } +} + void DirectionType::removeCoda(const CodaSetIterConst &value) { if (value != myCodaSet.cend()) @@ -366,14 +388,6 @@ void DirectionType::removeCoda(const CodaSetIterConst &value) } } -void DirectionType::addCoda(const CodaPtr &value) -{ - if (value) - { - myCodaSet.push_back(value); - } -} - void DirectionType::clearCodaSet() { myCodaSet.clear(); @@ -410,6 +424,14 @@ const DynamicsSet &DirectionType::getDynamicsSet() const return myDynamicsSet; } +void DirectionType::addDynamics(const DynamicsPtr &value) +{ + if (value) + { + myDynamicsSet.push_back(value); + } +} + void DirectionType::removeDynamics(const DynamicsSetIterConst &value) { if (value != myDynamicsSet.cend()) @@ -421,14 +443,6 @@ void DirectionType::removeDynamics(const DynamicsSetIterConst &value) } } -void DirectionType::addDynamics(const DynamicsPtr &value) -{ - if (value) - { - myDynamicsSet.push_back(value); - } -} - void DirectionType::clearDynamicsSet() { myDynamicsSet.clear(); @@ -634,6 +648,14 @@ const PercussionSet &DirectionType::getPercussionSet() const return myPercussionSet; } +void DirectionType::addPercussion(const PercussionPtr &value) +{ + if (value) + { + myPercussionSet.push_back(value); + } +} + void DirectionType::removePercussion(const PercussionSetIterConst &value) { if (value != myPercussionSet.cend()) @@ -645,14 +667,6 @@ void DirectionType::removePercussion(const PercussionSetIterConst &value) } } -void DirectionType::addPercussion(const PercussionPtr &value) -{ - if (value) - { - myPercussionSet.push_back(value); - } -} - void DirectionType::clearPercussionSet() { myPercussionSet.clear(); @@ -685,6 +699,5 @@ void DirectionType::setOtherDirection(const OtherDirectionPtr &value) } MX_FROM_XELEMENT_UNUSED(DirectionType); - } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/DirectionType.h b/src/private/mx/core/elements/DirectionType.h index 0f5fe7af..87b66f07 100644 --- a/src/private/mx/core/elements/DirectionType.h +++ b/src/private/mx/core/elements/DirectionType.h @@ -80,6 +80,8 @@ class DirectionType : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; + + /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */ DirectionType::Choice getChoice() const; void setChoice(const DirectionType::Choice value); diff --git a/src/private/mx/core/elements/Directive.cpp b/src/private/mx/core/elements/Directive.cpp index bd0b6f9e..963f638f 100644 --- a/src/private/mx/core/elements/Directive.cpp +++ b/src/private/mx/core/elements/Directive.cpp @@ -10,11 +10,11 @@ namespace mx { namespace core { -Directive::Directive() : myAttributes(std::make_shared()), myValue() +Directive::Directive() : myValue(), myAttributes(std::make_shared()) { } -Directive::Directive(const XsString &value) : myAttributes(std::make_shared()), myValue(value) +Directive::Directive(const XsString &value) : myValue(value), myAttributes(std::make_shared()) { } @@ -23,28 +23,31 @@ bool Directive::hasAttributes() const return myAttributes->hasValues(); } -std::ostream &Directive::streamAttributes(std::ostream &os) const +bool Directive::hasContents() const { - return myAttributes->toStream(os); + return true; } -std::ostream &Directive::streamName(std::ostream &os) const +std::ostream &Directive::streamAttributes(std::ostream &os) const { - os << "directive"; + if (myAttributes) + { + myAttributes->toStream(os); + } return os; } -bool Directive::hasContents() const +std::ostream &Directive::streamName(std::ostream &os) const { - return true; + os << "directive"; + return os; } std::ostream &Directive::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { MX_UNUSED(indentLevel); - MX_UNUSED(isOneLineOnly); isOneLineOnly = true; - core::toStream(os, myValue); + os << myValue; return os; } diff --git a/src/private/mx/core/elements/Directive.h b/src/private/mx/core/elements/Directive.h index 62982493..abec7ddd 100644 --- a/src/private/mx/core/elements/Directive.h +++ b/src/private/mx/core/elements/Directive.h @@ -43,12 +43,12 @@ class Directive : public ElementInterface Directive(const XsString &value); virtual bool hasAttributes() const; + virtual bool hasContents() const; virtual std::ostream &streamAttributes(std::ostream &os) const; virtual std::ostream &streamName(std::ostream &os) const; - virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; DirectiveAttributesPtr getAttributes() const; - void setAttributes(const DirectiveAttributesPtr &value); + void setAttributes(const DirectiveAttributesPtr &attributes); XsString getValue() const; void setValue(const XsString &value); @@ -56,8 +56,8 @@ class Directive : public ElementInterface virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); private: - DirectiveAttributesPtr myAttributes; XsString myValue; + DirectiveAttributesPtr myAttributes; }; } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/DirectiveAttributes.cpp b/src/private/mx/core/elements/DirectiveAttributes.cpp index 0d4e6ba3..cf2d3be0 100644 --- a/src/private/mx/core/elements/DirectiveAttributes.cpp +++ b/src/private/mx/core/elements/DirectiveAttributes.cpp @@ -12,16 +12,16 @@ namespace core { DirectiveAttributes::DirectiveAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), lang("it"), hasDefaultX(false), hasDefaultY(false), - hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false), hasLang(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), lang("it"), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), + hasFontSize(false), hasFontWeight(false), hasColor(false), hasLang(false) { } bool DirectiveAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasLang; + hasFontWeight || hasColor || hasLang; } std::ostream &DirectiveAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &DirectiveAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, lang, "xml:lang", hasLang); } return os; @@ -84,7 +85,7 @@ bool DirectiveAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) { continue; } @@ -94,7 +95,7 @@ bool DirectiveAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/DirectiveAttributes.h b/src/private/mx/core/elements/DirectiveAttributes.h index ffa053d0..8fef8613 100644 --- a/src/private/mx/core/elements/DirectiveAttributes.h +++ b/src/private/mx/core/elements/DirectiveAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -37,6 +38,7 @@ struct DirectiveAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; XmlLang lang; bool hasDefaultX; bool hasDefaultY; @@ -46,6 +48,7 @@ struct DirectiveAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasLang; private: diff --git a/src/private/mx/core/elements/DisplayStepOctaveGroup.cpp b/src/private/mx/core/elements/DisplayStepOctaveGroup.cpp index 968b6430..039a4d8b 100644 --- a/src/private/mx/core/elements/DisplayStepOctaveGroup.cpp +++ b/src/private/mx/core/elements/DisplayStepOctaveGroup.cpp @@ -39,10 +39,16 @@ bool DisplayStepOctaveGroup::hasContents() const std::ostream &DisplayStepOctaveGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - MX_UNUSED(isOneLineOnly); + bool isFirst = true; + if (!isFirst) + os << std::endl; myDisplayStep->toStream(os, indentLevel); - os << std::endl; + isFirst = false; + if (!isFirst) + os << std::endl; myDisplayOctave->toStream(os, indentLevel); + isFirst = false; + isOneLineOnly = !hasContents(); return os; } @@ -72,44 +78,7 @@ void DisplayStepOctaveGroup::setDisplayOctave(const DisplayOctavePtr &value) } } -bool DisplayStepOctaveGroup::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - bool isSuccess = true; - bool isDisplayStepFound = false; - bool isDisplayOctaveFound = false; - - for (auto it = xelement.begin(); it != xelement.end(); ++it) - { - const std::string elementName = it->getName(); - - if (elementName == "display-step") - { - isDisplayStepFound = true; - isSuccess &= myDisplayStep->fromXElement(message, *it); - } - else if (elementName == "display-octave") - { - isDisplayOctaveFound = true; - isSuccess &= myDisplayOctave->fromXElement(message, *it); - } - else - { - if (!isDisplayStepFound) - { - isSuccess = false; - message << "DisplayStepOctaveGroup: 'display-step' element is required but was not found" << std::endl; - } - if (!isDisplayOctaveFound) - { - isSuccess = false; - message << "DisplayStepOctaveGroup: 'display-octave' element is required but was not found" - << std::endl; - } - break; - } - } - MX_RETURN_IS_SUCCESS; -} +MX_FROM_XELEMENT_UNUSED(DisplayStepOctaveGroup); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/DisplayTextAttributes.cpp b/src/private/mx/core/elements/DisplayTextAttributes.cpp index 89b67093..c428cec3 100644 --- a/src/private/mx/core/elements/DisplayTextAttributes.cpp +++ b/src/private/mx/core/elements/DisplayTextAttributes.cpp @@ -11,22 +11,23 @@ namespace mx namespace core { DisplayTextAttributes::DisplayTextAttributes() - : justify(LeftCenterRight::center), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), halign(), - underline(), overline(), lineThrough(), rotation(), letterSpacing(), lineHeight(), lang(XmlLang{"it"}), - space(XmlSpace::default_), enclosure(EnclosureShape::rectangle), hasJustify(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasHalign(false), hasUnderline(false), hasOverline(false), - hasLineThrough(false), hasRotation(false), hasLetterSpacing(false), hasLineHeight(false), hasLang(false), - hasSpace(false), hasEnclosure(false) + : justify(LeftCenterRight::left), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + halign(LeftCenterRight::left), valign(Valign::bottom), underline(), overline(), lineThrough(), rotation(), + letterSpacing(), lineHeight(), lang(), space(), dir(), enclosure(EnclosureShape::none), hasJustify(false), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), + hasValign(false), hasUnderline(false), hasOverline(false), hasLineThrough(false), hasRotation(false), + hasLetterSpacing(false), hasLineHeight(false), hasLang(false), hasSpace(false), hasDir(false), hasEnclosure(false) { } bool DisplayTextAttributes::hasValues() const { return hasJustify || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight || hasHalign || hasUnderline || hasOverline || hasLineThrough || hasRotation || - hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasEnclosure; + hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign || hasUnderline || hasOverline || + hasLineThrough || hasRotation || hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasDir || + hasEnclosure; } std::ostream &DisplayTextAttributes::toStream(std::ostream &os) const @@ -42,7 +43,9 @@ std::ostream &DisplayTextAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); streamAttribute(os, underline, "underline", hasUnderline); streamAttribute(os, overline, "overline", hasOverline); streamAttribute(os, lineThrough, "line-through", hasLineThrough); @@ -51,6 +54,7 @@ std::ostream &DisplayTextAttributes::toStream(std::ostream &os) const streamAttribute(os, lineHeight, "line-height", hasLineHeight); streamAttribute(os, lang, "xml:lang", hasLang); streamAttribute(os, space, "xml:space", hasSpace); + streamAttribute(os, dir, "dir", hasDir); streamAttribute(os, enclosure, "enclosure", hasEnclosure); } return os; @@ -103,10 +107,18 @@ bool DisplayTextAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, underline, hasUnderline, "underline")) { continue; @@ -131,19 +143,15 @@ bool DisplayTextAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) - { - continue; - } if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, dir, hasDir, "dir", &parseTextDirection)) { continue; } @@ -154,7 +162,7 @@ bool DisplayTextAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/DisplayTextAttributes.h b/src/private/mx/core/elements/DisplayTextAttributes.h index ddf74917..d1de980a 100644 --- a/src/private/mx/core/elements/DisplayTextAttributes.h +++ b/src/private/mx/core/elements/DisplayTextAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -40,7 +41,9 @@ struct DisplayTextAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; NumberOfLines underline; NumberOfLines overline; NumberOfLines lineThrough; @@ -49,6 +52,7 @@ struct DisplayTextAttributes : public AttributesInterface NumberOrNormal lineHeight; XmlLang lang; XmlSpace space; + TextDirection dir; EnclosureShape enclosure; bool hasJustify; bool hasDefaultX; @@ -59,7 +63,9 @@ struct DisplayTextAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; bool hasUnderline; bool hasOverline; bool hasLineThrough; @@ -68,6 +74,7 @@ struct DisplayTextAttributes : public AttributesInterface bool hasLineHeight; bool hasLang; bool hasSpace; + bool hasDir; bool hasEnclosure; private: diff --git a/src/private/mx/core/elements/DisplayTextOrAccidentalText.cpp b/src/private/mx/core/elements/DisplayTextOrAccidentalText.cpp index d0de17f1..9ea2d83c 100644 --- a/src/private/mx/core/elements/DisplayTextOrAccidentalText.cpp +++ b/src/private/mx/core/elements/DisplayTextOrAccidentalText.cpp @@ -34,37 +34,21 @@ std::ostream &DisplayTextOrAccidentalText::streamName(std::ostream &os) const bool DisplayTextOrAccidentalText::hasContents() const { - if (myChoice == Choice::accidentalText) - { - return myAccidentalText->hasContents(); - } - if (myChoice == Choice::displayText) - { - return myDisplayText->hasContents(); - } - return false; + return true; } std::ostream &DisplayTextOrAccidentalText::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - MX_UNUSED(isOneLineOnly); - if (hasContents()) + if (myChoice == Choice::displayText) + { + myDisplayText->toStream(os, indentLevel); + } + if (myChoice == Choice::accidentalText) { - switch (myChoice) - { - case Choice::accidentalText: { - myAccidentalText->toStream(os, indentLevel); - } - break; - case Choice::displayText: { - myDisplayText->toStream(os, indentLevel); - } - break; - default: - break; - } + myAccidentalText->toStream(os, indentLevel); } + isOneLineOnly = false; return os; } @@ -73,7 +57,7 @@ DisplayTextOrAccidentalText::Choice DisplayTextOrAccidentalText::getChoice() con return myChoice; } -void DisplayTextOrAccidentalText::setChoice(const DisplayTextOrAccidentalText::Choice value) +void DisplayTextOrAccidentalText::setChoice(const Choice value) { myChoice = value; } @@ -104,12 +88,7 @@ void DisplayTextOrAccidentalText::setAccidentalText(const AccidentalTextPtr &val } } -bool DisplayTextOrAccidentalText::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - MX_CHOICE_IF(displayText, "display-text", DisplayText); - MX_CHOICE_IF(accidentalText, "accidental-text", AccidentalText); - MX_BAD_ELEMENT_FAILURE(DisplayTextOrAccidentalText); -} +MX_FROM_XELEMENT_UNUSED(DisplayTextOrAccidentalText); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/DisplayTextOrAccidentalText.h b/src/private/mx/core/elements/DisplayTextOrAccidentalText.h index 3e2daa32..10a55bdf 100644 --- a/src/private/mx/core/elements/DisplayTextOrAccidentalText.h +++ b/src/private/mx/core/elements/DisplayTextOrAccidentalText.h @@ -16,8 +16,8 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(AccidentalText) MX_FORWARD_DECLARE_ELEMENT(DisplayText) +MX_FORWARD_DECLARE_ELEMENT(AccidentalText) MX_FORWARD_DECLARE_ELEMENT(DisplayTextOrAccidentalText) inline DisplayTextOrAccidentalTextPtr makeDisplayTextOrAccidentalText() @@ -30,8 +30,8 @@ class DisplayTextOrAccidentalText : public ElementInterface public: enum class Choice { - displayText = 1, - accidentalText = 2 + displayText = 0, + accidentalText = 1 }; DisplayTextOrAccidentalText(); @@ -40,8 +40,8 @@ class DisplayTextOrAccidentalText : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - DisplayTextOrAccidentalText::Choice getChoice() const; - void setChoice(const DisplayTextOrAccidentalText::Choice value); + Choice getChoice() const; + void setChoice(const Choice value); DisplayTextPtr getDisplayText() const; void setDisplayText(const DisplayTextPtr &value); AccidentalTextPtr getAccidentalText() const; diff --git a/src/private/mx/core/elements/DistanceAttributes.cpp b/src/private/mx/core/elements/DistanceAttributes.cpp index 0340c1c5..9c245730 100644 --- a/src/private/mx/core/elements/DistanceAttributes.cpp +++ b/src/private/mx/core/elements/DistanceAttributes.cpp @@ -48,7 +48,7 @@ bool DistanceAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/Divisions.cpp b/src/private/mx/core/elements/Divisions.cpp index f3bd169e..91966dd4 100644 --- a/src/private/mx/core/elements/Divisions.cpp +++ b/src/private/mx/core/elements/Divisions.cpp @@ -60,6 +60,7 @@ void Divisions::setValue(const PositiveDivisionsValue &value) bool Divisions::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); + MX_UNUSED(xelement); myValue.parse(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/DynamicsAttributes.cpp b/src/private/mx/core/elements/DynamicsAttributes.cpp index f02c25b2..c2d5ec5b 100644 --- a/src/private/mx/core/elements/DynamicsAttributes.cpp +++ b/src/private/mx/core/elements/DynamicsAttributes.cpp @@ -11,10 +11,13 @@ namespace mx namespace core { DynamicsAttributes::DynamicsAttributes() - : hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), - hasValign(false), hasPlacement(false), hasUnderline(false), hasOverline(false), hasLineThrough(false), - hasEnclosure(false) + : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::left), + valign(Valign::bottom), placement(AboveBelow::below), underline(), overline(), lineThrough(), + enclosure(EnclosureShape::none), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), + hasHalign(false), hasValign(false), hasPlacement(false), hasUnderline(false), hasOverline(false), + hasLineThrough(false), hasEnclosure(false) { } diff --git a/src/private/mx/core/elements/EditorialGroup.cpp b/src/private/mx/core/elements/EditorialGroup.cpp index b7c94ca4..e08925ff 100644 --- a/src/private/mx/core/elements/EditorialGroup.cpp +++ b/src/private/mx/core/elements/EditorialGroup.cpp @@ -39,20 +39,23 @@ bool EditorialGroup::hasContents() const std::ostream &EditorialGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + bool firstItem = true; isOneLineOnly = true; if (myHasFootnote) { - myFootnote->toStream(os, indentLevel); - if (myHasLevel) - { + if (!firstItem) os << std::endl; - isOneLineOnly = false; - } + myFootnote->toStream(os, indentLevel); + firstItem = false; } if (myHasLevel) { + if (!firstItem) + os << std::endl; myLevel->toStream(os, indentLevel); + firstItem = false; } + isOneLineOnly = !hasContents(); return os; } @@ -103,5 +106,6 @@ void EditorialGroup::setHasLevel(const bool value) } MX_FROM_XELEMENT_UNUSED(EditorialGroup); + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/EditorialVoiceDirectionGroup.cpp b/src/private/mx/core/elements/EditorialVoiceDirectionGroup.cpp index 73170a60..2747ae70 100644 --- a/src/private/mx/core/elements/EditorialVoiceDirectionGroup.cpp +++ b/src/private/mx/core/elements/EditorialVoiceDirectionGroup.cpp @@ -46,6 +46,8 @@ std::ostream &EditorialVoiceDirectionGroup::streamContents(std::ostream &os, con isOneLineOnly = true; if (myHasFootnote) { + if (!firstItem) + os << std::endl; myFootnote->toStream(os, indentLevel); firstItem = false; } diff --git a/src/private/mx/core/elements/EditorialVoiceGroup.cpp b/src/private/mx/core/elements/EditorialVoiceGroup.cpp index 5a431f0f..169bdf6f 100644 --- a/src/private/mx/core/elements/EditorialVoiceGroup.cpp +++ b/src/private/mx/core/elements/EditorialVoiceGroup.cpp @@ -45,6 +45,8 @@ std::ostream &EditorialVoiceGroup::streamContents(std::ostream &os, const int in isOneLineOnly = true; if (myHasFootnote) { + if (!firstItem) + os << std::endl; myFootnote->toStream(os, indentLevel); firstItem = false; } diff --git a/src/private/mx/core/elements/ElisionAttributes.cpp b/src/private/mx/core/elements/ElisionAttributes.cpp index c58cfad7..9cc767c8 100644 --- a/src/private/mx/core/elements/ElisionAttributes.cpp +++ b/src/private/mx/core/elements/ElisionAttributes.cpp @@ -11,18 +11,18 @@ namespace mx namespace core { ElisionAttributes::ElisionAttributes() - : fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), underline(), overline(), lineThrough(), rotation(), letterSpacing(), - lang(XmlLang{"it"}), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasUnderline(false), hasOverline(false), hasLineThrough(false), hasRotation(false), hasLetterSpacing(false), - hasLang(false) + : fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + color(), underline(), overline(), lineThrough(), rotation(), letterSpacing(), lang(), dir(), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasUnderline(false), + hasOverline(false), hasLineThrough(false), hasRotation(false), hasLetterSpacing(false), hasLang(false), + hasDir(false) { } bool ElisionAttributes::hasValues() const { - return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasUnderline || hasOverline || - hasLineThrough || hasRotation || hasLetterSpacing || hasLang; + return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasUnderline || hasOverline || + hasLineThrough || hasRotation || hasLetterSpacing || hasLang || hasDir; } std::ostream &ElisionAttributes::toStream(std::ostream &os) const @@ -33,12 +33,14 @@ std::ostream &ElisionAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, underline, "underline", hasUnderline); streamAttribute(os, overline, "overline", hasOverline); streamAttribute(os, lineThrough, "line-through", hasLineThrough); streamAttribute(os, rotation, "rotation", hasRotation); streamAttribute(os, letterSpacing, "letter-spacing", hasLetterSpacing); streamAttribute(os, lang, "xml:lang", hasLang); + streamAttribute(os, dir, "dir", hasDir); } return os; } @@ -70,6 +72,10 @@ bool ElisionAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, underline, hasUnderline, "underline")) { continue; @@ -90,11 +96,11 @@ bool ElisionAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) + if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) + if (parseAttribute(message, it, className, isSuccess, dir, hasDir, "dir", &parseTextDirection)) { continue; } diff --git a/src/private/mx/core/elements/ElisionAttributes.h b/src/private/mx/core/elements/ElisionAttributes.h index 6d629667..eb6685da 100644 --- a/src/private/mx/core/elements/ElisionAttributes.h +++ b/src/private/mx/core/elements/ElisionAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -35,22 +36,26 @@ struct ElisionAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; NumberOfLines underline; NumberOfLines overline; NumberOfLines lineThrough; RotationDegrees rotation; NumberOrNormal letterSpacing; XmlLang lang; + TextDirection dir; bool hasFontFamily; bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasUnderline; bool hasOverline; bool hasLineThrough; bool hasRotation; bool hasLetterSpacing; bool hasLang; + bool hasDir; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/EmptyFontAttributes.cpp b/src/private/mx/core/elements/EmptyFontAttributes.cpp index 26933f4f..f47f472c 100644 --- a/src/private/mx/core/elements/EmptyFontAttributes.cpp +++ b/src/private/mx/core/elements/EmptyFontAttributes.cpp @@ -62,7 +62,7 @@ bool EmptyFontAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/EmptyLineAttributes.cpp b/src/private/mx/core/elements/EmptyLineAttributes.cpp index 197d5733..e4cbec27 100644 --- a/src/private/mx/core/elements/EmptyLineAttributes.cpp +++ b/src/private/mx/core/elements/EmptyLineAttributes.cpp @@ -11,18 +11,19 @@ namespace mx namespace core { EmptyLineAttributes::EmptyLineAttributes() - : lineShape(), lineType(), dashLength(), spaceLength(), defaultX(), defaultY(), relativeX(), relativeY(), - fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), - placement(AboveBelow::below), hasLineShape(false), hasLineType(false), hasDashLength(false), - hasSpaceLength(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasPlacement(false) + : lineShape(LineShape::straight), lineType(LineType::solid), dashLength(), spaceLength(), defaultX(), defaultY(), + relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), + fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), hasLineShape(false), hasLineType(false), + hasDashLength(false), hasSpaceLength(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), + hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), + hasColor(false), hasPlacement(false) { } bool EmptyLineAttributes::hasValues() const { return hasLineShape || hasLineType || hasDashLength || hasSpaceLength || hasDefaultX || hasDefaultY || - hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || + hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasPlacement; } @@ -30,7 +31,6 @@ std::ostream &EmptyLineAttributes::toStream(std::ostream &os) const { if (hasValues()) { - streamAttribute(os, dashLength, "dash-length", hasDashLength); streamAttribute(os, lineShape, "line-shape", hasLineShape); streamAttribute(os, lineType, "line-type", hasLineType); streamAttribute(os, dashLength, "dash-length", hasDashLength); @@ -43,6 +43,7 @@ std::ostream &EmptyLineAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -58,7 +59,6 @@ bool EmptyLineAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem for (; it != endIter; ++it) { - if (parseAttribute(message, it, className, isSuccess, lineShape, hasLineShape, "line-shape", &parseLineShape)) { continue; @@ -108,6 +108,10 @@ bool EmptyLineAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/EmptyLineAttributes.h b/src/private/mx/core/elements/EmptyLineAttributes.h index 682ec835..3f0bdf18 100644 --- a/src/private/mx/core/elements/EmptyLineAttributes.h +++ b/src/private/mx/core/elements/EmptyLineAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -40,6 +41,7 @@ struct EmptyLineAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasLineShape; bool hasLineType; @@ -53,6 +55,7 @@ struct EmptyLineAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/EmptyPlacementAttributes.cpp b/src/private/mx/core/elements/EmptyPlacementAttributes.cpp index f76ea22a..8b6c7258 100644 --- a/src/private/mx/core/elements/EmptyPlacementAttributes.cpp +++ b/src/private/mx/core/elements/EmptyPlacementAttributes.cpp @@ -12,16 +12,16 @@ namespace core { EmptyPlacementAttributes::EmptyPlacementAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), placement(AboveBelow::below), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool EmptyPlacementAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &EmptyPlacementAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &EmptyPlacementAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,6 +85,10 @@ bool EmptyPlacementAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/EmptyPlacementAttributes.h b/src/private/mx/core/elements/EmptyPlacementAttributes.h index e387d37f..4a5febae 100644 --- a/src/private/mx/core/elements/EmptyPlacementAttributes.h +++ b/src/private/mx/core/elements/EmptyPlacementAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct EmptyPlacementAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct EmptyPlacementAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/EmptyTrillSoundAttributes.cpp b/src/private/mx/core/elements/EmptyTrillSoundAttributes.cpp index 898fd469..818b2a36 100644 --- a/src/private/mx/core/elements/EmptyTrillSoundAttributes.cpp +++ b/src/private/mx/core/elements/EmptyTrillSoundAttributes.cpp @@ -12,20 +12,20 @@ namespace core { EmptyTrillSoundAttributes::EmptyTrillSoundAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), placement(AboveBelow::below), - startNote(StartNote::main), trillStep(), twoNoteTurn(TwoNoteTurn::none), accelerate(YesNo::no), beats(), - secondBeat(), lastBeat(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasPlacement(false), - hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), - hasSecondBeat(false), hasLastBeat(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), startNote(), + trillStep(), twoNoteTurn(), accelerate(YesNo::no), beats(), secondBeat(), lastBeat(), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), + hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false), hasStartNote(false), + hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), hasSecondBeat(false), + hasLastBeat(false) { } bool EmptyTrillSoundAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || hasAccelerate || - hasBeats || hasSecondBeat || hasLastBeat; + hasFontWeight || hasColor || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || + hasAccelerate || hasBeats || hasSecondBeat || hasLastBeat; } std::ostream &EmptyTrillSoundAttributes::toStream(std::ostream &os) const @@ -40,6 +40,7 @@ std::ostream &EmptyTrillSoundAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); streamAttribute(os, startNote, "start-note", hasStartNote); streamAttribute(os, trillStep, "trill-step", hasTrillStep); @@ -95,6 +96,10 @@ bool EmptyTrillSoundAttributes::fromXElementImpl(std::ostream &message, ::ezxml: { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/EmptyTrillSoundAttributes.h b/src/private/mx/core/elements/EmptyTrillSoundAttributes.h index 04bc1f1a..06056654 100644 --- a/src/private/mx/core/elements/EmptyTrillSoundAttributes.h +++ b/src/private/mx/core/elements/EmptyTrillSoundAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct EmptyTrillSoundAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; StartNote startNote; TrillStep trillStep; @@ -52,6 +54,7 @@ struct EmptyTrillSoundAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; bool hasStartNote; bool hasTrillStep; diff --git a/src/private/mx/core/elements/Encoder.cpp b/src/private/mx/core/elements/Encoder.cpp index 901fafe0..f3452297 100644 --- a/src/private/mx/core/elements/Encoder.cpp +++ b/src/private/mx/core/elements/Encoder.cpp @@ -79,7 +79,7 @@ bool Encoder::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelemen bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.setValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/EncoderAttributes.cpp b/src/private/mx/core/elements/EncoderAttributes.cpp index 104a9c46..1c7b7c3b 100644 --- a/src/private/mx/core/elements/EncoderAttributes.cpp +++ b/src/private/mx/core/elements/EncoderAttributes.cpp @@ -44,7 +44,7 @@ bool EncoderAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/EncodingChoice.h b/src/private/mx/core/elements/EncodingChoice.h index 89cef883..f5684161 100644 --- a/src/private/mx/core/elements/EncodingChoice.h +++ b/src/private/mx/core/elements/EncodingChoice.h @@ -47,7 +47,7 @@ class EncodingChoice : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - /* _________ Choice _________ */ + /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */ EncodingChoice::Choice getChoice() const; void setChoice(const EncodingChoice::Choice value); diff --git a/src/private/mx/core/elements/EndingAttributes.cpp b/src/private/mx/core/elements/EndingAttributes.cpp index 17b4466f..d01958c4 100644 --- a/src/private/mx/core/elements/EndingAttributes.cpp +++ b/src/private/mx/core/elements/EndingAttributes.cpp @@ -11,19 +11,19 @@ namespace mx namespace core { EndingAttributes::EndingAttributes() - : number("1"), type(StartStopDiscontinue::start), printObject(), defaultX(), defaultY(), relativeX(), relativeY(), - fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), endLength(), textX(), textY(), hasNumber(true), hasType(true), - hasPrintObject(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasEndLength(false), - hasTextX(false), hasTextY(false) + : number("1"), type(), printObject(YesNo::no), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), endLength(), + textX(), textY(), hasNumber(true), hasType(true), hasPrintObject(false), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), + hasFontWeight(false), hasColor(false), hasEndLength(false), hasTextX(false), hasTextY(false) { } bool EndingAttributes::hasValues() const { return hasNumber || hasType || hasPrintObject || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || - hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasEndLength || hasTextX || hasTextY; + hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasEndLength || hasTextX || + hasTextY; } std::ostream &EndingAttributes::toStream(std::ostream &os) const @@ -41,6 +41,7 @@ std::ostream &EndingAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, endLength, "end-length", hasEndLength); streamAttribute(os, textX, "text-x", hasTextX); streamAttribute(os, textY, "text-y", hasTextY); @@ -62,7 +63,6 @@ bool EndingAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { if (parseAttribute(message, it, className, isSuccess, number, isNumberFound, "number")) { - number.setUseSpaceBetweenItems(it->getValue().find(" ") != std::string::npos); continue; } if (parseAttribute(message, it, className, isSuccess, type, isTypeFound, "type", &parseStartStopDiscontinue)) @@ -106,6 +106,10 @@ bool EndingAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, endLength, hasEndLength, "end-length")) { continue; @@ -125,10 +129,11 @@ bool EndingAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement isSuccess = false; message << className << ": 'number' is a required attribute but was not found" << std::endl; } + if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/EndingAttributes.h b/src/private/mx/core/elements/EndingAttributes.h index 44a665d5..052475ae 100644 --- a/src/private/mx/core/elements/EndingAttributes.h +++ b/src/private/mx/core/elements/EndingAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/EndingNumber.h" @@ -40,6 +41,7 @@ struct EndingAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; TenthsValue endLength; TenthsValue textX; TenthsValue textY; @@ -54,6 +56,7 @@ struct EndingAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasEndLength; bool hasTextX; bool hasTextY; diff --git a/src/private/mx/core/elements/ExtendAttributes.cpp b/src/private/mx/core/elements/ExtendAttributes.cpp index ae4099b0..38241712 100644 --- a/src/private/mx/core/elements/ExtendAttributes.cpp +++ b/src/private/mx/core/elements/ExtendAttributes.cpp @@ -11,17 +11,17 @@ namespace mx namespace core { ExtendAttributes::ExtendAttributes() - : type(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), hasType(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false) + : type(StartStopContinue::start), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + hasType(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool ExtendAttributes::hasValues() const { return hasType || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight; + hasFontSize || hasFontWeight || hasColor; } std::ostream &ExtendAttributes::toStream(std::ostream &os) const @@ -37,6 +37,7 @@ std::ostream &ExtendAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -88,9 +89,13 @@ bool ExtendAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/ExtendAttributes.h b/src/private/mx/core/elements/ExtendAttributes.h index ff6d275d..4712f4bb 100644 --- a/src/private/mx/core/elements/ExtendAttributes.h +++ b/src/private/mx/core/elements/ExtendAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -37,6 +38,7 @@ struct ExtendAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasType; bool hasDefaultX; bool hasDefaultY; @@ -46,6 +48,7 @@ struct ExtendAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Eyeglasses.h b/src/private/mx/core/elements/Eyeglasses.h index 70842297..5101aed4 100644 --- a/src/private/mx/core/elements/Eyeglasses.h +++ b/src/private/mx/core/elements/Eyeglasses.h @@ -17,6 +17,7 @@ namespace mx namespace core { +MX_FORWARD_DECLARE_ATTRIBUTES(EmptyPrintObjectStyleAlignAttributes) MX_FORWARD_DECLARE_ELEMENT(Eyeglasses) inline EyeglassesPtr makeEyeglasses() diff --git a/src/private/mx/core/elements/FermataAttributes.cpp b/src/private/mx/core/elements/FermataAttributes.cpp index 31396834..0353e489 100644 --- a/src/private/mx/core/elements/FermataAttributes.cpp +++ b/src/private/mx/core/elements/FermataAttributes.cpp @@ -11,10 +11,10 @@ namespace mx namespace core { FermataAttributes::FermataAttributes() - : type(UprightInverted::upright), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), color(), - hasType(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) + : type(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasType(false), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), + hasFontSize(false), hasFontWeight(false), hasColor(false) { } diff --git a/src/private/mx/core/elements/Fifths.cpp b/src/private/mx/core/elements/Fifths.cpp index 102d0cf0..9a9c5daa 100644 --- a/src/private/mx/core/elements/Fifths.cpp +++ b/src/private/mx/core/elements/Fifths.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -Fifths::Fifths() : myValue{0} +Fifths::Fifths() : myValue() { } diff --git a/src/private/mx/core/elements/Figure.cpp b/src/private/mx/core/elements/Figure.cpp index 80f00b37..540c86d9 100644 --- a/src/private/mx/core/elements/Figure.cpp +++ b/src/private/mx/core/elements/Figure.cpp @@ -63,10 +63,10 @@ std::ostream &Figure::streamContents(std::ostream &os, const int indentLevel, bo os << std::endl; myExtend->toStream(os, indentLevel + 1); } - if (hasContents()) + if (myHasPrefix || myHasFigureNumber || myHasSuffix || myHasExtend) { - os << std::endl; isOneLineOnly = false; + os << std::endl; } else { diff --git a/src/private/mx/core/elements/FigureNumber.cpp b/src/private/mx/core/elements/FigureNumber.cpp index 59038595..fc775e26 100644 --- a/src/private/mx/core/elements/FigureNumber.cpp +++ b/src/private/mx/core/elements/FigureNumber.cpp @@ -80,7 +80,7 @@ bool FigureNumber::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xe bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.setValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/FigureNumberAttributes.cpp b/src/private/mx/core/elements/FigureNumberAttributes.cpp index 715ee331..f98bc0ea 100644 --- a/src/private/mx/core/elements/FigureNumberAttributes.cpp +++ b/src/private/mx/core/elements/FigureNumberAttributes.cpp @@ -12,16 +12,16 @@ namespace core { FigureNumberAttributes::FigureNumberAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasDefaultX(false), hasDefaultY(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + hasFontWeight(false), hasColor(false) { } bool FigureNumberAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight; + hasFontWeight || hasColor; } std::ostream &FigureNumberAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &FigureNumberAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -83,9 +84,13 @@ bool FigureNumberAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XE { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/FigureNumberAttributes.h b/src/private/mx/core/elements/FigureNumberAttributes.h index 95020c3e..a9373b3d 100644 --- a/src/private/mx/core/elements/FigureNumberAttributes.h +++ b/src/private/mx/core/elements/FigureNumberAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct FigureNumberAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; @@ -44,6 +46,7 @@ struct FigureNumberAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/FiguredBass.cpp b/src/private/mx/core/elements/FiguredBass.cpp index 1eb4c132..753eac40 100644 --- a/src/private/mx/core/elements/FiguredBass.cpp +++ b/src/private/mx/core/elements/FiguredBass.cpp @@ -43,6 +43,7 @@ bool FiguredBass::hasContents() const std::ostream &FiguredBass::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; for (auto x : myFigureSet) { os << std::endl; @@ -59,7 +60,6 @@ std::ostream &FiguredBass::streamContents(std::ostream &os, const int indentLeve myEditorialGroup->streamContents(os, indentLevel + 1, isOneLineOnly); } os << std::endl; - isOneLineOnly = false; return os; } @@ -103,10 +103,7 @@ void FiguredBass::addFigure(const FigurePtr &value) void FiguredBass::clearFigureSet() { myFigureSet.clear(); - while (myFigureSet.size() < 1) - { - myFigureSet.push_back(makeFigure()); - } + myFigureSet.push_back(makeFigure()); } FigurePtr FiguredBass::getFigure(const FigureSetIterConst &setIterator) const @@ -170,7 +167,7 @@ bool FiguredBass::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xel importGroup(message, it, endIter, isSuccess, myEditorialGroup); } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/FiguredBass.h b/src/private/mx/core/elements/FiguredBass.h index d7944767..cda8ac4c 100644 --- a/src/private/mx/core/elements/FiguredBass.h +++ b/src/private/mx/core/elements/FiguredBass.h @@ -39,7 +39,7 @@ class FiguredBass : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; FiguredBassAttributesPtr getAttributes() const; - void setAttributes(const FiguredBassAttributesPtr &value); + void setAttributes(const FiguredBassAttributesPtr &attributes); /* _________ Figure minOccurs = 1, maxOccurs = unbounded _________ */ const FigureSet &getFigureSet() const; diff --git a/src/private/mx/core/elements/Fingering.cpp b/src/private/mx/core/elements/Fingering.cpp index c3c54b61..1f8fe7ce 100644 --- a/src/private/mx/core/elements/Fingering.cpp +++ b/src/private/mx/core/elements/Fingering.cpp @@ -79,7 +79,7 @@ bool Fingering::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.setValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/FingeringAttributes.cpp b/src/private/mx/core/elements/FingeringAttributes.cpp index d9a3f08d..ea6f6138 100644 --- a/src/private/mx/core/elements/FingeringAttributes.cpp +++ b/src/private/mx/core/elements/FingeringAttributes.cpp @@ -12,17 +12,17 @@ namespace core { FingeringAttributes::FingeringAttributes() : substitution(YesNo::no), alternate(YesNo::no), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), - placement(), hasSubstitution(false), hasAlternate(false), hasDefaultX(false), hasDefaultY(false), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + placement(AboveBelow::below), hasSubstitution(false), hasAlternate(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false), hasPlacement(false) + hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool FingeringAttributes::hasValues() const { return hasSubstitution || hasAlternate || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || - hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasPlacement; + hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasPlacement; } std::ostream &FingeringAttributes::toStream(std::ostream &os) const @@ -39,6 +39,7 @@ std::ostream &FingeringAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -96,6 +97,10 @@ bool FingeringAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/FingeringAttributes.h b/src/private/mx/core/elements/FingeringAttributes.h index 419261c4..fc119942 100644 --- a/src/private/mx/core/elements/FingeringAttributes.h +++ b/src/private/mx/core/elements/FingeringAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -38,6 +39,7 @@ struct FingeringAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasSubstitution; bool hasAlternate; @@ -49,6 +51,7 @@ struct FingeringAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/FirstFretAttributes.h b/src/private/mx/core/elements/FirstFretAttributes.h index b9ee9300..1acc1afc 100644 --- a/src/private/mx/core/elements/FirstFretAttributes.h +++ b/src/private/mx/core/elements/FirstFretAttributes.h @@ -7,8 +7,6 @@ #include "mx/core/AttributesInterface.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" -#include "mx/core/XmlLang.h" -#include "mx/core/XsString.h" #include "mx/core/XsToken.h" #include diff --git a/src/private/mx/core/elements/Footnote.h b/src/private/mx/core/elements/Footnote.h index 6c64c74a..89e1ab40 100644 --- a/src/private/mx/core/elements/Footnote.h +++ b/src/private/mx/core/elements/Footnote.h @@ -6,6 +6,7 @@ #include "mx/core/ElementInterface.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsString.h" #include "mx/core/elements/FootnoteAttributes.h" #include diff --git a/src/private/mx/core/elements/FootnoteAttributes.cpp b/src/private/mx/core/elements/FootnoteAttributes.cpp index 1605ed18..d00a98ac 100644 --- a/src/private/mx/core/elements/FootnoteAttributes.cpp +++ b/src/private/mx/core/elements/FootnoteAttributes.cpp @@ -11,22 +11,23 @@ namespace mx namespace core { FootnoteAttributes::FootnoteAttributes() - : justify(LeftCenterRight::center), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), halign(), - underline(), overline(), lineThrough(), rotation(), letterSpacing(), lineHeight(), lang(XmlLang{"it"}), - space(XmlSpace::default_), enclosure(EnclosureShape::rectangle), hasJustify(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasHalign(false), hasUnderline(false), hasOverline(false), - hasLineThrough(false), hasRotation(false), hasLetterSpacing(false), hasLineHeight(false), hasLang(false), - hasSpace(false), hasEnclosure(false) + : justify(LeftCenterRight::left), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + halign(LeftCenterRight::left), valign(Valign::bottom), underline(), overline(), lineThrough(), rotation(), + letterSpacing(), lineHeight(), lang(), space(), dir(), enclosure(EnclosureShape::none), hasJustify(false), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), + hasValign(false), hasUnderline(false), hasOverline(false), hasLineThrough(false), hasRotation(false), + hasLetterSpacing(false), hasLineHeight(false), hasLang(false), hasSpace(false), hasDir(false), hasEnclosure(false) { } bool FootnoteAttributes::hasValues() const { return hasJustify || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight || hasHalign || hasUnderline || hasOverline || hasLineThrough || hasRotation || - hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasEnclosure; + hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign || hasUnderline || hasOverline || + hasLineThrough || hasRotation || hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasDir || + hasEnclosure; } std::ostream &FootnoteAttributes::toStream(std::ostream &os) const @@ -42,7 +43,9 @@ std::ostream &FootnoteAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); streamAttribute(os, underline, "underline", hasUnderline); streamAttribute(os, overline, "overline", hasOverline); streamAttribute(os, lineThrough, "line-through", hasLineThrough); @@ -51,6 +54,7 @@ std::ostream &FootnoteAttributes::toStream(std::ostream &os) const streamAttribute(os, lineHeight, "line-height", hasLineHeight); streamAttribute(os, lang, "xml:lang", hasLang); streamAttribute(os, space, "xml:space", hasSpace); + streamAttribute(os, dir, "dir", hasDir); streamAttribute(os, enclosure, "enclosure", hasEnclosure); } return os; @@ -103,10 +107,18 @@ bool FootnoteAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, underline, hasUnderline, "underline")) { continue; @@ -131,19 +143,15 @@ bool FootnoteAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) - { - continue; - } if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, dir, hasDir, "dir", &parseTextDirection)) { continue; } @@ -154,7 +162,7 @@ bool FootnoteAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/FootnoteAttributes.h b/src/private/mx/core/elements/FootnoteAttributes.h index 3dc211c1..5ac9de0c 100644 --- a/src/private/mx/core/elements/FootnoteAttributes.h +++ b/src/private/mx/core/elements/FootnoteAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -40,7 +41,9 @@ struct FootnoteAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; NumberOfLines underline; NumberOfLines overline; NumberOfLines lineThrough; @@ -49,6 +52,7 @@ struct FootnoteAttributes : public AttributesInterface NumberOrNormal lineHeight; XmlLang lang; XmlSpace space; + TextDirection dir; EnclosureShape enclosure; bool hasJustify; bool hasDefaultX; @@ -59,7 +63,9 @@ struct FootnoteAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; bool hasUnderline; bool hasOverline; bool hasLineThrough; @@ -68,6 +74,7 @@ struct FootnoteAttributes : public AttributesInterface bool hasLineHeight; bool hasLang; bool hasSpace; + bool hasDir; bool hasEnclosure; private: diff --git a/src/private/mx/core/elements/Forward.cpp b/src/private/mx/core/elements/Forward.cpp index 054a14da..af66dc51 100644 --- a/src/private/mx/core/elements/Forward.cpp +++ b/src/private/mx/core/elements/Forward.cpp @@ -42,6 +42,7 @@ bool Forward::hasContents() const std::ostream &Forward::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myDuration->toStream(os, indentLevel + 1); if (myEditorialVoiceGroup->hasContents()) @@ -54,7 +55,6 @@ std::ostream &Forward::streamContents(std::ostream &os, const int indentLevel, b os << std::endl; myStaff->toStream(os, indentLevel + 1); } - isOneLineOnly = false; os << std::endl; return os; } diff --git a/src/private/mx/core/elements/Forward.h b/src/private/mx/core/elements/Forward.h index d72490b4..e3c98793 100644 --- a/src/private/mx/core/elements/Forward.h +++ b/src/private/mx/core/elements/Forward.h @@ -18,7 +18,6 @@ namespace core MX_FORWARD_DECLARE_ELEMENT(Duration) MX_FORWARD_DECLARE_ELEMENT(EditorialVoiceGroup) -MX_FORWARD_DECLARE_ELEMENT(Footnote) MX_FORWARD_DECLARE_ELEMENT(Staff) MX_FORWARD_DECLARE_ELEMENT(Forward) @@ -42,7 +41,7 @@ class Forward : public ElementInterface DurationPtr getDuration() const; void setDuration(const DurationPtr &value); - /* _________ Footnote minOccurs = 1, maxOccurs = 1 _________ */ + /* _________ EditorialVoiceGroup minOccurs = 1, maxOccurs = 1 _________ */ EditorialVoiceGroupPtr getEditorialVoiceGroup() const; void setEditorialVoiceGroup(const EditorialVoiceGroupPtr &value); diff --git a/src/private/mx/core/elements/Frame.cpp b/src/private/mx/core/elements/Frame.cpp index 26e115db..2bb93fe3 100644 --- a/src/private/mx/core/elements/Frame.cpp +++ b/src/private/mx/core/elements/Frame.cpp @@ -189,14 +189,6 @@ bool Frame::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) importElementSet(message, it, endIter, isSuccess, "frame-note", myFrameNoteSet); } - if (!isFrameStringsFound) - { - message << "Frame: '" << myFrameStrings->getElementName() << "' is required but was not found" << std::endl; - } - if (!isFrameFretsFound) - { - message << "Frame: '" << myFrameFrets->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Frame.h b/src/private/mx/core/elements/Frame.h index b23b9f1f..859ce196 100644 --- a/src/private/mx/core/elements/Frame.h +++ b/src/private/mx/core/elements/Frame.h @@ -40,7 +40,7 @@ class Frame : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; FrameAttributesPtr getAttributes() const; - void setAttributes(const FrameAttributesPtr &value); + void setAttributes(const FrameAttributesPtr &attributes); /* _________ FrameStrings minOccurs = 1, maxOccurs = 1 _________ */ FrameStringsPtr getFrameStrings() const; diff --git a/src/private/mx/core/elements/FrameAttributes.cpp b/src/private/mx/core/elements/FrameAttributes.cpp index 3d9e0bd8..cc79da68 100644 --- a/src/private/mx/core/elements/FrameAttributes.cpp +++ b/src/private/mx/core/elements/FrameAttributes.cpp @@ -11,9 +11,10 @@ namespace mx namespace core { FrameAttributes::FrameAttributes() - : defaultX(), defaultY(), relativeX(), relativeY(), color(), halign(LeftCenterRight::center), valign(), height(), - width(), unplayed(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasColor(false), hasHalign(false), hasValign(false), hasHeight(false), hasWidth(false), hasUnplayed(false) + : defaultX(), defaultY(), relativeX(), relativeY(), color(), halign(LeftCenterRight::left), + valign(ValignImage::bottom), height(), width(), unplayed(), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasColor(false), hasHalign(false), hasValign(false), hasHeight(false), + hasWidth(false), hasUnplayed(false) { } diff --git a/src/private/mx/core/elements/FrameNote.cpp b/src/private/mx/core/elements/FrameNote.cpp index 2c5ec002..0bf71f18 100644 --- a/src/private/mx/core/elements/FrameNote.cpp +++ b/src/private/mx/core/elements/FrameNote.cpp @@ -161,14 +161,6 @@ bool FrameNote::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem } } - if (!isStringFound) - { - message << "FrameNote: '" << myString->getElementName() << "' is required but was not found" << std::endl; - } - if (!isFretFound) - { - message << "FrameNote: '" << myFret->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/FretAttributes.cpp b/src/private/mx/core/elements/FretAttributes.cpp index 69e00090..632a578b 100644 --- a/src/private/mx/core/elements/FretAttributes.cpp +++ b/src/private/mx/core/elements/FretAttributes.cpp @@ -11,15 +11,14 @@ namespace mx namespace core { FretAttributes::FretAttributes() - : fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + : fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + color(), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool FretAttributes::hasValues() const { - return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight; + return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &FretAttributes::toStream(std::ostream &os) const @@ -30,6 +29,7 @@ std::ostream &FretAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -61,6 +61,10 @@ bool FretAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/FretAttributes.h b/src/private/mx/core/elements/FretAttributes.h index e4c256cc..4a691320 100644 --- a/src/private/mx/core/elements/FretAttributes.h +++ b/src/private/mx/core/elements/FretAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" @@ -31,10 +32,12 @@ struct FretAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasFontFamily; bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/FunctionAttributes.cpp b/src/private/mx/core/elements/FunctionAttributes.cpp index 25eb2662..131dfffc 100644 --- a/src/private/mx/core/elements/FunctionAttributes.cpp +++ b/src/private/mx/core/elements/FunctionAttributes.cpp @@ -12,16 +12,16 @@ namespace core { FunctionAttributes::FunctionAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasDefaultX(false), hasDefaultY(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + hasFontWeight(false), hasColor(false) { } bool FunctionAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight; + hasFontWeight || hasColor; } std::ostream &FunctionAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &FunctionAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -83,6 +84,10 @@ bool FunctionAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/FunctionAttributes.h b/src/private/mx/core/elements/FunctionAttributes.h index 3ca96a5f..a5e8cdce 100644 --- a/src/private/mx/core/elements/FunctionAttributes.h +++ b/src/private/mx/core/elements/FunctionAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct FunctionAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; @@ -44,6 +46,7 @@ struct FunctionAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/GlissandoAttributes.cpp b/src/private/mx/core/elements/GlissandoAttributes.cpp index 74831303..4522d3e5 100644 --- a/src/private/mx/core/elements/GlissandoAttributes.cpp +++ b/src/private/mx/core/elements/GlissandoAttributes.cpp @@ -11,18 +11,19 @@ namespace mx namespace core { GlissandoAttributes::GlissandoAttributes() - : type(StartStop::start), number(1), lineType(LineType::solid), dashLength(), spaceLength(), defaultX(), defaultY(), - relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), hasType(true), hasNumber(false), hasLineType(false), hasDashLength(false), - hasSpaceLength(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false) + : type(StartStop::start), number(), lineType(LineType::solid), dashLength(), spaceLength(), defaultX(), defaultY(), + relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), + fontWeight(FontWeight::normal), color(), hasType(true), hasNumber(false), hasLineType(false), + hasDashLength(false), hasSpaceLength(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), + hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), + hasColor(false) { } bool GlissandoAttributes::hasValues() const { return hasType || hasNumber || hasLineType || hasDashLength || hasSpaceLength || hasDefaultX || hasDefaultY || - hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight; + hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &GlissandoAttributes::toStream(std::ostream &os) const @@ -42,6 +43,7 @@ std::ostream &GlissandoAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -110,15 +112,19 @@ bool GlissandoAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/GlissandoAttributes.h b/src/private/mx/core/elements/GlissandoAttributes.h index 903e041d..cc3ae430 100644 --- a/src/private/mx/core/elements/GlissandoAttributes.h +++ b/src/private/mx/core/elements/GlissandoAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -42,6 +43,7 @@ struct GlissandoAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; const bool hasType; bool hasNumber; bool hasLineType; @@ -55,6 +57,7 @@ struct GlissandoAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/GraceAttributes.cpp b/src/private/mx/core/elements/GraceAttributes.cpp index 6e5c1ad7..0f76f314 100644 --- a/src/private/mx/core/elements/GraceAttributes.cpp +++ b/src/private/mx/core/elements/GraceAttributes.cpp @@ -63,7 +63,7 @@ bool GraceAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/GroupAbbreviationAttributes.cpp b/src/private/mx/core/elements/GroupAbbreviationAttributes.cpp index 24ecb7b2..8aa5aae5 100644 --- a/src/private/mx/core/elements/GroupAbbreviationAttributes.cpp +++ b/src/private/mx/core/elements/GroupAbbreviationAttributes.cpp @@ -12,16 +12,16 @@ namespace core { GroupAbbreviationAttributes::GroupAbbreviationAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), justify(LeftCenterRight::center), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), justify(LeftCenterRight::left), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasJustify(false) + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasJustify(false) { } bool GroupAbbreviationAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasJustify; + hasFontWeight || hasColor || hasJustify; } std::ostream &GroupAbbreviationAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &GroupAbbreviationAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, justify, "justify", hasJustify); } return os; @@ -84,6 +85,10 @@ bool GroupAbbreviationAttributes::fromXElementImpl(std::ostream &message, ::ezxm { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, justify, hasJustify, "justify", &parseLeftCenterRight)) { continue; diff --git a/src/private/mx/core/elements/GroupAbbreviationAttributes.h b/src/private/mx/core/elements/GroupAbbreviationAttributes.h index 57885268..58e61ee3 100644 --- a/src/private/mx/core/elements/GroupAbbreviationAttributes.h +++ b/src/private/mx/core/elements/GroupAbbreviationAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct GroupAbbreviationAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight justify; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct GroupAbbreviationAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasJustify; private: diff --git a/src/private/mx/core/elements/GroupAbbreviationDisplay.cpp b/src/private/mx/core/elements/GroupAbbreviationDisplay.cpp index fc1a3279..e90ce15a 100644 --- a/src/private/mx/core/elements/GroupAbbreviationDisplay.cpp +++ b/src/private/mx/core/elements/GroupAbbreviationDisplay.cpp @@ -4,6 +4,8 @@ #include "mx/core/elements/GroupAbbreviationDisplay.h" #include "mx/core/FromXElement.h" +#include "mx/core/elements/AccidentalText.h" +#include "mx/core/elements/DisplayText.h" #include "mx/core/elements/DisplayTextOrAccidentalText.h" #include @@ -40,17 +42,13 @@ bool GroupAbbreviationDisplay::hasContents() const std::ostream &GroupAbbreviationDisplay::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - if (hasContents()) + for (auto x : myDisplayTextOrAccidentalTextSet) { - for (auto x : myDisplayTextOrAccidentalTextSet) - { - os << std::endl; - x->streamContents(os, indentLevel + 1, isOneLineOnly); - } - isOneLineOnly = false; os << std::endl; + x->streamContents(os, indentLevel + 1, isOneLineOnly); } - isOneLineOnly = !hasContents(); + isOneLineOnly = false; + os << std::endl; return os; } @@ -67,7 +65,7 @@ void GroupAbbreviationDisplay::setAttributes(const GroupAbbreviationDisplayAttri } } -const DisplayTextOrAccidentalTextSet &GroupAbbreviationDisplay::getDisplayTextOrAccidentalText() const +const DisplayTextOrAccidentalTextSet &GroupAbbreviationDisplay::getDisplayTextOrAccidentalTextSet() const { return myDisplayTextOrAccidentalTextSet; } @@ -80,12 +78,11 @@ void GroupAbbreviationDisplay::addDisplayTextOrAccidentalText(const DisplayTextO } } -void GroupAbbreviationDisplay::removeDisplayTextOrAccidentalText( - const DisplayTextOrAccidentalTextSetIterConst &setIterator) +void GroupAbbreviationDisplay::removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &value) { - if (setIterator != myDisplayTextOrAccidentalTextSet.cend()) + if (value != myDisplayTextOrAccidentalTextSet.cend()) { - myDisplayTextOrAccidentalTextSet.erase(setIterator); + myDisplayTextOrAccidentalTextSet.erase(value); } } @@ -101,12 +98,7 @@ DisplayTextOrAccidentalTextPtr GroupAbbreviationDisplay::getDisplayTextOrAcciden { return *setIterator; } - return makeDisplayTextOrAccidentalText(); -} - -const DisplayTextOrAccidentalTextSet &GroupAbbreviationDisplay::getDisplayTextOrAccidentalTextSet() const -{ - return myDisplayTextOrAccidentalTextSet; + return DisplayTextOrAccidentalTextPtr(); } bool GroupAbbreviationDisplay::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) @@ -117,9 +109,22 @@ bool GroupAbbreviationDisplay::fromXElementImpl(std::ostream &message, ::ezxml:: auto endIter = xelement.end(); for (auto it = xelement.begin(); it != endIter; ++it) { - auto item = makeDisplayTextOrAccidentalText(); - isSuccess &= item->fromXElement(message, *it); - myDisplayTextOrAccidentalTextSet.push_back(item); + if (it->getName() == "display-text") + { + auto choice = makeDisplayTextOrAccidentalText(); + choice->setChoice(DisplayTextOrAccidentalText::Choice::displayText); + isSuccess &= choice->getDisplayText()->fromXElement(message, *it); + myDisplayTextOrAccidentalTextSet.push_back(choice); + continue; + } + if (it->getName() == "accidental-text") + { + auto choice = makeDisplayTextOrAccidentalText(); + choice->setChoice(DisplayTextOrAccidentalText::Choice::accidentalText); + isSuccess &= choice->getAccidentalText()->fromXElement(message, *it); + myDisplayTextOrAccidentalTextSet.push_back(choice); + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/GroupAbbreviationDisplay.h b/src/private/mx/core/elements/GroupAbbreviationDisplay.h index bda914dc..18774b5a 100644 --- a/src/private/mx/core/elements/GroupAbbreviationDisplay.h +++ b/src/private/mx/core/elements/GroupAbbreviationDisplay.h @@ -29,11 +29,6 @@ inline GroupAbbreviationDisplayPtr makeGroupAbbreviationDisplay() class GroupAbbreviationDisplay : public ElementInterface { public: - enum class Choice - { - displayText = 1, - accidentalText = 2 - }; GroupAbbreviationDisplay(); virtual bool hasAttributes() const; @@ -45,13 +40,12 @@ class GroupAbbreviationDisplay : public ElementInterface void setAttributes(const GroupAbbreviationDisplayAttributesPtr &value); /* _________ DisplayTextOrAccidentalText minOccurs = 0, maxOccurs = unbounded _________ */ - const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalText() const; + const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalTextSet() const; void addDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextPtr &value); - void removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &setIterator); + void removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &value); void clearDisplayTextOrAccidentalTextSet(); DisplayTextOrAccidentalTextPtr getDisplayTextOrAccidentalText( const DisplayTextOrAccidentalTextSetIterConst &setIterator) const; - const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalTextSet() const; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/GroupBarlineAttributes.cpp b/src/private/mx/core/elements/GroupBarlineAttributes.cpp index 33559441..1fc5735f 100644 --- a/src/private/mx/core/elements/GroupBarlineAttributes.cpp +++ b/src/private/mx/core/elements/GroupBarlineAttributes.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -GroupBarlineAttributes::GroupBarlineAttributes() : color("#FFFFFF"), hasColor(false) +GroupBarlineAttributes::GroupBarlineAttributes() : color(), hasColor(false) { } diff --git a/src/private/mx/core/elements/GroupNameAttributes.cpp b/src/private/mx/core/elements/GroupNameAttributes.cpp index c2bf57c3..651f7157 100644 --- a/src/private/mx/core/elements/GroupNameAttributes.cpp +++ b/src/private/mx/core/elements/GroupNameAttributes.cpp @@ -12,16 +12,16 @@ namespace core { GroupNameAttributes::GroupNameAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), justify(LeftCenterRight::center), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), justify(LeftCenterRight::left), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasJustify(false) + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasJustify(false) { } bool GroupNameAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasJustify; + hasFontWeight || hasColor || hasJustify; } std::ostream &GroupNameAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &GroupNameAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, justify, "justify", hasJustify); } return os; @@ -84,6 +85,10 @@ bool GroupNameAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, justify, hasJustify, "justify", &parseLeftCenterRight)) { continue; diff --git a/src/private/mx/core/elements/GroupNameAttributes.h b/src/private/mx/core/elements/GroupNameAttributes.h index 80bf2d4d..93cdfc8b 100644 --- a/src/private/mx/core/elements/GroupNameAttributes.h +++ b/src/private/mx/core/elements/GroupNameAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct GroupNameAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight justify; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct GroupNameAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasJustify; private: diff --git a/src/private/mx/core/elements/GroupNameDisplay.cpp b/src/private/mx/core/elements/GroupNameDisplay.cpp index 333110d2..e6e7e394 100644 --- a/src/private/mx/core/elements/GroupNameDisplay.cpp +++ b/src/private/mx/core/elements/GroupNameDisplay.cpp @@ -4,6 +4,8 @@ #include "mx/core/elements/GroupNameDisplay.h" #include "mx/core/FromXElement.h" +#include "mx/core/elements/AccidentalText.h" +#include "mx/core/elements/DisplayText.h" #include "mx/core/elements/DisplayTextOrAccidentalText.h" #include @@ -39,17 +41,13 @@ bool GroupNameDisplay::hasContents() const std::ostream &GroupNameDisplay::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - if (hasContents()) + for (auto x : myDisplayTextOrAccidentalTextSet) { - for (auto x : myDisplayTextOrAccidentalTextSet) - { - os << std::endl; - x->streamContents(os, indentLevel + 1, isOneLineOnly); - } - isOneLineOnly = false; os << std::endl; + x->streamContents(os, indentLevel + 1, isOneLineOnly); } - isOneLineOnly = !hasContents(); + isOneLineOnly = false; + os << std::endl; return os; } @@ -66,7 +64,7 @@ void GroupNameDisplay::setAttributes(const GroupNameDisplayAttributesPtr &value) } } -const DisplayTextOrAccidentalTextSet &GroupNameDisplay::getDisplayTextOrAccidentalText() const +const DisplayTextOrAccidentalTextSet &GroupNameDisplay::getDisplayTextOrAccidentalTextSet() const { return myDisplayTextOrAccidentalTextSet; } @@ -79,11 +77,11 @@ void GroupNameDisplay::addDisplayTextOrAccidentalText(const DisplayTextOrAcciden } } -void GroupNameDisplay::removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &setIterator) +void GroupNameDisplay::removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &value) { - if (setIterator != myDisplayTextOrAccidentalTextSet.cend()) + if (value != myDisplayTextOrAccidentalTextSet.cend()) { - myDisplayTextOrAccidentalTextSet.erase(setIterator); + myDisplayTextOrAccidentalTextSet.erase(value); } } @@ -99,12 +97,7 @@ DisplayTextOrAccidentalTextPtr GroupNameDisplay::getDisplayTextOrAccidentalText( { return *setIterator; } - return makeDisplayTextOrAccidentalText(); -} - -const DisplayTextOrAccidentalTextSet &GroupNameDisplay::getDisplayTextOrAccidentalTextSet() const -{ - return myDisplayTextOrAccidentalTextSet; + return DisplayTextOrAccidentalTextPtr(); } bool GroupNameDisplay::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) @@ -115,9 +108,22 @@ bool GroupNameDisplay::fromXElementImpl(std::ostream &message, ::ezxml::XElement auto endIter = xelement.end(); for (auto it = xelement.begin(); it != endIter; ++it) { - auto item = makeDisplayTextOrAccidentalText(); - isSuccess &= item->fromXElement(message, *it); - myDisplayTextOrAccidentalTextSet.push_back(item); + if (it->getName() == "display-text") + { + auto choice = makeDisplayTextOrAccidentalText(); + choice->setChoice(DisplayTextOrAccidentalText::Choice::displayText); + isSuccess &= choice->getDisplayText()->fromXElement(message, *it); + myDisplayTextOrAccidentalTextSet.push_back(choice); + continue; + } + if (it->getName() == "accidental-text") + { + auto choice = makeDisplayTextOrAccidentalText(); + choice->setChoice(DisplayTextOrAccidentalText::Choice::accidentalText); + isSuccess &= choice->getAccidentalText()->fromXElement(message, *it); + myDisplayTextOrAccidentalTextSet.push_back(choice); + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/GroupNameDisplay.h b/src/private/mx/core/elements/GroupNameDisplay.h index 6b3a5fca..94e02bc6 100644 --- a/src/private/mx/core/elements/GroupNameDisplay.h +++ b/src/private/mx/core/elements/GroupNameDisplay.h @@ -40,13 +40,12 @@ class GroupNameDisplay : public ElementInterface void setAttributes(const GroupNameDisplayAttributesPtr &value); /* _________ DisplayTextOrAccidentalText minOccurs = 0, maxOccurs = unbounded _________ */ - const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalText() const; + const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalTextSet() const; void addDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextPtr &value); - void removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &setIterator); + void removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &value); void clearDisplayTextOrAccidentalTextSet(); DisplayTextOrAccidentalTextPtr getDisplayTextOrAccidentalText( const DisplayTextOrAccidentalTextSetIterConst &setIterator) const; - const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalTextSet() const; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/GroupNameDisplayAttributes.cpp b/src/private/mx/core/elements/GroupNameDisplayAttributes.cpp index 5f72a6fd..e8fadd8d 100644 --- a/src/private/mx/core/elements/GroupNameDisplayAttributes.cpp +++ b/src/private/mx/core/elements/GroupNameDisplayAttributes.cpp @@ -44,7 +44,7 @@ bool GroupNameDisplayAttributes::fromXElementImpl(std::ostream &message, ::ezxml } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/GroupSymbolAttributes.cpp b/src/private/mx/core/elements/GroupSymbolAttributes.cpp index 48ba2d38..1fa66883 100644 --- a/src/private/mx/core/elements/GroupSymbolAttributes.cpp +++ b/src/private/mx/core/elements/GroupSymbolAttributes.cpp @@ -11,14 +11,14 @@ namespace mx namespace core { GroupSymbolAttributes::GroupSymbolAttributes() - : defaultX(), defaultY(), relativeX(), relativeY(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false) + : defaultX(), defaultY(), relativeX(), relativeY(), color(), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasColor(false) { } bool GroupSymbolAttributes::hasValues() const { - return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY; + return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasColor; } std::ostream &GroupSymbolAttributes::toStream(std::ostream &os) const @@ -29,6 +29,7 @@ std::ostream &GroupSymbolAttributes::toStream(std::ostream &os) const streamAttribute(os, defaultY, "default-y", hasDefaultY); streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -59,6 +60,10 @@ bool GroupSymbolAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/GroupSymbolAttributes.h b/src/private/mx/core/elements/GroupSymbolAttributes.h index 281b7310..ccfeff72 100644 --- a/src/private/mx/core/elements/GroupSymbolAttributes.h +++ b/src/private/mx/core/elements/GroupSymbolAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Decimals.h" #include "mx/core/ForwardDeclare.h" @@ -29,10 +30,12 @@ struct GroupSymbolAttributes : public AttributesInterface TenthsValue defaultY; TenthsValue relativeX; TenthsValue relativeY; + Color color; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; bool hasRelativeY; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Grouping.h b/src/private/mx/core/elements/Grouping.h index bd1093f1..bb8c4504 100644 --- a/src/private/mx/core/elements/Grouping.h +++ b/src/private/mx/core/elements/Grouping.h @@ -37,7 +37,7 @@ class Grouping : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; GroupingAttributesPtr getAttributes() const; - void setAttributes(const GroupingAttributesPtr &value); + void setAttributes(const GroupingAttributesPtr &attributes); /* _________ Feature minOccurs = 0, maxOccurs = unbounded _________ */ const FeatureSet &getFeatureSet() const; diff --git a/src/private/mx/core/elements/GroupingAttributes.cpp b/src/private/mx/core/elements/GroupingAttributes.cpp index 4e5dfde5..26edc4d5 100644 --- a/src/private/mx/core/elements/GroupingAttributes.cpp +++ b/src/private/mx/core/elements/GroupingAttributes.cpp @@ -60,7 +60,7 @@ bool GroupingAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/HammerOnAttributes.cpp b/src/private/mx/core/elements/HammerOnAttributes.cpp index 6541834c..58210cd7 100644 --- a/src/private/mx/core/elements/HammerOnAttributes.cpp +++ b/src/private/mx/core/elements/HammerOnAttributes.cpp @@ -11,18 +11,18 @@ namespace mx namespace core { HammerOnAttributes::HammerOnAttributes() - : type(StartStop::start), number(1), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), - placement(), hasType(true), hasNumber(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasPlacement(false) + : type(StartStop::start), number(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + placement(AboveBelow::below), hasType(true), hasNumber(false), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), + hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool HammerOnAttributes::hasValues() const { return hasType || hasNumber || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || - hasFontStyle || hasFontSize || hasFontWeight || hasPlacement; + hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasPlacement; } std::ostream &HammerOnAttributes::toStream(std::ostream &os) const @@ -39,6 +39,7 @@ std::ostream &HammerOnAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -96,6 +97,10 @@ bool HammerOnAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; @@ -105,7 +110,7 @@ bool HammerOnAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/HammerOnAttributes.h b/src/private/mx/core/elements/HammerOnAttributes.h index ce3a500b..c33309b7 100644 --- a/src/private/mx/core/elements/HammerOnAttributes.h +++ b/src/private/mx/core/elements/HammerOnAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -39,6 +40,7 @@ struct HammerOnAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; const bool hasType; bool hasNumber; @@ -50,6 +52,7 @@ struct HammerOnAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/HandbellAttributes.cpp b/src/private/mx/core/elements/HandbellAttributes.cpp index 525d3b9d..5760048c 100644 --- a/src/private/mx/core/elements/HandbellAttributes.cpp +++ b/src/private/mx/core/elements/HandbellAttributes.cpp @@ -12,16 +12,16 @@ namespace core { HandbellAttributes::HandbellAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool HandbellAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &HandbellAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &HandbellAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,6 +85,10 @@ bool HandbellAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/HandbellAttributes.h b/src/private/mx/core/elements/HandbellAttributes.h index bef22555..acc105fb 100644 --- a/src/private/mx/core/elements/HandbellAttributes.h +++ b/src/private/mx/core/elements/HandbellAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct HandbellAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct HandbellAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/Harmonic.cpp b/src/private/mx/core/elements/Harmonic.cpp index d66bb3cd..e578741a 100644 --- a/src/private/mx/core/elements/Harmonic.cpp +++ b/src/private/mx/core/elements/Harmonic.cpp @@ -68,6 +68,19 @@ std::ostream &Harmonic::streamContents(std::ostream &os, const int indentLevel, return os; } +HarmonicAttributesPtr Harmonic::getAttributes() const +{ + return myAttributes; +} + +void Harmonic::setAttributes(const HarmonicAttributesPtr &value) +{ + if (value) + { + myAttributes = value; + } +} + HarmonicTypeChoicePtr Harmonic::getHarmonicTypeChoice() const { return myHarmonicTypeChoice; @@ -91,19 +104,6 @@ void Harmonic::setHasHarmonicTypeChoice(const bool value) myHasHarmonicTypeChoice = value; } -HarmonicAttributesPtr Harmonic::getAttributes() const -{ - return myAttributes; -} - -void Harmonic::setAttributes(const HarmonicAttributesPtr &value) -{ - if (value) - { - myAttributes = value; - } -} - HarmonicInfoChoicePtr Harmonic::getHarmonicInfoChoice() const { return myHarmonicInfoChoice; @@ -135,48 +135,44 @@ bool Harmonic::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xeleme auto endIter = xelement.end(); for (auto it = xelement.begin(); it != endIter; ++it) { - if (checkSetChoiceMember(message, *it, isSuccess, myHarmonicTypeChoice, "natural", - &HarmonicTypeChoice::getNatural, - static_cast(HarmonicTypeChoice::Choice::natural))) + if (it->getName() == "natural") { + myHarmonicTypeChoice->setChoice(HarmonicTypeChoice::Choice::natural); + isSuccess &= myHarmonicTypeChoice->getNatural()->fromXElement(message, *it); myHasHarmonicTypeChoice = true; continue; } - - if (checkSetChoiceMember(message, *it, isSuccess, myHarmonicTypeChoice, "artificial", - &HarmonicTypeChoice::getArtificial, - static_cast(HarmonicTypeChoice::Choice::artificial))) + if (it->getName() == "artificial") { + myHarmonicTypeChoice->setChoice(HarmonicTypeChoice::Choice::artificial); + isSuccess &= myHarmonicTypeChoice->getArtificial()->fromXElement(message, *it); myHasHarmonicTypeChoice = true; continue; } - - if (checkSetChoiceMember(message, *it, isSuccess, myHarmonicInfoChoice, "base-pitch", - &HarmonicInfoChoice::getBasePitch, - static_cast(HarmonicInfoChoice::Choice::basePitch))) + if (it->getName() == "base-pitch") { + myHarmonicInfoChoice->setChoice(HarmonicInfoChoice::Choice::basePitch); + isSuccess &= myHarmonicInfoChoice->getBasePitch()->fromXElement(message, *it); myHasHarmonicInfoChoice = true; continue; } - - if (checkSetChoiceMember(message, *it, isSuccess, myHarmonicInfoChoice, "touching-pitch", - &HarmonicInfoChoice::getTouchingPitch, - static_cast(HarmonicInfoChoice::Choice::touchingPitch))) + if (it->getName() == "touching-pitch") { + myHarmonicInfoChoice->setChoice(HarmonicInfoChoice::Choice::touchingPitch); + isSuccess &= myHarmonicInfoChoice->getTouchingPitch()->fromXElement(message, *it); myHasHarmonicInfoChoice = true; continue; } - - if (checkSetChoiceMember(message, *it, isSuccess, myHarmonicInfoChoice, "sounding-pitch", - &HarmonicInfoChoice::getSoundingPitch, - static_cast(HarmonicInfoChoice::Choice::soundingPitch))) + if (it->getName() == "sounding-pitch") { + myHarmonicInfoChoice->setChoice(HarmonicInfoChoice::Choice::soundingPitch); + isSuccess &= myHarmonicInfoChoice->getSoundingPitch()->fromXElement(message, *it); myHasHarmonicInfoChoice = true; continue; } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/Harmonic.h b/src/private/mx/core/elements/Harmonic.h index 6d8b3a8a..374e74a1 100644 --- a/src/private/mx/core/elements/Harmonic.h +++ b/src/private/mx/core/elements/Harmonic.h @@ -12,14 +12,19 @@ #include #include +namespace ezxml +{ +class XElementIterator; +} + namespace mx { namespace core { MX_FORWARD_DECLARE_ATTRIBUTES(HarmonicAttributes) -MX_FORWARD_DECLARE_ELEMENT(HarmonicInfoChoice) MX_FORWARD_DECLARE_ELEMENT(HarmonicTypeChoice) +MX_FORWARD_DECLARE_ELEMENT(HarmonicInfoChoice) MX_FORWARD_DECLARE_ELEMENT(Harmonic) inline HarmonicPtr makeHarmonic() diff --git a/src/private/mx/core/elements/HarmonicAttributes.cpp b/src/private/mx/core/elements/HarmonicAttributes.cpp index 65e630ec..3caead19 100644 --- a/src/private/mx/core/elements/HarmonicAttributes.cpp +++ b/src/private/mx/core/elements/HarmonicAttributes.cpp @@ -101,7 +101,7 @@ bool HarmonicAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/HarmonicInfoChoice.cpp b/src/private/mx/core/elements/HarmonicInfoChoice.cpp index bf0eb863..acc4cafa 100644 --- a/src/private/mx/core/elements/HarmonicInfoChoice.cpp +++ b/src/private/mx/core/elements/HarmonicInfoChoice.cpp @@ -41,19 +41,17 @@ bool HarmonicInfoChoice::hasContents() const std::ostream &HarmonicInfoChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - switch (myChoice) + if (myChoice == Choice::basePitch) { - case Choice::basePitch: myBasePitch->toStream(os, indentLevel); - break; - case Choice::touchingPitch: + } + if (myChoice == Choice::touchingPitch) + { myTouchingPitch->toStream(os, indentLevel); - break; - case Choice::soundingPitch: + } + if (myChoice == Choice::soundingPitch) + { mySoundingPitch->toStream(os, indentLevel); - break; - default: - break; } isOneLineOnly = false; return os; @@ -64,7 +62,7 @@ HarmonicInfoChoice::Choice HarmonicInfoChoice::getChoice() const return myChoice; } -void HarmonicInfoChoice::setChoice(const HarmonicInfoChoice::Choice value) +void HarmonicInfoChoice::setChoice(const Choice value) { myChoice = value; } diff --git a/src/private/mx/core/elements/HarmonicInfoChoice.h b/src/private/mx/core/elements/HarmonicInfoChoice.h index b18e4079..9c0d3245 100644 --- a/src/private/mx/core/elements/HarmonicInfoChoice.h +++ b/src/private/mx/core/elements/HarmonicInfoChoice.h @@ -17,8 +17,8 @@ namespace core { MX_FORWARD_DECLARE_ELEMENT(BasePitch) -MX_FORWARD_DECLARE_ELEMENT(SoundingPitch) MX_FORWARD_DECLARE_ELEMENT(TouchingPitch) +MX_FORWARD_DECLARE_ELEMENT(SoundingPitch) MX_FORWARD_DECLARE_ELEMENT(HarmonicInfoChoice) inline HarmonicInfoChoicePtr makeHarmonicInfoChoice() @@ -31,9 +31,9 @@ class HarmonicInfoChoice : public ElementInterface public: enum class Choice { - basePitch = 1, - touchingPitch = 2, - soundingPitch = 3 + basePitch = 0, + touchingPitch = 1, + soundingPitch = 2 }; HarmonicInfoChoice(); @@ -42,20 +42,12 @@ class HarmonicInfoChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - - /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */ - HarmonicInfoChoice::Choice getChoice() const; - void setChoice(const HarmonicInfoChoice::Choice value); - - /* _________ BasePitch minOccurs = 1, maxOccurs = 1 _________ */ + Choice getChoice() const; + void setChoice(const Choice value); BasePitchPtr getBasePitch() const; void setBasePitch(const BasePitchPtr &value); - - /* _________ TouchingPitch minOccurs = 1, maxOccurs = 1 _________ */ TouchingPitchPtr getTouchingPitch() const; void setTouchingPitch(const TouchingPitchPtr &value); - - /* _________ SoundingPitch minOccurs = 1, maxOccurs = 1 _________ */ SoundingPitchPtr getSoundingPitch() const; void setSoundingPitch(const SoundingPitchPtr &value); diff --git a/src/private/mx/core/elements/HarmonicTypeChoice.cpp b/src/private/mx/core/elements/HarmonicTypeChoice.cpp index 2c38a4ae..a81d3f02 100644 --- a/src/private/mx/core/elements/HarmonicTypeChoice.cpp +++ b/src/private/mx/core/elements/HarmonicTypeChoice.cpp @@ -39,16 +39,13 @@ bool HarmonicTypeChoice::hasContents() const std::ostream &HarmonicTypeChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - switch (myChoice) + if (myChoice == Choice::natural) { - case Choice::natural: myNatural->toStream(os, indentLevel); - break; - case Choice::artificial: + } + if (myChoice == Choice::artificial) + { myArtificial->toStream(os, indentLevel); - break; - default: - break; } isOneLineOnly = false; return os; @@ -59,7 +56,7 @@ HarmonicTypeChoice::Choice HarmonicTypeChoice::getChoice() const return myChoice; } -void HarmonicTypeChoice::setChoice(const HarmonicTypeChoice::Choice value) +void HarmonicTypeChoice::setChoice(const Choice value) { myChoice = value; } diff --git a/src/private/mx/core/elements/HarmonicTypeChoice.h b/src/private/mx/core/elements/HarmonicTypeChoice.h index e38db384..6456dc8d 100644 --- a/src/private/mx/core/elements/HarmonicTypeChoice.h +++ b/src/private/mx/core/elements/HarmonicTypeChoice.h @@ -16,8 +16,8 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(Artificial) MX_FORWARD_DECLARE_ELEMENT(Natural) +MX_FORWARD_DECLARE_ELEMENT(Artificial) MX_FORWARD_DECLARE_ELEMENT(HarmonicTypeChoice) inline HarmonicTypeChoicePtr makeHarmonicTypeChoice() @@ -30,8 +30,8 @@ class HarmonicTypeChoice : public ElementInterface public: enum class Choice { - natural = 1, - artificial = 2 + natural = 0, + artificial = 1 }; HarmonicTypeChoice(); @@ -40,16 +40,10 @@ class HarmonicTypeChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - - /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */ - HarmonicTypeChoice::Choice getChoice() const; - void setChoice(const HarmonicTypeChoice::Choice value); - - /* _________ Natural minOccurs = 1, maxOccurs = 1 _________ */ + Choice getChoice() const; + void setChoice(const Choice value); NaturalPtr getNatural() const; void setNatural(const NaturalPtr &value); - - /* _________ Artificial minOccurs = 1, maxOccurs = 1 _________ */ ArtificialPtr getArtificial() const; void setArtificial(const ArtificialPtr &value); diff --git a/src/private/mx/core/elements/HarpPedals.h b/src/private/mx/core/elements/HarpPedals.h index c4ed57d1..5de4278d 100644 --- a/src/private/mx/core/elements/HarpPedals.h +++ b/src/private/mx/core/elements/HarpPedals.h @@ -37,7 +37,7 @@ class HarpPedals : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; HarpPedalsAttributesPtr getAttributes() const; - void setAttributes(const HarpPedalsAttributesPtr &value); + void setAttributes(const HarpPedalsAttributesPtr &attributes); /* _________ PedalTuning minOccurs = 1, maxOccurs = unbounded _________ */ const PedalTuningSet &getPedalTuningSet() const; diff --git a/src/private/mx/core/elements/HarpPedalsAttributes.cpp b/src/private/mx/core/elements/HarpPedalsAttributes.cpp index 34316d69..6c3e1a10 100644 --- a/src/private/mx/core/elements/HarpPedalsAttributes.cpp +++ b/src/private/mx/core/elements/HarpPedalsAttributes.cpp @@ -12,9 +12,10 @@ namespace core { HarpPedalsAttributes::HarpPedalsAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::center), valign(), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::left), + valign(Valign::bottom), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), + hasHalign(false), hasValign(false) { } diff --git a/src/private/mx/core/elements/Heel.cpp b/src/private/mx/core/elements/Heel.cpp index 7149d470..e8582366 100644 --- a/src/private/mx/core/elements/Heel.cpp +++ b/src/private/mx/core/elements/Heel.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -Heel::Heel() : myAttributes(std::make_shared()) +Heel::Heel() : ElementInterface(), myAttributes(std::make_shared()) { } @@ -21,7 +21,11 @@ bool Heel::hasAttributes() const std::ostream &Heel::streamAttributes(std::ostream &os) const { - return myAttributes->toStream(os); + if (myAttributes) + { + myAttributes->toStream(os); + } + return os; } std::ostream &Heel::streamName(std::ostream &os) const diff --git a/src/private/mx/core/elements/Heel.h b/src/private/mx/core/elements/Heel.h index 684f3170..311356a1 100644 --- a/src/private/mx/core/elements/Heel.h +++ b/src/private/mx/core/elements/Heel.h @@ -36,7 +36,7 @@ class Heel : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; HeelAttributesPtr getAttributes() const; - void setAttributes(const HeelAttributesPtr &value); + void setAttributes(const HeelAttributesPtr &attributes); private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Hole.cpp b/src/private/mx/core/elements/Hole.cpp index a3772365..5468e63d 100644 --- a/src/private/mx/core/elements/Hole.cpp +++ b/src/private/mx/core/elements/Hole.cpp @@ -42,6 +42,7 @@ bool Hole::hasContents() const std::ostream &Hole::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; if (myHasHoleType) { os << std::endl; @@ -55,7 +56,6 @@ std::ostream &Hole::streamContents(std::ostream &os, const int indentLevel, bool myHoleShape->toStream(os, indentLevel + 1); } os << std::endl; - isOneLineOnly = false; return os; } @@ -154,10 +154,6 @@ bool Hole::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) } } - if (!isHoleClosedFound) - { - message << "Hole: '" << myHoleClosed->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Hole.h b/src/private/mx/core/elements/Hole.h index a8ee024b..2ada0b82 100644 --- a/src/private/mx/core/elements/Hole.h +++ b/src/private/mx/core/elements/Hole.h @@ -39,7 +39,7 @@ class Hole : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; HoleAttributesPtr getAttributes() const; - void setAttributes(const HoleAttributesPtr &value); + void setAttributes(const HoleAttributesPtr &attributes); /* _________ HoleType minOccurs = 0, maxOccurs = 1 _________ */ HoleTypePtr getHoleType() const; diff --git a/src/private/mx/core/elements/HoleClosed.cpp b/src/private/mx/core/elements/HoleClosed.cpp index 6be91c91..9e0e0c80 100644 --- a/src/private/mx/core/elements/HoleClosed.cpp +++ b/src/private/mx/core/elements/HoleClosed.cpp @@ -3,7 +3,7 @@ // Distributed under the MIT License #include "mx/core/elements/HoleClosed.h" -#include "ezxml/XElement.h" +#include "mx/core/FromXElement.h" #include namespace mx @@ -77,10 +77,11 @@ void HoleClosed::setValue(const HoleClosedValue &value) bool HoleClosed::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { - MX_UNUSED(message); - MX_UNUSED(xelement); + bool isSuccess = true; + isSuccess &= myAttributes->fromXElement(message, xelement); myValue = parseHoleClosedValue(xelement.getValue()); - return true; + MX_RETURN_IS_SUCCESS; } + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/HoleClosedAttributes.cpp b/src/private/mx/core/elements/HoleClosedAttributes.cpp index 843a8756..aa42d96f 100644 --- a/src/private/mx/core/elements/HoleClosedAttributes.cpp +++ b/src/private/mx/core/elements/HoleClosedAttributes.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -HoleClosedAttributes::HoleClosedAttributes() : location(HoleClosedLocation::top), hasLocation(false) +HoleClosedAttributes::HoleClosedAttributes() : location(), hasLocation(false) { } diff --git a/src/private/mx/core/elements/Identification.cpp b/src/private/mx/core/elements/Identification.cpp index 454eaa0a..7d404c97 100644 --- a/src/private/mx/core/elements/Identification.cpp +++ b/src/private/mx/core/elements/Identification.cpp @@ -286,7 +286,7 @@ bool Identification::fromXElementImpl(std::ostream &message, ::ezxml::XElement & } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/ImageAttributes.cpp b/src/private/mx/core/elements/ImageAttributes.cpp index b5bd5535..0488134c 100644 --- a/src/private/mx/core/elements/ImageAttributes.cpp +++ b/src/private/mx/core/elements/ImageAttributes.cpp @@ -11,15 +11,15 @@ namespace mx namespace core { ImageAttributes::ImageAttributes() - : source(), type(), defaultX(), defaultY(), relativeX(), relativeY(), halign(LeftCenterRight::center), - hasSource(true), hasType(true), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasHalign(false) + : source(), type(), defaultX(), defaultY(), relativeX(), relativeY(), halign(LeftCenterRight::left), + valign(ValignImage::bottom), hasSource(true), hasType(true), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasHalign(false), hasValign(false) { } bool ImageAttributes::hasValues() const { - return hasSource || hasType || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasHalign; + return hasSource || hasType || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasHalign || hasValign; } std::ostream &ImageAttributes::toStream(std::ostream &os) const @@ -33,6 +33,7 @@ std::ostream &ImageAttributes::toStream(std::ostream &os) const streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); } return os; } @@ -77,20 +78,25 @@ bool ImageAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValignImage)) + { + continue; + } } if (!isSourceFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'source' is a required attribute but was not found" << std::endl; } + if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/ImageAttributes.h b/src/private/mx/core/elements/ImageAttributes.h index 24057c86..4b88a1ce 100644 --- a/src/private/mx/core/elements/ImageAttributes.h +++ b/src/private/mx/core/elements/ImageAttributes.h @@ -35,6 +35,7 @@ struct ImageAttributes : public AttributesInterface TenthsValue relativeX; TenthsValue relativeY; LeftCenterRight halign; + ValignImage valign; const bool hasSource; const bool hasType; bool hasDefaultX; @@ -42,6 +43,7 @@ struct ImageAttributes : public AttributesInterface bool hasRelativeX; bool hasRelativeY; bool hasHalign; + bool hasValign; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/InstrumentAttributes.cpp b/src/private/mx/core/elements/InstrumentAttributes.cpp index 4cd77c0c..97522022 100644 --- a/src/private/mx/core/elements/InstrumentAttributes.cpp +++ b/src/private/mx/core/elements/InstrumentAttributes.cpp @@ -48,7 +48,7 @@ bool InstrumentAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle if (!isIdFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'id' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/InstrumentSound.cpp b/src/private/mx/core/elements/InstrumentSound.cpp index 63c032df..a7e81253 100644 --- a/src/private/mx/core/elements/InstrumentSound.cpp +++ b/src/private/mx/core/elements/InstrumentSound.cpp @@ -60,8 +60,7 @@ void InstrumentSound::setValue(const PlaybackSound &value) bool InstrumentSound::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); - MX_UNUSED(xelement); - myValue = (PlaybackSoundFromString(xelement.getValue())); + myValue = PlaybackSoundFromString(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/Interchangeable.cpp b/src/private/mx/core/elements/Interchangeable.cpp index a9614c4c..cb807260 100644 --- a/src/private/mx/core/elements/Interchangeable.cpp +++ b/src/private/mx/core/elements/Interchangeable.cpp @@ -142,17 +142,6 @@ bool Interchangeable::fromXElementImpl(std::ostream &message, ::ezxml::XElement } } - if (!isBeatsFound) - { - message << "Interchangeable: '" << myBeats->getElementName() << "' is required but was not found" << std::endl; - } - - if (!isBeatTypeFound) - { - message << "Interchangeable: '" << myBeatType->getElementName() << "' is required but was not found" - << std::endl; - } - MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Interchangeable.h b/src/private/mx/core/elements/Interchangeable.h index eb3bd447..f143e54e 100644 --- a/src/private/mx/core/elements/Interchangeable.h +++ b/src/private/mx/core/elements/Interchangeable.h @@ -39,7 +39,7 @@ class Interchangeable : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; InterchangeableAttributesPtr getAttributes() const; - void setAttributes(const InterchangeableAttributesPtr &value); + void setAttributes(const InterchangeableAttributesPtr &attributes); /* _________ TimeRelation minOccurs = 0, maxOccurs = 1 _________ */ TimeRelationPtr getTimeRelation() const; diff --git a/src/private/mx/core/elements/Inversion.cpp b/src/private/mx/core/elements/Inversion.cpp index 3215e487..bb9d6bae 100644 --- a/src/private/mx/core/elements/Inversion.cpp +++ b/src/private/mx/core/elements/Inversion.cpp @@ -80,7 +80,7 @@ bool Inversion::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.parse(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/InversionAttributes.cpp b/src/private/mx/core/elements/InversionAttributes.cpp index 25f27fee..9e99a8f7 100644 --- a/src/private/mx/core/elements/InversionAttributes.cpp +++ b/src/private/mx/core/elements/InversionAttributes.cpp @@ -12,16 +12,16 @@ namespace core { InversionAttributes::InversionAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasDefaultX(false), hasDefaultY(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + hasFontWeight(false), hasColor(false) { } bool InversionAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight; + hasFontWeight || hasColor; } std::ostream &InversionAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &InversionAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -83,9 +84,13 @@ bool InversionAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/InversionAttributes.h b/src/private/mx/core/elements/InversionAttributes.h index 181b7d3c..a0ecd389 100644 --- a/src/private/mx/core/elements/InversionAttributes.h +++ b/src/private/mx/core/elements/InversionAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct InversionAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; @@ -44,6 +46,7 @@ struct InversionAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/InvertedMordent.cpp b/src/private/mx/core/elements/InvertedMordent.cpp index 95d296e8..98b04ea0 100644 --- a/src/private/mx/core/elements/InvertedMordent.cpp +++ b/src/private/mx/core/elements/InvertedMordent.cpp @@ -3,13 +3,14 @@ // Distributed under the MIT License #include "mx/core/elements/InvertedMordent.h" +#include "mx/core/FromXElement.h" #include namespace mx { namespace core { -InvertedMordent::InvertedMordent() : myAttributes(std::make_shared()) +InvertedMordent::InvertedMordent() : ElementInterface(), myAttributes(std::make_shared()) { } @@ -20,7 +21,11 @@ bool InvertedMordent::hasAttributes() const std::ostream &InvertedMordent::streamAttributes(std::ostream &os) const { - return myAttributes->toStream(os); + if (myAttributes) + { + myAttributes->toStream(os); + } + return os; } std::ostream &InvertedMordent::streamName(std::ostream &os) const diff --git a/src/private/mx/core/elements/InvertedMordent.h b/src/private/mx/core/elements/InvertedMordent.h index c97b5958..efa86198 100644 --- a/src/private/mx/core/elements/InvertedMordent.h +++ b/src/private/mx/core/elements/InvertedMordent.h @@ -36,7 +36,7 @@ class InvertedMordent : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; InvertedMordentAttributesPtr getAttributes() const; - void setAttributes(const InvertedMordentAttributesPtr &value); + void setAttributes(const InvertedMordentAttributesPtr &attributes); private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/InvertedMordentAttributes.cpp b/src/private/mx/core/elements/InvertedMordentAttributes.cpp index c704174a..edf2e378 100644 --- a/src/private/mx/core/elements/InvertedMordentAttributes.cpp +++ b/src/private/mx/core/elements/InvertedMordentAttributes.cpp @@ -12,13 +12,13 @@ namespace core { InvertedMordentAttributes::InvertedMordentAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), - startNote(StartNote::main), trillStep(TrillStep::half), twoNoteTurn(TwoNoteTurn::none), accelerate(YesNo::no), - beats(), secondBeat(), lastBeat(), long_(YesNo::no), approach(AboveBelow::below), departure(AboveBelow::below), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false), - hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), - hasSecondBeat(false), hasLastBeat(false), hasLong(false), hasApproach(false), hasDeparture(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), startNote(), + trillStep(), twoNoteTurn(), accelerate(YesNo::no), beats(), secondBeat(), lastBeat(), long_(YesNo::no), + approach(AboveBelow::below), departure(AboveBelow::below), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), + hasFontWeight(false), hasColor(false), hasPlacement(false), hasStartNote(false), hasTrillStep(false), + hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), hasSecondBeat(false), hasLastBeat(false), + hasLong(false), hasApproach(false), hasDeparture(false) { } diff --git a/src/private/mx/core/elements/InvertedTurnAttributes.cpp b/src/private/mx/core/elements/InvertedTurnAttributes.cpp index 24d50a22..f74e1e5b 100644 --- a/src/private/mx/core/elements/InvertedTurnAttributes.cpp +++ b/src/private/mx/core/elements/InvertedTurnAttributes.cpp @@ -12,20 +12,20 @@ namespace core { InvertedTurnAttributes::InvertedTurnAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), placement(AboveBelow::below), - startNote(StartNote::main), trillStep(), twoNoteTurn(TwoNoteTurn::none), accelerate(YesNo::no), beats(), - secondBeat(), lastBeat(), slash(YesNo::no), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasPlacement(false), hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), - hasBeats(false), hasSecondBeat(false), hasLastBeat(false), hasSlash(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), startNote(), + trillStep(), twoNoteTurn(), accelerate(YesNo::no), beats(), secondBeat(), lastBeat(), slash(YesNo::no), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false), + hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), + hasSecondBeat(false), hasLastBeat(false), hasSlash(false) { } bool InvertedTurnAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || hasAccelerate || - hasBeats || hasSecondBeat || hasLastBeat || hasSlash; + hasFontWeight || hasColor || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || + hasAccelerate || hasBeats || hasSecondBeat || hasLastBeat || hasSlash; } std::ostream &InvertedTurnAttributes::toStream(std::ostream &os) const @@ -40,6 +40,7 @@ std::ostream &InvertedTurnAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); streamAttribute(os, startNote, "start-note", hasStartNote); streamAttribute(os, trillStep, "trill-step", hasTrillStep); @@ -96,6 +97,10 @@ bool InvertedTurnAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XE { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; @@ -135,7 +140,7 @@ bool InvertedTurnAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XE } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/InvertedTurnAttributes.h b/src/private/mx/core/elements/InvertedTurnAttributes.h index 95e5af3d..61bc59fd 100644 --- a/src/private/mx/core/elements/InvertedTurnAttributes.h +++ b/src/private/mx/core/elements/InvertedTurnAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct InvertedTurnAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; StartNote startNote; TrillStep trillStep; @@ -53,6 +55,7 @@ struct InvertedTurnAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; bool hasStartNote; bool hasTrillStep; diff --git a/src/private/mx/core/elements/KeyAttributes.cpp b/src/private/mx/core/elements/KeyAttributes.cpp index e5a0cd38..15133440 100644 --- a/src/private/mx/core/elements/KeyAttributes.cpp +++ b/src/private/mx/core/elements/KeyAttributes.cpp @@ -100,7 +100,7 @@ bool KeyAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/KeyChoice.cpp b/src/private/mx/core/elements/KeyChoice.cpp index 75076ba7..1acb137d 100644 --- a/src/private/mx/core/elements/KeyChoice.cpp +++ b/src/private/mx/core/elements/KeyChoice.cpp @@ -13,7 +13,7 @@ namespace mx namespace core { KeyChoice::KeyChoice() - : myChoice(Choice::traditionalKey), myTraditionalKey(std::make_shared()), myNonTraditionalKeySet() + : myChoice(Choice::traditionalKey), myTraditionalKey(makeTraditionalKey()), myNonTraditionalKeySet() { } @@ -46,7 +46,7 @@ std::ostream &KeyChoice::streamContents(std::ostream &os, const int indentLevel, myTraditionalKey->streamContents(os, indentLevel, isOneLineOnly); } } - else if (myChoice == Choice::nonTraditionalKey) + if (myChoice == Choice::nonTraditionalKey) { for (auto it = myNonTraditionalKeySet.cbegin(); it != myNonTraditionalKeySet.cend(); ++it) { @@ -61,6 +61,7 @@ std::ostream &KeyChoice::streamContents(std::ostream &os, const int indentLevel, isOneLineOnly = false; } } + isOneLineOnly = false; return os; } diff --git a/src/private/mx/core/elements/KeyChoice.h b/src/private/mx/core/elements/KeyChoice.h index 1107897d..61c2219f 100644 --- a/src/private/mx/core/elements/KeyChoice.h +++ b/src/private/mx/core/elements/KeyChoice.h @@ -16,8 +16,8 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(NonTraditionalKey) MX_FORWARD_DECLARE_ELEMENT(TraditionalKey) +MX_FORWARD_DECLARE_ELEMENT(NonTraditionalKey) MX_FORWARD_DECLARE_ELEMENT(KeyChoice) inline KeyChoicePtr makeKeyChoice() diff --git a/src/private/mx/core/elements/KeyOctave.cpp b/src/private/mx/core/elements/KeyOctave.cpp index 49802985..46f5c1ea 100644 --- a/src/private/mx/core/elements/KeyOctave.cpp +++ b/src/private/mx/core/elements/KeyOctave.cpp @@ -79,7 +79,7 @@ bool KeyOctave::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.parse(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/KeyOctaveAttributes.cpp b/src/private/mx/core/elements/KeyOctaveAttributes.cpp index a68a92a7..df469b70 100644 --- a/src/private/mx/core/elements/KeyOctaveAttributes.cpp +++ b/src/private/mx/core/elements/KeyOctaveAttributes.cpp @@ -10,8 +10,7 @@ namespace mx { namespace core { -KeyOctaveAttributes::KeyOctaveAttributes() - : number(PositiveInteger{1}), cancel(YesNo::no), hasNumber(true), hasCancel(false) +KeyOctaveAttributes::KeyOctaveAttributes() : number(), cancel(YesNo::no), hasNumber(true), hasCancel(false) { } diff --git a/src/private/mx/core/elements/KeyStep.cpp b/src/private/mx/core/elements/KeyStep.cpp index dd9394ce..638da07b 100644 --- a/src/private/mx/core/elements/KeyStep.cpp +++ b/src/private/mx/core/elements/KeyStep.cpp @@ -60,7 +60,6 @@ void KeyStep::setValue(const StepEnum &value) bool KeyStep::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); - MX_UNUSED(xelement); myValue = parseStepEnum(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/Kind.cpp b/src/private/mx/core/elements/Kind.cpp index 1ea8b8d8..92fb5413 100644 --- a/src/private/mx/core/elements/Kind.cpp +++ b/src/private/mx/core/elements/Kind.cpp @@ -79,7 +79,7 @@ bool Kind::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue = parseKindValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/KindAttributes.cpp b/src/private/mx/core/elements/KindAttributes.cpp index c395ad88..87b912a1 100644 --- a/src/private/mx/core/elements/KindAttributes.cpp +++ b/src/private/mx/core/elements/KindAttributes.cpp @@ -11,12 +11,13 @@ namespace mx namespace core { KindAttributes::KindAttributes() - : useSymbols(), text(), stackDegrees(), parenthesesDegrees(), bracketDegrees(), defaultX(), defaultY(), relativeX(), - relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), halign(), hasUseSymbols(false), hasText(false), hasStackDegrees(false), + : useSymbols(YesNo::no), text(), stackDegrees(YesNo::no), parenthesesDegrees(YesNo::no), bracketDegrees(YesNo::no), + defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::left), + valign(Valign::bottom), hasUseSymbols(false), hasText(false), hasStackDegrees(false), hasParenthesesDegrees(false), hasBracketDegrees(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false), hasHalign(false) + hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false) { } @@ -24,7 +25,7 @@ bool KindAttributes::hasValues() const { return hasUseSymbols || hasText || hasStackDegrees || hasParenthesesDegrees || hasBracketDegrees || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasHalign; + hasFontWeight || hasColor || hasHalign || hasValign; } std::ostream &KindAttributes::toStream(std::ostream &os) const @@ -44,7 +45,9 @@ std::ostream &KindAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); } return os; } @@ -115,10 +118,18 @@ bool KindAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/KindAttributes.h b/src/private/mx/core/elements/KindAttributes.h index 7c3c4792..f4577864 100644 --- a/src/private/mx/core/elements/KindAttributes.h +++ b/src/private/mx/core/elements/KindAttributes.h @@ -5,11 +5,13 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsToken.h" #include #include @@ -41,7 +43,9 @@ struct KindAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; bool hasUseSymbols; bool hasText; bool hasStackDegrees; @@ -55,7 +59,9 @@ struct KindAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/LayoutGroup.cpp b/src/private/mx/core/elements/LayoutGroup.cpp index bc75cf19..b75e3d95 100644 --- a/src/private/mx/core/elements/LayoutGroup.cpp +++ b/src/private/mx/core/elements/LayoutGroup.cpp @@ -31,7 +31,6 @@ std::ostream &LayoutGroup::streamAttributes(std::ostream &os) const std::ostream &LayoutGroup::streamName(std::ostream &os) const { - os << "work"; return os; } @@ -42,48 +41,29 @@ bool LayoutGroup::hasContents() const std::ostream &LayoutGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - if (hasContents()) + bool isFirst = true; + if (myHasPageLayout) { - bool isFirst = true; - if (myHasPageLayout) - { - myPageLayout->toStream(os, indentLevel); - isFirst = false; - } - if (myHasSystemLayout) - { - if (!isFirst) - { - os << std::endl; - isFirst = false; - } - mySystemLayout->toStream(os, indentLevel); - } - if (myStaffLayoutSet.size() > 0) - { - for (auto it = myStaffLayoutSet.cbegin(); it != myStaffLayoutSet.cend(); ++it) - { - if (it == myStaffLayoutSet.cbegin()) - { - if (!isFirst) - { - os << std::endl; - isFirst = false; - } - } - else - { - os << std::endl; - } - (*it)->toStream(os, indentLevel); - } - } - isOneLineOnly = false; + if (!isFirst) + os << std::endl; + myPageLayout->toStream(os, indentLevel); + isFirst = false; } - else + if (myHasSystemLayout) { - isOneLineOnly = true; + if (!isFirst) + os << std::endl; + mySystemLayout->toStream(os, indentLevel); + isFirst = false; } + for (auto x : myStaffLayoutSet) + { + if (!isFirst) + os << std::endl; + x->toStream(os, indentLevel); + isFirst = false; + } + isOneLineOnly = !hasContents(); return os; } @@ -138,19 +118,19 @@ const StaffLayoutSet &LayoutGroup::getStaffLayoutSet() const return myStaffLayoutSet; } -void LayoutGroup::addStaffLayout(const StaffLayoutPtr &value) +void LayoutGroup::removeStaffLayout(const StaffLayoutSetIterConst &value) { - if (value) + if (value != myStaffLayoutSet.cend()) { - myStaffLayoutSet.push_back(value); + myStaffLayoutSet.erase(value); } } -void LayoutGroup::removeStaffLayout(const StaffLayoutSetIterConst &value) +void LayoutGroup::addStaffLayout(const StaffLayoutPtr &value) { - if (value != myStaffLayoutSet.cend()) + if (value) { - myStaffLayoutSet.erase(value); + myStaffLayoutSet.push_back(value); } } diff --git a/src/private/mx/core/elements/LeftDivider.h b/src/private/mx/core/elements/LeftDivider.h index 6173f3cd..82e0698d 100644 --- a/src/private/mx/core/elements/LeftDivider.h +++ b/src/private/mx/core/elements/LeftDivider.h @@ -17,6 +17,7 @@ namespace mx namespace core { +MX_FORWARD_DECLARE_ATTRIBUTES(EmptyPrintObjectStyleAlignAttributes) MX_FORWARD_DECLARE_ELEMENT(LeftDivider) inline LeftDividerPtr makeLeftDivider() diff --git a/src/private/mx/core/elements/LevelAttributes.cpp b/src/private/mx/core/elements/LevelAttributes.cpp index 5e3ad62f..4b0e7335 100644 --- a/src/private/mx/core/elements/LevelAttributes.cpp +++ b/src/private/mx/core/elements/LevelAttributes.cpp @@ -11,8 +11,8 @@ namespace mx namespace core { LevelAttributes::LevelAttributes() - : reference(), parentheses(), bracket(), size(), hasReference(false), hasParentheses(false), hasBracket(false), - hasSize(false) + : reference(YesNo::no), parentheses(YesNo::no), bracket(YesNo::no), size(SymbolSize::full), hasReference(false), + hasParentheses(false), hasBracket(false), hasSize(false) { } diff --git a/src/private/mx/core/elements/LineWidth.cpp b/src/private/mx/core/elements/LineWidth.cpp index 7c2d5191..a36719bd 100644 --- a/src/private/mx/core/elements/LineWidth.cpp +++ b/src/private/mx/core/elements/LineWidth.cpp @@ -79,7 +79,7 @@ bool LineWidth::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.parse(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/LineWidthAttributes.cpp b/src/private/mx/core/elements/LineWidthAttributes.cpp index f88a80cc..d0a1bf77 100644 --- a/src/private/mx/core/elements/LineWidthAttributes.cpp +++ b/src/private/mx/core/elements/LineWidthAttributes.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -LineWidthAttributes::LineWidthAttributes() : type(LineWidthTypeEnum::beam), hasType(true) +LineWidthAttributes::LineWidthAttributes() : type(), hasType(true) { } @@ -48,7 +48,7 @@ bool LineWidthAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/Link.cpp b/src/private/mx/core/elements/Link.cpp index 279fcbf6..e9f91678 100644 --- a/src/private/mx/core/elements/Link.cpp +++ b/src/private/mx/core/elements/Link.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT License #include "mx/core/elements/Link.h" +#include "mx/core/FromXElement.h" #include namespace mx @@ -60,7 +61,6 @@ void Link::setAttributes(const LinkAttributesPtr &value) bool Link::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { - MX_UNUSED(message); return myAttributes->fromXElement(message, xelement); } diff --git a/src/private/mx/core/elements/LinkAttributes.cpp b/src/private/mx/core/elements/LinkAttributes.cpp index 39a7e170..13acae8d 100644 --- a/src/private/mx/core/elements/LinkAttributes.cpp +++ b/src/private/mx/core/elements/LinkAttributes.cpp @@ -11,10 +11,10 @@ namespace mx namespace core { LinkAttributes::LinkAttributes() - : href(), type(XlinkType::simple), role(), title(), show(XlinkShow::replace), actuate(XlinkActuate::onRequest), - name(), element(), position(), defaultX(), defaultY(), relativeX(), relativeY(), hasHref(true), hasType(false), - hasRole(false), hasTitle(false), hasShow(false), hasActuate(false), hasName(false), hasElement(false), - hasPosition(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false) + : href(), type(), role(), title(), show(XlinkShow::replace), actuate(), name(), element(), position(), defaultX(), + defaultY(), relativeX(), relativeY(), hasHref(true), hasType(false), hasRole(false), hasTitle(false), + hasShow(false), hasActuate(false), hasName(false), hasElement(false), hasPosition(false), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false) { } @@ -56,30 +56,27 @@ bool LinkAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & for (; it != endIter; ++it) { - if (parseAttribute(message, it, className, isSuccess, href, isHrefFound, "href")) + if (parseAttribute(message, it, className, isSuccess, href, isHrefFound, "xlink:href")) { continue; } - - if (it->getName() == "xlink:type") + if (parseAttribute(message, it, className, isSuccess, type, hasType, "xlink:type", &parseXlinkType)) { - hasType = true; continue; } - - if (parseAttribute(message, it, className, isSuccess, role, hasRole, "role")) + if (parseAttribute(message, it, className, isSuccess, role, hasRole, "xlink:role")) { continue; } - if (parseAttribute(message, it, className, isSuccess, title, hasTitle, "title")) + if (parseAttribute(message, it, className, isSuccess, title, hasTitle, "xlink:title")) { continue; } - if (parseAttribute(message, it, className, isSuccess, show, hasShow, "show", &parseXlinkShow)) + if (parseAttribute(message, it, className, isSuccess, show, hasShow, "xlink:show", &parseXlinkShow)) { continue; } - if (parseAttribute(message, it, className, isSuccess, actuate, hasActuate, "actuate", &parseXlinkActuate)) + if (parseAttribute(message, it, className, isSuccess, actuate, hasActuate, "xlink:actuate", &parseXlinkActuate)) { continue; } @@ -116,10 +113,10 @@ bool LinkAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & if (!isHrefFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'xlink:href' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/LinkAttributes.h b/src/private/mx/core/elements/LinkAttributes.h index a9f9f011..803c83fb 100644 --- a/src/private/mx/core/elements/LinkAttributes.h +++ b/src/private/mx/core/elements/LinkAttributes.h @@ -33,7 +33,7 @@ struct LinkAttributes : public AttributesInterface virtual bool hasValues() const; virtual std::ostream &toStream(std::ostream &os) const; XlinkHref href; - const XlinkType type; + XlinkType type; XlinkRole role; XlinkTitle title; XlinkShow show; diff --git a/src/private/mx/core/elements/LyricLanguageAttributes.cpp b/src/private/mx/core/elements/LyricLanguageAttributes.cpp index 2b3be52d..40174fe3 100644 --- a/src/private/mx/core/elements/LyricLanguageAttributes.cpp +++ b/src/private/mx/core/elements/LyricLanguageAttributes.cpp @@ -54,16 +54,12 @@ bool LyricLanguageAttributes::fromXElementImpl(std::ostream &message, ::ezxml::X { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, isLangFound, "lang")) - { - continue; - } } if (!isLangFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'xml:lang' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/MeasureAttributes.cpp b/src/private/mx/core/elements/MeasureAttributes.cpp index 92684046..e0a73838 100644 --- a/src/private/mx/core/elements/MeasureAttributes.cpp +++ b/src/private/mx/core/elements/MeasureAttributes.cpp @@ -3,7 +3,6 @@ // Distributed under the MIT License #include "mx/core/elements/MeasureAttributes.h" -#include "mx/core/FromString.h" #include "mx/core/FromXElement.h" #include @@ -70,7 +69,7 @@ bool MeasureAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen message << className << ": 'number' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/MeasureAttributes.h b/src/private/mx/core/elements/MeasureAttributes.h index a8e41518..89654870 100644 --- a/src/private/mx/core/elements/MeasureAttributes.h +++ b/src/private/mx/core/elements/MeasureAttributes.h @@ -18,6 +18,7 @@ namespace mx { namespace core { + MX_FORWARD_DECLARE_ATTRIBUTES(MeasureAttributes) struct MeasureAttributes : public AttributesInterface diff --git a/src/private/mx/core/elements/MeasureLayout.cpp b/src/private/mx/core/elements/MeasureLayout.cpp index 3a730efd..adf8d2f6 100644 --- a/src/private/mx/core/elements/MeasureLayout.cpp +++ b/src/private/mx/core/elements/MeasureLayout.cpp @@ -42,8 +42,11 @@ std::ostream &MeasureLayout::streamContents(std::ostream &os, const int indentLe { os << std::endl; myMeasureDistance->toStream(os, indentLevel + 1); - os << std::endl; + } + if (myHasMeasureDistance) + { isOneLineOnly = false; + os << std::endl; } else { @@ -88,7 +91,7 @@ bool MeasureLayout::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/MeasureNumberingAttributes.cpp b/src/private/mx/core/elements/MeasureNumberingAttributes.cpp index f3f72290..0ed06cee 100644 --- a/src/private/mx/core/elements/MeasureNumberingAttributes.cpp +++ b/src/private/mx/core/elements/MeasureNumberingAttributes.cpp @@ -12,16 +12,17 @@ namespace core { MeasureNumberingAttributes::MeasureNumberingAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), halign(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasHalign(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::left), + valign(Valign::bottom), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), + hasHalign(false), hasValign(false) { } bool MeasureNumberingAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasHalign; + hasFontWeight || hasColor || hasHalign || hasValign; } std::ostream &MeasureNumberingAttributes::toStream(std::ostream &os) const @@ -36,7 +37,9 @@ std::ostream &MeasureNumberingAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); } return os; } @@ -84,10 +87,18 @@ bool MeasureNumberingAttributes::fromXElementImpl(std::ostream &message, ::ezxml { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/MeasureNumberingAttributes.h b/src/private/mx/core/elements/MeasureNumberingAttributes.h index d193ac03..7aea0a65 100644 --- a/src/private/mx/core/elements/MeasureNumberingAttributes.h +++ b/src/private/mx/core/elements/MeasureNumberingAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,7 +37,9 @@ struct MeasureNumberingAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; @@ -45,7 +48,9 @@ struct MeasureNumberingAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/MeasureRepeat.cpp b/src/private/mx/core/elements/MeasureRepeat.cpp index b9abb391..1d801c0f 100644 --- a/src/private/mx/core/elements/MeasureRepeat.cpp +++ b/src/private/mx/core/elements/MeasureRepeat.cpp @@ -80,7 +80,7 @@ bool MeasureRepeat::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.parse(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/MeasureRepeatAttributes.cpp b/src/private/mx/core/elements/MeasureRepeatAttributes.cpp index cff92373..42faabff 100644 --- a/src/private/mx/core/elements/MeasureRepeatAttributes.cpp +++ b/src/private/mx/core/elements/MeasureRepeatAttributes.cpp @@ -53,7 +53,7 @@ bool MeasureRepeatAttributes::fromXElementImpl(std::ostream &message, ::ezxml::X if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/MeasureStyleChoice.cpp b/src/private/mx/core/elements/MeasureStyleChoice.cpp index 4f61be94..e5049deb 100644 --- a/src/private/mx/core/elements/MeasureStyleChoice.cpp +++ b/src/private/mx/core/elements/MeasureStyleChoice.cpp @@ -15,7 +15,6 @@ namespace mx namespace core { MeasureStyleChoice::MeasureStyleChoice() - : myChoice(MeasureStyleChoice::Choice::multipleRest), myMultipleRest(makeMultipleRest()), myMeasureRepeat(makeMeasureRepeat()), myBeatRepeat(makeBeatRepeat()), mySlash(makeSlash()) { @@ -69,7 +68,7 @@ MeasureStyleChoice::Choice MeasureStyleChoice::getChoice() const return myChoice; } -void MeasureStyleChoice::setChoice(const Choice value) +void MeasureStyleChoice::setChoice(const MeasureStyleChoice::Choice value) { myChoice = value; } diff --git a/src/private/mx/core/elements/MeasureStyleChoice.h b/src/private/mx/core/elements/MeasureStyleChoice.h index 11f44caf..98ef3995 100644 --- a/src/private/mx/core/elements/MeasureStyleChoice.h +++ b/src/private/mx/core/elements/MeasureStyleChoice.h @@ -46,12 +46,20 @@ class MeasureStyleChoice : public ElementInterface virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; Choice getChoice() const; void setChoice(const Choice value); + + /* _________ MultipleRest minOccurs = 1, maxOccurs = 1 _________ */ MultipleRestPtr getMultipleRest() const; void setMultipleRest(const MultipleRestPtr &value); + + /* _________ MeasureRepeat minOccurs = 1, maxOccurs = 1 _________ */ MeasureRepeatPtr getMeasureRepeat() const; void setMeasureRepeat(const MeasureRepeatPtr &value); + + /* _________ BeatRepeat minOccurs = 1, maxOccurs = 1 _________ */ BeatRepeatPtr getBeatRepeat() const; void setBeatRepeat(const BeatRepeatPtr &value); + + /* _________ Slash minOccurs = 1, maxOccurs = 1 _________ */ SlashPtr getSlash() const; void setSlash(const SlashPtr &value); diff --git a/src/private/mx/core/elements/Membrane.cpp b/src/private/mx/core/elements/Membrane.cpp index 119d57c5..d8abb50f 100644 --- a/src/private/mx/core/elements/Membrane.cpp +++ b/src/private/mx/core/elements/Membrane.cpp @@ -60,7 +60,6 @@ void Membrane::setValue(const MembraneEnum &value) bool Membrane::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); - MX_UNUSED(xelement); myValue = parseMembraneEnum(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/Metronome.cpp b/src/private/mx/core/elements/Metronome.cpp index 04f4c550..2682bdb2 100644 --- a/src/private/mx/core/elements/Metronome.cpp +++ b/src/private/mx/core/elements/Metronome.cpp @@ -4,11 +4,12 @@ #include "mx/core/elements/Metronome.h" #include "mx/core/FromXElement.h" +#include "mx/core/elements/BeatUnit.h" +#include "mx/core/elements/BeatUnitDot.h" #include "mx/core/elements/BeatUnitGroup.h" #include "mx/core/elements/BeatUnitPer.h" #include "mx/core/elements/BeatUnitPerOrNoteRelationNoteChoice.h" #include "mx/core/elements/MetronomeNote.h" -#include "mx/core/elements/MetronomeRelation.h" #include "mx/core/elements/MetronomeRelationGroup.h" #include "mx/core/elements/NoteRelationNote.h" #include "mx/core/elements/PerMinute.h" @@ -21,7 +22,7 @@ namespace core { Metronome::Metronome() : myAttributes(std::make_shared()), - myBeatUnitPerOrNoteRelationNoteChoice(makeBeatUnitPerOrNoteRelationNoteChoice()) + myBeatUnitPerOrNoteRelationNoteChoice(std::make_shared()) { } @@ -32,26 +33,35 @@ bool Metronome::hasAttributes() const std::ostream &Metronome::streamAttributes(std::ostream &os) const { - myAttributes->toStream(os); - return os; + return myAttributes->toStream(os); } std::ostream &Metronome::streamName(std::ostream &os) const { - return os << "metronome"; + os << "metronome"; + return os; } bool Metronome::hasContents() const { - return true; + return myBeatUnitPerOrNoteRelationNoteChoice->hasContents(); } std::ostream &Metronome::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - os << std::endl; - myBeatUnitPerOrNoteRelationNoteChoice->streamContents(os, indentLevel + 1, isOneLineOnly); - os << std::endl; - isOneLineOnly = false; + if (myBeatUnitPerOrNoteRelationNoteChoice->hasContents()) + { + os << std::endl; + if (myBeatUnitPerOrNoteRelationNoteChoice) + { + myBeatUnitPerOrNoteRelationNoteChoice->streamContents(os, indentLevel + 1, isOneLineOnly); + } + os << std::endl; + } + if (myBeatUnitPerOrNoteRelationNoteChoice->hasContents()) + { + isOneLineOnly = false; + } return os; } @@ -81,133 +91,134 @@ void Metronome::setBeatUnitPerOrNoteRelationNoteChoice(const BeatUnitPerOrNoteRe } } -/* - - - - - - - - - - */ - bool Metronome::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); - auto iter = xelement.begin(); auto endIter = xelement.end(); - - for (; iter != endIter; ++iter) + for (auto it = xelement.begin(); it != endIter; ++it) { - bool isIterIncremented = false; - - if ((iter->getName() == "beat-unit") || (iter->getName() == "beat-unit-dot")) + if (it->getName() == "beat-unit" || it->getName() == "beat-unit-dot") { - myBeatUnitPerOrNoteRelationNoteChoice->setChoice(BeatUnitPerOrNoteRelationNoteChoice::Choice::beatUnitPer); - auto beatUnitGroup = myBeatUnitPerOrNoteRelationNoteChoice->getBeatUnitPer()->getBeatUnitGroup(); - importGroup(message, iter, endIter, isSuccess, beatUnitGroup); - isIterIncremented = true; - ++iter; - - if (iter == endIter) - { - return false; - } - - if ((iter->getName() == "beat-unit") || (iter->getName() == "beat-unit-dot")) - { - auto beatUnitGroup2 = myBeatUnitPerOrNoteRelationNoteChoice->getBeatUnitPer() - ->getPerMinuteOrBeatUnitChoice() - ->getBeatUnitGroup(); - myBeatUnitPerOrNoteRelationNoteChoice->getBeatUnitPer()->getPerMinuteOrBeatUnitChoice()->setChoice( - PerMinuteOrBeatUnitChoice::Choice::beatUnitGroup); - importGroup(message, iter, endIter, isSuccess, beatUnitGroup2); - isIterIncremented = true; - ++iter; - } - else if (iter->getName() == "per-minute") + if (importContainerBeatUnitPer(message, it, endIter, isSuccess)) { - myBeatUnitPerOrNoteRelationNoteChoice->getBeatUnitPer()->getPerMinuteOrBeatUnitChoice()->setChoice( - PerMinuteOrBeatUnitChoice::Choice::perMinute); - myBeatUnitPerOrNoteRelationNoteChoice->getBeatUnitPer() - ->getPerMinuteOrBeatUnitChoice() - ->getPerMinute() - ->fromXElement(message, *iter); - isIterIncremented = true; - ++iter; + continue; } } - else if ((iter->getName() == "metronome-note")) + if (it->getName() == "metronome-note") { - myBeatUnitPerOrNoteRelationNoteChoice->setChoice( - BeatUnitPerOrNoteRelationNoteChoice::Choice::noteRelationNote); - auto noteRelationNote = myBeatUnitPerOrNoteRelationNoteChoice->getNoteRelationNote(); - bool isFirstMetronomeNoteAdded = false; - - while (iter != endIter && iter->getName() == "metronome-note") + if (importContainerNoteRelationNote(message, it, endIter, isSuccess)) { - auto metronomeNote = makeMetronomeNote(); - isSuccess &= metronomeNote->fromXElement(message, *iter); - - if (!isFirstMetronomeNoteAdded && noteRelationNote->getMetronomeNoteSet().size() == 1) - { - noteRelationNote->addMetronomeNote(metronomeNote); - noteRelationNote->removeMetronomeNote(noteRelationNote->getMetronomeNoteSet().cbegin()); - isFirstMetronomeNoteAdded = true; - } - else - { - noteRelationNote->addMetronomeNote(metronomeNote); - isFirstMetronomeNoteAdded = true; - } - isIterIncremented = true; - ++iter; + continue; } + } + } - if (iter == endIter) - { - MX_RETURN_IS_SUCCESS; - } + MX_RETURN_IS_SUCCESS; +} - if (iter->getName() == "metronome-relation") - { - myBeatUnitPerOrNoteRelationNoteChoice->getNoteRelationNote()->setHasMetronomeRelationGroup(true); - auto metronomeRelationGroup = - myBeatUnitPerOrNoteRelationNoteChoice->getNoteRelationNote()->getMetronomeRelationGroup(); - isSuccess &= metronomeRelationGroup->getMetronomeRelation()->fromXElement(message, *iter); - ++iter; - isIterIncremented = true; - - if (iter == endIter) - { - MX_RETURN_IS_SUCCESS; - } - - if (iter->getName() == "metronome-note") - { - myBeatUnitPerOrNoteRelationNoteChoice->getNoteRelationNote()->setHasMetronomeRelationGroup(true); - isSuccess &= metronomeRelationGroup->getMetronomeNote()->fromXElement(message, *iter); - ++iter; - isIterIncremented = true; - if (iter == endIter) - { - MX_RETURN_IS_SUCCESS; - } - } - } - } +bool Metronome::importContainerBeatUnitPer(std::ostream &message, ::ezxml::XElementIterator &it, + ::ezxml::XElementIterator &endIter, bool &isSuccess) +{ + if (it == endIter) + { + return false; + } + + if (it->getName() != "beat-unit" && it->getName() != "beat-unit-dot") + { + return false; + } - if (isIterIncremented) + myBeatUnitPerOrNoteRelationNoteChoice->setChoice(BeatUnitPerOrNoteRelationNoteChoice::Choice::beatUnitPer); + auto containerPtr = myBeatUnitPerOrNoteRelationNoteChoice->getBeatUnitPer(); + bool isIterIncremented = false; + + if (it != endIter) + { + auto groupPtr = containerPtr->getBeatUnitGroup(); + importGroup(message, it, endIter, isSuccess, groupPtr); + isIterIncremented = true; + ++it; + } + + auto perminuteorbeatunitchoice = containerPtr->getPerMinuteOrBeatUnitChoice(); + if (it != endIter && it->getName() == "per-minute") + { + perminuteorbeatunitchoice->setChoice(PerMinuteOrBeatUnitChoice::Choice::perMinute); + isSuccess &= perminuteorbeatunitchoice->getPerMinute()->fromXElement(message, *it); + isIterIncremented = true; + ++it; + } + else if (it != endIter && (it->getName() == "beat-unit" || it->getName() == "beat-unit-dot")) + { + perminuteorbeatunitchoice->setChoice(PerMinuteOrBeatUnitChoice::Choice::beatUnitGroup); + auto groupPtr = perminuteorbeatunitchoice->getBeatUnitGroup(); + importGroup(message, it, endIter, isSuccess, groupPtr); + isIterIncremented = true; + ++it; + } + + if (isIterIncremented) + { + --it; + } + + return true; +} + +bool Metronome::importContainerNoteRelationNote(std::ostream &message, ::ezxml::XElementIterator &it, + ::ezxml::XElementIterator &endIter, bool &isSuccess) +{ + if (it == endIter) + { + return false; + } + + if (it->getName() != "metronome-note") + { + return false; + } + + myBeatUnitPerOrNoteRelationNoteChoice->setChoice(BeatUnitPerOrNoteRelationNoteChoice::Choice::noteRelationNote); + auto containerPtr = myBeatUnitPerOrNoteRelationNoteChoice->getNoteRelationNote(); + bool isIterIncremented = false; + + while (it != endIter && it->getName() == "metronome-note") + { + auto item = makeMetronomeNote(); + isSuccess &= item->fromXElement(message, *it); + const auto &items = containerPtr->getMetronomeNoteSet(); + if (items.size() == 1 && !isIterIncremented) + { + containerPtr->addMetronomeNote(item); + containerPtr->removeMetronomeNote(items.cbegin()); + } + else { - --iter; + containerPtr->addMetronomeNote(item); } + isIterIncremented = true; + ++it; } - MX_RETURN_IS_SUCCESS; + bool hasMetronomeRelationGroup = false; + auto metronomerelationgroup = containerPtr->getMetronomeRelationGroup(); + importGroup(message, it, endIter, isSuccess, metronomerelationgroup, hasMetronomeRelationGroup); + if (hasMetronomeRelationGroup) + { + containerPtr->setHasMetronomeRelationGroup(true); + isIterIncremented = true; + ++it; + } + + if (isIterIncremented) + { + --it; + } + + return true; } } // namespace core diff --git a/src/private/mx/core/elements/Metronome.h b/src/private/mx/core/elements/Metronome.h index 76c7813b..4f64bcb4 100644 --- a/src/private/mx/core/elements/Metronome.h +++ b/src/private/mx/core/elements/Metronome.h @@ -12,6 +12,11 @@ #include #include +namespace ezxml +{ +class XElementIterator; +} + namespace mx { namespace core @@ -47,6 +52,11 @@ class Metronome : public ElementInterface private: MetronomeAttributesPtr myAttributes; BeatUnitPerOrNoteRelationNoteChoicePtr myBeatUnitPerOrNoteRelationNoteChoice; + + bool importContainerBeatUnitPer(std::ostream &message, ::ezxml::XElementIterator &it, + ::ezxml::XElementIterator &endIter, bool &isSuccess); + bool importContainerNoteRelationNote(std::ostream &message, ::ezxml::XElementIterator &it, + ::ezxml::XElementIterator &endIter, bool &isSuccess); }; } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/MetronomeAttributes.cpp b/src/private/mx/core/elements/MetronomeAttributes.cpp index 480c9792..a5194787 100644 --- a/src/private/mx/core/elements/MetronomeAttributes.cpp +++ b/src/private/mx/core/elements/MetronomeAttributes.cpp @@ -13,7 +13,7 @@ namespace core MetronomeAttributes::MetronomeAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::center), - valign(Valign::baseline), justify(LeftCenterRight::center), parentheses(YesNo::no), hasDefaultX(false), + valign(Valign::bottom), justify(LeftCenterRight::center), parentheses(YesNo::no), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false), hasJustify(false), hasParentheses(false) @@ -112,7 +112,7 @@ bool MetronomeAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/MetronomeBeamAttributes.cpp b/src/private/mx/core/elements/MetronomeBeamAttributes.cpp index f14aeef5..11c207d6 100644 --- a/src/private/mx/core/elements/MetronomeBeamAttributes.cpp +++ b/src/private/mx/core/elements/MetronomeBeamAttributes.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -MetronomeBeamAttributes::MetronomeBeamAttributes() : number(1), hasNumber(false) +MetronomeBeamAttributes::MetronomeBeamAttributes() : number(), hasNumber(false) { } diff --git a/src/private/mx/core/elements/MetronomeNote.cpp b/src/private/mx/core/elements/MetronomeNote.cpp index 9aa27a77..d18c18e0 100644 --- a/src/private/mx/core/elements/MetronomeNote.cpp +++ b/src/private/mx/core/elements/MetronomeNote.cpp @@ -191,11 +191,6 @@ bool MetronomeNote::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x } } - if (!isMetronomeTypeFound) - { - message << "MetronomeNote: '" << myMetronomeType->getElementName() << "' is required but was not found" - << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/MetronomeRelation.cpp b/src/private/mx/core/elements/MetronomeRelation.cpp index eb915aa4..6bce93f1 100644 --- a/src/private/mx/core/elements/MetronomeRelation.cpp +++ b/src/private/mx/core/elements/MetronomeRelation.cpp @@ -54,12 +54,13 @@ XsString MetronomeRelation::getValue() const void MetronomeRelation::setValue(const XsString &value) { - myValue.setValue(value.getValue()); + myValue = value; } bool MetronomeRelation::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); + MX_UNUSED(xelement); myValue.setValue(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/MetronomeRelation.h b/src/private/mx/core/elements/MetronomeRelation.h index 92011d6c..8f20afc5 100644 --- a/src/private/mx/core/elements/MetronomeRelation.h +++ b/src/private/mx/core/elements/MetronomeRelation.h @@ -24,6 +24,16 @@ inline MetronomeRelationPtr makeMetronomeRelation() return std::make_shared(); } +inline MetronomeRelationPtr makeMetronomeRelation(const XsString &value) +{ + return std::make_shared(value); +} + +inline MetronomeRelationPtr makeMetronomeRelation(XsString &&value) +{ + return std::make_shared(std::move(value)); +} + class MetronomeRelation : public ElementInterface { public: diff --git a/src/private/mx/core/elements/MetronomeRelationGroup.cpp b/src/private/mx/core/elements/MetronomeRelationGroup.cpp index b5a7b651..b2b56c5f 100644 --- a/src/private/mx/core/elements/MetronomeRelationGroup.cpp +++ b/src/private/mx/core/elements/MetronomeRelationGroup.cpp @@ -40,9 +40,15 @@ bool MetronomeRelationGroup::hasContents() const std::ostream &MetronomeRelationGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { isOneLineOnly = false; + bool isFirst = true; + if (!isFirst) + os << std::endl; myMetronomeRelation->toStream(os, indentLevel); - os << std::endl; + isFirst = false; + if (!isFirst) + os << std::endl; myMetronomeNote->toStream(os, indentLevel); + isFirst = false; return os; } diff --git a/src/private/mx/core/elements/MetronomeRelationGroup.h b/src/private/mx/core/elements/MetronomeRelationGroup.h index aad2d27e..698b7f25 100644 --- a/src/private/mx/core/elements/MetronomeRelationGroup.h +++ b/src/private/mx/core/elements/MetronomeRelationGroup.h @@ -35,12 +35,8 @@ class MetronomeRelationGroup : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - - /* _________ MetronomeRelation minOccurs = 1, maxOccurs = 1 _________ */ MetronomeRelationPtr getMetronomeRelation() const; void setMetronomeRelation(const MetronomeRelationPtr &value); - - /* _________ MetronomeNote minOccurs = 1, maxOccurs = 1 _________ */ MetronomeNotePtr getMetronomeNote() const; void setMetronomeNote(const MetronomeNotePtr &value); diff --git a/src/private/mx/core/elements/MetronomeTuplet.cpp b/src/private/mx/core/elements/MetronomeTuplet.cpp index 1b14afeb..81f939c9 100644 --- a/src/private/mx/core/elements/MetronomeTuplet.cpp +++ b/src/private/mx/core/elements/MetronomeTuplet.cpp @@ -5,7 +5,9 @@ #include "mx/core/elements/MetronomeTuplet.h" #include "mx/core/FromXElement.h" #include "mx/core/elements/ActualNotes.h" +#include "mx/core/elements/NormalDot.h" #include "mx/core/elements/NormalNotes.h" +#include "mx/core/elements/NormalType.h" #include "mx/core/elements/TimeModificationNormalTypeNormalDot.h" #include @@ -44,6 +46,7 @@ bool MetronomeTuplet::hasContents() const std::ostream &MetronomeTuplet::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myActualNotes->toStream(os, indentLevel + 1); os << std::endl; @@ -54,7 +57,6 @@ std::ostream &MetronomeTuplet::streamContents(std::ostream &os, const int indent myTimeModificationNormalTypeNormalDot->streamContents(os, indentLevel + 1, isOneLineOnly); } os << std::endl; - isOneLineOnly = false; return os; } @@ -145,16 +147,6 @@ bool MetronomeTuplet::fromXElementImpl(std::ostream &message, ::ezxml::XElement } } - if (!isActualNotesFound) - { - message << "MetronomeTuplet: '" << myActualNotes->getElementName() << "' is required but was not found" - << std::endl; - } - if (!isNormalNotesFound) - { - message << "MetronomeTuplet: '" << myNormalNotes->getElementName() << "' is required but was not found" - << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/MetronomeTuplet.h b/src/private/mx/core/elements/MetronomeTuplet.h index 4752b8d4..824319e5 100644 --- a/src/private/mx/core/elements/MetronomeTuplet.h +++ b/src/private/mx/core/elements/MetronomeTuplet.h @@ -39,7 +39,7 @@ class MetronomeTuplet : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; MetronomeTupletAttributesPtr getAttributes() const; - void setAttributes(const MetronomeTupletAttributesPtr &value); + void setAttributes(const MetronomeTupletAttributesPtr &attributes); /* _________ ActualNotes minOccurs = 1, maxOccurs = 1 _________ */ ActualNotesPtr getActualNotes() const; diff --git a/src/private/mx/core/elements/MetronomeTupletAttributes.cpp b/src/private/mx/core/elements/MetronomeTupletAttributes.cpp index 8d7d4379..7267ba12 100644 --- a/src/private/mx/core/elements/MetronomeTupletAttributes.cpp +++ b/src/private/mx/core/elements/MetronomeTupletAttributes.cpp @@ -11,8 +11,7 @@ namespace mx namespace core { MetronomeTupletAttributes::MetronomeTupletAttributes() - : type(StartStop::start), bracket(YesNo::no), showNumber(ShowTuplet::actual), hasType(true), hasBracket(false), - hasShowNumber(false) + : type(StartStop::start), bracket(YesNo::no), showNumber(), hasType(true), hasBracket(false), hasShowNumber(false) { } @@ -61,10 +60,10 @@ bool MetronomeTupletAttributes::fromXElementImpl(std::ostream &message, ::ezxml: if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/MidiDeviceInstrumentGroup.cpp b/src/private/mx/core/elements/MidiDeviceInstrumentGroup.cpp index 6b39bb5b..d811876d 100644 --- a/src/private/mx/core/elements/MidiDeviceInstrumentGroup.cpp +++ b/src/private/mx/core/elements/MidiDeviceInstrumentGroup.cpp @@ -41,26 +41,23 @@ bool MidiDeviceInstrumentGroup::hasContents() const std::ostream &MidiDeviceInstrumentGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - if (this->hasContents()) + bool firstItem = true; + isOneLineOnly = true; + if (myHasMidiDevice) { - if (myHasMidiDevice) - { - myMidiDevice->toStream(os, indentLevel); - if (myHasMidiInstrument) - { - os << std::endl; - } - } - if (myHasMidiInstrument) - { - myMidiInstrument->toStream(os, indentLevel); - } - isOneLineOnly = false; + if (!firstItem) + os << std::endl; + myMidiDevice->toStream(os, indentLevel); + firstItem = false; } - else + if (myHasMidiInstrument) { - isOneLineOnly = true; + if (!firstItem) + os << std::endl; + myMidiInstrument->toStream(os, indentLevel); + firstItem = false; } + isOneLineOnly = !hasContents(); return os; } diff --git a/src/private/mx/core/elements/MidiInstrument.cpp b/src/private/mx/core/elements/MidiInstrument.cpp index 443665b1..cdfa483f 100644 --- a/src/private/mx/core/elements/MidiInstrument.cpp +++ b/src/private/mx/core/elements/MidiInstrument.cpp @@ -91,7 +91,8 @@ std::ostream &MidiInstrument::streamContents(std::ostream &os, const int indentL os << std::endl; myElevation->toStream(os, indentLevel + 1); } - if (hasContents()) + if (myHasMidiChannel || myHasMidiName || myHasMidiBank || myHasMidiProgram || myHasMidiUnpitched || myHasVolume || + myHasPan || myHasElevation) { isOneLineOnly = false; os << std::endl; diff --git a/src/private/mx/core/elements/MidiInstrument.h b/src/private/mx/core/elements/MidiInstrument.h index 838a852e..ac547373 100644 --- a/src/private/mx/core/elements/MidiInstrument.h +++ b/src/private/mx/core/elements/MidiInstrument.h @@ -44,7 +44,7 @@ class MidiInstrument : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; MidiInstrumentAttributesPtr getAttributes() const; - void setAttributes(const MidiInstrumentAttributesPtr &value); + void setAttributes(const MidiInstrumentAttributesPtr &attributes); /* _________ MidiChannel minOccurs = 0, maxOccurs = 1 _________ */ MidiChannelPtr getMidiChannel() const; diff --git a/src/private/mx/core/elements/MidiInstrumentAttributes.cpp b/src/private/mx/core/elements/MidiInstrumentAttributes.cpp index 356cbf9c..e4511f0d 100644 --- a/src/private/mx/core/elements/MidiInstrumentAttributes.cpp +++ b/src/private/mx/core/elements/MidiInstrumentAttributes.cpp @@ -48,7 +48,7 @@ bool MidiInstrumentAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: if (!isIdFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'id' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/MidiName.h b/src/private/mx/core/elements/MidiName.h index 671d3343..56ce1461 100644 --- a/src/private/mx/core/elements/MidiName.h +++ b/src/private/mx/core/elements/MidiName.h @@ -16,6 +16,7 @@ namespace mx { namespace core { + MX_FORWARD_DECLARE_ELEMENT(MidiName) inline MidiNamePtr makeMidiName() diff --git a/src/private/mx/core/elements/Miscellaneous.cpp b/src/private/mx/core/elements/Miscellaneous.cpp index 1de4aed4..b4cce69e 100644 --- a/src/private/mx/core/elements/Miscellaneous.cpp +++ b/src/private/mx/core/elements/Miscellaneous.cpp @@ -43,7 +43,7 @@ std::ostream &Miscellaneous::streamContents(std::ostream &os, const int indentLe os << std::endl; x->toStream(os, indentLevel + 1); } - if (hasContents()) + if (myMiscellaneousFieldSet.size() > 0) { isOneLineOnly = false; os << std::endl; diff --git a/src/private/mx/core/elements/MiscellaneousFieldAttributes.cpp b/src/private/mx/core/elements/MiscellaneousFieldAttributes.cpp index adeeb866..89cfdd7f 100644 --- a/src/private/mx/core/elements/MiscellaneousFieldAttributes.cpp +++ b/src/private/mx/core/elements/MiscellaneousFieldAttributes.cpp @@ -48,7 +48,7 @@ bool MiscellaneousFieldAttributes::fromXElementImpl(std::ostream &message, ::ezx if (!isNameFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'name' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/Mode.cpp b/src/private/mx/core/elements/Mode.cpp index 112d7a02..d1d24ce0 100644 --- a/src/private/mx/core/elements/Mode.cpp +++ b/src/private/mx/core/elements/Mode.cpp @@ -60,8 +60,7 @@ void Mode::setValue(const ModeValue &value) bool Mode::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); - MX_UNUSED(xelement); - myValue.setValue(xelement.getValue()); + myValue = parseModeValue(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/Mordent.cpp b/src/private/mx/core/elements/Mordent.cpp index 1c0ef039..b734bf62 100644 --- a/src/private/mx/core/elements/Mordent.cpp +++ b/src/private/mx/core/elements/Mordent.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -Mordent::Mordent() : myAttributes(std::make_shared()) +Mordent::Mordent() : ElementInterface(), myAttributes(std::make_shared()) { } @@ -21,7 +21,11 @@ bool Mordent::hasAttributes() const std::ostream &Mordent::streamAttributes(std::ostream &os) const { - return myAttributes->toStream(os); + if (myAttributes) + { + myAttributes->toStream(os); + } + return os; } std::ostream &Mordent::streamName(std::ostream &os) const diff --git a/src/private/mx/core/elements/Mordent.h b/src/private/mx/core/elements/Mordent.h index 73fc5c4a..c6a3b2c6 100644 --- a/src/private/mx/core/elements/Mordent.h +++ b/src/private/mx/core/elements/Mordent.h @@ -36,7 +36,7 @@ class Mordent : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; MordentAttributesPtr getAttributes() const; - void setAttributes(const MordentAttributesPtr &value); + void setAttributes(const MordentAttributesPtr &attributes); private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/MordentAttributes.cpp b/src/private/mx/core/elements/MordentAttributes.cpp index c92e836d..16448d31 100644 --- a/src/private/mx/core/elements/MordentAttributes.cpp +++ b/src/private/mx/core/elements/MordentAttributes.cpp @@ -12,13 +12,13 @@ namespace core { MordentAttributes::MordentAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), - startNote(StartNote::main), trillStep(TrillStep::half), twoNoteTurn(TwoNoteTurn::none), accelerate(YesNo::no), - beats(), secondBeat(), lastBeat(), long_(YesNo::no), approach(AboveBelow::below), departure(AboveBelow::below), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false), - hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), - hasSecondBeat(false), hasLastBeat(false), hasLong(false), hasApproach(false), hasDeparture(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), startNote(), + trillStep(), twoNoteTurn(), accelerate(YesNo::no), beats(), secondBeat(), lastBeat(), long_(YesNo::no), + approach(AboveBelow::below), departure(AboveBelow::below), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), + hasFontWeight(false), hasColor(false), hasPlacement(false), hasStartNote(false), hasTrillStep(false), + hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), hasSecondBeat(false), hasLastBeat(false), + hasLong(false), hasApproach(false), hasDeparture(false) { } diff --git a/src/private/mx/core/elements/MultipleRestAttributes.cpp b/src/private/mx/core/elements/MultipleRestAttributes.cpp index 7b5f917a..99ed399f 100644 --- a/src/private/mx/core/elements/MultipleRestAttributes.cpp +++ b/src/private/mx/core/elements/MultipleRestAttributes.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -MultipleRestAttributes::MultipleRestAttributes() : useSymbols(), hasUseSymbols(false) +MultipleRestAttributes::MultipleRestAttributes() : useSymbols(YesNo::no), hasUseSymbols(false) { } diff --git a/src/private/mx/core/elements/NonArpeggiateAttributes.cpp b/src/private/mx/core/elements/NonArpeggiateAttributes.cpp index 1697b4a1..1f4a1c3b 100644 --- a/src/private/mx/core/elements/NonArpeggiateAttributes.cpp +++ b/src/private/mx/core/elements/NonArpeggiateAttributes.cpp @@ -11,15 +11,16 @@ namespace mx namespace core { NonArpeggiateAttributes::NonArpeggiateAttributes() - : type(TopBottom::top), number(), defaultX(), defaultY(), relativeX(), relativeY(), placement(AboveBelow::below), + : type(), number(), defaultX(), defaultY(), relativeX(), relativeY(), placement(AboveBelow::below), color(), hasType(true), hasNumber(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasPlacement(false) + hasPlacement(false), hasColor(false) { } bool NonArpeggiateAttributes::hasValues() const { - return hasType || hasNumber || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasPlacement; + return hasType || hasNumber || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasPlacement || + hasColor; } std::ostream &NonArpeggiateAttributes::toStream(std::ostream &os) const @@ -33,6 +34,7 @@ std::ostream &NonArpeggiateAttributes::toStream(std::ostream &os) const streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); streamAttribute(os, placement, "placement", hasPlacement); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -76,15 +78,19 @@ bool NonArpeggiateAttributes::fromXElementImpl(std::ostream &message, ::ezxml::X { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/NonArpeggiateAttributes.h b/src/private/mx/core/elements/NonArpeggiateAttributes.h index bbd37729..91860357 100644 --- a/src/private/mx/core/elements/NonArpeggiateAttributes.h +++ b/src/private/mx/core/elements/NonArpeggiateAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" @@ -34,6 +35,7 @@ struct NonArpeggiateAttributes : public AttributesInterface TenthsValue relativeX; TenthsValue relativeY; AboveBelow placement; + Color color; const bool hasType; bool hasNumber; bool hasDefaultX; @@ -41,6 +43,7 @@ struct NonArpeggiateAttributes : public AttributesInterface bool hasRelativeX; bool hasRelativeY; bool hasPlacement; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/NonTraditionalKey.cpp b/src/private/mx/core/elements/NonTraditionalKey.cpp index 4ec37bc5..d703339e 100644 --- a/src/private/mx/core/elements/NonTraditionalKey.cpp +++ b/src/private/mx/core/elements/NonTraditionalKey.cpp @@ -14,8 +14,8 @@ namespace mx namespace core { NonTraditionalKey::NonTraditionalKey() - : ElementInterface(), myKeyStep(std::make_shared()), myKeyAlter(std::make_shared()), - myKeyAccidental(std::make_shared()), myHasKeyAccidental(false) + : myKeyStep(makeKeyStep()), myKeyAlter(makeKeyAlter()), myKeyAccidental(makeKeyAccidental()), + myHasKeyAccidental(false) { } @@ -42,18 +42,21 @@ bool NonTraditionalKey::hasContents() const std::ostream &NonTraditionalKey::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { isOneLineOnly = false; - if (myKeyStep) - { - myKeyStep->toStream(os, indentLevel) << std::endl; - } - if (myKeyAlter) - { - myKeyAlter->toStream(os, indentLevel); - } - if (myKeyAccidental && myHasKeyAccidental) - { + bool isFirst = true; + if (!isFirst) os << std::endl; + myKeyStep->toStream(os, indentLevel); + isFirst = false; + if (!isFirst) + os << std::endl; + myKeyAlter->toStream(os, indentLevel); + isFirst = false; + if (myHasKeyAccidental) + { + if (!isFirst) + os << std::endl; myKeyAccidental->toStream(os, indentLevel); + isFirst = false; } return os; } @@ -107,41 +110,7 @@ void NonTraditionalKey::setHasKeyAccidental(const bool value) myHasKeyAccidental = value; } -bool NonTraditionalKey::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - bool isSuccess = true; - bool isKeyStepFound = false; - bool isKeyAlterFound = false; - - auto endIter = xelement.end(); - for (auto it = xelement.begin(); it != endIter; ++it) - { - if (importElement(message, *it, isSuccess, *myKeyStep, isKeyStepFound)) - { - continue; - } - if (importElement(message, *it, isSuccess, *myKeyAlter, isKeyAlterFound)) - { - continue; - } - if (importElement(message, *it, isSuccess, *myKeyAccidental, myHasKeyAccidental)) - { - continue; - } - } - - if (!isKeyStepFound) - { - message << "NonTraditionalKey: '" << myKeyStep->getElementName() << "' is required but was not found" - << std::endl; - } - if (!isKeyAlterFound) - { - message << "NonTraditionalKey: '" << myKeyAlter->getElementName() << "' is required but was not found" - << std::endl; - } - MX_RETURN_IS_SUCCESS; -} +MX_FROM_XELEMENT_UNUSED(NonTraditionalKey); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/NormalNoteGroup.cpp b/src/private/mx/core/elements/NormalNoteGroup.cpp index 68f60457..f8b8c92f 100644 --- a/src/private/mx/core/elements/NormalNoteGroup.cpp +++ b/src/private/mx/core/elements/NormalNoteGroup.cpp @@ -47,6 +47,7 @@ std::ostream &NormalNoteGroup::streamContents(std::ostream &os, const int indent os << std::endl; x->toStream(os, indentLevel); } + isOneLineOnly = false; return os; } @@ -115,5 +116,6 @@ TiePtr NormalNoteGroup::getTie(const TieSetIterConst &setIterator) const } MX_FROM_XELEMENT_UNUSED(NormalNoteGroup); + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/NormalTypeNormalDotGroup.cpp b/src/private/mx/core/elements/NormalTypeNormalDotGroup.cpp index 72165ae5..cd8f222d 100644 --- a/src/private/mx/core/elements/NormalTypeNormalDotGroup.cpp +++ b/src/private/mx/core/elements/NormalTypeNormalDotGroup.cpp @@ -39,13 +39,19 @@ bool NormalTypeNormalDotGroup::hasContents() const std::ostream &NormalTypeNormalDotGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - isOneLineOnly = false; + bool isFirst = true; + if (!isFirst) + os << std::endl; myNormalType->toStream(os, indentLevel); + isFirst = false; for (auto x : myNormalDotSet) { - os << std::endl; + if (!isFirst) + os << std::endl; x->toStream(os, indentLevel); + isFirst = false; } + isOneLineOnly = !hasContents(); return os; } @@ -102,7 +108,6 @@ bool NormalTypeNormalDotGroup::fromXElementImpl(std::ostream &message, ::ezxml:: bool isSuccess = true; bool isNormalTypeFound = false; bool isFirstNormalDotAdded = false; - for (auto it = xelement.begin(); it != xelement.end(); ++it) { const std::string elementName = it->getName(); @@ -132,13 +137,15 @@ bool NormalTypeNormalDotGroup::fromXElementImpl(std::ostream &message, ::ezxml:: { if (!isNormalTypeFound) { - isSuccess = false; - message << "NormalTypeNormalDotGroup: 'normal-type' element is required but was not found" << std::endl; + message << "NormalTypeNormalDotGroup: a 'normal-type' element is required but was not found" + << std::endl; + return false; } - break; } } + MX_RETURN_IS_SUCCESS; } + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/Notations.cpp b/src/private/mx/core/elements/Notations.cpp index f57fecbd..dfacf57d 100644 --- a/src/private/mx/core/elements/Notations.cpp +++ b/src/private/mx/core/elements/Notations.cpp @@ -145,126 +145,130 @@ bool Notations::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); - for (auto it = xelement.begin(); it != xelement.end(); ++it) + auto endIter = xelement.end(); + for (auto it = xelement.begin(); it != endIter; ++it) { - const std::string elementName = it->getName(); - - if (elementName == "footnote") - { - myEditorialGroup->setHasFootnote(true); - isSuccess &= myEditorialGroup->getFootnote()->fromXElement(message, *it); - } - else if (elementName == "level") + if (it->getName() == "footnote" || it->getName() == "level") { - myEditorialGroup->setHasLevel(true); - isSuccess &= myEditorialGroup->getLevel()->fromXElement(message, *it); + importGroup(message, it, endIter, isSuccess, myEditorialGroup); + continue; } - else if (elementName == "tied") + if (it->getName() == "tied") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::tied); isSuccess &= choice->getTied()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "tuplet") + if (it->getName() == "slur") { auto choice = makeNotationsChoice(); - choice->setChoice(NotationsChoice::Choice::tuplet); - isSuccess &= choice->getTuplet()->fromXElement(message, *it); + choice->setChoice(NotationsChoice::Choice::slur); + isSuccess &= choice->getSlur()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "slur") + if (it->getName() == "tuplet") { auto choice = makeNotationsChoice(); - choice->setChoice(NotationsChoice::Choice::slur); - isSuccess &= choice->getSlur()->fromXElement(message, *it); + choice->setChoice(NotationsChoice::Choice::tuplet); + isSuccess &= choice->getTuplet()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "glissando") + if (it->getName() == "glissando") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::glissando); isSuccess &= choice->getGlissando()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "slide") + if (it->getName() == "slide") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::slide); isSuccess &= choice->getSlide()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "ornaments") + if (it->getName() == "ornaments") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::ornaments); isSuccess &= choice->getOrnaments()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "technical") + if (it->getName() == "technical") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::technical); isSuccess &= choice->getTechnical()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "articulations") + if (it->getName() == "articulations") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::articulations); isSuccess &= choice->getArticulations()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "dynamics") + if (it->getName() == "dynamics") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::dynamics); isSuccess &= choice->getDynamics()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "fermata") + if (it->getName() == "fermata") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::fermata); isSuccess &= choice->getFermata()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "arpeggiate") + if (it->getName() == "arpeggiate") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::arpeggiate); isSuccess &= choice->getArpeggiate()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "non-arpeggiate") + if (it->getName() == "non-arpeggiate") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::nonArpeggiate); isSuccess &= choice->getNonArpeggiate()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "accidental-mark") + if (it->getName() == "accidental-mark") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::accidentalMark); isSuccess &= choice->getAccidentalMark()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); + continue; } - else if (elementName == "other-notations") + if (it->getName() == "other-notation") { auto choice = makeNotationsChoice(); choice->setChoice(NotationsChoice::Choice::otherNotation); isSuccess &= choice->getOtherNotation()->fromXElement(message, *it); myNotationsChoiceSet.push_back(choice); - } - else - { - isSuccess = false; - message << "Notations: fromXElement unexpected element '" << elementName << "' encountered" << std::endl; + continue; } } MX_RETURN_IS_SUCCESS; } + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/NotationsChoice.cpp b/src/private/mx/core/elements/NotationsChoice.cpp index 799bd207..bbdccdc1 100644 --- a/src/private/mx/core/elements/NotationsChoice.cpp +++ b/src/private/mx/core/elements/NotationsChoice.cpp @@ -55,54 +55,63 @@ bool NotationsChoice::hasContents() const std::ostream &NotationsChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - switch (myChoice) + if (myChoice == Choice::tied) { - case Choice::tied: myTied->toStream(os, indentLevel); - break; - case Choice::slur: + } + if (myChoice == Choice::slur) + { mySlur->toStream(os, indentLevel); - break; - case Choice::tuplet: + } + if (myChoice == Choice::tuplet) + { myTuplet->toStream(os, indentLevel); - break; - case Choice::glissando: + } + if (myChoice == Choice::glissando) + { myGlissando->toStream(os, indentLevel); - break; - case Choice::slide: + } + if (myChoice == Choice::slide) + { mySlide->toStream(os, indentLevel); - break; - case Choice::ornaments: + } + if (myChoice == Choice::ornaments) + { myOrnaments->toStream(os, indentLevel); - break; - case Choice::technical: + } + if (myChoice == Choice::technical) + { myTechnical->toStream(os, indentLevel); - break; - case Choice::articulations: + } + if (myChoice == Choice::articulations) + { myArticulations->toStream(os, indentLevel); - break; - case Choice::dynamics: + } + if (myChoice == Choice::dynamics) + { myDynamics->toStream(os, indentLevel); - break; - case Choice::fermata: + } + if (myChoice == Choice::fermata) + { myFermata->toStream(os, indentLevel); - break; - case Choice::arpeggiate: + } + if (myChoice == Choice::arpeggiate) + { myArpeggiate->toStream(os, indentLevel); - break; - case Choice::nonArpeggiate: + } + if (myChoice == Choice::nonArpeggiate) + { myNonArpeggiate->toStream(os, indentLevel); - break; - case Choice::accidentalMark: + } + if (myChoice == Choice::accidentalMark) + { myAccidentalMark->toStream(os, indentLevel); - break; - case Choice::otherNotation: + } + if (myChoice == Choice::otherNotation) + { myOtherNotation->toStream(os, indentLevel); - break; - default: - break; } - isOneLineOnly = hasContents(); + isOneLineOnly = false; return os; } @@ -111,7 +120,7 @@ NotationsChoice::Choice NotationsChoice::getChoice() const return myChoice; } -void NotationsChoice::setChoice(const NotationsChoice::Choice value) +void NotationsChoice::setChoice(const Choice value) { myChoice = value; } diff --git a/src/private/mx/core/elements/NotationsChoice.h b/src/private/mx/core/elements/NotationsChoice.h index ff3b394a..1e414764 100644 --- a/src/private/mx/core/elements/NotationsChoice.h +++ b/src/private/mx/core/elements/NotationsChoice.h @@ -16,20 +16,20 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(AccidentalMark) -MX_FORWARD_DECLARE_ELEMENT(Arpeggiate) +MX_FORWARD_DECLARE_ELEMENT(Tied) +MX_FORWARD_DECLARE_ELEMENT(Slur) +MX_FORWARD_DECLARE_ELEMENT(Tuplet) +MX_FORWARD_DECLARE_ELEMENT(Glissando) +MX_FORWARD_DECLARE_ELEMENT(Slide) +MX_FORWARD_DECLARE_ELEMENT(Ornaments) +MX_FORWARD_DECLARE_ELEMENT(Technical) MX_FORWARD_DECLARE_ELEMENT(Articulations) MX_FORWARD_DECLARE_ELEMENT(Dynamics) MX_FORWARD_DECLARE_ELEMENT(Fermata) -MX_FORWARD_DECLARE_ELEMENT(Glissando) +MX_FORWARD_DECLARE_ELEMENT(Arpeggiate) MX_FORWARD_DECLARE_ELEMENT(NonArpeggiate) -MX_FORWARD_DECLARE_ELEMENT(Ornaments) +MX_FORWARD_DECLARE_ELEMENT(AccidentalMark) MX_FORWARD_DECLARE_ELEMENT(OtherNotation) -MX_FORWARD_DECLARE_ELEMENT(Slide) -MX_FORWARD_DECLARE_ELEMENT(Slur) -MX_FORWARD_DECLARE_ELEMENT(Technical) -MX_FORWARD_DECLARE_ELEMENT(Tied) -MX_FORWARD_DECLARE_ELEMENT(Tuplet) MX_FORWARD_DECLARE_ELEMENT(NotationsChoice) inline NotationsChoicePtr makeNotationsChoice() @@ -42,20 +42,20 @@ class NotationsChoice : public ElementInterface public: enum class Choice { - tied = 1, - slur = 2, - tuplet = 3, - glissando = 4, - slide = 5, - ornaments = 6, - technical = 7, - articulations = 8, - dynamics = 9, - fermata = 10, - arpeggiate = 11, - nonArpeggiate = 12, - accidentalMark = 13, - otherNotation = 14 + tied = 0, + slur = 1, + tuplet = 2, + glissando = 3, + slide = 4, + ornaments = 5, + technical = 6, + articulations = 7, + dynamics = 8, + fermata = 9, + arpeggiate = 10, + nonArpeggiate = 11, + accidentalMark = 12, + otherNotation = 13 }; NotationsChoice(); @@ -64,64 +64,34 @@ class NotationsChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - - /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */ - NotationsChoice::Choice getChoice() const; - void setChoice(const NotationsChoice::Choice value); - - /* _________ Tied minOccurs = 1, maxOccurs = 1 _________ */ + Choice getChoice() const; + void setChoice(const Choice value); TiedPtr getTied() const; void setTied(const TiedPtr &value); - - /* _________ Slur minOccurs = 1, maxOccurs = 1 _________ */ SlurPtr getSlur() const; void setSlur(const SlurPtr &value); - - /* _________ Tuplet minOccurs = 1, maxOccurs = 1 _________ */ TupletPtr getTuplet() const; void setTuplet(const TupletPtr &value); - - /* _________ Glissando minOccurs = 1, maxOccurs = 1 _________ */ GlissandoPtr getGlissando() const; void setGlissando(const GlissandoPtr &value); - - /* _________ Slide minOccurs = 1, maxOccurs = 1 _________ */ SlidePtr getSlide() const; void setSlide(const SlidePtr &value); - - /* _________ Ornaments minOccurs = 1, maxOccurs = 1 _________ */ OrnamentsPtr getOrnaments() const; void setOrnaments(const OrnamentsPtr &value); - - /* _________ Technical minOccurs = 1, maxOccurs = 1 _________ */ TechnicalPtr getTechnical() const; void setTechnical(const TechnicalPtr &value); - - /* _________ Articulations minOccurs = 1, maxOccurs = 1 _________ */ ArticulationsPtr getArticulations() const; void setArticulations(const ArticulationsPtr &value); - - /* _________ Dynamics minOccurs = 1, maxOccurs = 1 _________ */ DynamicsPtr getDynamics() const; void setDynamics(const DynamicsPtr &value); - - /* _________ Fermata minOccurs = 1, maxOccurs = 1 _________ */ FermataPtr getFermata() const; void setFermata(const FermataPtr &value); - - /* _________ Arpeggiate minOccurs = 1, maxOccurs = 1 _________ */ ArpeggiatePtr getArpeggiate() const; void setArpeggiate(const ArpeggiatePtr &value); - - /* _________ NonArpeggiate minOccurs = 1, maxOccurs = 1 _________ */ NonArpeggiatePtr getNonArpeggiate() const; void setNonArpeggiate(const NonArpeggiatePtr &value); - - /* _________ AccidentalMark minOccurs = 1, maxOccurs = 1 _________ */ AccidentalMarkPtr getAccidentalMark() const; void setAccidentalMark(const AccidentalMarkPtr &value); - - /* _________ OtherNotation minOccurs = 1, maxOccurs = 1 _________ */ OtherNotationPtr getOtherNotation() const; void setOtherNotation(const OtherNotationPtr &value); diff --git a/src/private/mx/core/elements/Note.cpp b/src/private/mx/core/elements/Note.cpp index 7f258948..55c974db 100644 --- a/src/private/mx/core/elements/Note.cpp +++ b/src/private/mx/core/elements/Note.cpp @@ -6,6 +6,7 @@ #include "mx/core/FromXElement.h" #include "mx/core/elements/Accidental.h" #include "mx/core/elements/Beam.h" +#include "mx/core/elements/Chord.h" #include "mx/core/elements/Cue.h" #include "mx/core/elements/CueNoteGroup.h" #include "mx/core/elements/Dot.h" @@ -21,6 +22,7 @@ #include "mx/core/elements/Lyric.h" #include "mx/core/elements/NormalNoteGroup.h" #include "mx/core/elements/Notations.h" +#include "mx/core/elements/NoteAttributes.h" #include "mx/core/elements/NoteChoice.h" #include "mx/core/elements/Notehead.h" #include "mx/core/elements/NoteheadText.h" @@ -749,7 +751,7 @@ bool Note::parseNoteChoice(std::ostream &message, ::ezxml::XElement ¬eElement // we should now be pointing at the full note group isSuccess &= parseFullNoteGroup(message, noteElement, iter, fullNoteGroup); - // NormalNoteGroups and CueNoteGroups require a duration element + // GraceNoteGroups do not have a duration element if (elementName != "grace") { if (iter == noteElement.end() || iter->getName() != "duration") @@ -758,13 +760,13 @@ bool Note::parseNoteChoice(std::ostream &message, ::ezxml::XElement ¬eElement return false; } - if (getNoteChoice()->getChoice() == NoteChoice::Choice::normal) + if (getNoteChoice()->getChoice() == NoteChoice::Choice::cue) { - getNoteChoice()->getNormalNoteGroup()->getDuration()->fromXElement(message, *iter); + getNoteChoice()->getCueNoteGroup()->getDuration()->fromXElement(message, *iter); } - else if (getNoteChoice()->getChoice() == NoteChoice::Choice::cue) + else if (getNoteChoice()->getChoice() == NoteChoice::Choice::normal) { - getNoteChoice()->getCueNoteGroup()->getDuration()->fromXElement(message, *iter); + getNoteChoice()->getNormalNoteGroup()->getDuration()->fromXElement(message, *iter); } ++iter; } @@ -775,22 +777,22 @@ bool Note::parseNoteChoice(std::ostream &message, ::ezxml::XElement ¬eElement MX_RETURN_IS_SUCCESS; } - // now we may be pointing at tie elements, but only if the choice is normal or grace - if (getNoteChoice()->getChoice() == NoteChoice::Choice::normal || - getNoteChoice()->getChoice() == NoteChoice::Choice::grace) + // now we may be pointing at tie elements, but only if the choice supports them + if (getNoteChoice()->getChoice() == NoteChoice::Choice::grace || + getNoteChoice()->getChoice() == NoteChoice::Choice::normal) { std::string possibleTieElementName = iter->getName(); while (iter != noteElement.end() && iter->getName() == "tie") { auto tie = makeTie(); isSuccess &= tie->fromXElement(message, *iter); - if (getNoteChoice()->getChoice() == NoteChoice::Choice::normal) + if (getNoteChoice()->getChoice() == NoteChoice::Choice::grace) { - getNoteChoice()->getNormalNoteGroup()->addTie(tie); + getNoteChoice()->getGraceNoteGroup()->addTie(tie); } - else if (getNoteChoice()->getChoice() == NoteChoice::Choice::grace) + else if (getNoteChoice()->getChoice() == NoteChoice::Choice::normal) { - getNoteChoice()->getGraceNoteGroup()->addTie(tie); + getNoteChoice()->getNormalNoteGroup()->addTie(tie); } ++iter; } diff --git a/src/private/mx/core/elements/NoteAttributes.h b/src/private/mx/core/elements/NoteAttributes.h index 3b0609a5..43bacbd3 100644 --- a/src/private/mx/core/elements/NoteAttributes.h +++ b/src/private/mx/core/elements/NoteAttributes.h @@ -6,6 +6,7 @@ #include "mx/core/AttributesInterface.h" #include "mx/core/Color.h" +#include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" diff --git a/src/private/mx/core/elements/NoteRelationNote.cpp b/src/private/mx/core/elements/NoteRelationNote.cpp index 037107a8..d4d87d81 100644 --- a/src/private/mx/core/elements/NoteRelationNote.cpp +++ b/src/private/mx/core/elements/NoteRelationNote.cpp @@ -110,19 +110,7 @@ void NoteRelationNote::setHasMetronomeRelationGroup(const bool value) myHasMetronomeRelationGroup = value; } -bool NoteRelationNote::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - bool isSuccess = true; - - auto endIter = xelement.end(); - for (auto it = xelement.begin(); it != endIter; ++it) - { - importElementSet(message, it, endIter, isSuccess, "metronome-note", myMetronomeNoteSet); - importGroup(message, it, endIter, isSuccess, myMetronomeRelationGroup, myHasMetronomeRelationGroup); - } - - MX_RETURN_IS_SUCCESS; -} +MX_FROM_XELEMENT_UNUSED(NoteRelationNote); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/NoteSizeAttributes.cpp b/src/private/mx/core/elements/NoteSizeAttributes.cpp index 24103e6f..d1ae5b60 100644 --- a/src/private/mx/core/elements/NoteSizeAttributes.cpp +++ b/src/private/mx/core/elements/NoteSizeAttributes.cpp @@ -48,7 +48,7 @@ bool NoteSizeAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/Notehead.cpp b/src/private/mx/core/elements/Notehead.cpp index 8955e117..3a6d8092 100644 --- a/src/private/mx/core/elements/Notehead.cpp +++ b/src/private/mx/core/elements/Notehead.cpp @@ -79,7 +79,7 @@ bool Notehead::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xeleme bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue = parseNoteheadValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/NoteheadAttributes.cpp b/src/private/mx/core/elements/NoteheadAttributes.cpp index 41172897..423751e2 100644 --- a/src/private/mx/core/elements/NoteheadAttributes.cpp +++ b/src/private/mx/core/elements/NoteheadAttributes.cpp @@ -11,15 +11,15 @@ namespace mx namespace core { NoteheadAttributes::NoteheadAttributes() - : filled(), parentheses(), fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), hasFilled(false), hasParentheses(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) + : filled(YesNo::no), parentheses(YesNo::no), fontFamily(), fontStyle(FontStyle::normal), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasFilled(false), hasParentheses(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool NoteheadAttributes::hasValues() const { - return hasFilled || hasParentheses || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight | hasColor; + return hasFilled || hasParentheses || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &NoteheadAttributes::toStream(std::ostream &os) const @@ -78,7 +78,7 @@ bool NoteheadAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/NoteheadText.cpp b/src/private/mx/core/elements/NoteheadText.cpp index 57d5a046..514beeb8 100644 --- a/src/private/mx/core/elements/NoteheadText.cpp +++ b/src/private/mx/core/elements/NoteheadText.cpp @@ -41,13 +41,20 @@ bool NoteheadText::hasContents() const std::ostream &NoteheadText::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - isOneLineOnly = false; - for (auto x : myNoteheadTextChoiceSet) + if (myNoteheadTextChoiceSet.size() > 0) { + for (auto x : myNoteheadTextChoiceSet) + { + os << std::endl; + x->streamContents(os, indentLevel + 1, isOneLineOnly); + } + isOneLineOnly = false; os << std::endl; - x->streamContents(os, indentLevel + 1, isOneLineOnly); } - os << std::endl; + else + { + isOneLineOnly = true; + } return os; } @@ -56,19 +63,19 @@ const NoteheadTextChoiceSet &NoteheadText::getNoteheadTextChoiceSet() const return myNoteheadTextChoiceSet; } -void NoteheadText::removeNoteheadTextChoice(const NoteheadTextChoiceSetIterConst &value) +void NoteheadText::addNoteheadTextChoice(const NoteheadTextChoicePtr &value) { - if (value != myNoteheadTextChoiceSet.cend()) + if (value) { - myNoteheadTextChoiceSet.erase(value); + myNoteheadTextChoiceSet.push_back(value); } } -void NoteheadText::addNoteheadTextChoice(const NoteheadTextChoicePtr &value) +void NoteheadText::removeNoteheadTextChoice(const NoteheadTextChoiceSetIterConst &value) { - if (value) + if (value != myNoteheadTextChoiceSet.cend()) { - myNoteheadTextChoiceSet.push_back(value); + myNoteheadTextChoiceSet.erase(value); } } @@ -78,48 +85,37 @@ void NoteheadText::clearNoteheadTextChoiceSet() myNoteheadTextChoiceSet.push_back(makeNoteheadTextChoice()); } +NoteheadTextChoicePtr NoteheadText::getNoteheadTextChoice(const NoteheadTextChoiceSetIterConst &setIterator) const +{ + if (setIterator != myNoteheadTextChoiceSet.cend()) + { + return *setIterator; + } + return NoteheadTextChoicePtr(); +} + bool NoteheadText::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { bool isSuccess = true; - bool isFirstItemAdded = false; - auto it = xelement.begin(); - auto endIter = xelement.end(); - for (; it != endIter; ++it) + auto endIter = xelement.end(); + for (auto it = xelement.begin(); it != endIter; ++it) { - if (it->getName() == "accidental-text") + if (it->getName() == "display-text") { - auto item = makeNoteheadTextChoice(); - item->setChoice(NoteheadTextChoice::Choice::accidentalText); - isSuccess &= item->getAccidentalText()->fromXElement(message, xelement); - - if (!isFirstItemAdded && myNoteheadTextChoiceSet.size() == 1) - { - *myNoteheadTextChoiceSet.begin() = item; - isFirstItemAdded = true; - } - else - { - myNoteheadTextChoiceSet.push_back(item); - isFirstItemAdded = true; - } + auto choice = makeNoteheadTextChoice(); + choice->setChoice(NoteheadTextChoice::Choice::displayText); + isSuccess &= choice->getDisplayText()->fromXElement(message, *it); + myNoteheadTextChoiceSet.push_back(choice); + continue; } - else if (it->getName() == "display-text") + if (it->getName() == "accidental-text") { - auto item = makeNoteheadTextChoice(); - item->setChoice(NoteheadTextChoice::Choice::displayText); - isSuccess &= item->getDisplayText()->fromXElement(message, xelement); - - if (!isFirstItemAdded && myNoteheadTextChoiceSet.size() == 1) - { - *myNoteheadTextChoiceSet.begin() = item; - isFirstItemAdded = true; - } - else - { - myNoteheadTextChoiceSet.push_back(item); - isFirstItemAdded = true; - } + auto choice = makeNoteheadTextChoice(); + choice->setChoice(NoteheadTextChoice::Choice::accidentalText); + isSuccess &= choice->getAccidentalText()->fromXElement(message, *it); + myNoteheadTextChoiceSet.push_back(choice); + continue; } } diff --git a/src/private/mx/core/elements/NoteheadText.h b/src/private/mx/core/elements/NoteheadText.h index af10258f..ffe8c4c4 100644 --- a/src/private/mx/core/elements/NoteheadText.h +++ b/src/private/mx/core/elements/NoteheadText.h @@ -16,8 +16,6 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(AccidentalText) -MX_FORWARD_DECLARE_ELEMENT(DisplayText) MX_FORWARD_DECLARE_ELEMENT(NoteheadTextChoice) MX_FORWARD_DECLARE_ELEMENT(NoteheadText) @@ -37,7 +35,7 @@ class NoteheadText : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - /* _________ NoteheadTextChoice minOccurs = 1, maxOccurs = unbounded _________ */ + /* _________ NoteheadTextChoice minOccurs = 0, maxOccurs = unbounded _________ */ const NoteheadTextChoiceSet &getNoteheadTextChoiceSet() const; void addNoteheadTextChoice(const NoteheadTextChoicePtr &value); void removeNoteheadTextChoice(const NoteheadTextChoiceSetIterConst &value); diff --git a/src/private/mx/core/elements/NoteheadTextChoice.cpp b/src/private/mx/core/elements/NoteheadTextChoice.cpp index 22f0a91b..d462c9f6 100644 --- a/src/private/mx/core/elements/NoteheadTextChoice.cpp +++ b/src/private/mx/core/elements/NoteheadTextChoice.cpp @@ -29,7 +29,6 @@ std::ostream &NoteheadTextChoice::streamAttributes(std::ostream &os) const std::ostream &NoteheadTextChoice::streamName(std::ostream &os) const { - os << "notehead-text"; return os; } @@ -40,19 +39,15 @@ bool NoteheadTextChoice::hasContents() const std::ostream &NoteheadTextChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - MX_UNUSED(indentLevel); - MX_UNUSED(isOneLineOnly); - switch (myChoice) + if (myChoice == Choice::displayText) { - case Choice::displayText: myDisplayText->toStream(os, indentLevel); - break; - case Choice::accidentalText: + } + if (myChoice == Choice::accidentalText) + { myAccidentalText->toStream(os, indentLevel); - break; - default: - break; } + isOneLineOnly = false; return os; } @@ -61,7 +56,7 @@ NoteheadTextChoice::Choice NoteheadTextChoice::getChoice() const return myChoice; } -void NoteheadTextChoice::setChoice(NoteheadTextChoice::Choice value) +void NoteheadTextChoice::setChoice(const Choice value) { myChoice = value; } @@ -92,25 +87,7 @@ void NoteheadTextChoice::setAccidentalText(const AccidentalTextPtr &value) } } -bool NoteheadTextChoice::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - - if (xelement.getName() == "display-text") - { - myChoice = Choice::displayText; - return getDisplayText()->fromXElement(message, xelement); - } - - if (xelement.getName() == "accidental-text") - { - myChoice = Choice::accidentalText; - return getAccidentalText()->fromXElement(message, xelement); - } - - message << "NoteheadTextChoice: '" << xelement.getName() << "' is not a valid element" << std::endl; - - return false; -} +MX_FROM_XELEMENT_UNUSED(NoteheadTextChoice); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/NoteheadTextChoice.h b/src/private/mx/core/elements/NoteheadTextChoice.h index 1d092945..f9a2c9f4 100644 --- a/src/private/mx/core/elements/NoteheadTextChoice.h +++ b/src/private/mx/core/elements/NoteheadTextChoice.h @@ -16,8 +16,8 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(AccidentalText) MX_FORWARD_DECLARE_ELEMENT(DisplayText) +MX_FORWARD_DECLARE_ELEMENT(AccidentalText) MX_FORWARD_DECLARE_ELEMENT(NoteheadTextChoice) inline NoteheadTextChoicePtr makeNoteheadTextChoice() @@ -33,7 +33,6 @@ class NoteheadTextChoice : public ElementInterface displayText = 0, accidentalText = 1 }; - NoteheadTextChoice(); virtual bool hasAttributes() const; @@ -41,15 +40,10 @@ class NoteheadTextChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - - NoteheadTextChoice::Choice getChoice() const; - void setChoice(NoteheadTextChoice::Choice value); - - /* _________ DisplayText minOccurs = 1, maxOccurs = 1 _________ */ + Choice getChoice() const; + void setChoice(const Choice value); DisplayTextPtr getDisplayText() const; void setDisplayText(const DisplayTextPtr &value); - - /* _________ AccidentalText minOccurs = 1, maxOccurs = 1 _________ */ AccidentalTextPtr getAccidentalText() const; void setAccidentalText(const AccidentalTextPtr &value); diff --git a/src/private/mx/core/elements/OctaveChange.cpp b/src/private/mx/core/elements/OctaveChange.cpp index e20391a1..1d06cc9b 100644 --- a/src/private/mx/core/elements/OctaveChange.cpp +++ b/src/private/mx/core/elements/OctaveChange.cpp @@ -60,6 +60,7 @@ void OctaveChange::setValue(const Integer &value) bool OctaveChange::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); + MX_UNUSED(xelement); myValue.parse(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/OctaveShiftAttributes.cpp b/src/private/mx/core/elements/OctaveShiftAttributes.cpp index 0b2bed8a..39a532c8 100644 --- a/src/private/mx/core/elements/OctaveShiftAttributes.cpp +++ b/src/private/mx/core/elements/OctaveShiftAttributes.cpp @@ -11,18 +11,18 @@ namespace mx namespace core { OctaveShiftAttributes::OctaveShiftAttributes() - : type(UpDownStopContinue::up), number(), size(8), dashLength(), spaceLength(), defaultX(), defaultY(), relativeX(), - relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), - fontWeight(FontWeight::normal), hasType(true), hasNumber(false), hasSize(false), hasDashLength(false), - hasSpaceLength(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false) + : type(), number(), size(), dashLength(), spaceLength(), defaultX(), defaultY(), relativeX(), relativeY(), + fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + color(), hasType(true), hasNumber(false), hasSize(false), hasDashLength(false), hasSpaceLength(false), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool OctaveShiftAttributes::hasValues() const { return hasType || hasNumber || hasSize || hasDashLength || hasSpaceLength || hasDefaultX || hasDefaultY || - hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight; + hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &OctaveShiftAttributes::toStream(std::ostream &os) const @@ -42,6 +42,7 @@ std::ostream &OctaveShiftAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -110,15 +111,19 @@ bool OctaveShiftAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/OctaveShiftAttributes.h b/src/private/mx/core/elements/OctaveShiftAttributes.h index 9ba4bafe..ad9cb57f 100644 --- a/src/private/mx/core/elements/OctaveShiftAttributes.h +++ b/src/private/mx/core/elements/OctaveShiftAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -42,6 +43,7 @@ struct OctaveShiftAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; const bool hasType; bool hasNumber; bool hasSize; @@ -55,6 +57,7 @@ struct OctaveShiftAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/OffsetAttributes.cpp b/src/private/mx/core/elements/OffsetAttributes.cpp index a0ec470f..587f7766 100644 --- a/src/private/mx/core/elements/OffsetAttributes.cpp +++ b/src/private/mx/core/elements/OffsetAttributes.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -OffsetAttributes::OffsetAttributes() : sound(), hasSound(false) +OffsetAttributes::OffsetAttributes() : sound(YesNo::no), hasSound(false) { } diff --git a/src/private/mx/core/elements/OpusAttributes.cpp b/src/private/mx/core/elements/OpusAttributes.cpp index 877531c3..97eee050 100644 --- a/src/private/mx/core/elements/OpusAttributes.cpp +++ b/src/private/mx/core/elements/OpusAttributes.cpp @@ -11,8 +11,8 @@ namespace mx namespace core { OpusAttributes::OpusAttributes() - : href(), type(XlinkType::simple), role(), title(), show(XlinkShow::replace), actuate(XlinkActuate::onRequest), - hasHref(true), hasType(false), hasRole(false), hasTitle(false), hasShow(false), hasActuate(false) + : href(), type(), role(), title(), show(), actuate(), hasHref(true), hasType(false), hasRole(false), + hasTitle(false), hasShow(false), hasActuate(false) { } @@ -46,30 +46,27 @@ bool OpusAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & for (; it != endIter; ++it) { - if (parseAttribute(message, it, className, isSuccess, href, isHrefFound, "href")) + if (parseAttribute(message, it, className, isSuccess, href, isHrefFound, "xlink:href")) { continue; } - - if (it->getName() == "xlink:type") + if (parseAttribute(message, it, className, isSuccess, type, hasType, "xlink:type", &parseXlinkType)) { - hasType = true; continue; } - - if (parseAttribute(message, it, className, isSuccess, role, hasRole, "role")) + if (parseAttribute(message, it, className, isSuccess, role, hasRole, "xlink:role")) { continue; } - if (parseAttribute(message, it, className, isSuccess, title, hasTitle, "title")) + if (parseAttribute(message, it, className, isSuccess, title, hasTitle, "xlink:title")) { continue; } - if (parseAttribute(message, it, className, isSuccess, show, hasShow, "show", &parseXlinkShow)) + if (parseAttribute(message, it, className, isSuccess, show, hasShow, "xlink:show", &parseXlinkShow)) { continue; } - if (parseAttribute(message, it, className, isSuccess, actuate, hasActuate, "actuate", &parseXlinkActuate)) + if (parseAttribute(message, it, className, isSuccess, actuate, hasActuate, "xlink:actuate", &parseXlinkActuate)) { continue; } @@ -78,10 +75,10 @@ bool OpusAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & if (!isHrefFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'xlink:href' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/OpusAttributes.h b/src/private/mx/core/elements/OpusAttributes.h index c52693c7..80f138ed 100644 --- a/src/private/mx/core/elements/OpusAttributes.h +++ b/src/private/mx/core/elements/OpusAttributes.h @@ -29,7 +29,7 @@ struct OpusAttributes : public AttributesInterface virtual bool hasValues() const; virtual std::ostream &toStream(std::ostream &os) const; XlinkHref href; - const XlinkType type; + XlinkType type; XlinkRole role; XlinkTitle title; XlinkShow show; diff --git a/src/private/mx/core/elements/Ornaments.cpp b/src/private/mx/core/elements/Ornaments.cpp index 73cf281d..61af4f12 100644 --- a/src/private/mx/core/elements/Ornaments.cpp +++ b/src/private/mx/core/elements/Ornaments.cpp @@ -5,8 +5,20 @@ #include "mx/core/elements/Ornaments.h" #include "mx/core/FromXElement.h" #include "mx/core/elements/AccidentalMark.h" +#include "mx/core/elements/DelayedInvertedTurn.h" +#include "mx/core/elements/DelayedTurn.h" +#include "mx/core/elements/InvertedMordent.h" +#include "mx/core/elements/InvertedTurn.h" +#include "mx/core/elements/Mordent.h" #include "mx/core/elements/OrnamentsChoice.h" -#include +#include "mx/core/elements/OtherOrnament.h" +#include "mx/core/elements/Schleifer.h" +#include "mx/core/elements/Shake.h" +#include "mx/core/elements/Tremolo.h" +#include "mx/core/elements/TrillMark.h" +#include "mx/core/elements/Turn.h" +#include "mx/core/elements/VerticalTurn.h" +#include "mx/core/elements/WavyLine.h" #include namespace mx @@ -35,7 +47,7 @@ std::ostream &Ornaments::streamName(std::ostream &os) const bool Ornaments::hasContents() const { - return true; + return myOrnamentsChoiceSet.size() > 0 || myAccidentalMarkSet.size() > 0; } std::ostream &Ornaments::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const @@ -60,19 +72,19 @@ const OrnamentsChoiceSet &Ornaments::getOrnamentsChoiceSet() const return myOrnamentsChoiceSet; } -void Ornaments::removeOrnamentsChoice(const OrnamentsChoiceSetIterConst &value) +void Ornaments::addOrnamentsChoice(const OrnamentsChoicePtr &value) { - if (value != myOrnamentsChoiceSet.cend()) + if (value) { - myOrnamentsChoiceSet.erase(value); + myOrnamentsChoiceSet.push_back(value); } } -void Ornaments::addOrnamentsChoice(const OrnamentsChoicePtr &value) +void Ornaments::removeOrnamentsChoice(const OrnamentsChoiceSetIterConst &value) { - if (value) + if (value != myOrnamentsChoiceSet.cend()) { - myOrnamentsChoiceSet.push_back(value); + myOrnamentsChoiceSet.erase(value); } } @@ -95,19 +107,19 @@ const AccidentalMarkSet &Ornaments::getAccidentalMarkSet() const return myAccidentalMarkSet; } -void Ornaments::removeAccidentalMark(const AccidentalMarkSetIterConst &value) +void Ornaments::addAccidentalMark(const AccidentalMarkPtr &value) { - if (value != myAccidentalMarkSet.cend()) + if (value) { - myAccidentalMarkSet.erase(value); + myAccidentalMarkSet.push_back(value); } } -void Ornaments::addAccidentalMark(const AccidentalMarkPtr &value) +void Ornaments::removeAccidentalMark(const AccidentalMarkSetIterConst &value) { - if (value) + if (value != myAccidentalMarkSet.cend()) { - myAccidentalMarkSet.push_back(value); + myAccidentalMarkSet.erase(value); } } @@ -125,13 +137,6 @@ AccidentalMarkPtr Ornaments::getAccidentalMark(const AccidentalMarkSetIterConst return AccidentalMarkPtr(); } -constexpr const char *const ornamentsChoices[] = { - "trill-mark", "turn", "delayed-turn", "inverted-turn", "delayed-inverted-turn", - "vertical-turn", "shake", "wavy-line", "mordent", "inverted-mordent", - "schleifer", "tremolo", "other-ornament"}; - -constexpr size_t ornamentsChoicesSize = 13; - bool Ornaments::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { bool isSuccess = true; @@ -139,14 +144,110 @@ bool Ornaments::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem auto endIter = xelement.end(); for (auto it = xelement.begin(); it != endIter; ++it) { - if (isOrnamentsChoice(it->getName().c_str())) + if (it->getName() == "trill-mark") { - auto item = makeOrnamentsChoice(); - isSuccess &= item->fromXElement(message, *it); - addOrnamentsChoice(item); + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::trillMark); + isSuccess &= choice->getTrillMark()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "turn") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::turn); + isSuccess &= choice->getTurn()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "delayed-turn") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::delayedTurn); + isSuccess &= choice->getDelayedTurn()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "inverted-turn") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::invertedTurn); + isSuccess &= choice->getInvertedTurn()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "delayed-inverted-turn") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::delayedInvertedTurn); + isSuccess &= choice->getDelayedInvertedTurn()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "vertical-turn") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::verticalTurn); + isSuccess &= choice->getVerticalTurn()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "shake") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::shake); + isSuccess &= choice->getShake()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "wavy-line") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::wavyLine); + isSuccess &= choice->getWavyLine()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "mordent") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::mordent); + isSuccess &= choice->getMordent()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "inverted-mordent") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::invertedMordent); + isSuccess &= choice->getInvertedMordent()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "schleifer") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::schleifer); + isSuccess &= choice->getSchleifer()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "tremolo") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::tremolo); + isSuccess &= choice->getTremolo()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); + continue; + } + if (it->getName() == "other-ornament") + { + auto choice = makeOrnamentsChoice(); + choice->setChoice(OrnamentsChoice::Choice::otherOrnament); + isSuccess &= choice->getOtherOrnament()->fromXElement(message, *it); + myOrnamentsChoiceSet.push_back(choice); continue; } - if (importElementSet(message, it, endIter, isSuccess, "accidental-mark", myAccidentalMarkSet)) { continue; @@ -156,17 +257,5 @@ bool Ornaments::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem MX_RETURN_IS_SUCCESS; } -bool Ornaments::isOrnamentsChoice(const char *const name) const -{ - for (size_t i = 0; i < ornamentsChoicesSize; ++i) - { - if (strcmp(name, ornamentsChoices[i]) == 0) - { - return true; - } - } - return false; -} - } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/Ornaments.h b/src/private/mx/core/elements/Ornaments.h index 9cdfe7bf..25e80c35 100644 --- a/src/private/mx/core/elements/Ornaments.h +++ b/src/private/mx/core/elements/Ornaments.h @@ -16,8 +16,8 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(AccidentalMark) MX_FORWARD_DECLARE_ELEMENT(OrnamentsChoice) +MX_FORWARD_DECLARE_ELEMENT(AccidentalMark) MX_FORWARD_DECLARE_ELEMENT(Ornaments) inline OrnamentsPtr makeOrnaments() @@ -56,8 +56,6 @@ class Ornaments : public ElementInterface private: OrnamentsChoiceSet myOrnamentsChoiceSet; AccidentalMarkSet myAccidentalMarkSet; - - bool isOrnamentsChoice(const char *const name) const; }; } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/OrnamentsChoice.cpp b/src/private/mx/core/elements/OrnamentsChoice.cpp index 1a73cdc8..842d1c89 100644 --- a/src/private/mx/core/elements/OrnamentsChoice.cpp +++ b/src/private/mx/core/elements/OrnamentsChoice.cpp @@ -54,65 +54,59 @@ bool OrnamentsChoice::hasContents() const std::ostream &OrnamentsChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - MX_UNUSED(isOneLineOnly); - - switch (myChoice) + if (myChoice == Choice::trillMark) { - case Choice::trillMark: { myTrillMark->toStream(os, indentLevel); } - break; - case Choice::turn: { + if (myChoice == Choice::turn) + { myTurn->toStream(os, indentLevel); } - break; - case Choice::delayedTurn: { + if (myChoice == Choice::delayedTurn) + { myDelayedTurn->toStream(os, indentLevel); } - break; - case Choice::invertedTurn: { + if (myChoice == Choice::invertedTurn) + { myInvertedTurn->toStream(os, indentLevel); } - break; - case Choice::delayedInvertedTurn: { + if (myChoice == Choice::delayedInvertedTurn) + { myDelayedInvertedTurn->toStream(os, indentLevel); } - break; - case Choice::verticalTurn: { + if (myChoice == Choice::verticalTurn) + { myVerticalTurn->toStream(os, indentLevel); } - break; - case Choice::shake: { + if (myChoice == Choice::shake) + { myShake->toStream(os, indentLevel); } - break; - case Choice::wavyLine: { + if (myChoice == Choice::wavyLine) + { myWavyLine->toStream(os, indentLevel); } - break; - case Choice::mordent: { + if (myChoice == Choice::mordent) + { myMordent->toStream(os, indentLevel); } - break; - case Choice::invertedMordent: { + if (myChoice == Choice::invertedMordent) + { myInvertedMordent->toStream(os, indentLevel); } - break; - case Choice::schleifer: { + if (myChoice == Choice::schleifer) + { mySchleifer->toStream(os, indentLevel); } - break; - case Choice::tremolo: { + if (myChoice == Choice::tremolo) + { myTremolo->toStream(os, indentLevel); } - break; - case Choice::otherOrnament: { + if (myChoice == Choice::otherOrnament) + { myOtherOrnament->toStream(os, indentLevel); } - break; - default: - break; - } + isOneLineOnly = false; return os; } @@ -121,7 +115,7 @@ OrnamentsChoice::Choice OrnamentsChoice::getChoice() const return myChoice; } -void OrnamentsChoice::setChoice(const OrnamentsChoice::Choice value) +void OrnamentsChoice::setChoice(const Choice value) { myChoice = value; } @@ -295,85 +289,7 @@ void OrnamentsChoice::setOtherOrnament(const OtherOrnamentPtr &value) } } -bool OrnamentsChoice::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - bool isSuccess = true; - - const std::string elementName = xelement.getName(); - - if (elementName == "trill-mark") - { - myChoice = Choice::trillMark; - isSuccess &= getTrillMark()->fromXElement(message, xelement); - } - else if (elementName == "turn") - { - myChoice = Choice::turn; - isSuccess &= getTurn()->fromXElement(message, xelement); - } - else if (elementName == "delayed-turn") - { - myChoice = Choice::delayedTurn; - isSuccess &= getDelayedTurn()->fromXElement(message, xelement); - } - else if (elementName == "inverted-turn") - { - myChoice = Choice::invertedTurn; - isSuccess &= getInvertedTurn()->fromXElement(message, xelement); - } - else if (elementName == "delayed-inverted-turn") - { - myChoice = Choice::delayedInvertedTurn; - isSuccess &= getDelayedInvertedTurn()->fromXElement(message, xelement); - } - else if (elementName == "vertical-turn") - { - myChoice = Choice::verticalTurn; - isSuccess &= getVerticalTurn()->fromXElement(message, xelement); - } - else if (elementName == "shake") - { - myChoice = Choice::shake; - isSuccess &= getShake()->fromXElement(message, xelement); - } - else if (elementName == "wavy-line") - { - myChoice = Choice::wavyLine; - isSuccess &= getWavyLine()->fromXElement(message, xelement); - } - else if (elementName == "mordent") - { - myChoice = Choice::mordent; - isSuccess &= getMordent()->fromXElement(message, xelement); - } - else if (elementName == "inverted-mordent") - { - myChoice = Choice::invertedMordent; - isSuccess &= getInvertedMordent()->fromXElement(message, xelement); - } - else if (elementName == "schleifer") - { - myChoice = Choice::schleifer; - isSuccess &= getSchleifer()->fromXElement(message, xelement); - } - else if (elementName == "tremolo") - { - myChoice = Choice::tremolo; - isSuccess &= getTremolo()->fromXElement(message, xelement); - } - else if (elementName == "other-ornament") - { - myChoice = Choice::otherOrnament; - isSuccess &= getOtherOrnament()->fromXElement(message, xelement); - } - else - { - message << "OrnamentsChoice::fromXElement unrecognized element '" << elementName << "'" << std::endl; - isSuccess = false; - } - - MX_RETURN_IS_SUCCESS; -} +MX_FROM_XELEMENT_UNUSED(OrnamentsChoice); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/OrnamentsChoice.h b/src/private/mx/core/elements/OrnamentsChoice.h index a478d0f9..17a818df 100644 --- a/src/private/mx/core/elements/OrnamentsChoice.h +++ b/src/private/mx/core/elements/OrnamentsChoice.h @@ -16,19 +16,19 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(DelayedInvertedTurn) +MX_FORWARD_DECLARE_ELEMENT(TrillMark) +MX_FORWARD_DECLARE_ELEMENT(Turn) MX_FORWARD_DECLARE_ELEMENT(DelayedTurn) -MX_FORWARD_DECLARE_ELEMENT(InvertedMordent) MX_FORWARD_DECLARE_ELEMENT(InvertedTurn) +MX_FORWARD_DECLARE_ELEMENT(DelayedInvertedTurn) +MX_FORWARD_DECLARE_ELEMENT(VerticalTurn) +MX_FORWARD_DECLARE_ELEMENT(Shake) +MX_FORWARD_DECLARE_ELEMENT(WavyLine) MX_FORWARD_DECLARE_ELEMENT(Mordent) -MX_FORWARD_DECLARE_ELEMENT(OtherOrnament) +MX_FORWARD_DECLARE_ELEMENT(InvertedMordent) MX_FORWARD_DECLARE_ELEMENT(Schleifer) -MX_FORWARD_DECLARE_ELEMENT(Shake) MX_FORWARD_DECLARE_ELEMENT(Tremolo) -MX_FORWARD_DECLARE_ELEMENT(TrillMark) -MX_FORWARD_DECLARE_ELEMENT(Turn) -MX_FORWARD_DECLARE_ELEMENT(VerticalTurn) -MX_FORWARD_DECLARE_ELEMENT(WavyLine) +MX_FORWARD_DECLARE_ELEMENT(OtherOrnament) MX_FORWARD_DECLARE_ELEMENT(OrnamentsChoice) inline OrnamentsChoicePtr makeOrnamentsChoice() @@ -41,19 +41,19 @@ class OrnamentsChoice : public ElementInterface public: enum class Choice { - trillMark = 1, - turn = 2, - delayedTurn = 3, - invertedTurn = 4, - delayedInvertedTurn = 5, - verticalTurn = 6, - shake = 7, - wavyLine = 8, - mordent = 9, - invertedMordent = 10, - schleifer = 11, - tremolo = 12, - otherOrnament = 13 + trillMark = 0, + turn = 1, + delayedTurn = 2, + invertedTurn = 3, + delayedInvertedTurn = 4, + verticalTurn = 5, + shake = 6, + wavyLine = 7, + mordent = 8, + invertedMordent = 9, + schleifer = 10, + tremolo = 11, + otherOrnament = 12 }; OrnamentsChoice(); @@ -62,60 +62,32 @@ class OrnamentsChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - - /* _________ Choice enum _________ */ - OrnamentsChoice::Choice getChoice() const; - void setChoice(const OrnamentsChoice::Choice value); - - /* _________ TrillMark minOccurs = 1, maxOccurs = 1 _________ */ + Choice getChoice() const; + void setChoice(const Choice value); TrillMarkPtr getTrillMark() const; void setTrillMark(const TrillMarkPtr &value); - - /* _________ Turn minOccurs = 1, maxOccurs = 1 _________ */ TurnPtr getTurn() const; void setTurn(const TurnPtr &value); - - /* _________ DelayedTurn minOccurs = 1, maxOccurs = 1 _________ */ DelayedTurnPtr getDelayedTurn() const; void setDelayedTurn(const DelayedTurnPtr &value); - - /* _________ InvertedTurn minOccurs = 1, maxOccurs = 1 _________ */ InvertedTurnPtr getInvertedTurn() const; void setInvertedTurn(const InvertedTurnPtr &value); - - /* _________ DelayedInvertedTurn minOccurs = 1, maxOccurs = 1 _________ */ DelayedInvertedTurnPtr getDelayedInvertedTurn() const; void setDelayedInvertedTurn(const DelayedInvertedTurnPtr &value); - - /* _________ VerticalTurn minOccurs = 1, maxOccurs = 1 _________ */ VerticalTurnPtr getVerticalTurn() const; void setVerticalTurn(const VerticalTurnPtr &value); - - /* _________ Shake minOccurs = 1, maxOccurs = 1 _________ */ ShakePtr getShake() const; void setShake(const ShakePtr &value); - - /* _________ WavyLine minOccurs = 1, maxOccurs = 1 _________ */ WavyLinePtr getWavyLine() const; void setWavyLine(const WavyLinePtr &value); - - /* _________ Mordent minOccurs = 1, maxOccurs = 1 _________ */ MordentPtr getMordent() const; void setMordent(const MordentPtr &value); - - /* _________ InvertedMordent minOccurs = 1, maxOccurs = 1 _________ */ InvertedMordentPtr getInvertedMordent() const; void setInvertedMordent(const InvertedMordentPtr &value); - - /* _________ Schleifer minOccurs = 1, maxOccurs = 1 _________ */ SchleiferPtr getSchleifer() const; void setSchleifer(const SchleiferPtr &value); - - /* _________ Tremolo minOccurs = 1, maxOccurs = 1 _________ */ TremoloPtr getTremolo() const; void setTremolo(const TremoloPtr &value); - - /* _________ OtherOrnament minOccurs = 1, maxOccurs = 1 _________ */ OtherOrnamentPtr getOtherOrnament() const; void setOtherOrnament(const OtherOrnamentPtr &value); diff --git a/src/private/mx/core/elements/OtherAppearanceAttributes.cpp b/src/private/mx/core/elements/OtherAppearanceAttributes.cpp index 8d61f080..2e50a84d 100644 --- a/src/private/mx/core/elements/OtherAppearanceAttributes.cpp +++ b/src/private/mx/core/elements/OtherAppearanceAttributes.cpp @@ -48,7 +48,7 @@ bool OtherAppearanceAttributes::fromXElementImpl(std::ostream &message, ::ezxml: if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/OtherArticulationAttributes.cpp b/src/private/mx/core/elements/OtherArticulationAttributes.cpp index 567c87de..24800150 100644 --- a/src/private/mx/core/elements/OtherArticulationAttributes.cpp +++ b/src/private/mx/core/elements/OtherArticulationAttributes.cpp @@ -12,16 +12,16 @@ namespace core { OtherArticulationAttributes::OtherArticulationAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool OtherArticulationAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &OtherArticulationAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &OtherArticulationAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,6 +85,10 @@ bool OtherArticulationAttributes::fromXElementImpl(std::ostream &message, ::ezxm { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/OtherArticulationAttributes.h b/src/private/mx/core/elements/OtherArticulationAttributes.h index 8bc98c16..8e0f19d3 100644 --- a/src/private/mx/core/elements/OtherArticulationAttributes.h +++ b/src/private/mx/core/elements/OtherArticulationAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct OtherArticulationAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct OtherArticulationAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/OtherDirectionAttributes.cpp b/src/private/mx/core/elements/OtherDirectionAttributes.cpp index 0fc41548..4c46a1ab 100644 --- a/src/private/mx/core/elements/OtherDirectionAttributes.cpp +++ b/src/private/mx/core/elements/OtherDirectionAttributes.cpp @@ -11,17 +11,18 @@ namespace mx namespace core { OtherDirectionAttributes::OtherDirectionAttributes() - : printObject(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), halign(), hasPrintObject(false), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasHalign(false) + : printObject(YesNo::no), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + halign(LeftCenterRight::left), valign(Valign::bottom), hasPrintObject(false), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), + hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false) { } bool OtherDirectionAttributes::hasValues() const { return hasPrintObject || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || - hasFontStyle || hasFontSize || hasFontWeight || hasHalign; + hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign; } std::ostream &OtherDirectionAttributes::toStream(std::ostream &os) const @@ -37,7 +38,9 @@ std::ostream &OtherDirectionAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); } return os; } @@ -89,10 +92,18 @@ bool OtherDirectionAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/OtherDirectionAttributes.h b/src/private/mx/core/elements/OtherDirectionAttributes.h index 51a85e93..41aee7a9 100644 --- a/src/private/mx/core/elements/OtherDirectionAttributes.h +++ b/src/private/mx/core/elements/OtherDirectionAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -37,7 +38,9 @@ struct OtherDirectionAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; bool hasPrintObject; bool hasDefaultX; bool hasDefaultY; @@ -47,7 +50,9 @@ struct OtherDirectionAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/OtherNotationAttributes.cpp b/src/private/mx/core/elements/OtherNotationAttributes.cpp index 1d8adf42..d994448f 100644 --- a/src/private/mx/core/elements/OtherNotationAttributes.cpp +++ b/src/private/mx/core/elements/OtherNotationAttributes.cpp @@ -11,18 +11,18 @@ namespace mx namespace core { OtherNotationAttributes::OtherNotationAttributes() - : type(StartStopSingle::start), number(1), printObject(), defaultX(), defaultY(), relativeX(), relativeY(), - fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), placement(), hasType(true), hasNumber(false), hasPrintObject(false), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasPlacement(false) + : type(StartStopSingle::start), number(), printObject(YesNo::no), defaultX(), defaultY(), relativeX(), relativeY(), + fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + color(), placement(AboveBelow::below), hasType(true), hasNumber(false), hasPrintObject(false), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), + hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool OtherNotationAttributes::hasValues() const { return hasType || hasNumber || hasPrintObject || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || - hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasPlacement; + hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasPlacement; } std::ostream &OtherNotationAttributes::toStream(std::ostream &os) const @@ -40,6 +40,7 @@ std::ostream &OtherNotationAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -101,6 +102,10 @@ bool OtherNotationAttributes::fromXElementImpl(std::ostream &message, ::ezxml::X { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; @@ -110,7 +115,7 @@ bool OtherNotationAttributes::fromXElementImpl(std::ostream &message, ::ezxml::X if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/OtherNotationAttributes.h b/src/private/mx/core/elements/OtherNotationAttributes.h index 8c4051bb..36825dec 100644 --- a/src/private/mx/core/elements/OtherNotationAttributes.h +++ b/src/private/mx/core/elements/OtherNotationAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -40,6 +41,7 @@ struct OtherNotationAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; const bool hasType; bool hasNumber; @@ -52,6 +54,7 @@ struct OtherNotationAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/OtherOrnamentAttributes.cpp b/src/private/mx/core/elements/OtherOrnamentAttributes.cpp index c3057a3b..7a5a50db 100644 --- a/src/private/mx/core/elements/OtherOrnamentAttributes.cpp +++ b/src/private/mx/core/elements/OtherOrnamentAttributes.cpp @@ -12,16 +12,16 @@ namespace core { OtherOrnamentAttributes::OtherOrnamentAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::above), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool OtherOrnamentAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &OtherOrnamentAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &OtherOrnamentAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,6 +85,10 @@ bool OtherOrnamentAttributes::fromXElementImpl(std::ostream &message, ::ezxml::X { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/OtherOrnamentAttributes.h b/src/private/mx/core/elements/OtherOrnamentAttributes.h index 025a4176..b2de386d 100644 --- a/src/private/mx/core/elements/OtherOrnamentAttributes.h +++ b/src/private/mx/core/elements/OtherOrnamentAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct OtherOrnamentAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct OtherOrnamentAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/OtherPlay.cpp b/src/private/mx/core/elements/OtherPlay.cpp index 7a37e89e..9f7f25ac 100644 --- a/src/private/mx/core/elements/OtherPlay.cpp +++ b/src/private/mx/core/elements/OtherPlay.cpp @@ -79,7 +79,7 @@ bool OtherPlay::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.setValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/OtherPlayAttributes.cpp b/src/private/mx/core/elements/OtherPlayAttributes.cpp index 6ca53512..8bc1e6b5 100644 --- a/src/private/mx/core/elements/OtherPlayAttributes.cpp +++ b/src/private/mx/core/elements/OtherPlayAttributes.cpp @@ -48,10 +48,10 @@ bool OtherPlayAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/OtherPlayAttributes.h b/src/private/mx/core/elements/OtherPlayAttributes.h index 2c93213f..a9e34387 100644 --- a/src/private/mx/core/elements/OtherPlayAttributes.h +++ b/src/private/mx/core/elements/OtherPlayAttributes.h @@ -5,8 +5,8 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsToken.h" #include #include diff --git a/src/private/mx/core/elements/OtherTechnicalAttributes.cpp b/src/private/mx/core/elements/OtherTechnicalAttributes.cpp index 2dc2ed45..f7fffe7f 100644 --- a/src/private/mx/core/elements/OtherTechnicalAttributes.cpp +++ b/src/private/mx/core/elements/OtherTechnicalAttributes.cpp @@ -12,16 +12,16 @@ namespace core { OtherTechnicalAttributes::OtherTechnicalAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::above), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool OtherTechnicalAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &OtherTechnicalAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &OtherTechnicalAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,13 +85,17 @@ bool OtherTechnicalAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/OtherTechnicalAttributes.h b/src/private/mx/core/elements/OtherTechnicalAttributes.h index 156a2bbe..b110827c 100644 --- a/src/private/mx/core/elements/OtherTechnicalAttributes.h +++ b/src/private/mx/core/elements/OtherTechnicalAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct OtherTechnicalAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct OtherTechnicalAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/PageLayout.cpp b/src/private/mx/core/elements/PageLayout.cpp index 364fcb72..bb7d67df 100644 --- a/src/private/mx/core/elements/PageLayout.cpp +++ b/src/private/mx/core/elements/PageLayout.cpp @@ -40,6 +40,7 @@ bool PageLayout::hasContents() const std::ostream &PageLayout::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myPageHeight->toStream(os, indentLevel + 1); os << std::endl; @@ -50,7 +51,6 @@ std::ostream &PageLayout::streamContents(std::ostream &os, const int indentLevel x->toStream(os, indentLevel + 1); } os << std::endl; - isOneLineOnly = false; return os; } @@ -97,10 +97,7 @@ void PageLayout::addPageMargins(const PageMarginsPtr &value) { if (value) { - if (myPageMarginsSet.size() < 2) - { - myPageMarginsSet.push_back(value); - } + myPageMarginsSet.push_back(value); } } @@ -138,14 +135,6 @@ bool PageLayout::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xele importElementSet(message, it, endIter, isSuccess, "page-margins", myPageMarginsSet); } - if (!isPageHeightFound) - { - message << "PageLayout: '" << myPageHeight->getElementName() << "' is required but was not found" << std::endl; - } - if (!isPageWidthFound) - { - message << "PageLayout: '" << myPageWidth->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/PageLayout.h b/src/private/mx/core/elements/PageLayout.h index 399f453a..90db3dde 100644 --- a/src/private/mx/core/elements/PageLayout.h +++ b/src/private/mx/core/elements/PageLayout.h @@ -45,7 +45,7 @@ class PageLayout : public ElementInterface PageWidthPtr getPageWidth() const; void setPageWidth(const PageWidthPtr &value); - /* _________ PageMargins minOccurs = 0, maxOccurs = 2 _________ */ + /* _________ PageMargins minOccurs = 0, maxOccurs = unbounded _________ */ const PageMarginsSet &getPageMarginsSet() const; void addPageMargins(const PageMarginsPtr &value); void removePageMargins(const PageMarginsSetIterConst &value); diff --git a/src/private/mx/core/elements/PageMargins.cpp b/src/private/mx/core/elements/PageMargins.cpp index 97a80e01..4816b1ac 100644 --- a/src/private/mx/core/elements/PageMargins.cpp +++ b/src/private/mx/core/elements/PageMargins.cpp @@ -151,24 +151,6 @@ bool PageMargins::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xel } } - if (!isLeftMarginFound) - { - message << "PageMargins: '" << myLeftMargin->getElementName() << "' is required but was not found" << std::endl; - } - if (!isRightMarginFound) - { - message << "PageMargins: '" << myRightMargin->getElementName() << "' is required but was not found" - << std::endl; - } - if (!isTopMarginFound) - { - message << "PageMargins: '" << myTopMargin->getElementName() << "' is required but was not found" << std::endl; - } - if (!isBottomMarginFound) - { - message << "PageMargins: '" << myBottomMargin->getElementName() << "' is required but was not found" - << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/PageMargins.h b/src/private/mx/core/elements/PageMargins.h index ba10f1e1..da5f21c8 100644 --- a/src/private/mx/core/elements/PageMargins.h +++ b/src/private/mx/core/elements/PageMargins.h @@ -40,7 +40,7 @@ class PageMargins : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; PageMarginsAttributesPtr getAttributes() const; - void setAttributes(const PageMarginsAttributesPtr &value); + void setAttributes(const PageMarginsAttributesPtr &attributes); /* _________ LeftMargin minOccurs = 1, maxOccurs = 1 _________ */ LeftMarginPtr getLeftMargin() const; diff --git a/src/private/mx/core/elements/PageMarginsAttributes.cpp b/src/private/mx/core/elements/PageMarginsAttributes.cpp index b3f315db..dd970bd9 100644 --- a/src/private/mx/core/elements/PageMarginsAttributes.cpp +++ b/src/private/mx/core/elements/PageMarginsAttributes.cpp @@ -44,7 +44,7 @@ bool PageMarginsAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/PartAbbreviation.cpp b/src/private/mx/core/elements/PartAbbreviation.cpp index e2c46fc5..a24fcc98 100644 --- a/src/private/mx/core/elements/PartAbbreviation.cpp +++ b/src/private/mx/core/elements/PartAbbreviation.cpp @@ -80,7 +80,7 @@ bool PartAbbreviation::fromXElementImpl(std::ostream &message, ::ezxml::XElement bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.setValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/PartAbbreviationAttributes.cpp b/src/private/mx/core/elements/PartAbbreviationAttributes.cpp index 0168e1e0..c6633db2 100644 --- a/src/private/mx/core/elements/PartAbbreviationAttributes.cpp +++ b/src/private/mx/core/elements/PartAbbreviationAttributes.cpp @@ -12,9 +12,9 @@ namespace core { PartAbbreviationAttributes::PartAbbreviationAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), printObject(), - justify(LeftCenterRight::center), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), printObject(YesNo::no), + justify(LeftCenterRight::left), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPrintObject(false), hasJustify(false) { } @@ -22,7 +22,7 @@ PartAbbreviationAttributes::PartAbbreviationAttributes() bool PartAbbreviationAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPrintObject || hasJustify; + hasFontWeight || hasColor || hasPrintObject || hasJustify; } std::ostream &PartAbbreviationAttributes::toStream(std::ostream &os) const @@ -37,6 +37,7 @@ std::ostream &PartAbbreviationAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, printObject, "print-object", hasPrintObject); streamAttribute(os, justify, "justify", hasJustify); } @@ -86,6 +87,10 @@ bool PartAbbreviationAttributes::fromXElementImpl(std::ostream &message, ::ezxml { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, printObject, hasPrintObject, "print-object", &parseYesNo)) { continue; diff --git a/src/private/mx/core/elements/PartAbbreviationAttributes.h b/src/private/mx/core/elements/PartAbbreviationAttributes.h index b4b8f5af..8ce00ce5 100644 --- a/src/private/mx/core/elements/PartAbbreviationAttributes.h +++ b/src/private/mx/core/elements/PartAbbreviationAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct PartAbbreviationAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; YesNo printObject; LeftCenterRight justify; bool hasDefaultX; @@ -46,6 +48,7 @@ struct PartAbbreviationAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPrintObject; bool hasJustify; diff --git a/src/private/mx/core/elements/PartAbbreviationDisplay.cpp b/src/private/mx/core/elements/PartAbbreviationDisplay.cpp index 107ce12f..eaa884cb 100644 --- a/src/private/mx/core/elements/PartAbbreviationDisplay.cpp +++ b/src/private/mx/core/elements/PartAbbreviationDisplay.cpp @@ -4,6 +4,8 @@ #include "mx/core/elements/PartAbbreviationDisplay.h" #include "mx/core/FromXElement.h" +#include "mx/core/elements/AccidentalText.h" +#include "mx/core/elements/DisplayText.h" #include "mx/core/elements/DisplayTextOrAccidentalText.h" #include @@ -40,20 +42,13 @@ bool PartAbbreviationDisplay::hasContents() const std::ostream &PartAbbreviationDisplay::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - if (hasContents()) + for (auto x : myDisplayTextOrAccidentalTextSet) { - for (auto x : myDisplayTextOrAccidentalTextSet) - { - os << std::endl; - x->streamContents(os, indentLevel + 1, isOneLineOnly); - } - isOneLineOnly = !hasContents(); os << std::endl; + x->streamContents(os, indentLevel + 1, isOneLineOnly); } - else - { - isOneLineOnly = true; - } + isOneLineOnly = false; + os << std::endl; return os; } @@ -70,7 +65,7 @@ void PartAbbreviationDisplay::setAttributes(const PartAbbreviationDisplayAttribu } } -const DisplayTextOrAccidentalTextSet &PartAbbreviationDisplay::getDisplayTextOrAccidentalText() const +const DisplayTextOrAccidentalTextSet &PartAbbreviationDisplay::getDisplayTextOrAccidentalTextSet() const { return myDisplayTextOrAccidentalTextSet; } @@ -83,12 +78,11 @@ void PartAbbreviationDisplay::addDisplayTextOrAccidentalText(const DisplayTextOr } } -void PartAbbreviationDisplay::removeDisplayTextOrAccidentalText( - const DisplayTextOrAccidentalTextSetIterConst &setIterator) +void PartAbbreviationDisplay::removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &value) { - if (setIterator != myDisplayTextOrAccidentalTextSet.cend()) + if (value != myDisplayTextOrAccidentalTextSet.cend()) { - myDisplayTextOrAccidentalTextSet.erase(setIterator); + myDisplayTextOrAccidentalTextSet.erase(value); } } @@ -104,12 +98,7 @@ DisplayTextOrAccidentalTextPtr PartAbbreviationDisplay::getDisplayTextOrAccident { return *setIterator; } - return makeDisplayTextOrAccidentalText(); -} - -const DisplayTextOrAccidentalTextSet &PartAbbreviationDisplay::getDisplayTextOrAccidentalTextSet() const -{ - return myDisplayTextOrAccidentalTextSet; + return DisplayTextOrAccidentalTextPtr(); } bool PartAbbreviationDisplay::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) @@ -120,9 +109,22 @@ bool PartAbbreviationDisplay::fromXElementImpl(std::ostream &message, ::ezxml::X auto endIter = xelement.end(); for (auto it = xelement.begin(); it != endIter; ++it) { - auto item = makeDisplayTextOrAccidentalText(); - isSuccess &= item->fromXElement(message, *it); - myDisplayTextOrAccidentalTextSet.push_back(item); + if (it->getName() == "display-text") + { + auto choice = makeDisplayTextOrAccidentalText(); + choice->setChoice(DisplayTextOrAccidentalText::Choice::displayText); + isSuccess &= choice->getDisplayText()->fromXElement(message, *it); + myDisplayTextOrAccidentalTextSet.push_back(choice); + continue; + } + if (it->getName() == "accidental-text") + { + auto choice = makeDisplayTextOrAccidentalText(); + choice->setChoice(DisplayTextOrAccidentalText::Choice::accidentalText); + isSuccess &= choice->getAccidentalText()->fromXElement(message, *it); + myDisplayTextOrAccidentalTextSet.push_back(choice); + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/PartAbbreviationDisplay.h b/src/private/mx/core/elements/PartAbbreviationDisplay.h index 0056d55f..c8b7af33 100644 --- a/src/private/mx/core/elements/PartAbbreviationDisplay.h +++ b/src/private/mx/core/elements/PartAbbreviationDisplay.h @@ -40,13 +40,12 @@ class PartAbbreviationDisplay : public ElementInterface void setAttributes(const PartAbbreviationDisplayAttributesPtr &value); /* _________ DisplayTextOrAccidentalText minOccurs = 0, maxOccurs = unbounded _________ */ - const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalText() const; + const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalTextSet() const; void addDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextPtr &value); - void removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &setIterator); + void removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &value); void clearDisplayTextOrAccidentalTextSet(); DisplayTextOrAccidentalTextPtr getDisplayTextOrAccidentalText( const DisplayTextOrAccidentalTextSetIterConst &setIterator) const; - const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalTextSet() const; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/PartGroup.cpp b/src/private/mx/core/elements/PartGroup.cpp index 9af056fa..602d15ca 100644 --- a/src/private/mx/core/elements/PartGroup.cpp +++ b/src/private/mx/core/elements/PartGroup.cpp @@ -95,8 +95,8 @@ std::ostream &PartGroup::streamContents(std::ostream &os, const int indentLevel, os << std::endl; myEditorialGroup->streamContents(os, indentLevel + 1, isOneLineOnly); } - os << std::endl; isOneLineOnly = false; + os << std::endl; } else { @@ -333,5 +333,6 @@ bool PartGroup::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem MX_RETURN_IS_SUCCESS; } + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/PartGroup.h b/src/private/mx/core/elements/PartGroup.h index d3f057d4..18a67e63 100644 --- a/src/private/mx/core/elements/PartGroup.h +++ b/src/private/mx/core/elements/PartGroup.h @@ -44,7 +44,7 @@ class PartGroup : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; PartGroupAttributesPtr getAttributes() const; - void setAttributes(const PartGroupAttributesPtr &value); + void setAttributes(const PartGroupAttributesPtr &attributes); /* _________ GroupName minOccurs = 0, maxOccurs = 1 _________ */ GroupNamePtr getGroupName() const; diff --git a/src/private/mx/core/elements/PartGroupAttributes.cpp b/src/private/mx/core/elements/PartGroupAttributes.cpp index 102ff3cf..fa119300 100644 --- a/src/private/mx/core/elements/PartGroupAttributes.cpp +++ b/src/private/mx/core/elements/PartGroupAttributes.cpp @@ -54,7 +54,7 @@ bool PartGroupAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/PartNameAttributes.cpp b/src/private/mx/core/elements/PartNameAttributes.cpp index e10c1db0..caccbf05 100644 --- a/src/private/mx/core/elements/PartNameAttributes.cpp +++ b/src/private/mx/core/elements/PartNameAttributes.cpp @@ -12,10 +12,10 @@ namespace core { PartNameAttributes::PartNameAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), color(), printObject(), - justify(LeftCenterRight::center), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasColor(false), hasPrintObject(false), hasJustify(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), printObject(YesNo::no), + justify(LeftCenterRight::left), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), + hasPrintObject(false), hasJustify(false) { } @@ -101,7 +101,7 @@ bool PartNameAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/PartNameDisplay.cpp b/src/private/mx/core/elements/PartNameDisplay.cpp index 883f550a..40132a21 100644 --- a/src/private/mx/core/elements/PartNameDisplay.cpp +++ b/src/private/mx/core/elements/PartNameDisplay.cpp @@ -4,6 +4,8 @@ #include "mx/core/elements/PartNameDisplay.h" #include "mx/core/FromXElement.h" +#include "mx/core/elements/AccidentalText.h" +#include "mx/core/elements/DisplayText.h" #include "mx/core/elements/DisplayTextOrAccidentalText.h" #include @@ -69,7 +71,7 @@ void PartNameDisplay::setAttributes(const PartNameDisplayAttributesPtr &value) } } -const DisplayTextOrAccidentalTextSet &PartNameDisplay::getDisplayTextOrAccidentalText() const +const DisplayTextOrAccidentalTextSet &PartNameDisplay::getDisplayTextOrAccidentalTextSet() const { return myDisplayTextOrAccidentalTextSet; } @@ -82,11 +84,11 @@ void PartNameDisplay::addDisplayTextOrAccidentalText(const DisplayTextOrAccident } } -void PartNameDisplay::removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &setIterator) +void PartNameDisplay::removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &value) { - if (setIterator != myDisplayTextOrAccidentalTextSet.cend()) + if (value != myDisplayTextOrAccidentalTextSet.cend()) { - myDisplayTextOrAccidentalTextSet.erase(setIterator); + myDisplayTextOrAccidentalTextSet.erase(value); } } @@ -102,12 +104,7 @@ DisplayTextOrAccidentalTextPtr PartNameDisplay::getDisplayTextOrAccidentalText( { return *setIterator; } - return makeDisplayTextOrAccidentalText(); -} - -const DisplayTextOrAccidentalTextSet &PartNameDisplay::getDisplayTextOrAccidentalTextSet() const -{ - return myDisplayTextOrAccidentalTextSet; + return DisplayTextOrAccidentalTextPtr(); } bool PartNameDisplay::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) @@ -118,9 +115,22 @@ bool PartNameDisplay::fromXElementImpl(std::ostream &message, ::ezxml::XElement auto endIter = xelement.end(); for (auto it = xelement.begin(); it != endIter; ++it) { - auto item = makeDisplayTextOrAccidentalText(); - isSuccess &= item->fromXElement(message, *it); - myDisplayTextOrAccidentalTextSet.push_back(item); + if (it->getName() == "display-text") + { + auto choice = makeDisplayTextOrAccidentalText(); + choice->setChoice(DisplayTextOrAccidentalText::Choice::displayText); + isSuccess &= choice->getDisplayText()->fromXElement(message, *it); + myDisplayTextOrAccidentalTextSet.push_back(choice); + continue; + } + if (it->getName() == "accidental-text") + { + auto choice = makeDisplayTextOrAccidentalText(); + choice->setChoice(DisplayTextOrAccidentalText::Choice::accidentalText); + isSuccess &= choice->getAccidentalText()->fromXElement(message, *it); + myDisplayTextOrAccidentalTextSet.push_back(choice); + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/PartNameDisplay.h b/src/private/mx/core/elements/PartNameDisplay.h index 3dd52cb0..5d66885a 100644 --- a/src/private/mx/core/elements/PartNameDisplay.h +++ b/src/private/mx/core/elements/PartNameDisplay.h @@ -40,13 +40,12 @@ class PartNameDisplay : public ElementInterface void setAttributes(const PartNameDisplayAttributesPtr &value); /* _________ DisplayTextOrAccidentalText minOccurs = 0, maxOccurs = unbounded _________ */ - const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalText() const; + const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalTextSet() const; void addDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextPtr &value); - void removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &setIterator); + void removeDisplayTextOrAccidentalText(const DisplayTextOrAccidentalTextSetIterConst &value); void clearDisplayTextOrAccidentalTextSet(); DisplayTextOrAccidentalTextPtr getDisplayTextOrAccidentalText( const DisplayTextOrAccidentalTextSetIterConst &setIterator) const; - const DisplayTextOrAccidentalTextSet &getDisplayTextOrAccidentalTextSet() const; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/PartSymbolAttributes.cpp b/src/private/mx/core/elements/PartSymbolAttributes.cpp index 1b848d80..7fa0944a 100644 --- a/src/private/mx/core/elements/PartSymbolAttributes.cpp +++ b/src/private/mx/core/elements/PartSymbolAttributes.cpp @@ -11,14 +11,15 @@ namespace mx namespace core { PartSymbolAttributes::PartSymbolAttributes() - : topStaff(), bottomStaff(), defaultX(), defaultY(), relativeX(), relativeY(), hasTopStaff(false), - hasBottomStaff(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false) + : topStaff(), bottomStaff(), defaultX(), defaultY(), relativeX(), relativeY(), color(), hasTopStaff(false), + hasBottomStaff(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasColor(false) { } bool PartSymbolAttributes::hasValues() const { - return hasTopStaff || hasBottomStaff || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY; + return hasTopStaff || hasBottomStaff || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasColor; } std::ostream &PartSymbolAttributes::toStream(std::ostream &os) const @@ -31,6 +32,7 @@ std::ostream &PartSymbolAttributes::toStream(std::ostream &os) const streamAttribute(os, defaultY, "default-y", hasDefaultY); streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -69,6 +71,10 @@ bool PartSymbolAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/PartSymbolAttributes.h b/src/private/mx/core/elements/PartSymbolAttributes.h index c10dcc10..fafcbe41 100644 --- a/src/private/mx/core/elements/PartSymbolAttributes.h +++ b/src/private/mx/core/elements/PartSymbolAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Decimals.h" #include "mx/core/ForwardDeclare.h" #include "mx/core/Integers.h" @@ -32,12 +33,14 @@ struct PartSymbolAttributes : public AttributesInterface TenthsValue defaultY; TenthsValue relativeX; TenthsValue relativeY; + Color color; bool hasTopStaff; bool hasBottomStaff; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; bool hasRelativeY; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/PedalAttributes.cpp b/src/private/mx/core/elements/PedalAttributes.cpp index 31fbc57e..9cab07d7 100644 --- a/src/private/mx/core/elements/PedalAttributes.cpp +++ b/src/private/mx/core/elements/PedalAttributes.cpp @@ -11,18 +11,18 @@ namespace mx namespace core { PedalAttributes::PedalAttributes() - : type(StartStopChangeContinue::start), line(YesNo::no), sign(YesNo::no), defaultX(), defaultY(), relativeX(), - relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), - fontWeight(FontWeight::normal), halign(LeftCenterRight::center), hasType(true), hasLine(false), hasSign(false), + : type(), line(YesNo::no), sign(YesNo::no), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + halign(LeftCenterRight::left), valign(Valign::bottom), hasType(true), hasLine(false), hasSign(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasHalign(false) + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false) { } bool PedalAttributes::hasValues() const { return hasType || hasLine || hasSign || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || - hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasHalign; + hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign; } std::ostream &PedalAttributes::toStream(std::ostream &os) const @@ -40,7 +40,9 @@ std::ostream &PedalAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); } return os; } @@ -101,19 +103,27 @@ bool PedalAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/PedalAttributes.h b/src/private/mx/core/elements/PedalAttributes.h index 7c4582c7..6b6a309c 100644 --- a/src/private/mx/core/elements/PedalAttributes.h +++ b/src/private/mx/core/elements/PedalAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -39,7 +40,9 @@ struct PedalAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; const bool hasType; bool hasLine; bool hasSign; @@ -51,7 +54,9 @@ struct PedalAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/PedalTuning.cpp b/src/private/mx/core/elements/PedalTuning.cpp index 78ff5a96..89aecae2 100644 --- a/src/private/mx/core/elements/PedalTuning.cpp +++ b/src/private/mx/core/elements/PedalTuning.cpp @@ -93,14 +93,6 @@ bool PedalTuning::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xel } } - if (!isPedalStepFound) - { - message << "PedalTuning: '" << myPedalStep->getElementName() << "' is required but was not found" << std::endl; - } - if (!isPedalAlterFound) - { - message << "PedalTuning: '" << myPedalAlter->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/PerMinuteAttributes.cpp b/src/private/mx/core/elements/PerMinuteAttributes.cpp index 4710d162..a64f2a84 100644 --- a/src/private/mx/core/elements/PerMinuteAttributes.cpp +++ b/src/private/mx/core/elements/PerMinuteAttributes.cpp @@ -11,9 +11,8 @@ namespace mx namespace core { PerMinuteAttributes::PerMinuteAttributes() - : fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + : fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false) { } diff --git a/src/private/mx/core/elements/PerMinuteOrBeatUnitChoice.cpp b/src/private/mx/core/elements/PerMinuteOrBeatUnitChoice.cpp index 0277d688..d8ba89df 100644 --- a/src/private/mx/core/elements/PerMinuteOrBeatUnitChoice.cpp +++ b/src/private/mx/core/elements/PerMinuteOrBeatUnitChoice.cpp @@ -40,18 +40,16 @@ bool PerMinuteOrBeatUnitChoice::hasContents() const std::ostream &PerMinuteOrBeatUnitChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - switch (myChoice) + if (myChoice == Choice::perMinute) { - case Choice::perMinute: { myPerMinute->toStream(os, indentLevel); } - break; - case Choice::beatUnitGroup: { - myBeatUnitGroup->streamContents(os, indentLevel, isOneLineOnly); - } - break; - default: - break; + if (myChoice == Choice::beatUnitGroup) + { + if (myBeatUnitGroup) + { + myBeatUnitGroup->streamContents(os, indentLevel, isOneLineOnly); + } } isOneLineOnly = false; return os; @@ -62,7 +60,7 @@ PerMinuteOrBeatUnitChoice::Choice PerMinuteOrBeatUnitChoice::getChoice() const return myChoice; } -void PerMinuteOrBeatUnitChoice::setChoice(const PerMinuteOrBeatUnitChoice::Choice value) +void PerMinuteOrBeatUnitChoice::setChoice(const Choice value) { myChoice = value; } @@ -93,12 +91,7 @@ void PerMinuteOrBeatUnitChoice::setBeatUnitGroup(const BeatUnitGroupPtr &value) } } -bool PerMinuteOrBeatUnitChoice::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - MX_CHOICE_IF(perMinute, "per-minute", PerMinute); - MX_CHOICE_IF(beatUnitGroup, "beat-unit-group", BeatUnitGroup); - MX_BAD_ELEMENT_FAILURE(PerMinuteOrBeatUnitChoice); -} +MX_FROM_XELEMENT_UNUSED(PerMinuteOrBeatUnitChoice); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/PerMinuteOrBeatUnitChoice.h b/src/private/mx/core/elements/PerMinuteOrBeatUnitChoice.h index a24b6700..ad3c3844 100644 --- a/src/private/mx/core/elements/PerMinuteOrBeatUnitChoice.h +++ b/src/private/mx/core/elements/PerMinuteOrBeatUnitChoice.h @@ -16,8 +16,8 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(BeatUnitGroup) MX_FORWARD_DECLARE_ELEMENT(PerMinute) +MX_FORWARD_DECLARE_ELEMENT(BeatUnitGroup) MX_FORWARD_DECLARE_ELEMENT(PerMinuteOrBeatUnitChoice) inline PerMinuteOrBeatUnitChoicePtr makePerMinuteOrBeatUnitChoice() @@ -40,8 +40,8 @@ class PerMinuteOrBeatUnitChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - PerMinuteOrBeatUnitChoice::Choice getChoice() const; - void setChoice(const PerMinuteOrBeatUnitChoice::Choice value); + Choice getChoice() const; + void setChoice(const Choice value); PerMinutePtr getPerMinute() const; void setPerMinute(const PerMinutePtr &value); BeatUnitGroupPtr getBeatUnitGroup() const; diff --git a/src/private/mx/core/elements/PercussionAttributes.cpp b/src/private/mx/core/elements/PercussionAttributes.cpp index bd3616ff..67c2e953 100644 --- a/src/private/mx/core/elements/PercussionAttributes.cpp +++ b/src/private/mx/core/elements/PercussionAttributes.cpp @@ -12,10 +12,10 @@ namespace core { PercussionAttributes::PercussionAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::center), valign(), - enclosure(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), - hasHalign(false), hasValign(false), hasEnclosure(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::left), + valign(Valign::bottom), enclosure(EnclosureShape::none), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), + hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false), hasEnclosure(false) { } diff --git a/src/private/mx/core/elements/PercussionChoice.cpp b/src/private/mx/core/elements/PercussionChoice.cpp index 2a00f171..a68cc6ce 100644 --- a/src/private/mx/core/elements/PercussionChoice.cpp +++ b/src/private/mx/core/elements/PercussionChoice.cpp @@ -24,7 +24,6 @@ namespace mx namespace core { PercussionChoice::PercussionChoice() - : myChoice(PercussionChoice::Choice::glass), myGlass(makeGlass()), myMetal(makeMetal()), myWood(makeWood()), myPitched(makePitched()), myMembrane(makeMembrane()), myEffect(makeEffect()), myTimpani(makeTimpani()), myBeater(makeBeater()), myStick(makeStick()), myStickType(makeStickType()), myStickMaterial(makeStickMaterial()), @@ -44,6 +43,7 @@ std::ostream &PercussionChoice::streamAttributes(std::ostream &os) const std::ostream &PercussionChoice::streamName(std::ostream &os) const { + os << "percussion"; return os; } diff --git a/src/private/mx/core/elements/PercussionChoice.h b/src/private/mx/core/elements/PercussionChoice.h index 1ab933d3..37f53b48 100644 --- a/src/private/mx/core/elements/PercussionChoice.h +++ b/src/private/mx/core/elements/PercussionChoice.h @@ -62,6 +62,8 @@ class PercussionChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; + + /* _________ Choice minOccurs = 1, maxOccurs = 1 _________ */ PercussionChoice::Choice getChoice() const; void setChoice(const PercussionChoice::Choice value); diff --git a/src/private/mx/core/elements/Pitch.cpp b/src/private/mx/core/elements/Pitch.cpp index b668bf73..fc66272e 100644 --- a/src/private/mx/core/elements/Pitch.cpp +++ b/src/private/mx/core/elements/Pitch.cpp @@ -40,6 +40,7 @@ bool Pitch::hasContents() const std::ostream &Pitch::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myStep->toStream(os, indentLevel + 1); if (myHasAlter) @@ -49,7 +50,6 @@ std::ostream &Pitch::streamContents(std::ostream &os, const int indentLevel, boo } os << std::endl; myOctave->toStream(os, indentLevel + 1); - isOneLineOnly = false; os << std::endl; return os; } @@ -126,16 +126,6 @@ bool Pitch::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) } } - if (!isStepFound) - { - message << "Pitch: 'step' element is required but was not found" << std::endl; - } - - if (!isOctaveFound) - { - message << "Pitch: 'octave' element is required but was not found" << std::endl; - } - MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Pitched.cpp b/src/private/mx/core/elements/Pitched.cpp index 4802d044..e4655eda 100644 --- a/src/private/mx/core/elements/Pitched.cpp +++ b/src/private/mx/core/elements/Pitched.cpp @@ -60,7 +60,6 @@ void Pitched::setValue(const PitchedEnum &value) bool Pitched::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); - MX_UNUSED(xelement); myValue = parsePitchedEnum(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/Play.cpp b/src/private/mx/core/elements/Play.cpp index b2929e6b..0785b34f 100644 --- a/src/private/mx/core/elements/Play.cpp +++ b/src/private/mx/core/elements/Play.cpp @@ -15,7 +15,7 @@ namespace mx namespace core { Play::Play() - : myAttributes(std::make_shared()), myChoice(Choice::ipa), myIpa(makeIpa()), myMute(makeMute()), + : myChoice(Choice::ipa), myAttributes(std::make_shared()), myIpa(makeIpa()), myMute(makeMute()), mySemiPitched(makeSemiPitched()), myOtherPlay(makeOtherPlay()) { } @@ -43,30 +43,39 @@ bool Play::hasContents() const std::ostream &Play::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - os << std::endl; switch (myChoice) { case Choice::ipa: { + os << std::endl; myIpa->toStream(os, indentLevel + 1); + os << std::endl; + isOneLineOnly = false; } break; case Choice::mute: { + os << std::endl; myMute->toStream(os, indentLevel + 1); + os << std::endl; + isOneLineOnly = false; } break; case Choice::semiPitched: { + os << std::endl; mySemiPitched->toStream(os, indentLevel + 1); + os << std::endl; + isOneLineOnly = false; } break; case Choice::otherPlay: { + os << std::endl; myOtherPlay->toStream(os, indentLevel + 1); + os << std::endl; + isOneLineOnly = false; } break; default: break; } - isOneLineOnly = false; - os << std::endl; return os; } @@ -147,29 +156,35 @@ void Play::setOtherPlay(const OtherPlayPtr &value) bool Play::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { - if (xelement.getName() == "ipa") - { - myChoice = Choice::ipa; - return getIpa()->fromXElement(message, xelement); - } - else if (xelement.getName() == "mute") - { - myChoice = Choice::mute; - return getMute()->fromXElement(message, xelement); - } - else if (xelement.getName() == "semi-pitched") - { - myChoice = Choice::semiPitched; - return getSemiPitched()->fromXElement(message, xelement); - } - else if (xelement.getName() == "other-play") + bool isSuccess = true; + isSuccess &= myAttributes->fromXElement(message, xelement); + + auto endIter = xelement.end(); + for (auto it = xelement.begin(); it != endIter; ++it) { - myChoice = Choice::otherPlay; - return getOtherPlay()->fromXElement(message, xelement); + if (it->getName() == "ipa") + { + myChoice = Choice::ipa; + isSuccess &= myIpa->fromXElement(message, *it); + } + if (it->getName() == "mute") + { + myChoice = Choice::mute; + isSuccess &= myMute->fromXElement(message, *it); + } + if (it->getName() == "semi-pitched") + { + myChoice = Choice::semiPitched; + isSuccess &= mySemiPitched->fromXElement(message, *it); + } + if (it->getName() == "other-play") + { + myChoice = Choice::otherPlay; + isSuccess &= myOtherPlay->fromXElement(message, *it); + } } - message << "Encoding: '" << xelement.getName() << "' is not valid" << std::endl; - return false; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/Play.h b/src/private/mx/core/elements/Play.h index b630110d..710d929b 100644 --- a/src/private/mx/core/elements/Play.h +++ b/src/private/mx/core/elements/Play.h @@ -20,8 +20,8 @@ namespace core MX_FORWARD_DECLARE_ATTRIBUTES(PlayAttributes) MX_FORWARD_DECLARE_ELEMENT(Ipa) MX_FORWARD_DECLARE_ELEMENT(Mute) -MX_FORWARD_DECLARE_ELEMENT(OtherPlay) MX_FORWARD_DECLARE_ELEMENT(SemiPitched) +MX_FORWARD_DECLARE_ELEMENT(OtherPlay) MX_FORWARD_DECLARE_ELEMENT(Play) inline PlayPtr makePlay() @@ -49,7 +49,7 @@ class Play : public ElementInterface PlayAttributesPtr getAttributes() const; void setAttributes(const PlayAttributesPtr &value); - /* _________ Choice __________ */ + /* _________ Choice _________ */ Play::Choice getChoice() const; void setChoice(const Play::Choice value); @@ -73,8 +73,8 @@ class Play : public ElementInterface virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); private: - PlayAttributesPtr myAttributes; Choice myChoice; + PlayAttributesPtr myAttributes; IpaPtr myIpa; MutePtr myMute; SemiPitchedPtr mySemiPitched; diff --git a/src/private/mx/core/elements/PlayAttributes.h b/src/private/mx/core/elements/PlayAttributes.h index 4a222cc8..7cd639ad 100644 --- a/src/private/mx/core/elements/PlayAttributes.h +++ b/src/private/mx/core/elements/PlayAttributes.h @@ -5,7 +5,6 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/ForwardDeclare.h" #include "mx/core/XsIDREF.h" diff --git a/src/private/mx/core/elements/Pluck.cpp b/src/private/mx/core/elements/Pluck.cpp index bd85f196..4c0e2b1f 100644 --- a/src/private/mx/core/elements/Pluck.cpp +++ b/src/private/mx/core/elements/Pluck.cpp @@ -79,7 +79,7 @@ bool Pluck::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); myValue.setValue(xelement.getValue()); - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/PluckAttributes.cpp b/src/private/mx/core/elements/PluckAttributes.cpp index c876f36a..1dfcb538 100644 --- a/src/private/mx/core/elements/PluckAttributes.cpp +++ b/src/private/mx/core/elements/PluckAttributes.cpp @@ -12,16 +12,16 @@ namespace core { PluckAttributes::PluckAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool PluckAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &PluckAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &PluckAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,6 +85,10 @@ bool PluckAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/PluckAttributes.h b/src/private/mx/core/elements/PluckAttributes.h index 05127b27..5a1d505a 100644 --- a/src/private/mx/core/elements/PluckAttributes.h +++ b/src/private/mx/core/elements/PluckAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct PluckAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct PluckAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/PrefixAttributes.cpp b/src/private/mx/core/elements/PrefixAttributes.cpp index 12ae62f2..ed136db7 100644 --- a/src/private/mx/core/elements/PrefixAttributes.cpp +++ b/src/private/mx/core/elements/PrefixAttributes.cpp @@ -12,16 +12,16 @@ namespace core { PrefixAttributes::PrefixAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasDefaultX(false), hasDefaultY(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + hasFontWeight(false), hasColor(false) { } bool PrefixAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight; + hasFontWeight || hasColor; } std::ostream &PrefixAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &PrefixAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -83,9 +84,13 @@ bool PrefixAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/PrefixAttributes.h b/src/private/mx/core/elements/PrefixAttributes.h index dbc55a3c..13d3c5ab 100644 --- a/src/private/mx/core/elements/PrefixAttributes.h +++ b/src/private/mx/core/elements/PrefixAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct PrefixAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; @@ -44,6 +46,7 @@ struct PrefixAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/PrincipalVoiceAttributes.cpp b/src/private/mx/core/elements/PrincipalVoiceAttributes.cpp index 3204aeee..76903bda 100644 --- a/src/private/mx/core/elements/PrincipalVoiceAttributes.cpp +++ b/src/private/mx/core/elements/PrincipalVoiceAttributes.cpp @@ -12,17 +12,17 @@ namespace core { PrincipalVoiceAttributes::PrincipalVoiceAttributes() : type(StartStop::start), symbol(PrincipalVoiceSymbol::none), defaultX(), defaultY(), relativeX(), relativeY(), - fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), halign(), hasType(true), hasSymbol(true), hasDefaultX(false), hasDefaultY(false), - hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false), hasHalign(false) + fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + color(), halign(LeftCenterRight::left), valign(Valign::bottom), hasType(true), hasSymbol(true), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false) { } bool PrincipalVoiceAttributes::hasValues() const { return hasType || hasSymbol || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || - hasFontStyle || hasFontSize || hasFontWeight || hasHalign; + hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign; } std::ostream &PrincipalVoiceAttributes::toStream(std::ostream &os) const @@ -39,7 +39,9 @@ std::ostream &PrincipalVoiceAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); } return os; } @@ -98,21 +100,30 @@ bool PrincipalVoiceAttributes::fromXElementImpl(std::ostream &message, ::ezxml:: { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } + if (!isSymbolFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'symbol' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/PrincipalVoiceAttributes.h b/src/private/mx/core/elements/PrincipalVoiceAttributes.h index 46097d1b..c208cfca 100644 --- a/src/private/mx/core/elements/PrincipalVoiceAttributes.h +++ b/src/private/mx/core/elements/PrincipalVoiceAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -38,7 +39,9 @@ struct PrincipalVoiceAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; const bool hasType; const bool hasSymbol; bool hasDefaultX; @@ -49,7 +52,9 @@ struct PrincipalVoiceAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Print.cpp b/src/private/mx/core/elements/Print.cpp index 574d8e36..f0f1fb8b 100644 --- a/src/private/mx/core/elements/Print.cpp +++ b/src/private/mx/core/elements/Print.cpp @@ -74,8 +74,8 @@ std::ostream &Print::streamContents(std::ostream &os, const int indentLevel, boo os << std::endl; myPartAbbreviationDisplay->toStream(os, indentLevel + 1); } - os << std::endl; isOneLineOnly = false; + os << std::endl; } else { diff --git a/src/private/mx/core/elements/Print.h b/src/private/mx/core/elements/Print.h index 25a6d9c8..48ac59ba 100644 --- a/src/private/mx/core/elements/Print.h +++ b/src/private/mx/core/elements/Print.h @@ -41,7 +41,7 @@ class Print : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; PrintAttributesPtr getAttributes() const; - void setAttributes(const PrintAttributesPtr &value); + void setAttributes(const PrintAttributesPtr &attributes); /* _________ LayoutGroup minOccurs = 1, maxOccurs = 1 _________ */ LayoutGroupPtr getLayoutGroup() const; diff --git a/src/private/mx/core/elements/PrintAttributes.h b/src/private/mx/core/elements/PrintAttributes.h index 5a9b2640..10b962a4 100644 --- a/src/private/mx/core/elements/PrintAttributes.h +++ b/src/private/mx/core/elements/PrintAttributes.h @@ -5,11 +5,11 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" #include "mx/core/Integers.h" +#include "mx/core/XsToken.h" #include #include diff --git a/src/private/mx/core/elements/Properties.cpp b/src/private/mx/core/elements/Properties.cpp index 4adcd046..f8e60d6c 100644 --- a/src/private/mx/core/elements/Properties.cpp +++ b/src/private/mx/core/elements/Properties.cpp @@ -8,10 +8,8 @@ #include "mx/core/elements/Directive.h" #include "mx/core/elements/Divisions.h" #include "mx/core/elements/EditorialGroup.h" -#include "mx/core/elements/Footnote.h" #include "mx/core/elements/Instruments.h" #include "mx/core/elements/Key.h" -#include "mx/core/elements/Level.h" #include "mx/core/elements/MeasureStyle.h" #include "mx/core/elements/PartSymbol.h" #include "mx/core/elements/StaffDetails.h" @@ -57,70 +55,72 @@ bool Properties::hasContents() const std::ostream &Properties::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - if (this->hasContents()) + if (myEditorialGroup->hasContents()) + { + os << std::endl; + myEditorialGroup->streamContents(os, indentLevel + 1, isOneLineOnly); + } + if (myHasDivisions) + { + os << std::endl; + myDivisions->toStream(os, indentLevel + 1); + } + for (auto x : myKeySet) + { + os << std::endl; + x->toStream(os, indentLevel + 1); + } + for (auto x : myTimeSet) + { + os << std::endl; + x->toStream(os, indentLevel + 1); + } + if (myHasStaves) + { + os << std::endl; + myStaves->toStream(os, indentLevel + 1); + } + if (myHasPartSymbol) + { + os << std::endl; + myPartSymbol->toStream(os, indentLevel + 1); + } + if (myHasInstruments) + { + os << std::endl; + myInstruments->toStream(os, indentLevel + 1); + } + for (auto x : myClefSet) + { + os << std::endl; + x->toStream(os, indentLevel + 1); + } + for (auto x : myStaffDetailsSet) + { + os << std::endl; + x->toStream(os, indentLevel + 1); + } + for (auto x : myTransposeSet) + { + os << std::endl; + x->toStream(os, indentLevel + 1); + } + for (auto x : myDirectiveSet) + { + os << std::endl; + x->toStream(os, indentLevel + 1); + } + for (auto x : myMeasureStyleSet) { - if (myEditorialGroup->hasContents()) - { - os << std::endl; - myEditorialGroup->streamContents(os, indentLevel + 1, isOneLineOnly); - } - if (myHasDivisions) - { - os << std::endl; - myDivisions->toStream(os, indentLevel + 1); - } - for (auto x : myKeySet) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } - for (auto x : myTimeSet) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } - if (myHasStaves) - { - os << std::endl; - myStaves->toStream(os, indentLevel + 1); - } - if (myHasPartSymbol) - { - os << std::endl; - myPartSymbol->toStream(os, indentLevel + 1); - } - if (myHasInstruments) - { - os << std::endl; - myInstruments->toStream(os, indentLevel + 1); - } - for (auto x : myClefSet) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } - for (auto x : myStaffDetailsSet) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } - for (auto x : myTransposeSet) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } - for (auto x : myDirectiveSet) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } - for (auto x : myMeasureStyleSet) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } os << std::endl; + x->toStream(os, indentLevel + 1); + } + if (true || myHasDivisions || myKeySet.size() > 0 || myTimeSet.size() > 0 || myHasStaves || myHasPartSymbol || + myHasInstruments || myClefSet.size() > 0 || myStaffDetailsSet.size() > 0 || myTransposeSet.size() > 0 || + myDirectiveSet.size() > 0 || myMeasureStyleSet.size() > 0) + { isOneLineOnly = false; + os << std::endl; } else { @@ -482,37 +482,17 @@ MeasureStylePtr Properties::getMeasureStyle(const MeasureStyleSetIterConst &setI bool Properties::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { bool isSuccess = true; - ::ezxml::XElementIterator end = xelement.end(); - - bool hasFootnote = false; - bool hasLevel = false; - for (auto it = xelement.begin(); it != end; ++it) + auto endIter = xelement.end(); + for (auto it = xelement.begin(); it != endIter; ++it) { - importElement(message, *it, isSuccess, *myEditorialGroup->getFootnote(), hasFootnote); - if (hasFootnote) - { - myEditorialGroup->setHasFootnote(true); - } - - importElement(message, *it, isSuccess, *myEditorialGroup->getLevel(), hasLevel); - if (hasLevel) - { - myEditorialGroup->setHasLevel(true); - } - + importGroup(message, it, endIter, isSuccess, myEditorialGroup); if (importElement(message, *it, isSuccess, *myDivisions, myHasDivisions)) { continue; } - if (importElementSet(message, it, end, isSuccess, "key", myKeySet)) - { - continue; - } - if (importElementSet(message, it, end, isSuccess, "time", myTimeSet)) - { - continue; - } + importElementSet(message, it, endIter, isSuccess, "key", myKeySet); + importElementSet(message, it, endIter, isSuccess, "time", myTimeSet); if (importElement(message, *it, isSuccess, *myStaves, myHasStaves)) { continue; @@ -525,26 +505,11 @@ bool Properties::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xele { continue; } - if (importElementSet(message, it, end, isSuccess, "clef", myClefSet)) - { - continue; - } - if (importElementSet(message, it, end, isSuccess, "staff-details", myStaffDetailsSet)) - { - continue; - } - if (importElementSet(message, it, end, isSuccess, "transpose", myTransposeSet)) - { - continue; - } - if (importElementSet(message, it, end, isSuccess, "directive", myDirectiveSet)) - { - continue; - } - if (importElementSet(message, it, end, isSuccess, "measure-style", myMeasureStyleSet)) - { - continue; - } + importElementSet(message, it, endIter, isSuccess, "clef", myClefSet); + importElementSet(message, it, endIter, isSuccess, "staff-details", myStaffDetailsSet); + importElementSet(message, it, endIter, isSuccess, "transpose", myTransposeSet); + importElementSet(message, it, endIter, isSuccess, "directive", myDirectiveSet); + importElementSet(message, it, endIter, isSuccess, "measure-style", myMeasureStyleSet); } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/PullOffAttributes.cpp b/src/private/mx/core/elements/PullOffAttributes.cpp index fbc2bc5f..eaa87e71 100644 --- a/src/private/mx/core/elements/PullOffAttributes.cpp +++ b/src/private/mx/core/elements/PullOffAttributes.cpp @@ -11,18 +11,18 @@ namespace mx namespace core { PullOffAttributes::PullOffAttributes() - : type(StartStop::start), number(1), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), - placement(), hasType(true), hasNumber(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasPlacement(false) + : type(StartStop::start), number(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + placement(AboveBelow::below), hasType(true), hasNumber(false), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), + hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool PullOffAttributes::hasValues() const { return hasType || hasNumber || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || - hasFontStyle || hasFontSize || hasFontWeight || hasPlacement; + hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasPlacement; } std::ostream &PullOffAttributes::toStream(std::ostream &os) const @@ -39,6 +39,7 @@ std::ostream &PullOffAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -96,6 +97,10 @@ bool PullOffAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; @@ -105,7 +110,7 @@ bool PullOffAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/PullOffAttributes.h b/src/private/mx/core/elements/PullOffAttributes.h index 643ef5dc..734a7977 100644 --- a/src/private/mx/core/elements/PullOffAttributes.h +++ b/src/private/mx/core/elements/PullOffAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -39,6 +40,7 @@ struct PullOffAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; const bool hasType; bool hasNumber; @@ -50,6 +52,7 @@ struct PullOffAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/RehearsalAttributes.cpp b/src/private/mx/core/elements/RehearsalAttributes.cpp index ef8e8833..8be520b7 100644 --- a/src/private/mx/core/elements/RehearsalAttributes.cpp +++ b/src/private/mx/core/elements/RehearsalAttributes.cpp @@ -11,22 +11,23 @@ namespace mx namespace core { RehearsalAttributes::RehearsalAttributes() - : justify(LeftCenterRight::center), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), halign(), - underline(), overline(), lineThrough(), rotation(), letterSpacing(), lineHeight(), lang(XmlLang{"it"}), - space(XmlSpace::default_), enclosure(EnclosureShape::rectangle), hasJustify(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasHalign(false), hasUnderline(false), hasOverline(false), - hasLineThrough(false), hasRotation(false), hasLetterSpacing(false), hasLineHeight(false), hasLang(false), - hasSpace(false), hasEnclosure(false) + : justify(LeftCenterRight::left), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + halign(LeftCenterRight::left), valign(Valign::bottom), underline(), overline(), lineThrough(), rotation(), + letterSpacing(), lineHeight(), lang("it"), space(), dir(), enclosure(EnclosureShape::none), hasJustify(false), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), + hasValign(false), hasUnderline(false), hasOverline(false), hasLineThrough(false), hasRotation(false), + hasLetterSpacing(false), hasLineHeight(false), hasLang(false), hasSpace(false), hasDir(false), hasEnclosure(false) { } bool RehearsalAttributes::hasValues() const { return hasJustify || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight || hasHalign || hasUnderline || hasOverline || hasLineThrough || hasRotation || - hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasEnclosure; + hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign || hasUnderline || hasOverline || + hasLineThrough || hasRotation || hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasDir || + hasEnclosure; } std::ostream &RehearsalAttributes::toStream(std::ostream &os) const @@ -42,7 +43,9 @@ std::ostream &RehearsalAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); streamAttribute(os, underline, "underline", hasUnderline); streamAttribute(os, overline, "overline", hasOverline); streamAttribute(os, lineThrough, "line-through", hasLineThrough); @@ -51,6 +54,7 @@ std::ostream &RehearsalAttributes::toStream(std::ostream &os) const streamAttribute(os, lineHeight, "line-height", hasLineHeight); streamAttribute(os, lang, "xml:lang", hasLang); streamAttribute(os, space, "xml:space", hasSpace); + streamAttribute(os, dir, "dir", hasDir); streamAttribute(os, enclosure, "enclosure", hasEnclosure); } return os; @@ -103,10 +107,18 @@ bool RehearsalAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, underline, hasUnderline, "underline")) { continue; @@ -131,19 +143,15 @@ bool RehearsalAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) - { - continue; - } if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, dir, hasDir, "dir", &parseTextDirection)) { continue; } @@ -154,7 +162,7 @@ bool RehearsalAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/RehearsalAttributes.h b/src/private/mx/core/elements/RehearsalAttributes.h index 5c9d0077..2a8dbf37 100644 --- a/src/private/mx/core/elements/RehearsalAttributes.h +++ b/src/private/mx/core/elements/RehearsalAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -40,7 +41,9 @@ struct RehearsalAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; NumberOfLines underline; NumberOfLines overline; NumberOfLines lineThrough; @@ -49,6 +52,7 @@ struct RehearsalAttributes : public AttributesInterface NumberOrNormal lineHeight; XmlLang lang; XmlSpace space; + TextDirection dir; EnclosureShape enclosure; bool hasJustify; bool hasDefaultX; @@ -59,7 +63,9 @@ struct RehearsalAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; bool hasUnderline; bool hasOverline; bool hasLineThrough; @@ -68,6 +74,7 @@ struct RehearsalAttributes : public AttributesInterface bool hasLineHeight; bool hasLang; bool hasSpace; + bool hasDir; bool hasEnclosure; private: diff --git a/src/private/mx/core/elements/RelationAttributes.cpp b/src/private/mx/core/elements/RelationAttributes.cpp index f9efe426..63bc88dc 100644 --- a/src/private/mx/core/elements/RelationAttributes.cpp +++ b/src/private/mx/core/elements/RelationAttributes.cpp @@ -44,7 +44,7 @@ bool RelationAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/RelationAttributes.h b/src/private/mx/core/elements/RelationAttributes.h index 08968bae..3a9188ee 100644 --- a/src/private/mx/core/elements/RelationAttributes.h +++ b/src/private/mx/core/elements/RelationAttributes.h @@ -5,8 +5,8 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsToken.h" #include #include diff --git a/src/private/mx/core/elements/RepeatAttributes.cpp b/src/private/mx/core/elements/RepeatAttributes.cpp index e68af30c..96d31cb7 100644 --- a/src/private/mx/core/elements/RepeatAttributes.cpp +++ b/src/private/mx/core/elements/RepeatAttributes.cpp @@ -11,8 +11,7 @@ namespace mx namespace core { RepeatAttributes::RepeatAttributes() - : direction(BackwardForward::backward), times(), winged(Winged::none), hasDirection(true), hasTimes(false), - hasWinged(false) + : direction(), times(), winged(), hasDirection(true), hasTimes(false), hasWinged(false) { } @@ -61,7 +60,7 @@ bool RepeatAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement if (!isDirectionFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'direction' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/Rest.cpp b/src/private/mx/core/elements/Rest.cpp index 82f8160b..78fe0524 100644 --- a/src/private/mx/core/elements/Rest.cpp +++ b/src/private/mx/core/elements/Rest.cpp @@ -44,6 +44,9 @@ std::ostream &Rest::streamContents(std::ostream &os, const int indentLevel, bool { os << std::endl; myDisplayStepOctaveGroup->streamContents(os, indentLevel + 1, isOneLineOnly); + } + if (myHasDisplayStepOctaveGroup) + { isOneLineOnly = false; os << std::endl; } @@ -95,9 +98,12 @@ bool Rest::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); - auto it = xelement.begin(); auto endIter = xelement.end(); - importGroup(message, it, endIter, isSuccess, myDisplayStepOctaveGroup, myHasDisplayStepOctaveGroup); + for (auto it = xelement.begin(); it != endIter; ++it) + { + importGroup(message, it, endIter, isSuccess, myDisplayStepOctaveGroup, myHasDisplayStepOctaveGroup); + } + MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Rest.h b/src/private/mx/core/elements/Rest.h index 52253ba5..d9c6655d 100644 --- a/src/private/mx/core/elements/Rest.h +++ b/src/private/mx/core/elements/Rest.h @@ -37,7 +37,7 @@ class Rest : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; RestAttributesPtr getAttributes() const; - void setAttributes(const RestAttributesPtr &value); + void setAttributes(const RestAttributesPtr &attributes); /* _________ DisplayStepOctaveGroup minOccurs = 0, maxOccurs = 1 _________ */ DisplayStepOctaveGroupPtr getDisplayStepOctaveGroup() const; diff --git a/src/private/mx/core/elements/RightDivider.h b/src/private/mx/core/elements/RightDivider.h index 296c19c4..e39e0e89 100644 --- a/src/private/mx/core/elements/RightDivider.h +++ b/src/private/mx/core/elements/RightDivider.h @@ -17,6 +17,7 @@ namespace mx namespace core { +MX_FORWARD_DECLARE_ATTRIBUTES(EmptyPrintObjectStyleAlignAttributes) MX_FORWARD_DECLARE_ELEMENT(RightDivider) inline RightDividerPtr makeRightDivider() diff --git a/src/private/mx/core/elements/RightsAttributes.h b/src/private/mx/core/elements/RightsAttributes.h index f67f6ccb..90314903 100644 --- a/src/private/mx/core/elements/RightsAttributes.h +++ b/src/private/mx/core/elements/RightsAttributes.h @@ -5,8 +5,8 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsToken.h" #include #include diff --git a/src/private/mx/core/elements/Root.cpp b/src/private/mx/core/elements/Root.cpp index 505c8d3a..ee90cd06 100644 --- a/src/private/mx/core/elements/Root.cpp +++ b/src/private/mx/core/elements/Root.cpp @@ -39,6 +39,7 @@ bool Root::hasContents() const std::ostream &Root::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myRootStep->toStream(os, indentLevel + 1); if (myHasRootAlter) @@ -47,7 +48,6 @@ std::ostream &Root::streamContents(std::ostream &os, const int indentLevel, bool myRootAlter->toStream(os, indentLevel + 1); } os << std::endl; - isOneLineOnly = false; return os; } @@ -105,10 +105,6 @@ bool Root::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) } } - if (!isRootStepFound) - { - message << "Root: '" << myRootStep->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/RootAlterAttributes.cpp b/src/private/mx/core/elements/RootAlterAttributes.cpp index ba34ed8d..20073c9d 100644 --- a/src/private/mx/core/elements/RootAlterAttributes.cpp +++ b/src/private/mx/core/elements/RootAlterAttributes.cpp @@ -11,17 +11,18 @@ namespace mx namespace core { RootAlterAttributes::RootAlterAttributes() - : printObject(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), location(), hasPrintObject(false), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasLocation(false) + : printObject(YesNo::no), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), location(), + hasPrintObject(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), + hasLocation(false) { } bool RootAlterAttributes::hasValues() const { return hasPrintObject || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || - hasFontStyle || hasFontSize || hasFontWeight || hasLocation; + hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasLocation; } std::ostream &RootAlterAttributes::toStream(std::ostream &os) const @@ -37,6 +38,7 @@ std::ostream &RootAlterAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, location, "location", hasLocation); } return os; @@ -89,13 +91,17 @@ bool RootAlterAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, location, hasLocation, "location", &parseLeftRight)) { continue; } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/RootAlterAttributes.h b/src/private/mx/core/elements/RootAlterAttributes.h index 7b3d4d08..55c706bd 100644 --- a/src/private/mx/core/elements/RootAlterAttributes.h +++ b/src/private/mx/core/elements/RootAlterAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -37,6 +38,7 @@ struct RootAlterAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftRight location; bool hasPrintObject; bool hasDefaultX; @@ -47,6 +49,7 @@ struct RootAlterAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasLocation; private: diff --git a/src/private/mx/core/elements/RootStepAttributes.cpp b/src/private/mx/core/elements/RootStepAttributes.cpp index 3c2a353c..631a5b27 100644 --- a/src/private/mx/core/elements/RootStepAttributes.cpp +++ b/src/private/mx/core/elements/RootStepAttributes.cpp @@ -12,16 +12,16 @@ namespace core { RootStepAttributes::RootStepAttributes() : text(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasText(false), hasDefaultX(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasText(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false) + hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool RootStepAttributes::hasValues() const { return hasText || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight; + hasFontSize || hasFontWeight || hasColor; } std::ostream &RootStepAttributes::toStream(std::ostream &os) const @@ -37,6 +37,7 @@ std::ostream &RootStepAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -88,9 +89,13 @@ bool RootStepAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/RootStepAttributes.h b/src/private/mx/core/elements/RootStepAttributes.h index cd568ff2..3131fd4b 100644 --- a/src/private/mx/core/elements/RootStepAttributes.h +++ b/src/private/mx/core/elements/RootStepAttributes.h @@ -5,11 +5,13 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" #include "mx/core/ForwardDeclare.h" +#include "mx/core/XsToken.h" #include #include @@ -37,6 +39,7 @@ struct RootStepAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasText; bool hasDefaultX; bool hasDefaultY; @@ -46,6 +49,7 @@ struct RootStepAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Scaling.cpp b/src/private/mx/core/elements/Scaling.cpp index f919dc9b..04badc28 100644 --- a/src/private/mx/core/elements/Scaling.cpp +++ b/src/private/mx/core/elements/Scaling.cpp @@ -39,12 +39,12 @@ bool Scaling::hasContents() const std::ostream &Scaling::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myMillimeters->toStream(os, indentLevel + 1); os << std::endl; myTenths->toStream(os, indentLevel + 1); os << std::endl; - isOneLineOnly = false; return os; } @@ -93,14 +93,6 @@ bool Scaling::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelemen } } - if (!isMillimetersFound) - { - message << "Scaling: '" << myMillimeters->getElementName() << "' is required but was not found" << std::endl; - } - if (!isTenthsFound) - { - message << "Scaling: '" << myTenths->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/ScoreHeaderGroup.cpp b/src/private/mx/core/elements/ScoreHeaderGroup.cpp index ee7ba8c9..65f7d4cb 100644 --- a/src/private/mx/core/elements/ScoreHeaderGroup.cpp +++ b/src/private/mx/core/elements/ScoreHeaderGroup.cpp @@ -47,67 +47,54 @@ bool ScoreHeaderGroup::hasContents() const std::ostream &ScoreHeaderGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - isOneLineOnly = false; bool isFirst = true; if (myHasWork) { if (!isFirst) - { os << std::endl; - } myWork->toStream(os, indentLevel); isFirst = false; } if (myHasMovementNumber) { if (!isFirst) - { os << std::endl; - } myMovementNumber->toStream(os, indentLevel); isFirst = false; } if (myHasMovementTitle) { if (!isFirst) - { os << std::endl; - } myMovementTitle->toStream(os, indentLevel); isFirst = false; } if (myHasIdentification) { if (!isFirst) - { os << std::endl; - } myIdentification->toStream(os, indentLevel); isFirst = false; } if (myHasDefaults) { if (!isFirst) - { os << std::endl; - } myDefaults->toStream(os, indentLevel); isFirst = false; } for (auto x : myCreditSet) { if (!isFirst) - { os << std::endl; - } x->toStream(os, indentLevel); isFirst = false; } if (!isFirst) - { os << std::endl; - } myPartList->toStream(os, indentLevel); + isFirst = false; + isOneLineOnly = !hasContents(); return os; } @@ -231,19 +218,19 @@ const CreditSet &ScoreHeaderGroup::getCreditSet() const return myCreditSet; } -void ScoreHeaderGroup::addCredit(const CreditPtr &value) +void ScoreHeaderGroup::removeCredit(const CreditSetIterConst &value) { - if (value) + if (value != myCreditSet.cend()) { - myCreditSet.push_back(value); + myCreditSet.erase(value); } } -void ScoreHeaderGroup::removeCredit(const CreditSetIterConst &value) +void ScoreHeaderGroup::addCredit(const CreditPtr &value) { - if (value != myCreditSet.cend()) + if (value) { - myCreditSet.erase(value); + myCreditSet.push_back(value); } } diff --git a/src/private/mx/core/elements/ScoreInstrument.cpp b/src/private/mx/core/elements/ScoreInstrument.cpp index 03dc863d..e34eca23 100644 --- a/src/private/mx/core/elements/ScoreInstrument.cpp +++ b/src/private/mx/core/elements/ScoreInstrument.cpp @@ -21,7 +21,7 @@ ScoreInstrument::ScoreInstrument() : myAttributes(std::make_shared()), myInstrumentName(makeInstrumentName()), myInstrumentAbbreviation(makeInstrumentAbbreviation()), myHasInstrumentAbbreviation(false), myInstrumentSound(makeInstrumentSound()), myHasInstrumentSound(false), - mySoloOrEnsembleChoice(makeSoloOrEnsembleChoice()), myHasSoloOrEnsembleChoice(false), + mySoloOrEnsembleChoice(std::make_shared()), myHasSoloOrEnsembleChoice(false), myVirtualInstrument(makeVirtualInstrument()), myHasVirtualInstrument(false) { } @@ -215,35 +215,27 @@ bool ScoreInstrument::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } - - if (checkSetChoiceMember(message, *it, isSuccess, mySoloOrEnsembleChoice, "solo", - &SoloOrEnsembleChoice::getSolo, static_cast(SoloOrEnsembleChoice::Choice::solo))) + if (it->getName() == "solo") { + mySoloOrEnsembleChoice->setChoice(SoloOrEnsembleChoice::Choice::solo); + isSuccess &= mySoloOrEnsembleChoice->getSolo()->fromXElement(message, *it); myHasSoloOrEnsembleChoice = true; continue; } - - if (checkSetChoiceMember(message, *it, isSuccess, mySoloOrEnsembleChoice, "ensemble", - &SoloOrEnsembleChoice::getEnsemble, - static_cast(SoloOrEnsembleChoice::Choice::ensemble))) + if (it->getName() == "ensemble") { + mySoloOrEnsembleChoice->setChoice(SoloOrEnsembleChoice::Choice::ensemble); + isSuccess &= mySoloOrEnsembleChoice->getEnsemble()->fromXElement(message, *it); myHasSoloOrEnsembleChoice = true; continue; } - if (importElement(message, *it, isSuccess, *myVirtualInstrument, myHasVirtualInstrument)) { continue; } } - if (!isInstrumentNameFound) - { - message << "ScoreInstrument: 'instrument-name' not found" << std::endl; - isSuccess = false; - } - - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/ScoreInstrument.h b/src/private/mx/core/elements/ScoreInstrument.h index 94dd41b0..6482b664 100644 --- a/src/private/mx/core/elements/ScoreInstrument.h +++ b/src/private/mx/core/elements/ScoreInstrument.h @@ -12,14 +12,19 @@ #include #include +namespace ezxml +{ +class XElementIterator; +} + namespace mx { namespace core { MX_FORWARD_DECLARE_ATTRIBUTES(ScoreInstrumentAttributes) -MX_FORWARD_DECLARE_ELEMENT(InstrumentAbbreviation) MX_FORWARD_DECLARE_ELEMENT(InstrumentName) +MX_FORWARD_DECLARE_ELEMENT(InstrumentAbbreviation) MX_FORWARD_DECLARE_ELEMENT(InstrumentSound) MX_FORWARD_DECLARE_ELEMENT(SoloOrEnsembleChoice) MX_FORWARD_DECLARE_ELEMENT(VirtualInstrument) @@ -53,18 +58,18 @@ class ScoreInstrument : public ElementInterface bool getHasInstrumentAbbreviation() const; void setHasInstrumentAbbreviation(const bool value); - /* _________ SoloOrEnsembleChoice minOccurs = 0, maxOccurs = 1 _________ */ - SoloOrEnsembleChoicePtr getSoloOrEnsembleChoice() const; - void setSoloOrEnsembleChoice(const SoloOrEnsembleChoicePtr &value); - bool getHasSoloOrEnsembleChoice() const; - void setHasSoloOrEnsembleChoice(const bool value); - /* _________ InstrumentSound minOccurs = 0, maxOccurs = 1 _________ */ InstrumentSoundPtr getInstrumentSound() const; void setInstrumentSound(const InstrumentSoundPtr &value); bool getHasInstrumentSound() const; void setHasInstrumentSound(const bool value); + /* _________ SoloOrEnsembleChoice minOccurs = 0, maxOccurs = 1 _________ */ + SoloOrEnsembleChoicePtr getSoloOrEnsembleChoice() const; + void setSoloOrEnsembleChoice(const SoloOrEnsembleChoicePtr &value); + bool getHasSoloOrEnsembleChoice() const; + void setHasSoloOrEnsembleChoice(const bool value); + /* _________ VirtualInstrument minOccurs = 0, maxOccurs = 1 _________ */ VirtualInstrumentPtr getVirtualInstrument() const; void setVirtualInstrument(const VirtualInstrumentPtr &value); diff --git a/src/private/mx/core/elements/ScoreInstrumentAttributes.cpp b/src/private/mx/core/elements/ScoreInstrumentAttributes.cpp index 41e95c56..f8b72f6a 100644 --- a/src/private/mx/core/elements/ScoreInstrumentAttributes.cpp +++ b/src/private/mx/core/elements/ScoreInstrumentAttributes.cpp @@ -48,7 +48,7 @@ bool ScoreInstrumentAttributes::fromXElementImpl(std::ostream &message, ::ezxml: if (!isIdFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'id' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/ScoreInstrumentAttributes.h b/src/private/mx/core/elements/ScoreInstrumentAttributes.h index f90641b0..8d784b5b 100644 --- a/src/private/mx/core/elements/ScoreInstrumentAttributes.h +++ b/src/private/mx/core/elements/ScoreInstrumentAttributes.h @@ -5,7 +5,6 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/ForwardDeclare.h" #include "mx/core/XsID.h" diff --git a/src/private/mx/core/elements/ScorePart.cpp b/src/private/mx/core/elements/ScorePart.cpp index 809f90ca..13a52171 100644 --- a/src/private/mx/core/elements/ScorePart.cpp +++ b/src/private/mx/core/elements/ScorePart.cpp @@ -52,6 +52,7 @@ bool ScorePart::hasContents() const std::ostream &ScorePart::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; if (myHasIdentification) { os << std::endl; @@ -92,7 +93,6 @@ std::ostream &ScorePart::streamContents(std::ostream &os, const int indentLevel, x->streamContents(os, indentLevel + 1, isOneLineOnly); } } - isOneLineOnly = false; os << std::endl; return os; } @@ -290,19 +290,19 @@ const MidiDeviceInstrumentGroupSet &ScorePart::getMidiDeviceInstrumentGroupSet() return myMidiDeviceInstrumentGroupSet; } -void ScorePart::addMidiDeviceInstrumentGroup(const MidiDeviceInstrumentGroupPtr &value) +void ScorePart::removeMidiDeviceInstrumentGroup(const MidiDeviceInstrumentGroupSetIterConst &value) { - if (value) + if (value != myMidiDeviceInstrumentGroupSet.cend()) { - myMidiDeviceInstrumentGroupSet.push_back(value); + myMidiDeviceInstrumentGroupSet.erase(value); } } -void ScorePart::removeMidiDeviceInstrumentGroup(const MidiDeviceInstrumentGroupSetIterConst &value) +void ScorePart::addMidiDeviceInstrumentGroup(const MidiDeviceInstrumentGroupPtr &value) { - if (value != myMidiDeviceInstrumentGroupSet.cend()) + if (value) { - myMidiDeviceInstrumentGroupSet.erase(value); + myMidiDeviceInstrumentGroupSet.push_back(value); } } @@ -325,26 +325,34 @@ bool ScorePart::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem { bool isSuccess = true; isSuccess &= myAttributes->fromXElement(message, xelement); - bool isPartNameFound = false; - ::ezxml::XElementIterator end = xelement.end(); - - for (auto it = xelement.begin(); it != end; ++it) - { - importElement(message, *it, isSuccess, *myIdentification, myHasIdentification); - importElement(message, *it, isSuccess, *myPartName, isPartNameFound); - importElement(message, *it, isSuccess, *myPartNameDisplay, myHasPartNameDisplay); - importElement(message, *it, isSuccess, *myPartAbbreviation, myHasPartAbbreviation); - importElement(message, *it, isSuccess, *myPartAbbreviationDisplay, myHasPartAbbreviationDisplay); - importElementSet(message, it, end, isSuccess, "group", myGroupSet); - importElementSet(message, it, end, isSuccess, "score-instrument", myScoreInstrumentSet); - importMidiDeviceInstrumentGroupSet(message, it, end, isSuccess); - } - - if (!isPartNameFound) + auto endIter = xelement.end(); + for (auto it = xelement.begin(); it != endIter; ++it) { - message << "ScorePart: 'part-name' element is required but was not found" << std::endl; + if (importElement(message, *it, isSuccess, *myIdentification, myHasIdentification)) + { + continue; + } + if (importElement(message, *it, isSuccess, *myPartName, isPartNameFound)) + { + continue; + } + if (importElement(message, *it, isSuccess, *myPartNameDisplay, myHasPartNameDisplay)) + { + continue; + } + if (importElement(message, *it, isSuccess, *myPartAbbreviation, myHasPartAbbreviation)) + { + continue; + } + if (importElement(message, *it, isSuccess, *myPartAbbreviationDisplay, myHasPartAbbreviationDisplay)) + { + continue; + } + importElementSet(message, it, endIter, isSuccess, "group", myGroupSet); + importElementSet(message, it, endIter, isSuccess, "score-instrument", myScoreInstrumentSet); + importMidiDeviceInstrumentGroupSet(message, it, endIter, isSuccess); } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/ScorePart.h b/src/private/mx/core/elements/ScorePart.h index 4443c27c..8276d076 100644 --- a/src/private/mx/core/elements/ScorePart.h +++ b/src/private/mx/core/elements/ScorePart.h @@ -49,7 +49,7 @@ class ScorePart : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; ScorePartAttributesPtr getAttributes() const; - void setAttributes(const ScorePartAttributesPtr &value); + void setAttributes(const ScorePartAttributesPtr &attributes); /* _________ Identification minOccurs = 0, maxOccurs = 1 _________ */ IdentificationPtr getIdentification() const; diff --git a/src/private/mx/core/elements/ScorePartwise.cpp b/src/private/mx/core/elements/ScorePartwise.cpp index 5dedc7a1..95009899 100644 --- a/src/private/mx/core/elements/ScorePartwise.cpp +++ b/src/private/mx/core/elements/ScorePartwise.cpp @@ -135,7 +135,8 @@ bool ScorePartwise::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x const std::string elementName = it->getName(); if (elementName == "work" || elementName == "movement-number" || elementName == "movement-title" || - elementName == "identification" || elementName == "defaults" || elementName == "credit") + elementName == "identification" || elementName == "defaults" || elementName == "credit" || + elementName == "part-list") { continue; } diff --git a/src/private/mx/core/elements/ScorePartwiseAttributes.cpp b/src/private/mx/core/elements/ScorePartwiseAttributes.cpp index e632a180..7d806c64 100644 --- a/src/private/mx/core/elements/ScorePartwiseAttributes.cpp +++ b/src/private/mx/core/elements/ScorePartwiseAttributes.cpp @@ -3,10 +3,7 @@ // Distributed under the MIT License #include "mx/core/elements/ScorePartwiseAttributes.h" -#include "ezxml/XAttributeIterator.h" -#include "ezxml/XElement.h" #include "mx/core/FromXElement.h" - #include namespace mx diff --git a/src/private/mx/core/elements/ScorePartwiseAttributes.h b/src/private/mx/core/elements/ScorePartwiseAttributes.h index 2d6a285b..294ae990 100644 --- a/src/private/mx/core/elements/ScorePartwiseAttributes.h +++ b/src/private/mx/core/elements/ScorePartwiseAttributes.h @@ -16,6 +16,7 @@ namespace mx { namespace core { + MX_FORWARD_DECLARE_ATTRIBUTES(ScorePartwiseAttributes) struct ScorePartwiseAttributes : public AttributesInterface diff --git a/src/private/mx/core/elements/ScoreTimewise.cpp b/src/private/mx/core/elements/ScoreTimewise.cpp index 561461a2..11ad5dd5 100644 --- a/src/private/mx/core/elements/ScoreTimewise.cpp +++ b/src/private/mx/core/elements/ScoreTimewise.cpp @@ -137,7 +137,8 @@ bool ScoreTimewise::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x const std::string elementName = it->getName(); if (elementName == "work" || elementName == "movement-number" || elementName == "movement-title" || - elementName == "identification" || elementName == "defaults" || elementName == "credit") + elementName == "identification" || elementName == "defaults" || elementName == "credit" || + elementName == "part-list") { continue; } diff --git a/src/private/mx/core/elements/ScoreTimewise.h b/src/private/mx/core/elements/ScoreTimewise.h index 02c7f436..71a34784 100644 --- a/src/private/mx/core/elements/ScoreTimewise.h +++ b/src/private/mx/core/elements/ScoreTimewise.h @@ -19,8 +19,8 @@ namespace core MX_FORWARD_DECLARE_ATTRIBUTES(ScoreTimewiseAttributes) MX_FORWARD_DECLARE_ELEMENT(ScoreHeaderGroup) -MX_FORWARD_DECLARE_ELEMENT(TimewiseMeasure) MX_FORWARD_DECLARE_ELEMENT(ScoreTimewise) +MX_FORWARD_DECLARE_ELEMENT(TimewiseMeasure) inline ScoreTimewisePtr makeScoreTimewise() { @@ -51,6 +51,7 @@ class ScoreTimewise : public ElementInterface void clearTimewiseMeasureSet(); TimewiseMeasurePtr getTimewiseMeasure(const TimewiseMeasureSetIterConst &setIterator) const; + private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); private: diff --git a/src/private/mx/core/elements/ScoreTimewiseAttributes.cpp b/src/private/mx/core/elements/ScoreTimewiseAttributes.cpp index 575a6027..1b2e1d18 100644 --- a/src/private/mx/core/elements/ScoreTimewiseAttributes.cpp +++ b/src/private/mx/core/elements/ScoreTimewiseAttributes.cpp @@ -3,8 +3,6 @@ // Distributed under the MIT License #include "mx/core/elements/ScoreTimewiseAttributes.h" -#include "ezxml/XAttributeIterator.h" -#include "ezxml/XElement.h" #include "mx/core/FromXElement.h" #include diff --git a/src/private/mx/core/elements/ScoreTimewiseAttributes.h b/src/private/mx/core/elements/ScoreTimewiseAttributes.h index 87a4b18c..f38cc629 100644 --- a/src/private/mx/core/elements/ScoreTimewiseAttributes.h +++ b/src/private/mx/core/elements/ScoreTimewiseAttributes.h @@ -16,6 +16,7 @@ namespace mx { namespace core { + MX_FORWARD_DECLARE_ATTRIBUTES(ScoreTimewiseAttributes) struct ScoreTimewiseAttributes : public AttributesInterface diff --git a/src/private/mx/core/elements/Segno.h b/src/private/mx/core/elements/Segno.h index f3435517..cd60628c 100644 --- a/src/private/mx/core/elements/Segno.h +++ b/src/private/mx/core/elements/Segno.h @@ -17,6 +17,7 @@ namespace mx namespace core { +MX_FORWARD_DECLARE_ATTRIBUTES(EmptyPrintObjectStyleAlignAttributes) MX_FORWARD_DECLARE_ELEMENT(Segno) inline SegnoPtr makeSegno() diff --git a/src/private/mx/core/elements/Slash.cpp b/src/private/mx/core/elements/Slash.cpp index 5e529bf9..f1008e19 100644 --- a/src/private/mx/core/elements/Slash.cpp +++ b/src/private/mx/core/elements/Slash.cpp @@ -44,11 +44,8 @@ std::ostream &Slash::streamContents(std::ostream &os, const int indentLevel, boo mySlashType->toStream(os, indentLevel + 1); for (auto x : mySlashDotSet) { - if (x) - { - os << std::endl; - x->toStream(os, indentLevel + 1); - } + os << std::endl; + x->toStream(os, indentLevel + 1); } os << std::endl; return os; @@ -106,6 +103,15 @@ void Slash::clearSlashDotSet() mySlashDotSet.clear(); } +SlashDotPtr Slash::getSlashDot(const SlashDotSetIterConst &setIterator) const +{ + if (setIterator != mySlashDotSet.cend()) + { + return *setIterator; + } + return SlashDotPtr(); +} + bool Slash::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { bool isSuccess = true; @@ -122,10 +128,6 @@ bool Slash::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) importElementSet(message, it, endIter, isSuccess, "slash-dot", mySlashDotSet); } - if (!isSlashTypeFound) - { - message << "Slash: '" << mySlashType->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Slash.h b/src/private/mx/core/elements/Slash.h index 49ced3e9..5aba6a15 100644 --- a/src/private/mx/core/elements/Slash.h +++ b/src/private/mx/core/elements/Slash.h @@ -38,7 +38,7 @@ class Slash : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; SlashAttributesPtr getAttributes() const; - void setAttributes(const SlashAttributesPtr &value); + void setAttributes(const SlashAttributesPtr &attributes); /* _________ SlashType minOccurs = 1, maxOccurs = 1 _________ */ SlashTypePtr getSlashType() const; @@ -49,6 +49,7 @@ class Slash : public ElementInterface void addSlashDot(const SlashDotPtr &value); void removeSlashDot(const SlashDotSetIterConst &value); void clearSlashDotSet(); + SlashDotPtr getSlashDot(const SlashDotSetIterConst &setIterator) const; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/SlashAttributes.cpp b/src/private/mx/core/elements/SlashAttributes.cpp index 1101cf3f..b26b6720 100644 --- a/src/private/mx/core/elements/SlashAttributes.cpp +++ b/src/private/mx/core/elements/SlashAttributes.cpp @@ -60,10 +60,10 @@ bool SlashAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/SlideAttributes.cpp b/src/private/mx/core/elements/SlideAttributes.cpp index 6922e648..393ce3c1 100644 --- a/src/private/mx/core/elements/SlideAttributes.cpp +++ b/src/private/mx/core/elements/SlideAttributes.cpp @@ -11,19 +11,20 @@ namespace mx namespace core { SlideAttributes::SlideAttributes() - : type(StartStop::start), number(1), lineType(LineType::solid), dashLength(), spaceLength(), defaultX(), defaultY(), - relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), accelerate(), beats(), firstBeat(), lastBeat(), hasType(true), hasNumber(false), - hasLineType(false), hasDashLength(false), hasSpaceLength(false), hasDefaultX(false), hasDefaultY(false), - hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false), hasAccelerate(false), hasBeats(false), hasFirstBeat(false), hasLastBeat(false) + : type(StartStop::start), number(), lineType(LineType::solid), dashLength(), spaceLength(), defaultX(), defaultY(), + relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), + fontWeight(FontWeight::normal), color(), accelerate(YesNo::no), beats(), firstBeat(), lastBeat(), hasType(true), + hasNumber(false), hasLineType(false), hasDashLength(false), hasSpaceLength(false), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), + hasFontSize(false), hasFontWeight(false), hasColor(false), hasAccelerate(false), hasBeats(false), + hasFirstBeat(false), hasLastBeat(false) { } bool SlideAttributes::hasValues() const { return hasType || hasNumber || hasLineType || hasDashLength || hasSpaceLength || hasDefaultX || hasDefaultY || - hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || + hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasAccelerate || hasBeats || hasFirstBeat || hasLastBeat; } @@ -44,6 +45,7 @@ std::ostream &SlideAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, accelerate, "accelerate", hasAccelerate); streamAttribute(os, beats, "beats", hasBeats); streamAttribute(os, firstBeat, "first-beat", hasFirstBeat); @@ -116,6 +118,10 @@ bool SlideAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, accelerate, hasAccelerate, "accelerate", &parseYesNo)) { continue; @@ -137,7 +143,7 @@ bool SlideAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/SlideAttributes.h b/src/private/mx/core/elements/SlideAttributes.h index 7a4ebc50..405df735 100644 --- a/src/private/mx/core/elements/SlideAttributes.h +++ b/src/private/mx/core/elements/SlideAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -42,6 +43,7 @@ struct SlideAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; YesNo accelerate; TrillBeats beats; Percent firstBeat; @@ -59,6 +61,7 @@ struct SlideAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasAccelerate; bool hasBeats; bool hasFirstBeat; diff --git a/src/private/mx/core/elements/SlurAttributes.cpp b/src/private/mx/core/elements/SlurAttributes.cpp index 7f680e6c..4240e94a 100644 --- a/src/private/mx/core/elements/SlurAttributes.cpp +++ b/src/private/mx/core/elements/SlurAttributes.cpp @@ -11,7 +11,7 @@ namespace mx namespace core { SlurAttributes::SlurAttributes() - : type(StartStopContinue::start), number(1), lineType(LineType::solid), dashLength(), spaceLength(), defaultX(), + : type(StartStopContinue::start), number(), lineType(LineType::solid), dashLength(), spaceLength(), defaultX(), defaultY(), relativeX(), relativeY(), placement(AboveBelow::below), orientation(), bezierOffset(), bezierOffset2(), bezierX(), bezierY(), bezierX2(), bezierY2(), color(), hasType(true), hasNumber(false), hasLineType(false), hasDashLength(false), hasSpaceLength(false), hasDefaultX(false), hasDefaultY(false), @@ -23,9 +23,9 @@ SlurAttributes::SlurAttributes() bool SlurAttributes::hasValues() const { - return hasType || hasNumber || hasDashLength || hasSpaceLength || hasDefaultX || hasDefaultY || hasRelativeX || - hasRelativeY || hasPlacement || hasOrientation || hasBezierOffset || hasBezierOffset2 || hasBezierX || - hasBezierY || hasBezierX2 || hasBezierY2; + return hasType || hasNumber || hasLineType || hasDashLength || hasSpaceLength || hasDefaultX || hasDefaultY || + hasRelativeX || hasRelativeY || hasPlacement || hasOrientation || hasBezierOffset || hasBezierOffset2 || + hasBezierX || hasBezierY || hasBezierX2 || hasBezierY2 || hasColor; } std::ostream &SlurAttributes::toStream(std::ostream &os) const @@ -146,7 +146,7 @@ bool SlurAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/SlurAttributes.h b/src/private/mx/core/elements/SlurAttributes.h index df035c9d..ad56d977 100644 --- a/src/private/mx/core/elements/SlurAttributes.h +++ b/src/private/mx/core/elements/SlurAttributes.h @@ -22,34 +22,12 @@ namespace core MX_FORWARD_DECLARE_ATTRIBUTES(SlurAttributes) -/* - - - - - - - - - - */ - -/* - - - - - - -*/ - struct SlurAttributes : public AttributesInterface { public: SlurAttributes(); virtual bool hasValues() const; virtual std::ostream &toStream(std::ostream &os) const; - StartStopContinue type; NumberLevel number; LineType lineType; @@ -68,7 +46,6 @@ struct SlurAttributes : public AttributesInterface TenthsValue bezierX2; TenthsValue bezierY2; Color color; - const bool hasType; bool hasNumber; bool hasLineType; diff --git a/src/private/mx/core/elements/Solo.cpp b/src/private/mx/core/elements/Solo.cpp index da6dc3a0..107b0636 100644 --- a/src/private/mx/core/elements/Solo.cpp +++ b/src/private/mx/core/elements/Solo.cpp @@ -44,8 +44,8 @@ std::ostream &Solo::streamContents(std::ostream &os, const int indentLevel, bool bool Solo::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { - MX_UNUSED(xelement); MX_UNUSED(message); + MX_UNUSED(xelement); return true; } diff --git a/src/private/mx/core/elements/SoloOrEnsembleChoice.cpp b/src/private/mx/core/elements/SoloOrEnsembleChoice.cpp index 74a44bdb..52b38f6e 100644 --- a/src/private/mx/core/elements/SoloOrEnsembleChoice.cpp +++ b/src/private/mx/core/elements/SoloOrEnsembleChoice.cpp @@ -38,20 +38,15 @@ bool SoloOrEnsembleChoice::hasContents() const std::ostream &SoloOrEnsembleChoice::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - switch (myChoice) + if (myChoice == Choice::solo) { - case Choice::solo: { mySolo->toStream(os, indentLevel); } - break; - case Choice::ensemble: { + if (myChoice == Choice::ensemble) + { myEnsemble->toStream(os, indentLevel); } - break; - default: - break; - } - isOneLineOnly = true; + isOneLineOnly = false; return os; } @@ -60,7 +55,7 @@ SoloOrEnsembleChoice::Choice SoloOrEnsembleChoice::getChoice() const return myChoice; } -void SoloOrEnsembleChoice::setChoice(const SoloOrEnsembleChoice::Choice value) +void SoloOrEnsembleChoice::setChoice(const Choice value) { myChoice = value; } diff --git a/src/private/mx/core/elements/SoloOrEnsembleChoice.h b/src/private/mx/core/elements/SoloOrEnsembleChoice.h index a4b01d18..69d3f700 100644 --- a/src/private/mx/core/elements/SoloOrEnsembleChoice.h +++ b/src/private/mx/core/elements/SoloOrEnsembleChoice.h @@ -16,8 +16,8 @@ namespace mx namespace core { -MX_FORWARD_DECLARE_ELEMENT(Ensemble) MX_FORWARD_DECLARE_ELEMENT(Solo) +MX_FORWARD_DECLARE_ELEMENT(Ensemble) MX_FORWARD_DECLARE_ELEMENT(SoloOrEnsembleChoice) inline SoloOrEnsembleChoicePtr makeSoloOrEnsembleChoice() @@ -30,8 +30,8 @@ class SoloOrEnsembleChoice : public ElementInterface public: enum class Choice { - solo = 1, - ensemble = 2 + solo = 0, + ensemble = 1 }; SoloOrEnsembleChoice(); @@ -40,16 +40,10 @@ class SoloOrEnsembleChoice : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; - - /* _________ Choice _________ */ - SoloOrEnsembleChoice::Choice getChoice() const; - void setChoice(const SoloOrEnsembleChoice::Choice value); - - /* _________ Solo minOccurs = 1, maxOccurs = 1 _________ */ + Choice getChoice() const; + void setChoice(const Choice value); SoloPtr getSolo() const; void setSolo(const SoloPtr &value); - - /* _________ Ensemble minOccurs = 1, maxOccurs = 1 _________ */ EnsemblePtr getEnsemble() const; void setEnsemble(const EnsemblePtr &value); diff --git a/src/private/mx/core/elements/Sound.cpp b/src/private/mx/core/elements/Sound.cpp index 9f378906..093bace5 100644 --- a/src/private/mx/core/elements/Sound.cpp +++ b/src/private/mx/core/elements/Sound.cpp @@ -6,6 +6,7 @@ #include "mx/core/FromXElement.h" #include "mx/core/elements/MidiDevice.h" #include "mx/core/elements/MidiInstrument.h" +#include "mx/core/elements/Offset.h" #include "mx/core/elements/Play.h" #include @@ -15,7 +16,8 @@ namespace core { Sound::Sound() : myAttributes(std::make_shared()), myMidiDevice(makeMidiDevice()), myHasMidiDevice(false), - myMidiInstrument(makeMidiInstrument()), myHasMidiInstrument(false), myPlay(makePlay()), myHasPlay(false) + myMidiInstrument(makeMidiInstrument()), myHasMidiInstrument(false), myPlay(makePlay()), myHasPlay(false), + myOffset(makeOffset()), myHasOffset(false) { } @@ -37,7 +39,7 @@ std::ostream &Sound::streamName(std::ostream &os) const bool Sound::hasContents() const { - return myHasMidiDevice || myHasMidiInstrument || myHasPlay; + return myHasMidiDevice || myHasMidiInstrument || myHasPlay || myHasOffset; } std::ostream &Sound::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const @@ -47,7 +49,7 @@ std::ostream &Sound::streamContents(std::ostream &os, const int indentLevel, boo os << std::endl; myMidiDevice->toStream(os, indentLevel + 1); } - if (myMidiInstrument) + if (myHasMidiInstrument) { os << std::endl; myMidiInstrument->toStream(os, indentLevel + 1); @@ -57,10 +59,15 @@ std::ostream &Sound::streamContents(std::ostream &os, const int indentLevel, boo os << std::endl; myPlay->toStream(os, indentLevel + 1); } - if (hasContents()) + if (myHasOffset) { os << std::endl; + myOffset->toStream(os, indentLevel + 1); + } + if (myHasMidiDevice || myHasMidiInstrument || myHasPlay || myHasOffset) + { isOneLineOnly = false; + os << std::endl; } else { @@ -151,6 +158,29 @@ void Sound::setHasPlay(const bool value) myHasPlay = value; } +OffsetPtr Sound::getOffset() const +{ + return myOffset; +} + +void Sound::setOffset(const OffsetPtr &value) +{ + if (value) + { + myOffset = value; + } +} + +bool Sound::getHasOffset() const +{ + return myHasOffset; +} + +void Sound::setHasOffset(const bool value) +{ + myHasOffset = value; +} + bool Sound::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { bool isSuccess = true; @@ -171,6 +201,10 @@ bool Sound::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { continue; } + if (importElement(message, *it, isSuccess, *myOffset, myHasOffset)) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/Sound.h b/src/private/mx/core/elements/Sound.h index e061414f..a757e809 100644 --- a/src/private/mx/core/elements/Sound.h +++ b/src/private/mx/core/elements/Sound.h @@ -20,6 +20,7 @@ namespace core MX_FORWARD_DECLARE_ATTRIBUTES(SoundAttributes) MX_FORWARD_DECLARE_ELEMENT(MidiDevice) MX_FORWARD_DECLARE_ELEMENT(MidiInstrument) +MX_FORWARD_DECLARE_ELEMENT(Offset) MX_FORWARD_DECLARE_ELEMENT(Play) MX_FORWARD_DECLARE_ELEMENT(Sound) @@ -39,7 +40,7 @@ class Sound : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; SoundAttributesPtr getAttributes() const; - void setAttributes(const SoundAttributesPtr &value); + void setAttributes(const SoundAttributesPtr &attributes); /* _________ MidiDevice minOccurs = 0, maxOccurs = 1 _________ */ MidiDevicePtr getMidiDevice() const; @@ -59,6 +60,12 @@ class Sound : public ElementInterface bool getHasPlay() const; void setHasPlay(const bool value); + /* _________ Offset minOccurs = 0, maxOccurs = 1 _________ */ + OffsetPtr getOffset() const; + void setOffset(const OffsetPtr &value); + bool getHasOffset() const; + void setHasOffset(const bool value); + private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); @@ -70,6 +77,8 @@ class Sound : public ElementInterface bool myHasMidiInstrument; PlayPtr myPlay; bool myHasPlay; + OffsetPtr myOffset; + bool myHasOffset; }; } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/SoundAttributes.h b/src/private/mx/core/elements/SoundAttributes.h index 9f8e4705..c3c73bbd 100644 --- a/src/private/mx/core/elements/SoundAttributes.h +++ b/src/private/mx/core/elements/SoundAttributes.h @@ -5,11 +5,11 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" #include "mx/core/TimeOnly.h" +#include "mx/core/XsToken.h" #include "mx/core/YesNoNumber.h" #include diff --git a/src/private/mx/core/elements/StaffDetails.cpp b/src/private/mx/core/elements/StaffDetails.cpp index bc727929..be542d73 100644 --- a/src/private/mx/core/elements/StaffDetails.cpp +++ b/src/private/mx/core/elements/StaffDetails.cpp @@ -45,7 +45,6 @@ bool StaffDetails::hasContents() const std::ostream &StaffDetails::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - isOneLineOnly = !(myHasStaffType || myHasStaffLines || myStaffTuningSet.size() > 0 || myHasCapo || myHasStaffSize); if (myHasStaffType) { os << std::endl; @@ -71,10 +70,15 @@ std::ostream &StaffDetails::streamContents(std::ostream &os, const int indentLev os << std::endl; myStaffSize->toStream(os, indentLevel + 1); } - if (hasContents()) + if (myHasStaffType || myHasStaffLines || myStaffTuningSet.size() > 0 || myHasCapo || myHasStaffSize) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; } @@ -163,6 +167,15 @@ void StaffDetails::clearStaffTuningSet() myStaffTuningSet.clear(); } +StaffTuningPtr StaffDetails::getStaffTuning(const StaffTuningSetIterConst &setIterator) const +{ + if (setIterator != myStaffTuningSet.cend()) + { + return *setIterator; + } + return StaffTuningPtr(); +} + CapoPtr StaffDetails::getCapo() const { return myCapo; diff --git a/src/private/mx/core/elements/StaffDetails.h b/src/private/mx/core/elements/StaffDetails.h index 21a091a8..21eea57e 100644 --- a/src/private/mx/core/elements/StaffDetails.h +++ b/src/private/mx/core/elements/StaffDetails.h @@ -41,7 +41,7 @@ class StaffDetails : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; StaffDetailsAttributesPtr getAttributes() const; - void setAttributes(const StaffDetailsAttributesPtr &value); + void setAttributes(const StaffDetailsAttributesPtr &attributes); /* _________ StaffType minOccurs = 0, maxOccurs = 1 _________ */ StaffTypePtr getStaffType() const; @@ -59,9 +59,8 @@ class StaffDetails : public ElementInterface const StaffTuningSet &getStaffTuningSet() const; void addStaffTuning(const StaffTuningPtr &value); void removeStaffTuning(const StaffTuningSetIterConst &value); - bool getHasStaffTuning() const; - void setHasStaffTuning(const bool value); void clearStaffTuningSet(); + StaffTuningPtr getStaffTuning(const StaffTuningSetIterConst &setIterator) const; /* _________ Capo minOccurs = 0, maxOccurs = 1 _________ */ CapoPtr getCapo() const; diff --git a/src/private/mx/core/elements/StaffDetailsAttributes.cpp b/src/private/mx/core/elements/StaffDetailsAttributes.cpp index 408a38d9..dc48f5da 100644 --- a/src/private/mx/core/elements/StaffDetailsAttributes.cpp +++ b/src/private/mx/core/elements/StaffDetailsAttributes.cpp @@ -11,8 +11,8 @@ namespace mx namespace core { StaffDetailsAttributes::StaffDetailsAttributes() - : number(), showFrets(ShowFrets::numbers), printObject(YesNo::no), printSpacing(YesNo::no), hasNumber(false), - hasShowFrets(false), hasPrintObject(false), hasPrintSpacing(false) + : number(), showFrets(), printObject(YesNo::no), printSpacing(YesNo::no), hasNumber(false), hasShowFrets(false), + hasPrintObject(false), hasPrintSpacing(false) { } @@ -62,7 +62,7 @@ bool StaffDetailsAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XE } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/StaffLayout.cpp b/src/private/mx/core/elements/StaffLayout.cpp index 12e92f84..dc2cac04 100644 --- a/src/private/mx/core/elements/StaffLayout.cpp +++ b/src/private/mx/core/elements/StaffLayout.cpp @@ -45,11 +45,15 @@ std::ostream &StaffLayout::streamContents(std::ostream &os, const int indentLeve os << std::endl; myStaffDistance->toStream(os, indentLevel + 1); } - isOneLineOnly = !hasContents(); - if (!isOneLineOnly) + if (myHasStaffDistance) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; } diff --git a/src/private/mx/core/elements/StaffLayout.h b/src/private/mx/core/elements/StaffLayout.h index 1bcfbff4..ee7ee729 100644 --- a/src/private/mx/core/elements/StaffLayout.h +++ b/src/private/mx/core/elements/StaffLayout.h @@ -37,7 +37,7 @@ class StaffLayout : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; StaffLayoutAttributesPtr getAttributes() const; - void setAttributes(const StaffLayoutAttributesPtr &value); + void setAttributes(const StaffLayoutAttributesPtr &attributes); /* _________ StaffDistance minOccurs = 0, maxOccurs = 1 _________ */ StaffDistancePtr getStaffDistance() const; diff --git a/src/private/mx/core/elements/StaffLayoutAttributes.cpp b/src/private/mx/core/elements/StaffLayoutAttributes.cpp index 41c99820..e666734f 100644 --- a/src/private/mx/core/elements/StaffLayoutAttributes.cpp +++ b/src/private/mx/core/elements/StaffLayoutAttributes.cpp @@ -44,7 +44,7 @@ bool StaffLayoutAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/StaffTuning.cpp b/src/private/mx/core/elements/StaffTuning.cpp index 89c7f1aa..cabca9d3 100644 --- a/src/private/mx/core/elements/StaffTuning.cpp +++ b/src/private/mx/core/elements/StaffTuning.cpp @@ -142,15 +142,6 @@ bool StaffTuning::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xel } } - if (!isTuningStepFound) - { - message << "StaffTuning: '" << myTuningStep->getElementName() << "' is required but was not found" << std::endl; - } - if (!isTuningOctaveFound) - { - message << "StaffTuning: '" << myTuningOctave->getElementName() << "' is required but was not found" - << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/StaffTuning.h b/src/private/mx/core/elements/StaffTuning.h index 0f26dfbf..95b399b8 100644 --- a/src/private/mx/core/elements/StaffTuning.h +++ b/src/private/mx/core/elements/StaffTuning.h @@ -39,7 +39,7 @@ class StaffTuning : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; StaffTuningAttributesPtr getAttributes() const; - void setAttributes(const StaffTuningAttributesPtr &value); + void setAttributes(const StaffTuningAttributesPtr &attributes); /* _________ TuningStep minOccurs = 1, maxOccurs = 1 _________ */ TuningStepPtr getTuningStep() const; diff --git a/src/private/mx/core/elements/StaffTuningAttributes.cpp b/src/private/mx/core/elements/StaffTuningAttributes.cpp index 068912a6..1d1e5bc5 100644 --- a/src/private/mx/core/elements/StaffTuningAttributes.cpp +++ b/src/private/mx/core/elements/StaffTuningAttributes.cpp @@ -44,7 +44,7 @@ bool StaffTuningAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEl } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/StaffType.cpp b/src/private/mx/core/elements/StaffType.cpp index 05b572fa..de225796 100644 --- a/src/private/mx/core/elements/StaffType.cpp +++ b/src/private/mx/core/elements/StaffType.cpp @@ -60,7 +60,6 @@ void StaffType::setValue(const StaffTypeEnum &value) bool StaffType::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); - MX_UNUSED(xelement); myValue = parseStaffTypeEnum(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/StemAttributes.cpp b/src/private/mx/core/elements/StemAttributes.cpp index fb4564e9..d2f74851 100644 --- a/src/private/mx/core/elements/StemAttributes.cpp +++ b/src/private/mx/core/elements/StemAttributes.cpp @@ -11,14 +11,14 @@ namespace mx namespace core { StemAttributes::StemAttributes() - : defaultX(), defaultY(), relativeX(), relativeY(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false) + : defaultX(), defaultY(), relativeX(), relativeY(), color(), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasColor(false) { } bool StemAttributes::hasValues() const { - return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY; + return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasColor; } std::ostream &StemAttributes::toStream(std::ostream &os) const @@ -29,6 +29,7 @@ std::ostream &StemAttributes::toStream(std::ostream &os) const streamAttribute(os, defaultY, "default-y", hasDefaultY); streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -59,6 +60,10 @@ bool StemAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/StemAttributes.h b/src/private/mx/core/elements/StemAttributes.h index 8a439f7b..9c34b706 100644 --- a/src/private/mx/core/elements/StemAttributes.h +++ b/src/private/mx/core/elements/StemAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Decimals.h" #include "mx/core/ForwardDeclare.h" @@ -29,10 +30,12 @@ struct StemAttributes : public AttributesInterface TenthsValue defaultY; TenthsValue relativeX; TenthsValue relativeY; + Color color; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; bool hasRelativeY; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/Stick.cpp b/src/private/mx/core/elements/Stick.cpp index eaf85b7a..557cd7f0 100644 --- a/src/private/mx/core/elements/Stick.cpp +++ b/src/private/mx/core/elements/Stick.cpp @@ -109,14 +109,6 @@ bool Stick::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) } } - if (!isStickTypeFound) - { - message << "Stick: '" << myStickType->getElementName() << "' is required but was not found" << std::endl; - } - if (!isStickMaterialFound) - { - message << "Stick: '" << myStickMaterial->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Stick.h b/src/private/mx/core/elements/Stick.h index 16f72e22..284c513c 100644 --- a/src/private/mx/core/elements/Stick.h +++ b/src/private/mx/core/elements/Stick.h @@ -38,7 +38,7 @@ class Stick : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; StickAttributesPtr getAttributes() const; - void setAttributes(const StickAttributesPtr &value); + void setAttributes(const StickAttributesPtr &attributes); /* _________ StickType minOccurs = 1, maxOccurs = 1 _________ */ StickTypePtr getStickType() const; diff --git a/src/private/mx/core/elements/String.cpp b/src/private/mx/core/elements/String.cpp index e65553e1..ee46456d 100644 --- a/src/private/mx/core/elements/String.cpp +++ b/src/private/mx/core/elements/String.cpp @@ -10,11 +10,11 @@ namespace mx { namespace core { -String::String() : myAttributes(std::make_shared()), myValue() +String::String() : myValue(), myAttributes(std::make_shared()) { } -String::String(const StringNumber &value) : myAttributes(std::make_shared()), myValue(value) +String::String(const StringNumber &value) : myValue(value), myAttributes(std::make_shared()) { } @@ -25,7 +25,11 @@ bool String::hasAttributes() const std::ostream &String::streamAttributes(std::ostream &os) const { - return myAttributes->toStream(os); + if (myAttributes) + { + myAttributes->toStream(os); + } + return os; } std::ostream &String::streamName(std::ostream &os) const @@ -43,7 +47,7 @@ std::ostream &String::streamContents(std::ostream &os, const int indentLevel, bo { MX_UNUSED(indentLevel); isOneLineOnly = true; - core::toStream(os, myValue); + os << myValue; return os; } diff --git a/src/private/mx/core/elements/String.h b/src/private/mx/core/elements/String.h index c2f63026..8bf8eb27 100644 --- a/src/private/mx/core/elements/String.h +++ b/src/private/mx/core/elements/String.h @@ -48,7 +48,7 @@ class String : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; StringAttributesPtr getAttributes() const; - void setAttributes(const StringAttributesPtr &value); + void setAttributes(const StringAttributesPtr &attributes); StringNumber getValue() const; void setValue(const StringNumber &value); @@ -56,8 +56,8 @@ class String : public ElementInterface virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); private: - StringAttributesPtr myAttributes; StringNumber myValue; + StringAttributesPtr myAttributes; }; } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/StringMuteAttributes.cpp b/src/private/mx/core/elements/StringMuteAttributes.cpp index 235aa2f4..b782d297 100644 --- a/src/private/mx/core/elements/StringMuteAttributes.cpp +++ b/src/private/mx/core/elements/StringMuteAttributes.cpp @@ -12,16 +12,17 @@ namespace core { StringMuteAttributes::StringMuteAttributes() : type(OnOff::on), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), halign(LeftCenterRight::center), hasType(true), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), - hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasHalign(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), halign(LeftCenterRight::left), + valign(Valign::bottom), hasType(true), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), + hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), + hasColor(false), hasHalign(false), hasValign(false) { } bool StringMuteAttributes::hasValues() const { return hasType || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight || hasHalign; + hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign; } std::ostream &StringMuteAttributes::toStream(std::ostream &os) const @@ -37,7 +38,9 @@ std::ostream &StringMuteAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); } return os; } @@ -90,16 +93,24 @@ bool StringMuteAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/StringMuteAttributes.h b/src/private/mx/core/elements/StringMuteAttributes.h index 94f122db..20749d61 100644 --- a/src/private/mx/core/elements/StringMuteAttributes.h +++ b/src/private/mx/core/elements/StringMuteAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -37,7 +38,9 @@ struct StringMuteAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; const bool hasType; bool hasDefaultX; bool hasDefaultY; @@ -47,7 +50,9 @@ struct StringMuteAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/StrongAccent.cpp b/src/private/mx/core/elements/StrongAccent.cpp index 9cdb5ec6..bf698311 100644 --- a/src/private/mx/core/elements/StrongAccent.cpp +++ b/src/private/mx/core/elements/StrongAccent.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -StrongAccent::StrongAccent() : myAttributes(std::make_shared()) +StrongAccent::StrongAccent() : ElementInterface(), myAttributes(std::make_shared()) { } @@ -21,7 +21,11 @@ bool StrongAccent::hasAttributes() const std::ostream &StrongAccent::streamAttributes(std::ostream &os) const { - return myAttributes->toStream(os); + if (myAttributes) + { + myAttributes->toStream(os); + } + return os; } std::ostream &StrongAccent::streamName(std::ostream &os) const diff --git a/src/private/mx/core/elements/StrongAccent.h b/src/private/mx/core/elements/StrongAccent.h index 3669fbe9..2e9b6578 100644 --- a/src/private/mx/core/elements/StrongAccent.h +++ b/src/private/mx/core/elements/StrongAccent.h @@ -36,7 +36,7 @@ class StrongAccent : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; StrongAccentAttributesPtr getAttributes() const; - void setAttributes(const StrongAccentAttributesPtr &value); + void setAttributes(const StrongAccentAttributesPtr &attributes); private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/StrongAccentAttributes.cpp b/src/private/mx/core/elements/StrongAccentAttributes.cpp index 9178a294..a5fd0e97 100644 --- a/src/private/mx/core/elements/StrongAccentAttributes.cpp +++ b/src/private/mx/core/elements/StrongAccentAttributes.cpp @@ -12,10 +12,10 @@ namespace core { StrongAccentAttributes::StrongAccentAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), - type(UpDown::up), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), - hasPlacement(false), hasType(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), type(), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false), + hasType(false) { } diff --git a/src/private/mx/core/elements/SuffixAttributes.cpp b/src/private/mx/core/elements/SuffixAttributes.cpp index 6ea127ae..7f0072a6 100644 --- a/src/private/mx/core/elements/SuffixAttributes.cpp +++ b/src/private/mx/core/elements/SuffixAttributes.cpp @@ -12,16 +12,16 @@ namespace core { SuffixAttributes::SuffixAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), hasDefaultX(false), hasDefaultY(false), + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + hasFontWeight(false), hasColor(false) { } bool SuffixAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight; + hasFontWeight || hasColor; } std::ostream &SuffixAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &SuffixAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -83,6 +84,10 @@ bool SuffixAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/SuffixAttributes.h b/src/private/mx/core/elements/SuffixAttributes.h index d9f3a2fe..a05f1210 100644 --- a/src/private/mx/core/elements/SuffixAttributes.h +++ b/src/private/mx/core/elements/SuffixAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct SuffixAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; @@ -44,6 +46,7 @@ struct SuffixAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/SupportsAttributes.cpp b/src/private/mx/core/elements/SupportsAttributes.cpp index 5dc4423c..6a134bee 100644 --- a/src/private/mx/core/elements/SupportsAttributes.cpp +++ b/src/private/mx/core/elements/SupportsAttributes.cpp @@ -66,15 +66,16 @@ bool SupportsAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } + if (!isElementFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'element' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/SupportsAttributes.h b/src/private/mx/core/elements/SupportsAttributes.h index 63661f7b..fba60461 100644 --- a/src/private/mx/core/elements/SupportsAttributes.h +++ b/src/private/mx/core/elements/SupportsAttributes.h @@ -5,7 +5,6 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" #include "mx/core/XsNMToken.h" diff --git a/src/private/mx/core/elements/Syllabic.cpp b/src/private/mx/core/elements/Syllabic.cpp index d21daec3..ab8264ef 100644 --- a/src/private/mx/core/elements/Syllabic.cpp +++ b/src/private/mx/core/elements/Syllabic.cpp @@ -60,7 +60,6 @@ void Syllabic::setValue(const SyllabicEnum &value) bool Syllabic::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); - MX_UNUSED(xelement); myValue = parseSyllabicEnum(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/SystemDistance.cpp b/src/private/mx/core/elements/SystemDistance.cpp index 1aa3656b..7cb08490 100644 --- a/src/private/mx/core/elements/SystemDistance.cpp +++ b/src/private/mx/core/elements/SystemDistance.cpp @@ -64,5 +64,6 @@ bool SystemDistance::fromXElementImpl(std::ostream &message, ::ezxml::XElement & myValue.parse(xelement.getValue()); return true; } + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/SystemDividers.cpp b/src/private/mx/core/elements/SystemDividers.cpp index 0fd1f777..d9124cc5 100644 --- a/src/private/mx/core/elements/SystemDividers.cpp +++ b/src/private/mx/core/elements/SystemDividers.cpp @@ -93,16 +93,6 @@ bool SystemDividers::fromXElementImpl(std::ostream &message, ::ezxml::XElement & } } - if (!isLeftDividerFound) - { - message << "SystemDividers: '" << myLeftDivider->getElementName() << "' is required but was not found" - << std::endl; - } - if (!isRightDividerFound) - { - message << "SystemDividers: '" << myRightDivider->getElementName() << "' is required but was not found" - << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/SystemLayout.cpp b/src/private/mx/core/elements/SystemLayout.cpp index 516dff98..49a9f44a 100644 --- a/src/private/mx/core/elements/SystemLayout.cpp +++ b/src/private/mx/core/elements/SystemLayout.cpp @@ -64,11 +64,15 @@ std::ostream &SystemLayout::streamContents(std::ostream &os, const int indentLev os << std::endl; mySystemDividers->toStream(os, indentLevel + 1); } - isOneLineOnly = !hasContents(); - if (!isOneLineOnly) + if (myHasSystemMargins || myHasSystemDistance || myHasTopSystemDistance || myHasSystemDividers) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; } diff --git a/src/private/mx/core/elements/SystemLayout.h b/src/private/mx/core/elements/SystemLayout.h index 3c6c5961..f3a40a71 100644 --- a/src/private/mx/core/elements/SystemLayout.h +++ b/src/private/mx/core/elements/SystemLayout.h @@ -43,6 +43,8 @@ class SystemLayout : public ElementInterface void setSystemMargins(const SystemMarginsPtr &value); bool getHasSystemMargins() const; void setHasSystemMargins(const bool value); + + /* _________ SystemDistance minOccurs = 0, maxOccurs = 1 _________ */ SystemDistancePtr getSystemDistance() const; void setSystemDistance(const SystemDistancePtr &value); bool getHasSystemDistance() const; diff --git a/src/private/mx/core/elements/SystemMargins.cpp b/src/private/mx/core/elements/SystemMargins.cpp index 42e0db60..93f2443c 100644 --- a/src/private/mx/core/elements/SystemMargins.cpp +++ b/src/private/mx/core/elements/SystemMargins.cpp @@ -93,17 +93,7 @@ bool SystemMargins::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x } } - if (!isLeftMarginFound) - { - message << "SystemMargins: '" << myLeftMargin->getElementName() << "' is required but was not found" - << std::endl; - } - if (!isRightMarginFound) - { - message << "SystemMargins: '" << myRightMargin->getElementName() << "' is required but was not found" - << std::endl; - } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/TapAttributes.cpp b/src/private/mx/core/elements/TapAttributes.cpp index 55c5da86..7898bae7 100644 --- a/src/private/mx/core/elements/TapAttributes.cpp +++ b/src/private/mx/core/elements/TapAttributes.cpp @@ -12,16 +12,16 @@ namespace core { TapAttributes::TapAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool TapAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &TapAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &TapAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,6 +85,10 @@ bool TapAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/TapAttributes.h b/src/private/mx/core/elements/TapAttributes.h index 36d10b34..e0cac4e6 100644 --- a/src/private/mx/core/elements/TapAttributes.h +++ b/src/private/mx/core/elements/TapAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct TapAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct TapAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/TextAttributes.cpp b/src/private/mx/core/elements/TextAttributes.cpp index a6505ace..77d5f4bf 100644 --- a/src/private/mx/core/elements/TextAttributes.cpp +++ b/src/private/mx/core/elements/TextAttributes.cpp @@ -11,18 +11,18 @@ namespace mx namespace core { TextAttributes::TextAttributes() - : fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), underline(), overline(), lineThrough(), rotation(), letterSpacing(), - lang(XmlLang{"it"}), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), + : fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + color(), underline(), overline(), lineThrough(), rotation(), letterSpacing(), lang("it"), dir(), + hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasUnderline(false), hasOverline(false), hasLineThrough(false), hasRotation(false), hasLetterSpacing(false), - hasLang(false) + hasLang(false), hasDir(false) { } bool TextAttributes::hasValues() const { - return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasUnderline || hasOverline || - hasLineThrough || hasRotation || hasLetterSpacing || hasLang; + return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor || hasUnderline || hasOverline || + hasLineThrough || hasRotation || hasLetterSpacing || hasLang || hasDir; } std::ostream &TextAttributes::toStream(std::ostream &os) const @@ -33,12 +33,14 @@ std::ostream &TextAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, underline, "underline", hasUnderline); streamAttribute(os, overline, "overline", hasOverline); streamAttribute(os, lineThrough, "line-through", hasLineThrough); streamAttribute(os, rotation, "rotation", hasRotation); streamAttribute(os, letterSpacing, "letter-spacing", hasLetterSpacing); streamAttribute(os, lang, "xml:lang", hasLang); + streamAttribute(os, dir, "dir", hasDir); } return os; } @@ -70,6 +72,10 @@ bool TextAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, underline, hasUnderline, "underline")) { continue; @@ -90,11 +96,11 @@ bool TextAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) + if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) + if (parseAttribute(message, it, className, isSuccess, dir, hasDir, "dir", &parseTextDirection)) { continue; } diff --git a/src/private/mx/core/elements/TextAttributes.h b/src/private/mx/core/elements/TextAttributes.h index ed9f64f5..fae3a040 100644 --- a/src/private/mx/core/elements/TextAttributes.h +++ b/src/private/mx/core/elements/TextAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -35,22 +36,26 @@ struct TextAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; NumberOfLines underline; NumberOfLines overline; NumberOfLines lineThrough; RotationDegrees rotation; NumberOrNormal letterSpacing; XmlLang lang; + TextDirection dir; bool hasFontFamily; bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasUnderline; bool hasOverline; bool hasLineThrough; bool hasRotation; bool hasLetterSpacing; bool hasLang; + bool hasDir; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/TieAttributes.cpp b/src/private/mx/core/elements/TieAttributes.cpp index f2501283..028ed3d1 100644 --- a/src/private/mx/core/elements/TieAttributes.cpp +++ b/src/private/mx/core/elements/TieAttributes.cpp @@ -53,10 +53,10 @@ bool TieAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement &x if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/TieAttributes.h b/src/private/mx/core/elements/TieAttributes.h index efe4b699..54a21521 100644 --- a/src/private/mx/core/elements/TieAttributes.h +++ b/src/private/mx/core/elements/TieAttributes.h @@ -5,7 +5,6 @@ #pragma once #include "mx/core/AttributesInterface.h" -#include "mx/core/CommaSeparatedText.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" #include "mx/core/TimeOnly.h" diff --git a/src/private/mx/core/elements/TiedAttributes.cpp b/src/private/mx/core/elements/TiedAttributes.cpp index 20a4d109..df7a4454 100644 --- a/src/private/mx/core/elements/TiedAttributes.cpp +++ b/src/private/mx/core/elements/TiedAttributes.cpp @@ -114,7 +114,7 @@ bool TiedAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } - if (parseAttribute(message, it, className, isSuccess, bezierOffset2, hasBezierOffset2, "bezier-offset-2")) + if (parseAttribute(message, it, className, isSuccess, bezierOffset2, hasBezierOffset2, "bezier-offset2")) { continue; } @@ -126,11 +126,11 @@ bool TiedAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } - if (parseAttribute(message, it, className, isSuccess, bezierX2, hasBezierX2, "bezier-x-2")) + if (parseAttribute(message, it, className, isSuccess, bezierX2, hasBezierX2, "bezier-x2")) { continue; } - if (parseAttribute(message, it, className, isSuccess, bezierY2, hasBezierY2, "bezier-y-2")) + if (parseAttribute(message, it, className, isSuccess, bezierY2, hasBezierY2, "bezier-y2")) { continue; } @@ -143,7 +143,7 @@ bool TiedAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/Time.cpp b/src/private/mx/core/elements/Time.cpp index 49038285..d0251cca 100644 --- a/src/private/mx/core/elements/Time.cpp +++ b/src/private/mx/core/elements/Time.cpp @@ -67,7 +67,10 @@ TimeChoicePtr Time::getTimeChoice() const void Time::setTimeChoice(const TimeChoicePtr &value) { - myChoice = value; + if (value) + { + myChoice = value; + } } bool Time::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) diff --git a/src/private/mx/core/elements/TimeAttributes.cpp b/src/private/mx/core/elements/TimeAttributes.cpp index 64ad350b..1ed9dc85 100644 --- a/src/private/mx/core/elements/TimeAttributes.cpp +++ b/src/private/mx/core/elements/TimeAttributes.cpp @@ -13,7 +13,7 @@ namespace core TimeAttributes::TimeAttributes() : number(), symbol(), separator(), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), - halign(LeftCenterRight::center), valign(), printObject(YesNo::no), hasNumber(false), hasSymbol(false), + halign(LeftCenterRight::left), valign(Valign::bottom), printObject(YesNo::no), hasNumber(false), hasSymbol(false), hasSeparator(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), hasValign(false), hasPrintObject(false) diff --git a/src/private/mx/core/elements/TimeModification.cpp b/src/private/mx/core/elements/TimeModification.cpp index 6e31f7e0..c5ce2313 100644 --- a/src/private/mx/core/elements/TimeModification.cpp +++ b/src/private/mx/core/elements/TimeModification.cpp @@ -44,6 +44,7 @@ bool TimeModification::hasContents() const std::ostream &TimeModification::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { + isOneLineOnly = false; os << std::endl; myActualNotes->toStream(os, indentLevel + 1); os << std::endl; @@ -54,7 +55,6 @@ std::ostream &TimeModification::streamContents(std::ostream &os, const int inden myNormalTypeNormalDotGroup->streamContents(os, indentLevel + 1, isOneLineOnly); } os << std::endl; - isOneLineOnly = false; return os; } @@ -129,27 +129,16 @@ bool TimeModification::fromXElementImpl(std::ostream &message, ::ezxml::XElement myHasNormalTypeNormalDotGroup = true; isSuccess = myNormalTypeNormalDotGroup->getNormalType()->fromXElement(message, *it); } - if (it->getName() == "normal-dot") { myHasNormalTypeNormalDotGroup = true; - auto item = makeNormalDot(); - isSuccess &= item->fromXElement(message, *it); - myNormalTypeNormalDotGroup->addNormalDot(item); + auto normalDot = makeNormalDot(); + isSuccess &= normalDot->fromXElement(message, *it); + myNormalTypeNormalDotGroup->addNormalDot(normalDot); } } - if (!isActualNotesFound) - { - message << "TimeModification: '" << myActualNotes->getElementName() << "' is required but was not found" - << std::endl; - } - if (!isNormalNotesFound) - { - message << "TimeModification: '" << myNormalNotes->getElementName() << "' is required but was not found" - << std::endl; - } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/TimeModificationNormalTypeNormalDot.cpp b/src/private/mx/core/elements/TimeModificationNormalTypeNormalDot.cpp index 277927a4..fb8778a0 100644 --- a/src/private/mx/core/elements/TimeModificationNormalTypeNormalDot.cpp +++ b/src/private/mx/core/elements/TimeModificationNormalTypeNormalDot.cpp @@ -40,13 +40,19 @@ bool TimeModificationNormalTypeNormalDot::hasContents() const std::ostream &TimeModificationNormalTypeNormalDot::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - isOneLineOnly = false; + bool isFirst = true; + if (!isFirst) + os << std::endl; myNormalType->toStream(os, indentLevel); + isFirst = false; for (auto x : myNormalDotSet) { - os << std::endl; + if (!isFirst) + os << std::endl; x->toStream(os, indentLevel); + isFirst = false; } + isOneLineOnly = !hasContents(); return os; } @@ -102,22 +108,43 @@ bool TimeModificationNormalTypeNormalDot::fromXElementImpl(std::ostream &message { bool isSuccess = true; bool isNormalTypeFound = false; - - auto endIter = xelement.end(); - for (auto it = xelement.begin(); it != endIter; ++it) + bool isFirstNormalDotAdded = false; + for (auto it = xelement.begin(); it != xelement.end(); ++it) { - if (importElement(message, *it, isSuccess, *myNormalType, isNormalTypeFound)) + const std::string elementName = it->getName(); + + if (elementName == "normal-type") + { + isNormalTypeFound = true; + isSuccess &= myNormalType->fromXElement(message, *it); + } + else if (elementName == "normal-dot") + { + auto normalDot = makeNormalDot(); + isSuccess &= normalDot->fromXElement(message, *it); + + if (!isFirstNormalDotAdded && myNormalDotSet.size() == 1) + { + *(myNormalDotSet.begin()) = normalDot; + isFirstNormalDotAdded = true; + } + else + { + myNormalDotSet.push_back(normalDot); + isFirstNormalDotAdded = true; + } + } + else { - continue; + if (!isNormalTypeFound) + { + message << "TimeModificationNormalTypeNormalDot: a 'normal-type' element is required but was not found" + << std::endl; + return false; + } } - importElementSet(message, it, endIter, isSuccess, "normal-dot", myNormalDotSet); } - if (!isNormalTypeFound) - { - message << "TimeModificationNormalTypeNormalDot: '" << myNormalType->getElementName() - << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/TimeSignatureGroup.cpp b/src/private/mx/core/elements/TimeSignatureGroup.cpp index 79123d3f..c07b2fa7 100644 --- a/src/private/mx/core/elements/TimeSignatureGroup.cpp +++ b/src/private/mx/core/elements/TimeSignatureGroup.cpp @@ -41,15 +41,23 @@ bool TimeSignatureGroup::hasContents() const std::ostream &TimeSignatureGroup::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - isOneLineOnly = false; + bool isFirst = true; + if (!isFirst) + os << std::endl; myBeats->toStream(os, indentLevel); - os << std::endl; + isFirst = false; + if (!isFirst) + os << std::endl; myBeatType->toStream(os, indentLevel); + isFirst = false; if (myHasInterchangeable) { - os << std::endl; + if (!isFirst) + os << std::endl; myInterchangeable->toStream(os, indentLevel); + isFirst = false; } + isOneLineOnly = !hasContents(); return os; } @@ -103,5 +111,6 @@ void TimeSignatureGroup::setHasInterchangeable(const bool value) } MX_FROM_XELEMENT_UNUSED(TimeSignatureGroup); + } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/TimeSignatureGroup.h b/src/private/mx/core/elements/TimeSignatureGroup.h index 537e1326..d937aa13 100644 --- a/src/private/mx/core/elements/TimeSignatureGroup.h +++ b/src/private/mx/core/elements/TimeSignatureGroup.h @@ -36,10 +36,16 @@ class TimeSignatureGroup : public ElementInterface virtual std::ostream &streamName(std::ostream &os) const; virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; + + /* _________ Beats minOccurs = 1, maxOccurs = 1 _________ */ BeatsPtr getBeats() const; void setBeats(const BeatsPtr &value); + + /* _________ BeatType minOccurs = 1, maxOccurs = 1 _________ */ BeatTypePtr getBeatType() const; void setBeatType(const BeatTypePtr &value); + + /* _________ Interchangeable minOccurs = 0, maxOccurs = 1 _________ */ InterchangeablePtr getInterchangeable() const; void setInterchangeable(const InterchangeablePtr &value); bool getHasInterchangeable() const; diff --git a/src/private/mx/core/elements/TimewiseMeasure.cpp b/src/private/mx/core/elements/TimewiseMeasure.cpp index 9a580f86..5e8a1cee 100644 --- a/src/private/mx/core/elements/TimewiseMeasure.cpp +++ b/src/private/mx/core/elements/TimewiseMeasure.cpp @@ -146,6 +146,5 @@ bool TimewiseMeasure::fromXElementImpl(std::ostream &message, ::ezxml::XElement return isSuccess; } - } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/TimewiseMeasure.h b/src/private/mx/core/elements/TimewiseMeasure.h index 99a497ca..9869daa4 100644 --- a/src/private/mx/core/elements/TimewiseMeasure.h +++ b/src/private/mx/core/elements/TimewiseMeasure.h @@ -18,8 +18,8 @@ namespace core { MX_FORWARD_DECLARE_ATTRIBUTES(MeasureAttributes) -MX_FORWARD_DECLARE_ELEMENT(TimewisePart) MX_FORWARD_DECLARE_ELEMENT(TimewiseMeasure) +MX_FORWARD_DECLARE_ELEMENT(TimewisePart) inline TimewiseMeasurePtr makeTimewiseMeasure() { @@ -39,7 +39,7 @@ class TimewiseMeasure : public ElementInterface MeasureAttributesPtr getAttributes() const; void setAttributes(const MeasureAttributesPtr &value); - /* _________ TimewisePart minOccurs = 1, maxOccurs = unbounded _________ */ + /* _________ TimewisePart minOccurs = 0, maxOccurs = unbounded _________ */ const TimewisePartSet &getTimewisePartSet() const; void addTimewisePart(const TimewisePartPtr &value); void removeTimewisePart(const TimewisePartSetIterConst &value); diff --git a/src/private/mx/core/elements/TimewisePart.cpp b/src/private/mx/core/elements/TimewisePart.cpp index d5fdcaa6..95eaf407 100644 --- a/src/private/mx/core/elements/TimewisePart.cpp +++ b/src/private/mx/core/elements/TimewisePart.cpp @@ -19,7 +19,6 @@ #include "mx/core/elements/Print.h" #include "mx/core/elements/Properties.h" #include "mx/core/elements/Sound.h" - #include namespace mx diff --git a/src/private/mx/core/elements/Toe.cpp b/src/private/mx/core/elements/Toe.cpp index d76e4622..ac8a7ce9 100644 --- a/src/private/mx/core/elements/Toe.cpp +++ b/src/private/mx/core/elements/Toe.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -Toe::Toe() : myAttributes(std::make_shared()) +Toe::Toe() : ElementInterface(), myAttributes(std::make_shared()) { } @@ -21,7 +21,11 @@ bool Toe::hasAttributes() const std::ostream &Toe::streamAttributes(std::ostream &os) const { - return myAttributes->toStream(os); + if (myAttributes) + { + myAttributes->toStream(os); + } + return os; } std::ostream &Toe::streamName(std::ostream &os) const diff --git a/src/private/mx/core/elements/Toe.h b/src/private/mx/core/elements/Toe.h index 7c0bc38b..88c9db47 100644 --- a/src/private/mx/core/elements/Toe.h +++ b/src/private/mx/core/elements/Toe.h @@ -36,7 +36,7 @@ class Toe : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; ToeAttributesPtr getAttributes() const; - void setAttributes(const ToeAttributesPtr &value); + void setAttributes(const ToeAttributesPtr &attributes); private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/TraditionalKey.cpp b/src/private/mx/core/elements/TraditionalKey.cpp index 317ec110..afa69b28 100644 --- a/src/private/mx/core/elements/TraditionalKey.cpp +++ b/src/private/mx/core/elements/TraditionalKey.cpp @@ -7,7 +7,6 @@ #include "mx/core/elements/Cancel.h" #include "mx/core/elements/Fifths.h" #include "mx/core/elements/Mode.h" - #include namespace mx @@ -15,9 +14,7 @@ namespace mx namespace core { TraditionalKey::TraditionalKey() - : ElementInterface(), myCancel(std::make_shared(FifthsValue{0})), - myFifths(std::make_shared(FifthsValue{0})), myMode(std::make_shared()), myHasCancel(false), - myHasMode(false) + : myCancel(makeCancel()), myHasCancel(false), myFifths(makeFifths()), myMode(makeMode()), myHasMode(false) { } @@ -43,20 +40,26 @@ bool TraditionalKey::hasContents() const std::ostream &TraditionalKey::streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const { - if (getHasCancel() && myCancel) - { - myCancel->toStream(os, indentLevel) << std::endl; - } - if (myFifths) + isOneLineOnly = false; + bool isFirst = true; + if (myHasCancel) { - myFifths->toStream(os, indentLevel); + if (!isFirst) + os << std::endl; + myCancel->toStream(os, indentLevel); + isFirst = false; } - if (getHasMode() && myMode) - { + if (!isFirst) os << std::endl; + myFifths->toStream(os, indentLevel); + isFirst = false; + if (myHasMode) + { + if (!isFirst) + os << std::endl; myMode->toStream(os, indentLevel); + isFirst = false; } - isOneLineOnly = (!myCancel && !myMode); return os; } @@ -119,34 +122,7 @@ void TraditionalKey::setHasMode(const bool value) myHasMode = value; } -bool TraditionalKey::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) -{ - bool isSuccess = true; - bool isFifthsFound = false; - - auto endIter = xelement.end(); - for (auto it = xelement.begin(); it != endIter; ++it) - { - if (importElement(message, *it, isSuccess, *myCancel, myHasCancel)) - { - continue; - } - if (importElement(message, *it, isSuccess, *myFifths, isFifthsFound)) - { - continue; - } - if (importElement(message, *it, isSuccess, *myMode, myHasMode)) - { - continue; - } - } - - if (!isFifthsFound) - { - message << "TraditionalKey: '" << myFifths->getElementName() << "' is required but was not found" << std::endl; - } - MX_RETURN_IS_SUCCESS; -} +MX_FROM_XELEMENT_UNUSED(TraditionalKey); } // namespace core } // namespace mx diff --git a/src/private/mx/core/elements/TraditionalKey.h b/src/private/mx/core/elements/TraditionalKey.h index 83fc2c69..d6d7239f 100644 --- a/src/private/mx/core/elements/TraditionalKey.h +++ b/src/private/mx/core/elements/TraditionalKey.h @@ -52,9 +52,9 @@ class TraditionalKey : public ElementInterface private: CancelPtr myCancel; + bool myHasCancel; FifthsPtr myFifths; ModePtr myMode; - bool myHasCancel; bool myHasMode; }; } // namespace core diff --git a/src/private/mx/core/elements/Transpose.cpp b/src/private/mx/core/elements/Transpose.cpp index d64c4476..a4aea5f8 100644 --- a/src/private/mx/core/elements/Transpose.cpp +++ b/src/private/mx/core/elements/Transpose.cpp @@ -188,10 +188,6 @@ bool Transpose::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem } } - if (!isChromaticFound) - { - message << "Transpose: '" << myChromatic->getElementName() << "' is required but was not found" << std::endl; - } MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/Transpose.h b/src/private/mx/core/elements/Transpose.h index 649ae55a..a3b63d7b 100644 --- a/src/private/mx/core/elements/Transpose.h +++ b/src/private/mx/core/elements/Transpose.h @@ -40,7 +40,7 @@ class Transpose : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; TransposeAttributesPtr getAttributes() const; - void setAttributes(const TransposeAttributesPtr &value); + void setAttributes(const TransposeAttributesPtr &attributes); /* _________ Diatonic minOccurs = 0, maxOccurs = 1 _________ */ DiatonicPtr getDiatonic() const; diff --git a/src/private/mx/core/elements/TremoloAttributes.cpp b/src/private/mx/core/elements/TremoloAttributes.cpp index f08d751c..58b321ab 100644 --- a/src/private/mx/core/elements/TremoloAttributes.cpp +++ b/src/private/mx/core/elements/TremoloAttributes.cpp @@ -12,17 +12,17 @@ namespace core { TremoloAttributes::TremoloAttributes() : type(StartStopSingle::single), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), hasType(false), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasPlacement(false) + hasColor(false), hasPlacement(false) { } bool TremoloAttributes::hasValues() const { return hasType || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight || hasPlacement; + hasFontSize || hasFontWeight || hasColor || hasPlacement; } std::ostream &TremoloAttributes::toStream(std::ostream &os) const @@ -38,6 +38,7 @@ std::ostream &TremoloAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -90,6 +91,10 @@ bool TremoloAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/TremoloAttributes.h b/src/private/mx/core/elements/TremoloAttributes.h index 1ed65e5a..71d37cca 100644 --- a/src/private/mx/core/elements/TremoloAttributes.h +++ b/src/private/mx/core/elements/TremoloAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -37,6 +38,7 @@ struct TremoloAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasType; bool hasDefaultX; @@ -47,6 +49,7 @@ struct TremoloAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/Tuplet.cpp b/src/private/mx/core/elements/Tuplet.cpp index 4e2403c7..efb3acb8 100644 --- a/src/private/mx/core/elements/Tuplet.cpp +++ b/src/private/mx/core/elements/Tuplet.cpp @@ -51,11 +51,15 @@ std::ostream &Tuplet::streamContents(std::ostream &os, const int indentLevel, bo os << std::endl; myTupletNormal->toStream(os, indentLevel + 1); } - isOneLineOnly = !hasContents(); - if (!isOneLineOnly) + if (myHasTupletActual || myHasTupletNormal) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; } @@ -136,7 +140,7 @@ bool Tuplet::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/Tuplet.h b/src/private/mx/core/elements/Tuplet.h index b4261b26..30e2850b 100644 --- a/src/private/mx/core/elements/Tuplet.h +++ b/src/private/mx/core/elements/Tuplet.h @@ -38,7 +38,7 @@ class Tuplet : public ElementInterface virtual bool hasContents() const; virtual std::ostream &streamContents(std::ostream &os, const int indentLevel, bool &isOneLineOnly) const; TupletAttributesPtr getAttributes() const; - void setAttributes(const TupletAttributesPtr &value); + void setAttributes(const TupletAttributesPtr &attributes); /* _________ TupletActual minOccurs = 0, maxOccurs = 1 _________ */ TupletActualPtr getTupletActual() const; diff --git a/src/private/mx/core/elements/TupletActual.cpp b/src/private/mx/core/elements/TupletActual.cpp index bf6d1bb0..ed38aaa0 100644 --- a/src/private/mx/core/elements/TupletActual.cpp +++ b/src/private/mx/core/elements/TupletActual.cpp @@ -57,7 +57,7 @@ std::ostream &TupletActual::streamContents(std::ostream &os, const int indentLev os << std::endl; x->toStream(os, indentLevel + 1); } - if (hasContents()) + if (myHasTupletNumber || myHasTupletType || myTupletDotSet.size() > 0) { isOneLineOnly = false; os << std::endl; diff --git a/src/private/mx/core/elements/TupletAttributes.cpp b/src/private/mx/core/elements/TupletAttributes.cpp index c3a3c1d2..dbadb3f9 100644 --- a/src/private/mx/core/elements/TupletAttributes.cpp +++ b/src/private/mx/core/elements/TupletAttributes.cpp @@ -11,10 +11,10 @@ namespace mx namespace core { TupletAttributes::TupletAttributes() - : type(), number(), bracket(YesNo::no), showNumber(), showType(), lineShape(), defaultX(), defaultY(), relativeX(), - relativeY(), placement(AboveBelow::below), hasType(true), hasNumber(false), hasBracket(false), - hasShowNumber(false), hasShowType(false), hasLineShape(false), hasDefaultX(false), hasDefaultY(false), - hasRelativeX(false), hasRelativeY(false), hasPlacement(false) + : type(StartStop::start), number(), bracket(YesNo::no), showNumber(), showType(), lineShape(LineShape::straight), + defaultX(), defaultY(), relativeX(), relativeY(), placement(AboveBelow::below), hasType(true), hasNumber(false), + hasBracket(false), hasShowNumber(false), hasShowType(false), hasLineShape(false), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasPlacement(false) { } @@ -104,7 +104,7 @@ bool TupletAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/TupletDotAttributes.cpp b/src/private/mx/core/elements/TupletDotAttributes.cpp index 6c5d7470..fd58f9f3 100644 --- a/src/private/mx/core/elements/TupletDotAttributes.cpp +++ b/src/private/mx/core/elements/TupletDotAttributes.cpp @@ -12,13 +12,13 @@ namespace core { TupletDotAttributes::TupletDotAttributes() : fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), - hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false) + color(), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool TupletDotAttributes::hasValues() const { - return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight; + return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &TupletDotAttributes::toStream(std::ostream &os) const @@ -29,6 +29,7 @@ std::ostream &TupletDotAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -60,6 +61,10 @@ bool TupletDotAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElem { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/TupletDotAttributes.h b/src/private/mx/core/elements/TupletDotAttributes.h index 0b7bdad1..ff9f46c7 100644 --- a/src/private/mx/core/elements/TupletDotAttributes.h +++ b/src/private/mx/core/elements/TupletDotAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" @@ -31,10 +32,12 @@ struct TupletDotAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasFontFamily; bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/TupletNormal.cpp b/src/private/mx/core/elements/TupletNormal.cpp index b3a1b5ed..617adf79 100644 --- a/src/private/mx/core/elements/TupletNormal.cpp +++ b/src/private/mx/core/elements/TupletNormal.cpp @@ -57,7 +57,7 @@ std::ostream &TupletNormal::streamContents(std::ostream &os, const int indentLev os << std::endl; x->toStream(os, indentLevel + 1); } - if (hasContents()) + if (myHasTupletNumber || myHasTupletType || myTupletDotSet.size() > 0) { isOneLineOnly = false; os << std::endl; diff --git a/src/private/mx/core/elements/TupletNumberAttributes.cpp b/src/private/mx/core/elements/TupletNumberAttributes.cpp index eee47dce..3ff32473 100644 --- a/src/private/mx/core/elements/TupletNumberAttributes.cpp +++ b/src/private/mx/core/elements/TupletNumberAttributes.cpp @@ -11,15 +11,14 @@ namespace mx namespace core { TupletNumberAttributes::TupletNumberAttributes() - : fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + : fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + color(), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool TupletNumberAttributes::hasValues() const { - return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight; + return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &TupletNumberAttributes::toStream(std::ostream &os) const @@ -30,6 +29,7 @@ std::ostream &TupletNumberAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -61,9 +61,13 @@ bool TupletNumberAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XE { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/TupletNumberAttributes.h b/src/private/mx/core/elements/TupletNumberAttributes.h index 3eaab4bd..3fda49c3 100644 --- a/src/private/mx/core/elements/TupletNumberAttributes.h +++ b/src/private/mx/core/elements/TupletNumberAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" @@ -31,10 +32,12 @@ struct TupletNumberAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasFontFamily; bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/TupletTypeAttributes.cpp b/src/private/mx/core/elements/TupletTypeAttributes.cpp index 2d0099fd..c050fe6b 100644 --- a/src/private/mx/core/elements/TupletTypeAttributes.cpp +++ b/src/private/mx/core/elements/TupletTypeAttributes.cpp @@ -11,15 +11,14 @@ namespace mx namespace core { TupletTypeAttributes::TupletTypeAttributes() - : fontFamily(), fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), - fontWeight(FontWeight::normal), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), - hasFontWeight(false) + : fontFamily(), fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), + color(), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false) { } bool TupletTypeAttributes::hasValues() const { - return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight; + return hasFontFamily || hasFontStyle || hasFontSize || hasFontWeight || hasColor; } std::ostream &TupletTypeAttributes::toStream(std::ostream &os) const @@ -30,6 +29,7 @@ std::ostream &TupletTypeAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -61,6 +61,10 @@ bool TupletTypeAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEle { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/TupletTypeAttributes.h b/src/private/mx/core/elements/TupletTypeAttributes.h index a34e856f..4a4f906a 100644 --- a/src/private/mx/core/elements/TupletTypeAttributes.h +++ b/src/private/mx/core/elements/TupletTypeAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Enums.h" #include "mx/core/FontSize.h" @@ -31,10 +32,12 @@ struct TupletTypeAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; bool hasFontFamily; bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/TurnAttributes.cpp b/src/private/mx/core/elements/TurnAttributes.cpp index 47751b1a..cc7d9fb4 100644 --- a/src/private/mx/core/elements/TurnAttributes.cpp +++ b/src/private/mx/core/elements/TurnAttributes.cpp @@ -12,20 +12,20 @@ namespace core { TurnAttributes::TurnAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), placement(AboveBelow::below), - startNote(StartNote::main), trillStep(), twoNoteTurn(TwoNoteTurn::none), accelerate(YesNo::no), beats(), - secondBeat(), lastBeat(), slash(YesNo::no), hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), - hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), hasFontSize(false), hasFontWeight(false), - hasPlacement(false), hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), - hasBeats(false), hasSecondBeat(false), hasLastBeat(false), hasSlash(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), startNote(), + trillStep(), twoNoteTurn(), accelerate(YesNo::no), beats(), secondBeat(), lastBeat(), slash(YesNo::no), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false), + hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), + hasSecondBeat(false), hasLastBeat(false), hasSlash(false) { } bool TurnAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || hasAccelerate || - hasBeats || hasSecondBeat || hasLastBeat || hasSlash; + hasFontWeight || hasColor || hasPlacement || hasStartNote || hasTrillStep || hasTwoNoteTurn || + hasAccelerate || hasBeats || hasSecondBeat || hasLastBeat || hasSlash; } std::ostream &TurnAttributes::toStream(std::ostream &os) const @@ -40,6 +40,7 @@ std::ostream &TurnAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); streamAttribute(os, startNote, "start-note", hasStartNote); streamAttribute(os, trillStep, "trill-step", hasTrillStep); @@ -96,6 +97,10 @@ bool TurnAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; diff --git a/src/private/mx/core/elements/TurnAttributes.h b/src/private/mx/core/elements/TurnAttributes.h index 138e7d75..69a8148b 100644 --- a/src/private/mx/core/elements/TurnAttributes.h +++ b/src/private/mx/core/elements/TurnAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct TurnAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; StartNote startNote; TrillStep trillStep; @@ -53,6 +55,7 @@ struct TurnAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; bool hasStartNote; bool hasTrillStep; diff --git a/src/private/mx/core/elements/TypeAttributes.cpp b/src/private/mx/core/elements/TypeAttributes.cpp index d7a7a603..6efe34ec 100644 --- a/src/private/mx/core/elements/TypeAttributes.cpp +++ b/src/private/mx/core/elements/TypeAttributes.cpp @@ -10,7 +10,7 @@ namespace mx { namespace core { -TypeAttributes::TypeAttributes() : size(), hasSize(false) +TypeAttributes::TypeAttributes() : size(SymbolSize::full), hasSize(false) { } @@ -44,7 +44,7 @@ bool TypeAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement & } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/Unpitched.cpp b/src/private/mx/core/elements/Unpitched.cpp index b0702c62..c6e48cf0 100644 --- a/src/private/mx/core/elements/Unpitched.cpp +++ b/src/private/mx/core/elements/Unpitched.cpp @@ -42,6 +42,9 @@ std::ostream &Unpitched::streamContents(std::ostream &os, const int indentLevel, { os << std::endl; myDisplayStepOctaveGroup->streamContents(os, indentLevel + 1, isOneLineOnly); + } + if (myHasDisplayStepOctaveGroup) + { isOneLineOnly = false; os << std::endl; } @@ -79,9 +82,12 @@ bool Unpitched::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelem { bool isSuccess = true; - auto it = xelement.begin(); auto endIter = xelement.end(); - importGroup(message, it, endIter, isSuccess, myDisplayStepOctaveGroup, myHasDisplayStepOctaveGroup); + for (auto it = xelement.begin(); it != endIter; ++it) + { + importGroup(message, it, endIter, isSuccess, myDisplayStepOctaveGroup, myHasDisplayStepOctaveGroup); + } + MX_RETURN_IS_SUCCESS; } diff --git a/src/private/mx/core/elements/VirtualInstrument.cpp b/src/private/mx/core/elements/VirtualInstrument.cpp index 241fce61..ec7d7afe 100644 --- a/src/private/mx/core/elements/VirtualInstrument.cpp +++ b/src/private/mx/core/elements/VirtualInstrument.cpp @@ -51,11 +51,15 @@ std::ostream &VirtualInstrument::streamContents(std::ostream &os, const int inde os << std::endl; myVirtualName->toStream(os, indentLevel + 1); } - isOneLineOnly = !hasContents(); - if (!isOneLineOnly) + if (myHasVirtualLibrary || myHasVirtualName) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; } diff --git a/src/private/mx/core/elements/Volume.cpp b/src/private/mx/core/elements/Volume.cpp index 77321c93..477d517b 100644 --- a/src/private/mx/core/elements/Volume.cpp +++ b/src/private/mx/core/elements/Volume.cpp @@ -60,6 +60,7 @@ void Volume::setValue(const Percent &value) bool Volume::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); + MX_UNUSED(xelement); myValue.parse(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/WavyLineAttributes.cpp b/src/private/mx/core/elements/WavyLineAttributes.cpp index ffb9eaba..0b30fcf8 100644 --- a/src/private/mx/core/elements/WavyLineAttributes.cpp +++ b/src/private/mx/core/elements/WavyLineAttributes.cpp @@ -12,9 +12,9 @@ namespace core { WavyLineAttributes::WavyLineAttributes() : type(StartStopContinue::start), number(), defaultX(), defaultY(), relativeX(), relativeY(), - placement(AboveBelow::below), startNote(StartNote::main), trillStep(), twoNoteTurn(TwoNoteTurn::none), - accelerate(YesNo::no), beats(), secondBeat(), lastBeat(), hasType(true), hasNumber(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasPlacement(false), hasStartNote(false), + placement(AboveBelow::below), color(), startNote(), trillStep(), twoNoteTurn(), accelerate(YesNo::no), beats(), + secondBeat(), lastBeat(), hasType(true), hasNumber(false), hasDefaultX(false), hasDefaultY(false), + hasRelativeX(false), hasRelativeY(false), hasPlacement(false), hasColor(false), hasStartNote(false), hasTrillStep(false), hasTwoNoteTurn(false), hasAccelerate(false), hasBeats(false), hasSecondBeat(false), hasLastBeat(false) { @@ -23,7 +23,8 @@ WavyLineAttributes::WavyLineAttributes() bool WavyLineAttributes::hasValues() const { return hasType || hasNumber || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasPlacement || - hasStartNote || hasTrillStep || hasTwoNoteTurn || hasAccelerate || hasBeats || hasSecondBeat || hasLastBeat; + hasColor || hasStartNote || hasTrillStep || hasTwoNoteTurn || hasAccelerate || hasBeats || hasSecondBeat || + hasLastBeat; } std::ostream &WavyLineAttributes::toStream(std::ostream &os) const @@ -37,6 +38,7 @@ std::ostream &WavyLineAttributes::toStream(std::ostream &os) const streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); streamAttribute(os, placement, "placement", hasPlacement); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, startNote, "start-note", hasStartNote); streamAttribute(os, trillStep, "trill-step", hasTrillStep); streamAttribute(os, twoNoteTurn, "two-note-turn", hasTwoNoteTurn); @@ -87,6 +89,10 @@ bool WavyLineAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, startNote, hasStartNote, "start-note", &parseStartNote)) { continue; @@ -121,10 +127,10 @@ bool WavyLineAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XEleme if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/WavyLineAttributes.h b/src/private/mx/core/elements/WavyLineAttributes.h index a9f87363..7931ef43 100644 --- a/src/private/mx/core/elements/WavyLineAttributes.h +++ b/src/private/mx/core/elements/WavyLineAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" @@ -34,6 +35,7 @@ struct WavyLineAttributes : public AttributesInterface TenthsValue relativeX; TenthsValue relativeY; AboveBelow placement; + Color color; StartNote startNote; TrillStep trillStep; TwoNoteTurn twoNoteTurn; @@ -48,6 +50,7 @@ struct WavyLineAttributes : public AttributesInterface bool hasRelativeX; bool hasRelativeY; bool hasPlacement; + bool hasColor; bool hasStartNote; bool hasTrillStep; bool hasTwoNoteTurn; diff --git a/src/private/mx/core/elements/WedgeAttributes.cpp b/src/private/mx/core/elements/WedgeAttributes.cpp index 7db9fab9..4a929b7b 100644 --- a/src/private/mx/core/elements/WedgeAttributes.cpp +++ b/src/private/mx/core/elements/WedgeAttributes.cpp @@ -11,17 +11,17 @@ namespace mx namespace core { WedgeAttributes::WedgeAttributes() - : type(WedgeType::crescendo), number(), lineType(LineType::solid), spread(), niente(YesNo::no), dashLength(), - spaceLength(), defaultX(), defaultY(), relativeX(), relativeY(), hasType(true), hasNumber(false), - hasLineType(false), hasSpread(false), hasNiente(false), hasDashLength(false), hasSpaceLength(false), - hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false) + : type(), number(), spread(), niente(YesNo::no), lineType(LineType::solid), dashLength(), spaceLength(), defaultX(), + defaultY(), relativeX(), relativeY(), color(), hasType(true), hasNumber(false), hasSpread(false), + hasNiente(false), hasLineType(false), hasDashLength(false), hasSpaceLength(false), hasDefaultX(false), + hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasColor(false) { } bool WedgeAttributes::hasValues() const { - return hasType || hasNumber || hasLineType || hasSpread || hasNiente || hasDashLength || hasSpaceLength || - hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY; + return hasType || hasNumber || hasSpread || hasNiente || hasLineType || hasDashLength || hasSpaceLength || + hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasColor; } std::ostream &WedgeAttributes::toStream(std::ostream &os) const @@ -30,15 +30,16 @@ std::ostream &WedgeAttributes::toStream(std::ostream &os) const { streamAttribute(os, type, "type", hasType); streamAttribute(os, number, "number", hasNumber); - streamAttribute(os, lineType, "line-type", hasLineType); streamAttribute(os, spread, "spread", hasSpread); streamAttribute(os, niente, "niente", hasNiente); + streamAttribute(os, lineType, "line-type", hasLineType); streamAttribute(os, dashLength, "dash-length", hasDashLength); streamAttribute(os, spaceLength, "space-length", hasSpaceLength); streamAttribute(os, defaultX, "default-x", hasDefaultX); streamAttribute(os, defaultY, "default-y", hasDefaultY); streamAttribute(os, relativeX, "relative-x", hasRelativeX); streamAttribute(os, relativeY, "relative-y", hasRelativeY); + streamAttribute(os, color, "color", hasColor); } return os; } @@ -62,15 +63,15 @@ bool WedgeAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } - if (parseAttribute(message, it, className, isSuccess, lineType, hasLineType, "line-type", &parseLineType)) + if (parseAttribute(message, it, className, isSuccess, spread, hasSpread, "spread")) { continue; } - if (parseAttribute(message, it, className, isSuccess, spread, hasSpread, "spread")) + if (parseAttribute(message, it, className, isSuccess, niente, hasNiente, "niente", &parseYesNo)) { continue; } - if (parseAttribute(message, it, className, isSuccess, niente, hasNiente, "niente", &parseYesNo)) + if (parseAttribute(message, it, className, isSuccess, lineType, hasLineType, "line-type", &parseLineType)) { continue; } @@ -98,12 +99,16 @@ bool WedgeAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } } if (!isTypeFound) { isSuccess = false; - message << className << ": 'number' is a required attribute but was not found" << std::endl; + message << className << ": 'type' is a required attribute but was not found" << std::endl; } MX_RETURN_IS_SUCCESS; diff --git a/src/private/mx/core/elements/WedgeAttributes.h b/src/private/mx/core/elements/WedgeAttributes.h index 3da17190..b61f0673 100644 --- a/src/private/mx/core/elements/WedgeAttributes.h +++ b/src/private/mx/core/elements/WedgeAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" #include "mx/core/ForwardDeclare.h" @@ -29,26 +30,28 @@ struct WedgeAttributes : public AttributesInterface virtual std::ostream &toStream(std::ostream &os) const; WedgeType type; NumberLevel number; - LineType lineType; TenthsValue spread; YesNo niente; + LineType lineType; TenthsValue dashLength; TenthsValue spaceLength; TenthsValue defaultX; TenthsValue defaultY; TenthsValue relativeX; TenthsValue relativeY; + Color color; const bool hasType; bool hasNumber; - bool hasLineType; bool hasSpread; bool hasNiente; + bool hasLineType; bool hasDashLength; bool hasSpaceLength; bool hasDefaultX; bool hasDefaultY; bool hasRelativeX; bool hasRelativeY; + bool hasColor; private: virtual bool fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement); diff --git a/src/private/mx/core/elements/WithBarAttributes.cpp b/src/private/mx/core/elements/WithBarAttributes.cpp index 3f88cd0f..a3bc2b74 100644 --- a/src/private/mx/core/elements/WithBarAttributes.cpp +++ b/src/private/mx/core/elements/WithBarAttributes.cpp @@ -12,16 +12,16 @@ namespace core { WithBarAttributes::WithBarAttributes() : defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), fontStyle(FontStyle::normal), - fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), placement(), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasPlacement(false) + fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), placement(AboveBelow::below), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasPlacement(false) { } bool WithBarAttributes::hasValues() const { return hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || hasFontSize || - hasFontWeight || hasPlacement; + hasFontWeight || hasColor || hasPlacement; } std::ostream &WithBarAttributes::toStream(std::ostream &os) const @@ -36,6 +36,7 @@ std::ostream &WithBarAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, placement, "placement", hasPlacement); } return os; @@ -84,13 +85,17 @@ bool WithBarAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElemen { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, placement, hasPlacement, "placement", &parseAboveBelow)) { continue; } } - return isSuccess; + MX_RETURN_IS_SUCCESS; } } // namespace core diff --git a/src/private/mx/core/elements/WithBarAttributes.h b/src/private/mx/core/elements/WithBarAttributes.h index 6758df92..2c634be1 100644 --- a/src/private/mx/core/elements/WithBarAttributes.h +++ b/src/private/mx/core/elements/WithBarAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -36,6 +37,7 @@ struct WithBarAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; AboveBelow placement; bool hasDefaultX; bool hasDefaultY; @@ -45,6 +47,7 @@ struct WithBarAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasPlacement; private: diff --git a/src/private/mx/core/elements/Wood.cpp b/src/private/mx/core/elements/Wood.cpp index 69399488..f4c3dbf4 100644 --- a/src/private/mx/core/elements/Wood.cpp +++ b/src/private/mx/core/elements/Wood.cpp @@ -60,7 +60,6 @@ void Wood::setValue(const WoodEnum &value) bool Wood::fromXElementImpl(std::ostream &message, ::ezxml::XElement &xelement) { MX_UNUSED(message); - MX_UNUSED(xelement); myValue = parseWoodEnum(xelement.getValue()); return true; } diff --git a/src/private/mx/core/elements/WordsAttributes.cpp b/src/private/mx/core/elements/WordsAttributes.cpp index a81442be..c465711b 100644 --- a/src/private/mx/core/elements/WordsAttributes.cpp +++ b/src/private/mx/core/elements/WordsAttributes.cpp @@ -11,22 +11,23 @@ namespace mx namespace core { WordsAttributes::WordsAttributes() - : justify(LeftCenterRight::center), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), - fontStyle(FontStyle::normal), fontSize(FontSize{CssFontSize::medium}), fontWeight(FontWeight::normal), halign(), - underline(), overline(), lineThrough(), rotation(), letterSpacing(), lineHeight(), lang("it"), - space(XmlSpace::default_), enclosure(EnclosureShape::rectangle), hasJustify(false), hasDefaultX(false), - hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), hasFontStyle(false), - hasFontSize(false), hasFontWeight(false), hasHalign(false), hasUnderline(false), hasOverline(false), - hasLineThrough(false), hasRotation(false), hasLetterSpacing(false), hasLineHeight(false), hasLang(false), - hasSpace(false), hasEnclosure(false) + : justify(LeftCenterRight::left), defaultX(), defaultY(), relativeX(), relativeY(), fontFamily(), + fontStyle(FontStyle::normal), fontSize(CssFontSize::medium), fontWeight(FontWeight::normal), color(), + halign(LeftCenterRight::left), valign(Valign::bottom), underline(), overline(), lineThrough(), rotation(), + letterSpacing(), lineHeight(), lang("it"), space(), dir(), enclosure(EnclosureShape::none), hasJustify(false), + hasDefaultX(false), hasDefaultY(false), hasRelativeX(false), hasRelativeY(false), hasFontFamily(false), + hasFontStyle(false), hasFontSize(false), hasFontWeight(false), hasColor(false), hasHalign(false), + hasValign(false), hasUnderline(false), hasOverline(false), hasLineThrough(false), hasRotation(false), + hasLetterSpacing(false), hasLineHeight(false), hasLang(false), hasSpace(false), hasDir(false), hasEnclosure(false) { } bool WordsAttributes::hasValues() const { return hasJustify || hasDefaultX || hasDefaultY || hasRelativeX || hasRelativeY || hasFontFamily || hasFontStyle || - hasFontSize || hasFontWeight || hasHalign || hasUnderline || hasOverline || hasLineThrough || hasRotation || - hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasEnclosure; + hasFontSize || hasFontWeight || hasColor || hasHalign || hasValign || hasUnderline || hasOverline || + hasLineThrough || hasRotation || hasLetterSpacing || hasLineHeight || hasLang || hasSpace || hasDir || + hasEnclosure; } std::ostream &WordsAttributes::toStream(std::ostream &os) const @@ -42,7 +43,9 @@ std::ostream &WordsAttributes::toStream(std::ostream &os) const streamAttribute(os, fontStyle, "font-style", hasFontStyle); streamAttribute(os, fontSize, "font-size", hasFontSize); streamAttribute(os, fontWeight, "font-weight", hasFontWeight); + streamAttribute(os, color, "color", hasColor); streamAttribute(os, halign, "halign", hasHalign); + streamAttribute(os, valign, "valign", hasValign); streamAttribute(os, underline, "underline", hasUnderline); streamAttribute(os, overline, "overline", hasOverline); streamAttribute(os, lineThrough, "line-through", hasLineThrough); @@ -51,6 +54,7 @@ std::ostream &WordsAttributes::toStream(std::ostream &os) const streamAttribute(os, lineHeight, "line-height", hasLineHeight); streamAttribute(os, lang, "xml:lang", hasLang); streamAttribute(os, space, "xml:space", hasSpace); + streamAttribute(os, dir, "dir", hasDir); streamAttribute(os, enclosure, "enclosure", hasEnclosure); } return os; @@ -103,10 +107,18 @@ bool WordsAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } + if (parseAttribute(message, it, className, isSuccess, color, hasColor, "color")) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, halign, hasHalign, "halign", &parseLeftCenterRight)) { continue; } + if (parseAttribute(message, it, className, isSuccess, valign, hasValign, "valign", &parseValign)) + { + continue; + } if (parseAttribute(message, it, className, isSuccess, underline, hasUnderline, "underline")) { continue; @@ -131,19 +143,15 @@ bool WordsAttributes::fromXElementImpl(std::ostream &message, ::ezxml::XElement { continue; } - if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "lang")) - { - continue; - } if (parseAttribute(message, it, className, isSuccess, lang, hasLang, "xml:lang")) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) { continue; } - if (parseAttribute(message, it, className, isSuccess, space, hasSpace, "xml:space", &parseXmlSpace)) + if (parseAttribute(message, it, className, isSuccess, dir, hasDir, "dir", &parseTextDirection)) { continue; } diff --git a/src/private/mx/core/elements/WordsAttributes.h b/src/private/mx/core/elements/WordsAttributes.h index eed749a9..6f51cbc7 100644 --- a/src/private/mx/core/elements/WordsAttributes.h +++ b/src/private/mx/core/elements/WordsAttributes.h @@ -5,6 +5,7 @@ #pragma once #include "mx/core/AttributesInterface.h" +#include "mx/core/Color.h" #include "mx/core/CommaSeparatedText.h" #include "mx/core/Decimals.h" #include "mx/core/Enums.h" @@ -40,7 +41,9 @@ struct WordsAttributes : public AttributesInterface FontStyle fontStyle; FontSize fontSize; FontWeight fontWeight; + Color color; LeftCenterRight halign; + Valign valign; NumberOfLines underline; NumberOfLines overline; NumberOfLines lineThrough; @@ -49,6 +52,7 @@ struct WordsAttributes : public AttributesInterface NumberOrNormal lineHeight; XmlLang lang; XmlSpace space; + TextDirection dir; EnclosureShape enclosure; bool hasJustify; bool hasDefaultX; @@ -59,7 +63,9 @@ struct WordsAttributes : public AttributesInterface bool hasFontStyle; bool hasFontSize; bool hasFontWeight; + bool hasColor; bool hasHalign; + bool hasValign; bool hasUnderline; bool hasOverline; bool hasLineThrough; @@ -68,6 +74,7 @@ struct WordsAttributes : public AttributesInterface bool hasLineHeight; bool hasLang; bool hasSpace; + bool hasDir; bool hasEnclosure; private: diff --git a/src/private/mx/core/elements/Work.cpp b/src/private/mx/core/elements/Work.cpp index a03afe7a..0a4f23cc 100644 --- a/src/private/mx/core/elements/Work.cpp +++ b/src/private/mx/core/elements/Work.cpp @@ -57,11 +57,15 @@ std::ostream &Work::streamContents(std::ostream &os, const int indentLevel, bool os << std::endl; myOpus->toStream(os, indentLevel + 1); } - isOneLineOnly = !hasContents(); - if (!isOneLineOnly) + if (myHasWorkNumber || myHasWorkTitle || myHasOpus) { + isOneLineOnly = false; os << std::endl; } + else + { + isOneLineOnly = true; + } return os; }