/** * Parse a cue sheet file. * * @param file A cue sheet file. * @return A representation of the cue sheet. * @throws IOException */ public static CueSheet parse(final File file) throws IOException { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parse(File)", file); final CueSheet result = CueParser.parse(new LineNumberReader(new FileReader(file))); CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parse(File)", result); return result; }
addWarning(input, WARNING_EMPTY_LINES); } else if (inputLine.length() < 2) { addWarning(input, WARNING_UNPARSEABLE_INPUT); } else { case 'a': case 'A': CueParser.parseCatalog(input); break; case 'd': case 'D': CueParser.parseCdTextFile(input); break; default: addWarning(input, WARNING_UNPARSEABLE_INPUT); break; case 'i': case 'I': CueParser.parseFile(input); break; case 'l': case 'L': CueParser.parseFlags(input); break; default: addWarning(input, WARNING_UNPARSEABLE_INPUT); break;
CueParser.logger.entering(CueParser.class.getCanonicalName(), "parseRem(LineOfInput)", input); if (startsWith(input, "REM")) { case 'c': case 'C': if (contains(input, PATTERN_REM_COMMENT)) { parseRemComment(input); if (contains(input, PATTERN_REM_DATE)) { parseRemDate(input); } else if (contains(input, PATTERN_REM_DISCID)) { parseRemDiscid(input); if (contains(input, PATTERN_REM_GENRE)) { parseRemGenre(input); addWarning(input, WARNING_UNPARSEABLE_INPUT);
/** * Parse the POSTGAP command. * <p/> * POSTGAP [mm:ss:ff] * Must come after all INDEX fields for a track. Only one per track allowed. * * @param input */ private static void parsePostgap(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parsePostgap(LineOfInput)", input); Matcher postgapMatcher = PATTERN_POSTGAP.matcher(input.getInput()); if (startsWith(input, "POSTGAP") && postgapMatcher.matches()) { TrackData trackData = getLastTrackData(input); if (trackData.getPostgap() != null) { addWarning(input, WARNING_DATUM_APPEARS_TOO_OFTEN); } trackData.setPostgap(parsePosition(input, postgapMatcher.group(1))); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); } CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parsePostgap(LineOfInput)"); }
/** * Parse the ISRC command. * <p/> * ISRC [code] * International Standard Recording Code of track. Must come after TRACK, but before INDEX. * * @param input */ private static void parseIsrc(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parseIsrc(LineOfInput)", input); if (startsWith(input, "ISRC")) { String isrcCode = input.getInput().substring("ISRC".length()).trim(); if (!PATTERN_ISRC_CODE.matcher(isrcCode).matches()) { addWarning(input, WARNING_NONCOMPLIANT_ISRC_CODE); } TrackData trackData = getLastTrackData(input); if (trackData.getIndices().size() > 0) { addWarning(input, WARNING_ISRC_IN_WRONG_PLACE); } if (trackData.getIsrcCode() != null) { addWarning(input, WARNING_DATUM_APPEARS_TOO_OFTEN); } trackData.setIsrcCode(isrcCode); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); } CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parseIsrc(LineOfInput)"); }
if (startsWith(input, "TRACK") && trackMatcher.matches()) { if (trackMatcher.group(1).length() != 2) { addWarning(input, WARNING_WRONG_NUMBER_OF_DIGITS); addWarning(input, WARNING_NONCOMPLIANT_DATA_TYPE); || !trackDataList.isEmpty() && trackDataList.get(trackDataList.size() - 1).getNumber() != trackNumber - 1 ) { addWarning(input, WARNING_INVALID_TRACK_NUMBER); FileData lastFileData = getLastFileData(input); lastFileData.getTrackData().add(new TrackData(lastFileData, trackNumber, dataType)); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT);
/** * Parse the CATALOG command. * <p/> * CATALOG [media-catalog-number] * CD catalog number. Code follows UPC/EAN rules. * Usually the first command, but this is not required. Not a mandatory command. * * @param input */ private static void parseCatalog(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parseCatalog(LineOfInput)", input); if (startsWith(input, "CATALOG")) { String catalogNumber = input.getInput().substring("CATALOG".length()).trim(); if (!PATTERN_CATALOG_NUMBER.matcher(catalogNumber).matches()) { addWarning(input, WARNING_INVALID_CATALOG_NUMBER); } if (input.getAssociatedSheet().getCatalog() != null) { addWarning(input, WARNING_DATUM_APPEARS_TOO_OFTEN); } input.getAssociatedSheet().setCatalog(catalogNumber); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); } CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parseCatalog(LineOfInput)"); }
/** * Parse the non-standard REM DATE command. * <p/> * REM DATE [year] * * @param input */ private static void parseRemDate(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parseRemDate(LineOfInput)", input); Matcher matcher = PATTERN_REM_DATE.matcher(input.getInput()); if (matcher.find()) { int year = Integer.parseInt(matcher.group(2)); if (year < 1 || year > 9999) { addWarning(input, WARNING_INVALID_YEAR); } input.getAssociatedSheet().setYear(year); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); } CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parseRemDate(LineOfInput)"); }
/** * Get the last {@link jwbroek.cuelib.TrackData} element. If none exist, an empty one is created and a warning * added. * * @param input * @return The last {@link jwbroek.cuelib.TrackData} element. If none exist, an empty one is created and a * warning added. */ private static TrackData getLastTrackData(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "getLastTrackData(LineOfInput)", input); FileData lastFileData = getLastFileData(input); List<TrackData> trackDataList = lastFileData.getTrackData(); if (trackDataList.size() == 0) { trackDataList.add(new TrackData(lastFileData)); addWarning(input, WARNING_NO_TRACK_SPECIFIED); } TrackData result = trackDataList.get(trackDataList.size() - 1); CueParser.logger.exiting(CueParser.class.getCanonicalName(), "getLastTrackData(LineOfInput)", result); return result; }
/** * Parse the PREGAP command. * <p/> * PREGAP [mm:ss:ff] * Must come after TRACK, but before INDEX fields for that track. * * @param input */ private static void parsePregap(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parsePregap(LineOfInput)", input); Matcher pregapMatcher = PATTERN_PREGAP.matcher(input.getInput()); if (startsWith(input, "PREGAP") && pregapMatcher.matches()) { TrackData trackData = getLastTrackData(input); if (trackData.getPregap() != null) { addWarning(input, WARNING_DATUM_APPEARS_TOO_OFTEN); } if (trackData.getIndices().size() > 0) { addWarning(input, WARNING_PREGAP_IN_WRONG_PLACE); } trackData.setPregap(parsePosition(input, pregapMatcher.group(1))); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); } CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parsePregap(LineOfInput)"); }
if (startsWith(input, "FLAGS") && flagsMatcher.matches()) { if (null == flagsMatcher.group(1)) { addWarning(input, WARNING_NO_FLAGS); } else { TrackData trackData = getLastTrackData(input); addWarning(input, WARNING_FLAGS_IN_WRONG_PLACE); addWarning(input, WARNING_DATUM_APPEARS_TOO_OFTEN); String flag = flagScanner.next(); if (!COMPLIANT_FLAGS.contains(flag)) { addWarning(input, WARNING_NONCOMPLIANT_FLAG); addWarning(input, WARNING_UNPARSEABLE_INPUT);
if (startsWith(input, "FILE") && fileMatcher.matches()) { if (!COMPLIANT_FILE_TYPES.contains(fileMatcher.group(2))) { if (COMPLIANT_FILE_TYPES.contains(fileMatcher.group(2).toUpperCase())) { addWarning(input, WARNING_TOKEN_NOT_UPPERCASE); } else { addWarning(input, WARNING_NONCOMPLIANT_FILE_TYPE); addWarning(input, WARNING_UNPARSEABLE_INPUT);
/** * Parse the non-standard REM COMMENT command. * <p/> * REM COMMENT [comment] * * @param input */ private static void parseRemComment(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parseRemComment(LineOfInput)", input); Matcher matcher = PATTERN_REM_COMMENT.matcher(input.getInput()); if (matcher.find()) { String comment = matcher.group(2); if (comment.charAt(0) == '"' && comment.charAt(comment.length() - 1) == '"') { comment = comment.substring(1, comment.length() - 1); } input.getAssociatedSheet().setComment(comment); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); } CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parseRemComment(LineOfInput)"); }
if (startsWith(input, "INDEX") && indexMatcher.matches()) { if (indexMatcher.group(1).length() != 2) { addWarning(input, WARNING_WRONG_NUMBER_OF_DIGITS); TrackData trackData = getLastTrackData(input); List<Index> trackIndices = trackData.getIndices(); addWarning(input, WARNING_INDEX_AFTER_POSTGAP); || !trackIndices.isEmpty() && trackIndices.get(trackIndices.size() - 1).getNumber() != indexNumber - 1 ) { addWarning(input, WARNING_INVALID_INDEX_NUMBER); List<Index> fileIndices = getLastFileData(input).getAllIndices(); Position position = parsePosition(input, indexMatcher.group(2)); addWarning(input, WARNING_INVALID_FIRST_POSITION); addWarning(input, WARNING_UNPARSEABLE_INPUT);
/** * Parse a cue sheet that will be read from the InputStream. * * @param inputStream An {@link java.io.InputStream} that produces a cue sheet. The stream will be closed * afterward. * @return A representation of the cue sheet. * @throws IOException */ public static CueSheet parse(final InputStream inputStream) throws IOException { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parse(InputStream)", inputStream); final CueSheet result = CueParser.parse(new LineNumberReader(new InputStreamReader(inputStream))); CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parse(InputStream)", result); return result; }
/** * Parse the CDTEXTFILE command. * <p/> * CDTEXTFILE [filename] * File that contains cd text data. Not mandatory. * * @param input */ private static void parseCdTextFile(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parseCdTextFile(LineOfInput)", input); Matcher cdTextFileMatcher = PATTERN_CDTEXTFILE.matcher(input.getInput()); if (startsWith(input, "CDTEXTFILE") && cdTextFileMatcher.matches()) { if (input.getAssociatedSheet().getCdTextFile() != null) { CueParser.logger.warning(WARNING_DATUM_APPEARS_TOO_OFTEN); input.getAssociatedSheet().addWarning(input, WARNING_DATUM_APPEARS_TOO_OFTEN); } // If the file name is enclosed in quotes, remove those. String file = cdTextFileMatcher.group(1); if (file.length() > 0 && file.charAt(0) == '"' && file.charAt(file.length() - 1) == '"') { file = file.substring(1, file.length() - 1); } input.getAssociatedSheet().setCdTextFile(file); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); } CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parseCdTextFile(LineOfInput)"); }
addWarning(input, WARNING_WRONG_NUMBER_OF_DIGITS); addWarning(input, WARNING_INVALID_SECONDS_VALUE); addWarning(input, WARNING_INVALID_FRAMES_VALUE); return result; } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); Position result = new Position(); CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parsePosition(LineOfInput)", result);
if (startsWith(input, "PERFORMER") && performerMatcher.matches()) { String performer = performerMatcher.group(1); addWarning(input, WARNING_FIELD_LENGTH_OVER_80); || getLastFileData(input).getTrackData().size() == 0 ) { addWarning(input, WARNING_DATUM_APPEARS_TOO_OFTEN); } else { TrackData trackData = getLastTrackData(input); if (trackData.getPerformer() != null) { addWarning(input, WARNING_DATUM_APPEARS_TOO_OFTEN); addWarning(input, WARNING_UNPARSEABLE_INPUT);
sheet = CueParser.parse(file);
/** * Parse the non-standard REM GENRE command. * <p/> * REM GENRE [genre] * * @param input */ private static void parseRemGenre(final LineOfInput input) { CueParser.logger.entering(CueParser.class.getCanonicalName(), "parseRemGenre(LineOfInput)", input); Matcher matcher = PATTERN_REM_GENRE.matcher(input.getInput()); if (matcher.find()) { String genre = matcher.group(2); if (genre.charAt(0) == '"' && genre.charAt(genre.length() - 1) == '"') { genre = genre.substring(1, genre.length() - 1); } input.getAssociatedSheet().setGenre(genre); } else { addWarning(input, WARNING_UNPARSEABLE_INPUT); } CueParser.logger.exiting(CueParser.class.getCanonicalName(), "parseRemGenre(LineOfInput)"); }