// This file is part of Penn TotalRecall <http://memory.psych.upenn.edu/TotalRecall>. // // TotalRecall is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 3 only. // // TotalRecall is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with TotalRecall. If not, see <http://www.gnu.org/licenses/>. package components.annotations; import info.Constants; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import util.OSPath; /** * Handles manipulations of annotation files, e.g. adding and removing annotations. * * @author Yuvi Masory */ public class AnnotationFileParser { private static final Pattern delimiter = Pattern.compile(Constants.annotationFileDelimiter); private static final Pattern commentPattern = Pattern.compile("(.*)" + Constants.inlineCommentIndicator + ".*"); /** * Private constructor to prevent instantiation. */ private AnnotationFileParser() { } private static Annotation parseLine(String line) { Scanner sc = new Scanner(line).useDelimiter(delimiter); if(sc.hasNextDouble()) { double time = sc.nextDouble(); if(sc.hasNextInt()) { int wordNum = sc.nextInt(); if(sc.hasNext()) { String text = sc.next().toUpperCase(); return new Annotation(time, wordNum, text); } } } return null; } private static String makeLine(Annotation ann) { return ann.getTime() + delimiter.toString() + ann.getWordNum() + delimiter.toString() + ann.getText(); } /** * Parses <code>Annotations</code> from a <code>File</code>. * * Proceeds line by line, parsing at most one <code>Annotation</code> per line. * * @param file The file to be parsed * @return A <code>List</code> of <code>Annotations</code> from the file, or <code>null</code> if the file does not exist or causes an <code>IOException</code> */ public static List<Annotation> parse(File file) { BufferedReader br; try { br = new BufferedReader(new FileReader(file)); } catch (FileNotFoundException e) { e.printStackTrace(); return null; } ArrayList<Annotation> anns = new ArrayList<Annotation>(); String line; try { int lineNum = 1; line = br.readLine(); while(line != null) { Matcher m = commentPattern.matcher(line); if(m.matches()) { line = m.group(1); } if(line.length() > 0) { Annotation ann = parseLine(line); if(ann != null) { anns.add(ann); } else { Matcher whiteSpace = Pattern.compile("\\s*").matcher(line); if(whiteSpace.matches() == false) { System.err.println("line #" + lineNum + " unparseable: " + line); } } } line = br.readLine(); lineNum++; } } catch (IOException e) { e.printStackTrace(); return anns; } return anns; } public static boolean removeAnnotation(Annotation annToDelete, File oFile) throws IOException { File nFile = new File(OSPath.basename(oFile.getAbsolutePath()) + "." + Constants.deletionTempFileExtension); BufferedReader reader = null; BufferedWriter writer = null; reader = new BufferedReader(new FileReader(oFile)); writer = new BufferedWriter(new FileWriter(nFile)); boolean foundTarget = false; String curLine; while((curLine = reader.readLine()) != null) { Annotation curLineAnn = parseLine(curLine); if(curLineAnn != null && curLineAnn.equals(annToDelete)) { if(foundTarget) { System.err.println("duplicate match?: " + curLine); } else { foundTarget = true; } } else { writer.write(curLine + "\n"); } } writer.close(); reader.close(); if(oFile.delete() == false) { throw new IOException("could not delete old file"); } if(nFile.renameTo(oFile) == false) { throw new IOException("could not rename temp deletion file to normal temp file"); } return foundTarget; } public static void appendAnnotation(Annotation ann, File oFile) throws IOException { ArrayList<String> inLines = new ArrayList<String>(); ArrayList<String> outLines = new ArrayList<String>(); BufferedReader reader = new BufferedReader(new FileReader(oFile)); String curLine; while((curLine = reader.readLine()) != null) { inLines.add(curLine); } String lineToAdd = makeLine(ann); boolean foundPosition = false; for(int i = 0; i < inLines.size(); i++) { if(foundPosition == false) { Annotation curAnn = parseLine(inLines.get(i)); if(curAnn != null) { if(ann.getTime() < curAnn.getTime()) { outLines.add(lineToAdd); foundPosition = true; } } } outLines.add(inLines.get(i)); } if(foundPosition == false) { outLines.add(lineToAdd); } reader.close(); File nFile = new File(OSPath.basename(oFile.getAbsolutePath()) + "." + Constants.deletionTempFileExtension); BufferedWriter writer = new BufferedWriter(new FileWriter(nFile)); for(int i = 0; i < outLines.size(); i++) { writer.write(outLines.get(i) + "\n"); } writer.close(); oFile.delete(); if(nFile.renameTo(oFile) == false) { throw new IOException("could not rename temp deletion file to normal temp file"); } } public static boolean headerExists(File oFile) { if(oFile.exists()) { BufferedReader br; try { br = new BufferedReader(new FileReader(oFile)); String firstLine = br.readLine(); br.close(); if(firstLine != null) { return firstLine.contains(Constants.headerStartLine); } else { return false; } } catch (IOException e) { e.printStackTrace(); return false; } } else { return false; } } public static void prependHeader(File oFile, String annotatorName) throws IOException { if(oFile.exists() == false) { throw new FileNotFoundException(oFile + " not found"); } File tmpFile = new File(oFile.getAbsolutePath() + "." + Constants.deletionTempFileExtension); BufferedWriter fw = new BufferedWriter(new FileWriter(tmpFile)); fw.write(Constants.headerStartLine + "\n"); fw.write(Constants.commentStart + "Annotator: " + annotatorName + "\n"); Date date = new Date(); String utcString = DateFormat.getDateInstance(DateFormat.LONG).format(date) + Constants.annotationFileDelimiter + DateFormat.getTimeInstance(DateFormat.LONG).format(date); fw.write(Constants.commentStart + "UTC Locally Formatted: " + utcString + "\n"); fw.write(Constants.commentStart + "UNIX: " + System.currentTimeMillis()/1000 + "\n"); fw.write(Constants.commentStart + "Program Version: " + Constants.programVersion + "\n"); String[] osPropertyStrings = {"os.name", "os.arch", "user.name", "user.country", "user.language"}; writePropertyLine("OS Properties", osPropertyStrings, fw); String[] javaPropertyStrings = { "java.runtime.version", "java.specification.name", "java.specification.vendor", "java.specification.version", "java.vendor", "java.version", "java.vm.name", "java.vm.specification.name", "java.vm.specification.vendor", "java.vm.specification.version", "java.vm.vendor", "java.vm.version"}; writePropertyLine("Java Properties", javaPropertyStrings, fw); fw.write("\n"); BufferedReader br = new BufferedReader(new FileReader(oFile)); String curLine; while((curLine = br.readLine()) != null) { fw.write(curLine + "\n"); } br.close(); fw.close(); if(oFile.delete() == false) { throw new IOException("could not delete old file"); } if(tmpFile.renameTo(oFile) == false) { throw new IOException("could not rename temp deletion file to normal temp file"); } } private static void writePropertyLine(String name, String[] properties, BufferedWriter fw) throws IOException { fw.write(Constants.commentStart + name + ": "); for(String prop: properties) { String propVal = System.getProperty(prop); if(propVal != null) { if(propVal.contains(Constants.propertyPairOpenBrace) || propVal.contains(Constants.propertyPairCloseBrace) || propVal.contains(Constants.annotationFileDelimiter)) { System.err.println("cannot store property value: " + propVal + " because it contains a reserved character"); continue; } } else { System.err.println("no such property: " + prop); continue; } fw.write(Constants.propertyPairOpenBrace); fw.write(prop); fw.write(Constants.annotationFileDelimiter); fw.write(propVal); fw.write(Constants.propertyPairCloseBrace); } fw.write("\n"); } public static void addField(File oFile, String string) throws IOException { if(oFile.exists() == false) { throw new FileNotFoundException(oFile + " not found"); } File tmpFile = new File(oFile.getAbsolutePath() + "." + Constants.deletionTempFileExtension); BufferedWriter fw = new BufferedWriter(new FileWriter(tmpFile)); BufferedReader br = new BufferedReader(new FileReader(oFile)); String curLine; boolean wroteTime = false; while((curLine = br.readLine()) != null) { if(curLine.startsWith("#") == false && wroteTime == false) { fw.write(Constants.commentStart + string + "\n"); wroteTime = true; } fw.write(curLine + "\n"); } br.close(); fw.close(); if(oFile.delete() == false) { throw new IOException("could not delete old file"); } if(tmpFile.renameTo(oFile) == false) { throw new IOException("could not rename temp deletion file to normal temp file"); } } }