org.privatenotes.Note.java Source code

Java tutorial

Introduction

Here is the source code for org.privatenotes.Note.java

Source

/*
 * Tomdroid
 * Tomboy on Android
 * http://www.launchpad.net/tomdroid
 * 
 * Copyright 2008, 2009 Olivier Bilodeau <olivier@bottomlesspit.org>
 * Copyright 2009, Benoit Garret <benoit.garret_launchpad@gadz.org>
 * 
 * This file is part of Tomdroid.
 * 
 * Tomdroid 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, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Tomdroid 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 Tomdroid.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.privatenotes;

import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONException;
import org.json.JSONArray;
import org.json.JSONObject;
import org.privatenotes.util.NoteContentBuilder;
import org.privatenotes.util.XmlUtils;

import android.database.Cursor;
import android.os.Handler;
import android.text.SpannableStringBuilder;
import android.text.format.Time;
import android.util.Log;
import android.util.TimeFormatException;

public class Note implements Cloneable {

    // Static references to fields (used in Bundles, ContentResolvers, etc.)
    public static final String ID = "_id";
    public static final String GUID = "guid";
    public static final String TITLE = "title";
    public static final String MODIFIED_DATE = "modified_date";
    public static final String IS_SYNCED = "is_synced";
    public static final String URL = "url";
    public static final String FILE = "file";
    public static final String TAGS = "tags";
    public static final String NOTE_CONTENT = "content";
    public final static String ENCRYPTED = "encrypted";
    public final static String ENCRYPTED_ALGORITHM = "encryptedAlgorithm";

    // Logging info
    private static final String TAG = "Note";

    // Notes constants
    // TODO this is a weird yellow that was usable for the android emulator, I must confirm this for real usage
    public static final int NOTE_HIGHLIGHT_COLOR = 0xFFFFFF77;
    public static final String NOTE_MONOSPACE_TYPEFACE = "monospace";
    public static final float NOTE_SIZE_SMALL_FACTOR = 1.0f;
    public static final float NOTE_SIZE_LARGE_FACTOR = 1.5f;
    public static final float NOTE_SIZE_HUGE_FACTOR = 1.8f;

    // Members
    private SpannableStringBuilder noteContent;
    private String xmlContent;
    private String url;
    private String fileName;
    private String title;
    private String tags;

    private boolean encrypted = false;
    private String encryptedAlgorithm;

    private Time lastChangeDate;
    private int dbId;
    private UUID guid;
    // TODO before guid were of the UUID object type, now they are simple strings 
    // but at some point we probably need to validate their uniqueness (per note collection or universe-wide?) 
    private long lastSyncRevision;
    private boolean isSynced = true;

    // Date converter pattern (remove extra sub milliseconds from datetime string)
    // ex: will strip 3020 in 2010-01-23T12:07:38.7743020-05:00
    private static final Pattern dateCleaner = Pattern
            .compile("(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3})" + // matches: 2010-01-23T12:07:38.774
                    ".+" + // matches what we are getting rid of
                    "([-\\+]\\d{2}:\\d{2})"); // matches timezone (-xx:xx or +xx:xx)

    public Note() {
        tags = new String();
        setTitle("no tilte");
        setGuid(UUID.randomUUID());
        lastSyncRevision = 0;
        changeXmlContent("no content");
        setTags("");
    }

    public Note(JSONObject json) {

        // These methods return an empty string if the key is not found
        setTitle(XmlUtils.unescape(json.optString("title")));
        setGuid(json.optString("guid"));
        setLastChangeDate(json.optString("last-change-date"));
        lastSyncRevision = json.optInt("last-sync-revision", -1);
        setXmlContent(json.optString("note-content"));
        JSONArray jtags = json.optJSONArray("tags");
        String tag;
        tags = new String();
        if (jtags != null) {
            for (int i = 0; i < jtags.length(); i++) {
                tag = jtags.optString(i);
                tags += tag + ",";
            }
        }

        // encryption/decryption
        if (json.optString("encrypted") != null) {
            //<encrypted>
            setEncrypted(json.optString("encrypted").toLowerCase().trim().equals("true"));
        }
        //<encryptedAlgorithm>
        setEncryptedAlgorithm(json.optString("encryptedAlgorithm"));
    }

    public Note(Cursor cursor) {
        String content = cursor.getString(cursor.getColumnIndexOrThrow(Note.NOTE_CONTENT));
        setXmlContent(content);

        String title = cursor.getString(cursor.getColumnIndexOrThrow(Note.TITLE));
        setTitle(title);

        String lastChangeDate = cursor.getString(cursor.getColumnIndexOrThrow(Note.MODIFIED_DATE));
        setLastChangeDate(lastChangeDate);

        setGuid(cursor.getString(cursor.getColumnIndexOrThrow(Note.GUID)));

        setTags(cursor.getString(cursor.getColumnIndexOrThrow(Note.TAGS)));

        setEncrypted(
                cursor.getString(cursor.getColumnIndexOrThrow(Note.ENCRYPTED)).trim().equalsIgnoreCase("true"));
        setEncryptedAlgorithm(cursor.getString(cursor.getColumnIndexOrThrow(Note.ENCRYPTED_ALGORITHM)));
        setFileName(cursor.getString(cursor.getColumnIndexOrThrow(Note.FILE)));

        int synced = cursor.getInt(cursor.getColumnIndexOrThrow(Note.IS_SYNCED));
        isSynced(synced == 1 ? true : false);
    }

    /**
     * Weather the note is in sync with the server or not. Default is 'true'.
     */
    public boolean isSynced() {
        return isSynced;
    }

    public void isSynced(boolean flag) {
        isSynced = flag;
    }

    public void setEncryptedAlgorithm(String _encryptedAlgorithm) {
        encryptedAlgorithm = _encryptedAlgorithm;

    }

    public void setEncrypted(boolean _encrypted) {
        encrypted = _encrypted;
    }

    public boolean isEncrypted() {
        return encrypted;
    }

    public String getEncryptedAlgorithm() {
        return encryptedAlgorithm;
    }

    public String getTags() {
        return tags;
    }

    public void setTags(String tags) {
        this.tags = tags;
    }

    public String getUrl() {
        return url;
    }

    public long getLastSyncRevision() {
        return lastSyncRevision;
    }

    public void setLastSyncRevision(long revision) {
        lastSyncRevision = revision;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Time getLastChangeDate() {
        return lastChangeDate;
    }

    public void setLastChangeDate(Time lastChangeDate) {
        this.lastChangeDate = lastChangeDate;
        lastChangeDate.switchTimezone(Time.TIMEZONE_UTC);
    }

    public void setLastChangeDate(String lastChangeDateStr) throws TimeFormatException {

        // regexp out the sub-milliseconds from tomboy's datetime format
        // Normal RFC 3339 format: 2008-10-13T16:00:00.000-07:00
        // Tomboy's (C# library) format: 2010-01-23T12:07:38.7743020-05:00
        Matcher m = dateCleaner.matcher(lastChangeDateStr);
        if (m.find()) {
            Log.d(TAG, "I had to clean out extra sub-milliseconds from the date");
            lastChangeDateStr = m.group(1) + m.group(2);
            Log.v(TAG, "new date: " + lastChangeDateStr);
            lastChangeDateStr = m.group(1) + m.group(2);
            Log.v(TAG, "new date: " + lastChangeDateStr);
        }

        lastChangeDate = new Time();
        lastChangeDate.parse3339(lastChangeDateStr);
        lastChangeDate.switchTimezone(Time.TIMEZONE_UTC);
    }

    public int getDbId() {
        return dbId;
    }

    public void setDbId(int id) {
        this.dbId = id;
    }

    public UUID getGuid() {
        return guid;
    }

    public void setGuid(String guid) {
        this.guid = UUID.fromString(fixUUID(guid));
    }

    public void setGuid(UUID guid) {
        this.guid = guid;
    }

    // TODO: should this handler passed around evolve into an observer pattern?
    public SpannableStringBuilder getNoteContent(Handler handler) {

        // TODO not sure this is the right place to do this
        noteContent = new NoteContentBuilder().setCaller(handler).setInputSource(getXmlContent()).build();
        return noteContent;
    }

    public String getXmlContent() {
        return xmlContent;
    }

    public void setXmlContent(String xmlContent) {
        this.xmlContent = xmlContent;
    }

    /**
     * Updates the content and sets last-change-date to now and flags the note as "not in sync with server".
     */
    public void changeXmlContent(String xmlContent) {
        this.xmlContent = xmlContent;
        Time time = new Time();
        time.setToNow();
        setLastChangeDate(time);
        isSynced(false);
    }

    public JSONObject toJsonWithoutContent() throws JSONException {
        JSONObject json = toJson();
        json.remove("note-content");
        return json;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Note))
            return false;

        Note note = (Note) obj;
        if (note.getGuid().equals(getGuid()) && note.getLastChangeDate().equals(getLastChangeDate())
                && note.getTitle().equals(getTitle()))
            return true;

        return false;
    }

    public JSONObject toJson() throws JSONException {
        return new JSONObject("{'guid':'" + getGuid() + "', 'title':'" + getTitle() + "', 'note-content':'"
                + getJsonPreparedXmlContent() + "', 'last-change-date':'" + getLastChangeDate().format3339(false)
                + "', 'note-content-version':0.1}");
    }

    /**
     * makes sure we have a valid uuid, sometimes they arrive without the
     * dashes (seen in Google play crash reports)
     * @param uuid
     * @return
     */
    public static String fixUUID(String uuid) {
        if (uuid.contains("-")) {
            return uuid;
        } else {
            Log.d(TAG, "fixing guid " + uuid);
            StringBuilder sb = new StringBuilder(38);
            sb.append(uuid.substring(0, 8));
            sb.append("-");
            sb.append(uuid.substring(8, 12));
            sb.append("-");
            sb.append(uuid.substring(12, 16));
            sb.append("-");
            sb.append(uuid.substring(16, 20));
            sb.append("-");
            sb.append(uuid.substring(20));
            return sb.toString();
        }
    }

    private String getJsonPreparedXmlContent() {
        return getXmlContent().replace("\n", "\\n").replace("\"", "\\\"").replace("'", "\\'").replace("\b", "\\b")
                .replace("\f", "\\f").replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t");
    }

    @Override
    public String toString() {

        return new String("Note: " + getTitle() + " (" + getLastChangeDate() + ")");
    }

    public Note clone() {

        Note clone = new Note();

        clone.noteContent = noteContent;
        clone.xmlContent = xmlContent;
        clone.url = url;
        clone.fileName = fileName;
        clone.title = title;
        clone.lastChangeDate = lastChangeDate;
        clone.lastSyncRevision = lastSyncRevision;
        clone.dbId = dbId;
        clone.guid = guid;
        clone.tags = tags;

        return clone;

    }

}