Added documentation, fleshed out accessors for class Timeline.

This commit is contained in:
Jonathan Bernard 2010-01-06 09:57:17 -06:00
parent 6d212ea771
commit 7f2c1a6d53
9 changed files with 160 additions and 65 deletions

View File

@ -1,3 +1,4 @@
.*.swp .*.swp
.*.swo .*.swo
staging staging
dist

BIN
CHANGE ME Normal file

Binary file not shown.

BIN
doc/uml.tsm Normal file

Binary file not shown.

View File

@ -8,8 +8,12 @@ class TimeStamperMainController {
def model def model
def view def view
def syncTimers = [:]
void mvcGroupInit(Map args) { void mvcGroupInit(Map args) {
def configFile
logger.traceIfEnabled("Initializing TimeStamperMain MVC...") logger.traceIfEnabled("Initializing TimeStamperMain MVC...")
def thisMVC = ['model': model, 'view': view, 'controller': this] def thisMVC = ['model': model, 'view': view, 'controller': this]
@ -23,13 +27,13 @@ class TimeStamperMainController {
// load application properties // load application properties
Properties prop = new Properties() Properties prop = new Properties()
String userHomeDir = System.getProperty('user.home') String userHomeDir = System.getProperty('user.home')
model.configFile = new File(userHomeDir, ".timestamperrc") configFile = new File(userHomeDir, ".timestamperrc")
if (!model.configFile.exists()) model.configFile.createNewFile() if (!configFile.exists()) configFile.createNewFile()
logger.traceIfEnabled("Reading configuration from " logger.traceIfEnabled("Reading configuration from "
+ "'${model.configFile.name}'") + "'${configFile.name}'")
try { model.configFile.withInputStream { prop.load(it) } } try { configFile.withInputStream { prop.load(it) } }
catch (IOException ioe) { catch (IOException ioe) {
logger.error('Unable to load configuration', ioe) logger.error('Unable to load configuration', ioe)
} }
@ -45,8 +49,9 @@ class TimeStamperMainController {
logger.traceIfEnabled("Reading Timeline properties from '${lastUsed}'") logger.traceIfEnabled("Reading Timeline properties from '${lastUsed}'")
File propertyFile = new File(lastUsed) model.timelinePropertiesFile = new File(lastUsed)
if (!propertyFile.exists()) propertyFile.createNewFile() if (!model.timelinePropertiesFile.exists())
model.timelinePropertiesFile.createNewFile()
load(propertyFile) load(propertyFile)
} }

View File

@ -13,7 +13,7 @@ class TimeStamperMainModel {
@Bindable Timeline timeline @Bindable Timeline timeline
@Bindable TimelineProperties timelineProperties @Bindable TimelineProperties timelineProperties
Properties config Properties config
File configFile File timelinePropertiesFile
def notesDialogMVC def notesDialogMVC
def punchcardDialogMVC def punchcardDialogMVC

View File

@ -6,8 +6,8 @@ import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
/** /**
* * A remote target synchronized against the local timeline.
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com}) * @author Jonathan Bernard (jonathan.bernard@gemalto.com)
*/ */
public class SyncTarget { public class SyncTarget {
@ -23,6 +23,9 @@ public class SyncTarget {
protected boolean pullEnabled = true; protected boolean pullEnabled = true;
protected boolean syncOnExit = true; protected boolean syncOnExit = true;
/**
*
*/
protected class SyncTask extends TimerTask { protected class SyncTask extends TimerTask {
@Override public void run() { @Override public void run() {
synchronized(this) { synchronized(this) {
@ -109,21 +112,21 @@ public class SyncTarget {
public long getSyncInterval() { return syncInterval; } public long getSyncInterval() { return syncInterval; }
public synchronized void enablePush(boolean enablePush) { public synchronized void setPushEnabled(boolean pushEnabled) {
this.pullEnabled = enablePush; this.pullEnabled = pushEnabled;
} }
public boolean isPushEnabled() { return pushEnabled; } public boolean getPushEnabled() { return pushEnabled; }
public synchronized void enablePull(boolean enablePull) { public synchronized void setPullEnabled(boolean pullEnabled) {
this.pullEnabled = enablePull; this.pullEnabled = pullEnabled;
} }
public boolean isPullEnabled() { return pullEnabled; } public boolean getPullEnabled() { return pullEnabled; }
public synchronized void enableSyncOnExit(boolean syncOnExit) { public synchronized void setSyncOnExit(boolean syncOnExit) {
this.syncOnExit = syncOnExit; this.syncOnExit = syncOnExit;
} }
public boolean isSyncOnExitEnabled() { return syncOnExit; } public boolean getSyncOnExit() { return syncOnExit; }
} }

View File

@ -8,10 +8,11 @@ import java.util.Iterator;
import java.util.TreeSet; import java.util.TreeSet;
/** /**
* @author Jonathan Bernard {@literal <jdbernard@gmail.com>}
* A Timeline object represents a series of markers at specific points in time. * A Timeline object represents a series of markers at specific points in time.
* The markers have a name or symbol (the 'mark') and notes associated with that * It represents on logical timeline. The markers have a name or symbol (the
* mark. * 'mark') and notes associated with that mark.
* @author Jonathan Bernard {@literal <jdbernard@gmail.com>}
* @see com.jdbernard.timestamper.core.TimelineSource
*/ */
public class Timeline implements Iterable<TimelineMarker> { public class Timeline implements Iterable<TimelineMarker> {
@ -19,31 +20,39 @@ public class Timeline implements Iterable<TimelineMarker> {
public static SimpleDateFormat longFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); public static SimpleDateFormat longFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
private TreeSet<TimelineMarker> timelineList; private TreeSet<TimelineMarker> timelineList;
/**
* Create a new, empty Timeline.
*/
public Timeline() { public Timeline() {
timelineList = new TreeSet<TimelineMarker>(); timelineList = new TreeSet<TimelineMarker>();
} }
public void addMarker(TimelineMarker tm) { timelineList.add(tm); } /**
* Add a marker to the timeline.
* @param tm The TimelineMarker to add.
* @return <code>true</code> if this Timeline was modified.
*/
public boolean addMarker(TimelineMarker tm) { return timelineList.add(tm); }
public void addMarker(Date timestamp, String name, String notes) { /**
timelineList.add(new TimelineMarker(timestamp, name, notes)); * Add a marker to the timeline.
} * @param timestamp The date and time of the marker.
* @param name The name of the marker (activity, project, etc)
public String getLastName(Date timestamp) { * @param notes Additional notes about this marker.
TimelineMarker lastMarker = getLastMarker(timestamp); * @return <code>true</code> if this Timeline was modified.
return (lastMarker == null ? */
"No previous marker." : public boolean addMarker(Date timestamp, String name, String notes) {
lastMarker.getMark()); return timelineList.add(new TimelineMarker(timestamp, name, notes));
}
public String getLastNotes(Date timestamp) {
TimelineMarker lastMarker = getLastMarker(timestamp);
return (lastMarker == null ?
"No previous marker." :
lastMarker.getNotes());
} }
/**
* Get the last marker placed before or on the given timestamp. If you
* think of the markers as demarcating time, then this is effectivly,
* "get the current task."
* @param timestamp The cut-off point for the query.
* @return The latest TimelineMarker placed on or before the given
* timestamp.
*/
public TimelineMarker getLastMarker(Date timestamp) { public TimelineMarker getLastMarker(Date timestamp) {
TimelineMarker lastMarker = null; TimelineMarker lastMarker = null;
for (TimelineMarker tm : timelineList) { for (TimelineMarker tm : timelineList) {
@ -55,8 +64,13 @@ public class Timeline implements Iterable<TimelineMarker> {
return lastMarker; return lastMarker;
} }
public void removeMarker(TimelineMarker marker) { /**
timelineList.remove(marker); * Remove a TimelineMarker from this Timelnie.
* @param marker The marker to remove.
* @return <code>true</code> if this Timeline was changed.
*/
public boolean removeMarker(TimelineMarker marker) {
return timelineList.remove(marker);
} }
public Iterator<TimelineMarker> iterator() { public Iterator<TimelineMarker> iterator() {
@ -84,14 +98,31 @@ public class Timeline implements Iterable<TimelineMarker> {
return difference; return difference;
} }
public void addAll(Timeline t) { /**
* Add all TimelineMarkers from <code>t</code> to <code>this</code>
* Timeline, excluding markers already present in <code>this</code>.
* @param t
* @return <code>true</code> if this Timeline was modified.
*/
public boolean addAll(Timeline t) {
boolean modified = false;
for (TimelineMarker tm : t) { for (TimelineMarker tm : t) {
if (!timelineList.contains(tm)) if (!timelineList.contains(tm)) {
timelineList.add(tm); timelineList.add(tm);
modified = true;
} }
} }
public void addAll(Collection<TimelineMarker> c) { return modified;
timelineList.addAll(c); }
/**
* Add all TimelineMarkers from <code>c</code> to <code>this</code>
* Timeline, excluding markers already present in <code>this</code>.
* @param c A Collection of TimelineMarkers
* @return <code>true</code> if this TImeline was modified.
*/
public boolean addAll(Collection<TimelineMarker> c) {
return timelineList.addAll(c);
} }
} }

View File

@ -4,7 +4,9 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Collection; import java.util.Collection;
@ -15,8 +17,32 @@ import java.util.regex.Pattern;
/** /**
* * Represents a Timeline configuration. A configuration has one primary, or
* local, Timeline with an associated TimelineSource, and a list of remote
* Timelines synched to the local Timeline.
* <br>
* <table>
* <tr><th>Property</th><th>Description</th></tr>
* <tr><td><code>timeline.uri</code></td><td>The URI of the primary (local)
* timeline</td></tr>
* <tr><td><code>remote.timeline.</code><i>name</i><code>.uri</code></td>
* <td>The URI for the <i>name</i> remote timeline.</td></tr>
* <tr><td><code>remote.timeline.</code><i>name</i><code>.push</code></td>
* <td><code>true</code> to enable pushing updates to the <i>name</i>
* remote timeline, <code>false</code> to disable.</td></tr>
* <tr><td><code>remote.timeline.</code><i>name</i><code>.pull</code></td>
* <td><code>true</code> to enable pulling updates from the <i>name</i>
* remote timeline, <code>false</code> to disable.</td></tr>
* <tr><td><code>remote.timeline.</code><i>name</i><code>.save-on-exit</code>
* </td><td><code>true</code> to force sync the <i>name</i> remote
* timeline on exit.</td></tr>
* <tr><td><code>remote.timeline.</code><i>name</i>
* <code>.update-interval</code></td><td>The time in milliseconds between
* synching the <i>name</i> remote timeline.</td></tr></table>
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com}) * @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com})
* @see com.jdbernard.timestamper.core.Timeline
* @see com.jdbernard.timestamper.core.TimelineSource
* @see com.jdbernard.timestamper.core.SyncTarget
*/ */
public class TimelineProperties { public class TimelineProperties {
@ -26,13 +52,19 @@ public class TimelineProperties {
private static final Pattern remoteTimelinePropPattern = private static final Pattern remoteTimelinePropPattern =
Pattern.compile("\\Q" + REMOTE_TIMELINE_BASE + "\\E([^\\s\\.=]+?)[\\.=].*"); Pattern.compile("\\Q" + REMOTE_TIMELINE_BASE + "\\E([^\\s\\.=]+?)[\\.=].*");
private File propertyFile;
private Timeline timeline; private Timeline timeline;
private TimelineSource timelineSource; private TimelineSource timelineSource;
private LinkedList<SyncTarget> syncTargets = new LinkedList<SyncTarget>(); private LinkedList<SyncTarget> syncTargets = new LinkedList<SyncTarget>();
/**
* Create new TimelineProperties, using default values. This will create
* a new configuration using a FileTimelineSource pointed at
* <code>'timeline.default.txt'</code> in the current directory and no
* remote Timelines. It will save this configuration to
* <code>'timeline.default.properties'</code> in the current directory.
*/
public TimelineProperties() { public TimelineProperties() {
this.propertyFile = new File("timeline.default.properties"); File propertyFile = new File("timeline.default.properties");
Properties config = new Properties(); Properties config = new Properties();
File timelineFile = new File("timeline.default.txt"); File timelineFile = new File("timeline.default.txt");
@ -53,14 +85,17 @@ public class TimelineProperties {
} }
} }
public TimelineProperties(File propertyFile) throws IOException { /**
* Load TimelineProperties from an InputStream.
* @param is
*/
public TimelineProperties(InputStream is) throws IOException {
String strURI; String strURI;
URI timelineURI; URI timelineURI;
this.propertyFile = propertyFile;
Properties config = new Properties(); Properties config = new Properties();
try { try {
config.load(new InputStreamReader(new FileInputStream(propertyFile))); config.load(new InputStreamReader(is));
} catch (IOException ioe) { } catch (IOException ioe) {
// TODO // TODO
} }
@ -112,11 +147,11 @@ public class TimelineProperties {
syncTargets.add(st); syncTargets.add(st);
// check for synch options // check for synch options
st.enablePull(Boolean.parseBoolean( st.setPullEnabled(Boolean.parseBoolean(
config.getProperty(remoteBase + ".pull", "true"))); config.getProperty(remoteBase + ".pull", "true")));
st.enablePush(Boolean.parseBoolean( st.setPushEnabled(Boolean.parseBoolean(
config.getProperty(remoteBase + ".push", "true"))); config.getProperty(remoteBase + ".push", "true")));
st.enableSyncOnExit(Boolean.parseBoolean( st.setSyncOnExit(Boolean.parseBoolean(
config.getProperty(remoteBase + ".sync-on-exit", "true"))); config.getProperty(remoteBase + ".sync-on-exit", "true")));
st.setSyncInterval(Long.parseLong( st.setSyncInterval(Long.parseLong(
config.getProperty(remoteBase + ".update-interval", config.getProperty(remoteBase + ".update-interval",
@ -125,7 +160,7 @@ public class TimelineProperties {
} }
} }
public void save() throws IOException { public void save(OutputStream os) throws IOException {
Properties config = new Properties(); Properties config = new Properties();
timelineSource.persist(timeline); timelineSource.persist(timeline);
@ -137,27 +172,22 @@ public class TimelineProperties {
config.setProperty(remoteBase + ".uri", config.setProperty(remoteBase + ".uri",
st.getSource().getURI().toString()); st.getSource().getURI().toString());
config.setProperty(remoteBase + ".pull", config.setProperty(remoteBase + ".pull",
Boolean.toString(st.isPullEnabled())); Boolean.toString(st.getPullEnabled()));
config.setProperty(remoteBase + ".push", config.setProperty(remoteBase + ".push",
Boolean.toString(st.isPushEnabled())); Boolean.toString(st.getPushEnabled()));
config.setProperty(remoteBase + ".sync-on-exit", config.setProperty(remoteBase + ".sync-on-exit",
Boolean.toString(st.isSyncOnExitEnabled())); Boolean.toString(st.getSyncOnExit()));
config.setProperty(remoteBase + ".update-interval", config.setProperty(remoteBase + ".update-interval",
Long.toString(st.getSyncInterval())); Long.toString(st.getSyncInterval()));
} }
try { try {
config.store(new FileOutputStream(propertyFile), ""); config.store(os, "");
} catch (IOException ioe) { } catch (IOException ioe) {
// TODO // TODO
} }
} }
public void save(File newFile) throws IOException {
propertyFile = newFile;
save();
}
public Timeline getTimeline() { return timeline; } public Timeline getTimeline() { return timeline; }
public void setTimelineSource(TimelineSource newSource) { public void setTimelineSource(TimelineSource newSource) {

View File

@ -4,8 +4,8 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
/** /**
* * A means of loading and persisting a Timeline.
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com}) * @author Jonathan Bernard (jonathan.bernard@gemalto.com)
*/ */
public abstract class TimelineSource { public abstract class TimelineSource {
@ -13,11 +13,36 @@ public abstract class TimelineSource {
public TimelineSource(URI uri) { this.uri = uri; } public TimelineSource(URI uri) { this.uri = uri; }
/**
* Read the Timeline from the source.
* @throws IOException
*/
public abstract Timeline read() throws IOException; public abstract Timeline read() throws IOException;
/**
* Persist a give timeline to this source.
* @param t
* @throws IOException
*/
public abstract void persist(Timeline t) throws IOException; public abstract void persist(Timeline t) throws IOException;
/**
* Is this source authenticated and ready for IO.
* @return <code>true</code> if the source is authenticated (or if no
* authentication is required).
*/
public abstract boolean isAuthenticated(); public abstract boolean isAuthenticated();
/**
* Authenticate the client to this source.
* @throws AuthenticationException
*/
public abstract void authenticate() throws AuthenticationException; public abstract void authenticate() throws AuthenticationException;
/**
* Get the URI representing this source.
* @return The {@link java.net.URI} representing this source.
*/
public URI getURI() { public URI getURI() {
return uri; return uri;
} }