package com.xenoage.zong.musicxml.types;
import com.xenoage.utils.annotations.MaybeNull;
import com.xenoage.utils.annotations.NonNull;
import com.xenoage.utils.xml.XmlReader;
import com.xenoage.utils.xml.XmlWriter;
import com.xenoage.zong.musicxml.types.choice.*;
import com.xenoage.zong.musicxml.types.enums.MxlNoteTypeValue;
import com.xenoage.zong.musicxml.types.groups.MxlEditorialVoice;
import com.xenoage.zong.musicxml.util.IncompleteMusicXML;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
/**
* MusicXML note.
*
* @author Andreas Wenger
*/
@IncompleteMusicXML(missing = "accidental,time-modification,notehead,"
+ "x-position,font,color,printout,dynamics,end-dynamics,attack,release,time-only,pizzicato",
children = "beam,editorial-voice,notations,lyric", partly = "type,dot")
@AllArgsConstructor @Getter @Setter
public final class MxlNote
implements MxlMusicDataContent {
public static final String elemName = "note";
@NonNull private MxlNoteContent content;
@MaybeNull private MxlInstrument instrument;
@MaybeNull private MxlEditorialVoice editorialVoice;
@MaybeNull private MxlNoteTypeValue noteType;
private int dots;
@MaybeNull private MxlStem stem;
@MaybeNull private Integer staff;
@MaybeNull private List<MxlBeam> beams;
@MaybeNull private List<MxlNotations> notations;
@MaybeNull private List<MxlLyric> lyrics;
@Override public MxlMusicDataContentType getMusicDataContentType() {
return MxlMusicDataContentType.Note;
}
@NonNull public static MxlNote read(XmlReader reader) {
MxlNoteContent content = null;
MxlInstrument instrument = null;
MxlEditorialVoice editorialVoice = new MxlEditorialVoice();
MxlNoteTypeValue noteType = null;
int dots = 0;
MxlStem stem = null;
Integer staff = null;
List<MxlBeam> beams = null;
List<MxlNotations> notations = null;
List<MxlLyric> lyrics = null;
while (reader.openNextChildElement()) {
String n = reader.getElementName();
//first element determines note content
//but, be tolerant for errors, and also accept late grace or cue elements
if (n.equals(MxlGraceNote.elemName)) {
MxlGraceNote graceNote = MxlGraceNote.read(reader);
if (content instanceof MxlNormalNote) //grace element too late, but accept it
graceNote.setFullNote(((MxlNormalNote) content).getFullNote());
content = graceNote;
}
else if (n.equals(MxlCueNote.elemName)) {
MxlCueNote cueNote = MxlCueNote.read();
if (content instanceof MxlNormalNote) //cue element too late, but accept it
cueNote.setFullNote(((MxlNormalNote) content).getFullNote());
content = cueNote;
}
else if (content == null) {
content = MxlNormalNote.read();
}
//read content of child elements
switch (n) {
case MxlStem.elemName:
stem = MxlStem.read(reader);
break;
case "staff":
staff = reader.getTextIntNotNull();
break;
case MxlBeam.elemName:
if (beams == null)
beams = new ArrayList<>();
beams.add(MxlBeam.read(reader));
break;
case MxlInstrument.elemName:
instrument = MxlInstrument.read(reader);
break;
case MxlNotations.elemName:
if (notations == null)
notations = new ArrayList<>();
notations.add(MxlNotations.read(reader));
break;
case MxlLyric.elemName:
if (lyrics == null)
lyrics = new ArrayList<>();
lyrics.add(MxlLyric.read(reader));
break;
case "type":
noteType = MxlNoteTypeValue.read(reader.getText());
break;
case "dot":
dots++;
break;
default:
boolean read = content.readElement(reader);
if (!read)
editorialVoice.readElement(reader);
break;
}
reader.closeElement();
}
content.check(reader);
if (false == editorialVoice.isUsed())
editorialVoice = null;
return new MxlNote(content, instrument, editorialVoice, noteType, dots, stem, staff, beams,
notations, lyrics);
}
@Override public void write(XmlWriter writer) {
writer.writeElementStart(elemName);
content.write(writer);
if (instrument != null)
instrument.write(writer);
if (editorialVoice != null)
editorialVoice.write(writer);
if (noteType != null)
writer.writeElementText("type", noteType.write());
for (int i = 0; i < dots; i++)
writer.writeElementEmpty("dot");
if (stem != null)
stem.write(writer);
writer.writeElementText("staff", staff);
if (beams != null) {
for (MxlBeam beam : beams)
beam.write(writer);
}
if (notations != null) {
for (MxlNotations n : notations)
n.write(writer);
}
if (lyrics != null) {
for (MxlLyric lyric : lyrics)
lyric.write(writer);
}
writer.writeElementEnd();
}
}