package com.xenoage.zong.io.musicxml.in.readers; import lombok.RequiredArgsConstructor; import com.xenoage.utils.annotations.MaybeNull; import com.xenoage.zong.core.music.format.Placement; import com.xenoage.zong.core.music.format.Position; import com.xenoage.zong.core.music.format.Positioning; import com.xenoage.zong.io.musicxml.in.util.StaffDetails; import com.xenoage.zong.musicxml.types.MxlFormattedText; import com.xenoage.zong.musicxml.types.attributes.MxlEmptyPlacement; import com.xenoage.zong.musicxml.types.attributes.MxlPosition; import com.xenoage.zong.musicxml.types.attributes.MxlPrintStyle; import com.xenoage.zong.musicxml.types.enums.MxlPlacement; import com.xenoage.zong.musicxml.types.util.MxlEmptyPlacementContent; import com.xenoage.zong.musicxml.types.util.MxlFormattedTextContent; import com.xenoage.zong.musicxml.types.util.MxlPlacementContent; import com.xenoage.zong.musicxml.types.util.MxlPositionContent; import com.xenoage.zong.musicxml.types.util.MxlPrintStyleContent; /** * Reads {@link Positioning} information from any MusicXML element. * * @author Andreas Wenger */ @RequiredArgsConstructor public class PositioningReader { private final StaffDetails staffDetails; /** * Reads the positioning from any given MusicXML element. * An inner {@link MxlFormattedText} has highest priority, then an inner {@link MxlEmptyPlacement}, * then an inner {@link MxlPrintStyle}, then a {@link MxlPosition}, then a {@link MxlPlacement}. */ @MaybeNull public Positioning readFromAny(Object anyElement) { Positioning positioning = null; if (anyElement == null) return null; //first priority: inner MxlFormattedText if (anyElement instanceof MxlFormattedTextContent) positioning = readFromPrintStyle(((MxlFormattedTextContent) anyElement).getFormattedText()); //second priority: inner MxlEmptyPlacement if (anyElement instanceof MxlEmptyPlacementContent) positioning = readFromAny(((MxlEmptyPlacementContent) anyElement).getEmptyPlacement()); //third priority: inner MxlPrintStyle if (positioning == null && anyElement instanceof MxlPrintStyleContent) positioning = readFromPrintStyle(((MxlPrintStyleContent) anyElement)); //fourth priority: position if (positioning == null && anyElement instanceof MxlPositionContent) positioning = readFromPosition(((MxlPositionContent) anyElement)); //fifth priority: placement if (positioning == null && anyElement instanceof MxlPlacementContent) positioning = readFromPlacement(((MxlPlacementContent) anyElement)); return positioning; } /** * Like {@link #readFromAny(Object)}, but an additional elements are considered * as long as the previous ones contains no positioning information. */ @MaybeNull public Positioning readFromAny(Object... anyElements) { for (Object anyElement : anyElements) { Positioning positioning = readFromAny(anyElement); if (positioning != null) return positioning; } return null; } @MaybeNull public Position readFromPrintStyle(MxlPrintStyleContent mxlPrintStyleContent) { MxlPrintStyle mxlPrintStyle = mxlPrintStyleContent.getPrintStyle(); return readFromPosition(mxlPrintStyle); } @MaybeNull public Position readFromPosition(MxlPositionContent mxlPositionContent) { return readPosition(mxlPositionContent.getPosition()); } @MaybeNull public Position readPosition(MxlPosition mxlPosition) { Float x = mxlPosition.getDefaultX(); Float y = mxlPosition.getDefaultY(); Float rx = mxlPosition.getRelativeX(); Float ry = mxlPosition.getRelativeY(); if (x == null && y == null && rx == null && ry == null) { return null; } else { Float fx = null; if (x != null) { fx = x / 10 * staffDetails.tenthsMm; } Float fy = null; if (y != null) { fy = (staffDetails.linesCount - 1) * 2 + y / 10 * 2; } Float frx = null; if (rx != null) { frx = rx / 10 * staffDetails.tenthsMm; } Float fry = null; if (ry != null) { fry = ry / 10 * 2; } return new Position(fx, fy, frx, fry); } } @MaybeNull public static Placement readFromPlacement(MxlPlacementContent mxlPlacementContent) { MxlPlacement mxlPlacement = mxlPlacementContent.getPlacement(); return readPlacement(mxlPlacement); } @MaybeNull public static Placement readPlacement(MxlPlacement mxlPlacement) { switch (mxlPlacement) { case Above: return Placement.Above; case Below: return Placement.Below; default: return null; } } }