package info.toyonos.subtitles4j.factory; import info.toyonos.subtitles4j.model.SubtitlesContainer; import info.toyonos.subtitles4j.model.SubtitlesContainer.Caption; import info.toyonos.subtitles4j.model.SubtitlesContainer.StyleProperty; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractFormatFactory implements SubtitlesVisitor, SubtitlesFactory { private static final String UTF8_BOM = "\uFEFF"; protected static final Logger logger = LoggerFactory.getLogger(AbstractFormatFactory.class); protected PrintWriter subtitlesWriter; /** * @return The <code>SimpleDateFormat</code> using by timestamps */ protected abstract SimpleDateFormat getTimestampDateFormat(); protected MalformedSubtitlesException malformedFileException(String content, Integer lineNumber, Object... args) { return lineNumber != null ? new MalformedSubtitlesException(String.format(content + " at line %d", ArrayUtils.addAll(args, lineNumber))) : new MalformedSubtitlesException(String.format(content, args)); } protected long getMilliseconds(String timestamp, Integer lineNumber) throws MalformedSubtitlesException { try { Calendar calendar = GregorianCalendar.getInstance(); calendar.setTime(getTimestampDateFormat().parse(timestamp)); return calendar.getTimeInMillis() + calendar.getTimeZone().getOffset(calendar.getTimeInMillis()); } catch (ParseException e) { throw malformedFileException("Malformed timestamp '%s'", lineNumber, timestamp); } } protected long getMilliseconds(String timestamp) throws MalformedSubtitlesException { return getMilliseconds(timestamp, null); } protected String formatMilliseconds(long millis) { Calendar calendar = GregorianCalendar.getInstance(); calendar.setTimeInMillis(millis - calendar.get(Calendar.ZONE_OFFSET)); return getTimestampDateFormat().format(calendar.getTime()); } protected String removeUTF8BOM(String s) { if (s.startsWith(UTF8_BOM)) { s = s.substring(1); } return s; } @Override public SubtitlesContainer fromFile(File input) throws MalformedSubtitlesException, IOException { return fromStream(new FileInputStream(input)); } @Override public File toFile(SubtitlesContainer container, File output) throws SubtitlesGenerationException, IOException { toStream(container, new FileOutputStream(output)); return output; } public OutputStream toStream(SubtitlesContainer container, OutputStream output) throws SubtitlesGenerationException { try { subtitlesWriter = new PrintWriter(output); container.accept(this); return output; } finally { logger.trace("SubtitlesContainer has been transformed with success"); subtitlesWriter.close(); subtitlesWriter = null; } } @Override public void visit(SubtitlesContainer container) throws SubtitlesGenerationException { logger.trace("Visiting SubtitlesContainer"); if (subtitlesWriter == null) throw new IllegalStateException("You can't call visit directly from " + this.getClass().getSimpleName()); } @Override public void visit(Caption caption) throws SubtitlesGenerationException { logger.trace("Visiting Caption"); if (subtitlesWriter == null) throw new IllegalStateException("You can't call visit directly from " + this.getClass().getSimpleName()); } protected static class StyleMapping { public String name; public boolean mandatory; public String defaultValue; public StyleProperty mirroredProperty; public StyleMapping(String name, String defaultValue) { this.name = name; this.mandatory = false; this.defaultValue = defaultValue; this.mirroredProperty = null; } public StyleMapping(String name, StyleProperty mirroredProperty) { this.name = name; this.mandatory = false; this.defaultValue = null; this.mirroredProperty = mirroredProperty; } public StyleMapping(String name) { this.name = name; this.mandatory = true; this.defaultValue = null; this.mirroredProperty = null; } @Override public String toString() { return name; } @Override public int hashCode() { return name.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj instanceof StyleMapping) { StyleMapping mapping = (StyleMapping) obj; return name == null ? mapping.name == null : name.equals( mapping.name); } else if (obj instanceof String) { String mapping = (String) obj; return name == null ? mapping == null : name.equals(mapping); } return false; } } }