/** * Parses the given .abc File into an ADT, throws RuntimeExceptions upon encountering ABC Grammar * breaking constructs * * <p>Note: allows a near indefinite break between the end of the Key signature and the start of * music, this break can contain comments, but once the music starts, it must adhere to the * grammar * * @return An ABCMusic file which contains all the methods necessary to both play and analyze the * .abc file contained by the ABCParser */ public ABCMusic parse() { next = lexer.GetNextTokenType(); if (next == Tokens.Type.FIELD_NUMBER) { parseHeader(); next = lexer.GetNextTokenType(); while (next == Tokens.Type.END_OF_LINE) { checkAndTrashEoL(); next = lexer.GetNextTokenType(); } if (!(next == Tokens.Type.FIELD_VOICE || next == Tokens.Type.COMMENT || next == Tokens.Type.NOTE || next == Tokens.Type.CHORD || next == Tokens.Type.TUPLET || next == Tokens.Type.REST)) { throw new IllegalArgumentException("Expected start of music instead got: " + next); } parseMusic(); next = lexer.GetNextTokenType(); if (next != Tokens.Type.END_OF_LINE) { throw new IllegalArgumentException( "Expected music to be over, instead there was a: " + next); } } else { throw new IllegalArgumentException("Expected a music ID, received: " + next); } return ThePiece; }
/** * Verifies an EoL Character terminates any given line for which it is run, as well as Checking * for Comment blocks and discarding them (since they are part of an EoL) * * <p>Note: Assumes that comments are unused or needed, and thus they will never be printed to * screen */ private void checkAndTrashEoL() { next = lexer.GetNextTokenType(); if (next == Tokens.Type.END_OF_LINE) { // Little dirty discard of EoL characters @SuppressWarnings("unused") Tokens dummy = lexer.getEndOfLineToken(); } else if (next == Tokens.Type.COMMENT) { // Little dirty discard of Comments, they dont matter i think? // Otherwise this is where Comments would be set to print out @SuppressWarnings("unused") Tokens dummy = lexer.getNextElemToken(); checkAndTrashEoL(); } else { throw new IllegalArgumentException("Expected a EoL, received: " + next); } }
/** * Parses the ABCMusic information as prescribed by the ABC Grammar understood by the ABCParser, * does so recursively via sub-functions and stores the information in the ABCMusic output file's * VoicesList */ private void parseMusic() { workingIndex = 0; workingVoice = ThePiece.VoicesList.get(0); // Until we are out of Music while (!lexer.IsDone()) { next = lexer.GetNextTokenType(); if (next == Tokens.Type.END_OF_LINE || next == Tokens.Type.COMMENT) { // Trash found comments and EoL chars checkAndTrashEoL(); } else if (next == Tokens.Type.FIELD_VOICE) { String nextVoice = lexer.getNextElemToken().getText(); int index = 0; // Try all the voices to see what we should switch to for (Iterator<Voice> i = ThePiece.VoicesList.iterator(); i.hasNext(); ) { if (i.next().getName().equals(nextVoice)) { workingVoice = ThePiece.VoicesList.get(index); workingIndex = index; } else { index++; // If we have tried all Voices, then it must not exist if (index >= ThePiece.VoicesList.size()) { throw new IllegalArgumentException( "Invalid Voice name in the Music Section: " + nextVoice); } } } } else { // Recurse on a given Voice to parse it parseBar(); } } // When we reach the end, all remaining meters to all Voices to finalize the piece for (int index = 0; index < ThePiece.VoicesList.size(); index++) { workingVoice = ThePiece.VoicesList.get(index); workingIndex = index; workingVoice.addCopyElts(repeatTracker.get(workingIndex)); } }
/** * Parses the ABCHeader information as prescribed by the ABC Grammar subset understood by the * ABCParser */ private void parseHeader() { // All the components of a valid ABCMusic List<Voice> VoicesList = new ArrayList<Voice>(); String name, title, IDNum; int bpm; int[] meterSum = new int[2]; int[] noteLength = new int[2]; NoteToken key; // Booleans for having set the optional quantities boolean setL = Boolean.FALSE; boolean setM = Boolean.FALSE; boolean setQ = Boolean.FALSE; boolean setC = Boolean.FALSE; // Establish Defaults for unnecessary fields so that we only need to add // the the vars bpm = 100; noteLength[0] = 1; noteLength[1] = 8; meterSum[0] = 4; meterSum[1] = 4; name = "Unknown"; // Now grab X and T since they are in a manditory order IDNum = lexer.getNextElemToken().getText(); checkAndTrashEoL(); next = lexer.GetNextTokenType(); if (next == Tokens.Type.FIELD_TITLE) { title = lexer.getNextElemToken().getText(); } else { throw new IllegalArgumentException("Expected a music Title, received: " + next); } checkAndTrashEoL(); // Until you encounter the Field Key, look for all optional classes, and only allow them to be // set once while (next != Tokens.Type.FIELD_KEY) { next = lexer.GetNextTokenType(); switch (next) { case FIELD_DEFAULT_LENGTH: if (!setL) { ElemToken tempToken = lexer.getNextElemToken(); int[] settingArray = tempToken.getVals(); noteLength[0] = settingArray[0]; noteLength[1] = settingArray[1]; setL = Boolean.TRUE; } else { throw new IllegalArgumentException("Cannot Set Default Length more than once"); } checkAndTrashEoL(); break; case FIELD_METER: if (!setM) { ElemToken tempToken = lexer.getNextElemToken(); int[] settingArray = tempToken.getVals(); meterSum[0] = settingArray[0]; meterSum[1] = settingArray[1]; setM = Boolean.TRUE; } else { throw new IllegalArgumentException("Cannot Set Meter Length more than once"); } checkAndTrashEoL(); break; case FIELD_TEMPO: if (!setQ) { ElemToken tempToken = lexer.getNextElemToken(); int[] settingArray = tempToken.getVals(); bpm = settingArray[0]; setQ = Boolean.TRUE; } else { throw new IllegalArgumentException("Cannot Set Tempo more than once"); } checkAndTrashEoL(); break; case FIELD_COMPOSER: if (!setC) { ElemToken tempToken = lexer.getNextElemToken(); name = tempToken.getText(); setC = Boolean.TRUE; } else { throw new IllegalArgumentException("Cannot Set Composer more than once"); } checkAndTrashEoL(); break; case FIELD_VOICE: VoicesList.add(new Voice(lexer.getNextElemToken().getText())); checkAndTrashEoL(); break; default: break; } } // Last element should be the field key if (next == Tokens.Type.FIELD_KEY) { key = lexer.getNextElemToken().getKey(); } else { throw new IllegalArgumentException("Expected a music Key for the piece, received: " + next); } checkAndTrashEoL(); // If the file has no voices, then we need at least one for the ABCMusic file to be valid // Construct a dummy one if (VoicesList.size() == 0) { VoicesList.add(new Voice("DummyVoice")); } // Initialize the new ABCMusic file ThePiece = new ABCMusic(VoicesList, name, key, noteLength, meterSum, bpm, title, IDNum); }