import java.io.File; import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; import java.io.FileNotFoundException; import android.util.Log; class Notebook { // Error levels specification // STRICT: Fail on unexpected behaviour // READONLY: Unexpected behaviour prevents writing // IGNORE: Unexpected headings are rewritten unmodified, missing headers are added public enum ErrorLevel { STRICT, READONLY, IGNORE } // Logical state variables private static String rootPath; // Path to the notebook folder private boolean isNewNotebook = false; private boolean dataIsLoaded; // True when notebook.zim has been parsed private ErrorLevel policy = ErrorLevel.STRICT; private boolean hasErrors = false; // Notebook metadata variables private String name; private String version; private String interwiki; private String homePage; private String iconPath; private String documentPath; private boolean shared; private String endOfLine; private boolean disableTrash; private String profile; private ArrayList<String> extraEntries; // Storage for unrecognised information // Constants // public enum newlineStyle = { WINDOWS, UNIX, MAC }; private String logTag = "zimdroid notebook"; /** Creates a new Notebook object located at the path given. * @param root The path of the Zim notebook's root folder * @param policy The behaviour to take when errors are encountered * @throws IOException */ public Notebook(String root, ErrorLevel policy) throws IOException { this.rootPath = root; this.policy = policy; this.loadData(); } /** Creates a new Notebook object located at the path given. * @param root The path of the Zim notebook's root folder * @throws IOException */ public Notebook(String root) throws IOException { this.Notebook(root, ErrorLevel.STRICT); } /** Returns true if the directory exists and is a valid notebook. */ public boolean exists() { File n = new File(this.rootPath); File zim = new File(this.rootPath + File.separator + "notebook.zim"); return n.exists() && zim.exists(); } /** Returns true if this notebook will be created by this instance. */ public boolean isNewNotebook() { return this.isNewNotebook; } //{{{ GETTERS AND SETTERS /** Returns the name of the notebook. */ public String getName() { return this.name; } /** Returns the version of the notebook. */ public String getVersion() { return this.version; } /** Returns the interwiki keyword of the notebook. */ public String getInterwiki() { return this.interwiki; } /** Returns the name of the notebook's home page. */ public String getHomePage() { return this.homePage; } /** Returns the path of the notebook's icon. */ public String getIconPath() { return this.iconPath; } /** Returns the path of the notebook's document root. */ public String getDocumentPath() { return this.documentPath; } /** Returns true if the notebook is shared. */ public boolean getShared() { return this.shared; } /** Returns the notebook's end of line style. * Valid values are Notebook.DOS, Notebook.UNIX, and Notebook.MAC */ public String getEndOfLine() { return this.endOfLine; } /** Returns true if the notebook's trash is disabled. */ public boolean getTrashDisabled() { return this.disableTrash; } /** Returns the profile name of the notebook. */ public String getProfile() { return this.profile; } //}}} GETTERS AND SETTERS /** Writes the entries in notebook.zim to file * @throws IOException */ public void saveData() throws IOException { if (this.policy == ErrorLevel.STRICT && this.hasErrors) { return; } BufferedWriter metadataFile = new BufferedWriter(new Filewriter(this.rootPath + File.separator + "notebook.zim")); // Write the identifier String sectionHeader = "[Notebook]\n"; metadataFile.write(sectionHeader, 0, sectionHeader.length()); // Prepare the known entries String versionEntry = "version="; String nameEntry = "name="; String interwikiEntry = "interwiki="; String homeEntry = "home="; String iconEntry = "icon="; String documentEntry = "document_root="; String sharedEntry = "shared="; String eolEntry = "endofline="; String trashEntry = "disable_trash="; String profileEntry = "profile="; if (this.version==null) { versionEntry = versionEntry + "\n"; } else { versionEntry = versionEntry + this.version + "\n"; } if (this.name==null) { nameEntry = nameEntry + "\n"; } else { nameEntry = nameEntry + this.name + "\n"; } if (this.interwiki==null) { interwikiEntry = interwikiEntry + "\n"; } else { interwikiEntry = interwikiEntry + this.interwiki + "\n"; } if (this.homePage==null) { homeEntry = homeEntry + "\n"; } else { homeEntry = homeEntry + this.homePage + "\n"; } if (this.iconPath==null) { iconEntry = iconEntry + "\n"; } else { iconEntry = iconEntry + this.iconPath + "\n"; } if (this.documentPath==null) { documentEntry = documentEntry + "\n"; } else { documentEntry = documentEntry + this.documentPath + "\n"; } if (this.shared==null) { sharedEntry = sharedEntry + "\n"; } else if (shared==true) { sharedEntry = sharedEntry + "True\n"; } else { sharedEntry = sharedEntry + "False\n"; } if (this.endOfLine==null) { eolEntry = eolEntry + "\n"; } else { eolEntry = eolEntry + this.endOfLine + "\n"; } if (this.disableTrash==null) { trashEntry = trashEntry + "\n"; } else if (this.disableTrash==true) { trashEntry = trashEntry + "True\n"; } else { trashEntry = trashEntry + "False\n"; } if (this.profile==null) { profileEntry = profileEntry + "\n"; } else { profileEntry = profileEntry + this.profile + "\n"; } // Write the known entries metadataFile.write(versionEntry, 0, versionEntry.length()); metadataFile.write(nameEntry, 0, nameEntry.length()); metadataFile.write(interwikiEntry, 0, interwikiEntry.length()); metadataFile.write(homeEntry, 0, homeEntry.length()); metadataFile.write(iconEntry, 0, iconEntry.length()); metadataFile.write(documentEntry, 0, documentEntry.length()); metadataFile.write(sharedEntry, 0, sharedEntry.length()); metadataFile.write(eolEntry, 0, eolEntry.length()); metadataFile.write(trashEntry, 0, trashEntry.length()); metadataFile.write(profileEntry, 0, profileEntry.length()); // Write the unknown entries for (String e : this.extraEntries) { metadataFile.write(e + "\n", 0, e.length() + 1); } // Save the file metadataFile.close(); } /** Loads the values from notebook.zim. */ private void loadData() throws IOException { BufferedReader metadataFile = new BufferedReader(new FileReader(rootPath + File.separator + "notebook.zim")); this.extraEntries = new ArrayList<String>(); String nextLine; try { // The first line must be '[Notebook]' nextLine = metadataFile.readLine(); if (nextLine == null || ! nextLine.equals("[Notebook]")) { throw new IOException("Not a zim notebook: notebook.zim doesn't start with a Notebook section"); } // The second line must be the version entry nextLine = metadatFile.readLine(); if (nextLine == null || !nextLine.startsWith("version=")) { throw new IOException("Not a zim notebook: no version entry in notebook.zim"); } else { int sepPos = nextLine.lastIndexOf("="); this.version = nextLine.substring(sepPos + 1, nextLine.length()); } // Read the remaining entries while (metadataFile.ready()) { nextLine = metadataFile.readLine(); // Check that the line is a key=value format line if (nextLine.contains("=")) { String[] tokens = nextLine.split("="); switch (tokens[0]) { case "name": if (tokens.length > 1) { this.name = tokens[1]; } else { throw new IOException("Error parsing notebook.zim: empty name"); } break; case "interwiki": if (tokens.length > 1) { this.interwiki = tokens[1]; } break; case "home": if (tokens.length > 1) { this.homePage = tokens[1]; } break; case "icon": if (tokens.length > 1) { this.iconPath = tokens[1]; } break; case "document_root": if (tokens.length > 1) { this.documentPath = tokens[1]; } break; case "shared": if (tokens.length > 1) { this.shared = (Boolean.valueOf(tokens[1])).booleanValue(); } break; case "endofline": if (tokens.length > 1) { this.endOfLine = tokens[1]; } break; case "disable_trash": if (tokens.length > 1) { this.disableTrash = (Boolean.valueOf(tokens[1])).booleanValue(); } break; case "profile": if (tokens.length > 1) { this.profile = tokens[1]; } break; default: Log.w("Warning: unknown entry parsing notebook.zim for " + this.rootPath); this.extraEntries.add(nextLine); this.hasErrors = true; break; } } else { Log.w(logTag, "Warning: Notebook.zim contains more than simple entries after [Notebook] section."); this.hasErrors = true; } } metadataFile.close(); this.dataIsLoaded = true; } catch (FileNotFoundException e) { // This means that this is a new notebook. Let's set some defaults this.version = "0.4"; this.name = "Notes"; this.interwiki = null; this.homePage = "Home"; this.iconPath = null; this.documentPath = null; this.shared = false; this.endOfLine = this.unix; this.disable_trash = false; this.profile = null; this.isNewNotebook = true; } } public String getPageFilename(String pageName) { String pageFilename = this.rootPath; if (pageName.contains(":")) { String[] pageNameParts = pageName.split(":"); int i = 0; while (i < pageNameParts.length) { pageFilename = pageFilename + File.separator + pageNameParts[i]; i++; } } else { pageFilename = pageFilename + File.separator + pageName; } pageFilename = pageFilename + ".txt"; return pageFilename; } private String prepareEntry(String base, String value) { if (value==null) { return base + "\n"; } else { return base + value + "\n"; } } private String prepareEntry(String base, boolean value) { if (value==null) { return base + "\n"; } else if (value==true) { return base + "True\n"; } else { return base + "False\n"; } } }