Restructured the project. Moving to multiple-source timelines

that can automatically sync with one another.
This commit is contained in:
Jonathan Bernard 2009-12-17 07:32:28 -06:00
parent 42d2d82c74
commit 89ad2ac447
40 changed files with 1898 additions and 1393 deletions

View File

@ -1 +1,4 @@
release
build
.*.swp
.*.swo

BIN
doc/feed.pdf Normal file

Binary file not shown.

77
doc/feed.rst Normal file
View File

@ -0,0 +1,77 @@
Centralized vs. decentralized
-----------------------------
Centralized
```````````
- one central list
- remote apps that sync with central?
Decentralized
`````````````
- sync to URL(s)?
- need a network protocol
- HTTP?
- SSL?
- group-wise sync?
- establish master/slaves?
- easier than coordinated group-update:::
map each URL to synch -> the last time updated.
if (update_period):
forall URLs: synch
else if (incoming_update):
forall (URLs older than incoming update): synch
- synch based on hash of updates?
- need canonicalizer for text. Use XML?
- hash algorithm:::
SHA-1 of:
concatenate:
date (YYYYMMDDhhmmssSSS)
name
notes
External Feeds
--------------
Item format
```````````
- time started
- name/description
- notes
- category?
Pull from
`````````
- needs to be optional
- standardized input format
- easy to parse
- no errors, false positives
- restrictive.
- flexible input format
- matches regex's?
- map groups to fields
Push to
```````
- optional
- standardized output
- cannot be flexible to match output medium
- flexible input format
- choose fields and format values

View File

@ -0,0 +1,8 @@
# sync-options:
# LOCAL TIMELINE: this file
# SYNC TO: ssh://jdbernard@jdbernard.no-ip.org/timelines/jdbernard.timeline.txt
# SYNC TO: http://www.twitter.com/jdbernard
# pull only
# update every 30 sec
# SYNC TO: file:///home/jdbernard/timelines/jdbernard.timeline.bak
# push only

View File

@ -21,6 +21,13 @@ is divided into following sections:
-->
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="TimeStamper-impl">
<import file="jnlp-impl.xml"/>
<fail message="Please build using Ant 1.7.1 or higher.">
<condition>
<not>
<antversion atleast="1.7.1"/>
</not>
</condition>
</fail>
<target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
<!--
======================
@ -153,10 +160,18 @@ is divided into following sections:
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="${javac.debug}" name="debug"/>
<attribute default="" name="sourcepath"/>
<attribute default="${empty.dir}" name="sourcepath"/>
<attribute default="${empty.dir}" name="gensrcdir"/>
<element name="customize" optional="true"/>
<sequential>
<property location="${build.dir}/empty" name="empty.dir"/>
<mkdir dir="${empty.dir}"/>
<javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
<src>
<dirset dir="@{gensrcdir}" erroronmissingdir="false">
<include name="*"/>
</dirset>
</src>
<classpath>
<path path="@{classpath}"/>
</classpath>
@ -219,13 +234,13 @@ is divided into following sections:
</sequential>
</macrodef>
</target>
<target name="-init-macrodef-nbjpda">
<target depends="-init-debug-args" name="-init-macrodef-nbjpda">
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
<attribute default="${main.class}" name="name"/>
<attribute default="${debug.classpath}" name="classpath"/>
<attribute default="" name="stopclassname"/>
<sequential>
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
<classpath>
<path path="@{classpath}"/>
</classpath>
@ -256,6 +271,12 @@ is divided into following sections:
<condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
<istrue value="${have-jdk-older-than-1.4}"/>
</condition>
<condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
<os family="windows"/>
</condition>
<condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
<isset property="debug.transport"/>
</condition>
</target>
<target depends="-init-debug-args" name="-init-macrodef-debug">
<macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
@ -265,7 +286,9 @@ is divided into following sections:
<sequential>
<java classname="@{classname}" dir="${work.dir}" fork="true">
<jvmarg line="${debug-args-line}"/>
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
<jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
<jvmarg value="-Dfile.encoding=${source.encoding}"/>
<redirector errorencoding="${source.encoding}" inputencoding="${source.encoding}" outputencoding="${source.encoding}"/>
<jvmarg line="${run.jvmargs}"/>
<classpath>
<path path="@{classpath}"/>
@ -282,12 +305,15 @@ is divided into following sections:
<target name="-init-macrodef-java">
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
<attribute default="${main.class}" name="classname"/>
<attribute default="${run.classpath}" name="classpath"/>
<element name="customize" optional="true"/>
<sequential>
<java classname="@{classname}" dir="${work.dir}" fork="true">
<jvmarg value="-Dfile.encoding=${source.encoding}"/>
<redirector errorencoding="${source.encoding}" inputencoding="${source.encoding}" outputencoding="${source.encoding}"/>
<jvmarg line="${run.jvmargs}"/>
<classpath>
<path path="${run.classpath}"/>
<path path="@{classpath}"/>
</classpath>
<syspropertyset>
<propertyref prefix="run-sys-prop."/>
@ -312,6 +338,13 @@ is divided into following sections:
===================
-->
<target depends="init" name="deps-jar" unless="no.deps"/>
<target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
<target depends="init" name="-check-automatic-build">
<available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
</target>
<target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
<antcall target="clean"/>
</target>
<target depends="init,deps-jar" name="-pre-pre-compile">
<mkdir dir="${build.classes.dir}"/>
</target>
@ -320,10 +353,15 @@ is divided into following sections:
<!-- You can override this target in the ../build.xml file. -->
</target>
<target if="do.depend.true" name="-compile-depend">
<j2seproject3:depend/>
<pathconvert property="build.generated.subdirs">
<dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
<include name="*"/>
</dirset>
</pathconvert>
<j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/>
</target>
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
<j2seproject3:javac/>
<j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
<copy todir="${build.classes.dir}">
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
</copy>
@ -332,7 +370,7 @@ is divided into following sections:
<!-- Empty placeholder for easier customization. -->
<!-- You can override this target in the ../build.xml file. -->
</target>
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
<target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
<target name="-pre-compile-single">
<!-- Empty placeholder for easier customization. -->
<!-- You can override this target in the ../build.xml file. -->
@ -340,13 +378,13 @@ is divided into following sections:
<target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
<j2seproject3:force-recompile/>
<j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
<j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/>
</target>
<target name="-post-compile-single">
<!-- Empty placeholder for easier customization. -->
<!-- You can override this target in the ../build.xml file. -->
</target>
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
<target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
<!--
====================
JAR BUILDING SECTION
@ -406,11 +444,29 @@ is divided into following sections:
<property location="${dist.jar}" name="dist.jar.resolved"/>
<echo>java -jar "${dist.jar.resolved}"</echo>
</target>
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="libs.CopyLibs.classpath" name="-do-jar-with-libraries-without-manifest" unless="manifest.available+main.class">
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
<pathconvert property="run.classpath.without.build.classes.dir">
<path path="${run.classpath}"/>
<map from="${build.classes.dir.resolved}" to=""/>
</pathconvert>
<pathconvert pathsep=" " property="jar.classpath">
<path path="${run.classpath.without.build.classes.dir}"/>
<chainedmapper>
<flattenmapper/>
<globmapper from="*" to="lib/*"/>
</chainedmapper>
</pathconvert>
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
<fileset dir="${build.classes.dir}"/>
</copylibs>
</target>
<target name="-post-jar">
<!-- Empty placeholder for easier customization. -->
<!-- You can override this target in the ../build.xml file. -->
</target>
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar,jnlp" description="Build JAR." name="jar"/>
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-do-jar-with-libraries-without-manifest,-post-jar,jnlp" description="Build JAR." name="jar"/>
<!--
=================
EXECUTION SECTION
@ -430,6 +486,10 @@ is divided into following sections:
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
<j2seproject1:java classname="${run.class}"/>
</target>
<target depends="init,-do-not-recompile,compile-test-single" name="run-test-with-main">
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
<j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
</target>
<!--
=================
DEBUGGING SECTION
@ -438,6 +498,9 @@ is divided into following sections:
<target depends="init" if="netbeans.home" name="-debug-start-debugger">
<j2seproject1:nbjpdastart name="${debug.class}"/>
</target>
<target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
</target>
<target depends="init,compile" name="-debug-start-debuggee">
<j2seproject3:debug>
<customize>
@ -455,6 +518,11 @@ is divided into following sections:
<j2seproject3:debug classname="${debug.class}"/>
</target>
<target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
<target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
<j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
</target>
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
<target depends="init" name="-pre-debug-fix">
<fail unless="fix.includes">Must set fix.includes</fail>
<property name="javac.includes" value="${fix.includes}.java"/>
@ -477,6 +545,9 @@ is divided into following sections:
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
<filename name="**/*.java"/>
</fileset>
<fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
<include name="**/*.java"/>
</fileset>
</javadoc>
</target>
<target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
@ -538,7 +609,7 @@ is divided into following sections:
<j2seproject3:junit testincludes="**/*Test.java"/>
</target>
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
<fail if="tests.failed">Some tests failed; see details above.</fail>
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
</target>
<target depends="init" if="have.tests" name="test-report"/>
<target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
@ -551,7 +622,7 @@ is divided into following sections:
<j2seproject3:junit excludes="" includes="${test.includes}"/>
</target>
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
<fail if="tests.failed">Some tests failed; see details above.</fail>
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
</target>
<target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
<!--

View File

@ -3,6 +3,6 @@ build.xml.script.CRC32=6e5ad54e
build.xml.stylesheet.CRC32=be360661
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=52850847
nbproject/build-impl.xml.script.CRC32=adeac2b5
nbproject/build-impl.xml.stylesheet.CRC32=487672f9
nbproject/build-impl.xml.data.CRC32=618e720f
nbproject/build-impl.xml.script.CRC32=70e39602
nbproject/build-impl.xml.stylesheet.CRC32=5c621a33@1.26.2.45

View File

@ -1,7 +1,8 @@
compile.on.save=false
do.depend=false
do.jar=true
javac.debug=true
javadoc.preview=true
jaxws.endorsed.dir=C:\\Program Files\\NetBeans 6.5\\java2\\modules\\ext\\jaxws21\\api:C:\\Program Files\\NetBeans 6.5\\ide10\\modules\\ext\\jaxb\\api
user.properties.file=C:\\Documents and Settings\\jbernard\\.netbeans\\6.5\\build.properties
jaxws.endorsed.dir=C:\\Program Files\\NetBeans 6.7.1\\java2\\modules\\ext\\jaxws21\\api:C:\\Program Files\\NetBeans 6.7.1\\ide11\\modules\\ext\\jaxb\\api
user.properties.file=C:\\Documents and Settings\\jbernard\\.netbeans\\6.7\\build.properties
axis2.deploy.war=C:\\Program Files\\glassfish-v2ur2\\domains\\domain1\\autodeploy\\axis2.war

View File

@ -8,6 +8,7 @@ build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
@ -57,7 +58,7 @@ jnlp.enabled=false
jnlp.icon=C:\\Documents and Settings\\jbernard\\My Documents\\Development\\TimeStamper\\src\\jdbernard\\timestamper\\resources\\icons\\appointment-new-16x16.png
jnlp.offline-allowed=true
jnlp.signed=true
main.class=jdbernard.timestamper.TimeStamperApp
main.class=jdbernard.timestamper.gui.TimeStamperApp
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
platform.active=default_platform

View File

@ -18,7 +18,7 @@
</test-roots>
</data>
<swingapp xmlns="http://www.netbeans.org/ns/form-swingapp/1">
<application-class name="jdbernard.timestamper.TimeStamperApp"/>
<application-class name="jdbernard.timestamper.gui.TimeStamperApp"/>
</swingapp>
</configuration>
</project>

View File

@ -1,77 +1,26 @@
package jdbernard.timestamper;
import java.util.Date;
import java.util.Scanner;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import jdbernard.timestamper.core.TimelineProperties;
public class Test {
public static void main(String[] args) throws Exception {
public static void main(String[] args) throws Exception {
Timeline t = new Timeline();
String choice;
Scanner in = new Scanner(System.in);
System.out.println("Enter config file, terminate with EOF");
BufferedReader bin = new BufferedReader(new InputStreamReader(System.in));
boolean loop = true;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(baos);
while(loop) {
System.out.println("[N]ew Timestamp, [S]ave, [L]oad, [P]rint, [Q]uit: ");
choice = in.nextLine();
String filename;
for(String line = bin.readLine(); line != null && !"EOF".equals(line); line = bin.readLine())
pw.println(line);
switch (choice.toLowerCase().charAt(0)) {
case 'n':
System.out.println("Enter time (HH:mm:ss) ");
Date d = new Date();
d.setHours(0);
d.setMinutes(0);
d.setSeconds(0);
d.setTime(d.getTime() + Timeline.shortFormat.parse(in.nextLine()).getTime());
pw.flush();
pw.close();
System.out.println("Enter mark ('EOM' to end) ");
String line = "";
StringBuilder mark = new StringBuilder();
line = in.nextLine();
while (!line.endsWith("EOM")) {
mark.append(line);
line = in.nextLine();
}
mark.append(line.substring(0, line.length() - 3));
System.out.println("Enter notes ('EON' to end) ");
StringBuilder notes = new StringBuilder();
line = in.nextLine();
while (!line.endsWith("EON")) {
notes.append(line);
line = in.nextLine();
}
notes.append(line.substring(0, line.length() - 3));
t.addMarker(d, mark.toString(), notes.toString());
break;
case 's':
System.out.print("Enter filename to save: ");
filename = in.nextLine();
Timeline.writeToFile(filename, t);
break;
case 'l':
System.out.println("Enter filename to load: ");
filename = in.nextLine();
t = Timeline.readFromFile(filename);
break;
case 'p':
Timeline.writeToStream(System.out, t);
break;
case 'q':
loop = false;
break;
default:
}
}
}
}

View File

@ -1,281 +0,0 @@
package jdbernard.timestamper;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.TreeSet;
/**
* @author Jonathan Bernard <jdbernard@gmail.com>
* 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
* mark.
*/
public class Timeline implements Iterable<Timeline.TimelineMarker> {
/**
* This represents a marker on the timeline.
* The date of the marker and the mark cannot be changed once assigned.
*/
public class TimelineMarker implements Comparable<TimelineMarker> {
private final Date timestamp;
private final String mark;
private String notes;
public TimelineMarker(Date timestamp, String mark, String notes) {
if (timestamp == null || mark == null)
throw new IllegalArgumentException("Null timestamp or mark"
+ " is not permitted.");
this.timestamp = timestamp;
this.mark = mark;
this.notes = notes;
}
public Date getTimestamp() { return timestamp; }
public String getMark() { return mark; }
public String getNotes() { return notes; }
public void setNotes(String notes) { this.notes = notes; }
@Override
public int compareTo(TimelineMarker that) {
if (that == null) return Integer.MAX_VALUE;
return this.timestamp.compareTo(that.timestamp);
}
@Override
public boolean equals(Object that) {
if (that == null) return false;
if (!(that instanceof TimelineMarker)) return false;
return this.timestamp.equals(((TimelineMarker)that).timestamp);
}
@Override
public int hashCode() {
int hash = 5;
hash = 53 * hash + (this.timestamp != null ? this.timestamp.hashCode() : 0);
return hash;
}
}
private static enum ReadingState {
NewMarker,
StartMark,
ReadMark,
StartNotes,
ReadNotes,
EndMarker
};
public static SimpleDateFormat shortFormat = new SimpleDateFormat("HH:mm:ss");
public static SimpleDateFormat longFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
private static int lineWrap = 78;
private TreeSet<TimelineMarker> timelineList;
public Timeline() {
timelineList = new TreeSet<TimelineMarker>();
}
public void addMarker(Date timestamp, String name, String notes) {
timelineList.add(new TimelineMarker(timestamp, name, notes));
}
public String getLastName(Date timestamp) {
TimelineMarker lastMarker = getLastMarker(timestamp);
return (lastMarker == null ?
"No previous marker." :
lastMarker.getMark());
}
public String getLastNotes(Date timestamp) {
TimelineMarker lastMarker = getLastMarker(timestamp);
return (lastMarker == null ?
"No previous marker." :
lastMarker.getNotes());
}
public TimelineMarker getLastMarker(Date timestamp) {
TimelineMarker lastMarker = null;
for (TimelineMarker tm : timelineList) {
if (tm.getTimestamp().after(timestamp))
break;
lastMarker = tm;
}
return lastMarker;
}
public void removeMarker(TimelineMarker marker) {
timelineList.remove(marker);
}
public Iterator<TimelineMarker> iterator() {
return timelineList.iterator();
}
/**
* Write the a representation of the timeline to a stream. This method
* flushes the stream after it finishes writing but does not close the
* stream.
* @param stream An open stream to write the timeline representation to.
* @param timeline The timeline to write.
* @throws java.io.IOException
*/
public static void writeToStream(OutputStream stream, Timeline timeline)
throws IOException {
Writer out = new OutputStreamWriter(stream);
for (TimelineMarker tm : timeline.timelineList) {
// write timestamp
out.write(longFormat.format(tm.getTimestamp()) + "\n");
// write mark
String mark = tm.getMark().replace('\n', '\u0000');
if (mark.length() < lineWrap) out.write(mark + "\n");
else {
// wrap lines if neccessary
int i;
for (i = 0; (i + lineWrap) < mark.length(); i+=lineWrap)
out.write(mark.substring(i, i+lineWrap) + "\\\n");
if (i < mark.length())
out.write(mark.substring(i, mark.length()) + "\n");
}
// write notes
String notes = tm.getNotes().replace('\n', '\u0000');
if (notes.length() < lineWrap) out.write(notes + "\n");
else {
// wrap lines if neccessary
int i;
for (i = 0; (i + lineWrap) < notes.length(); i+=lineWrap)
out.write(notes.substring(i, i+lineWrap) + "\\\n");
if (i < notes.length())
out.write(notes.substring(i, notes.length()) + "\n");
}
out.write("\n");
}
out.flush();
}
/**
* Write a representation of a timeline to a file.
* @param filename The path to the destination file.
* @param timeline The timeline to write.
* @throws java.io.IOException
* @throws java.io.FileNotFoundException
*/
public static void writeToFile(String filename, Timeline timeline)
throws IOException, FileNotFoundException {
OutputStream out = new FileOutputStream(filename);
writeToStream(out, timeline);
out.close();
}
/**
* Create a <b>Timeline</b> instance from a given stream.
* @param stream The stream to read from.
* @return A new <b>Timeline</b> instance specified by the input stream.
* @throws java.io.IOException
* @throws java.io.FileNotFoundException
*/
public static Timeline readFromStream(InputStream stream)
throws IOException, FileNotFoundException {
Scanner in = new Scanner(stream);
Timeline timeline = new Timeline();
ReadingState readingState = ReadingState.NewMarker;
Date d = null;
StringBuilder mark = null;
StringBuilder notes = null;
String line;
int lineNumber = 0;
while (in.hasNextLine()) {
line = in.nextLine();
lineNumber++;
switch (readingState) {
case NewMarker:
try { d = longFormat.parse(line); }
catch (ParseException pe) {
throw new IOException("Error parsing timeline file at line "
+ lineNumber + ": expected a new marker date but could not parse"
+ " the date. Error: " + pe.getLocalizedMessage());
}
readingState = ReadingState.StartMark;
break;
case StartMark:
mark = new StringBuilder();
// fall through to ReadMark
case ReadMark:
if (line.endsWith("\\")) {
readingState = ReadingState.ReadMark;
line = line.substring(0, line.length() - 1);
}
else readingState = ReadingState.StartNotes;
mark.append(line);
break;
case StartNotes:
notes = new StringBuilder();
// fall through to ReadNotes
case ReadNotes:
if (line.endsWith("\\")) {
readingState = ReadingState.ReadNotes;
line = line.substring(0, line.length() - 1);
}
else readingState = ReadingState.EndMarker;
notes.append(line);
break;
case EndMarker:
String sMark = mark.toString().replace('\u0000', '\n');
String sNotes = notes.toString().replace('\u0000', '\n');
timeline.addMarker(d, sMark, sNotes);
readingState = ReadingState.NewMarker;
}
}
//if (readingState != ReadingState.NewMarker)
//TODO: warning of invalid marker file
return timeline;
}
/**
* Create a Timline instance from a file representation.
* @param filename The path of the source file.
* @return The new Timeline instance.
* @throws java.io.IOException
* @throws java.io.FileNotFoundException
*/
public static Timeline readFromFile(String filename)
throws IOException, FileNotFoundException {
InputStream in = new FileInputStream(filename);
Timeline t = readFromStream(in);
in.close();
return t;
}
}

View File

@ -0,0 +1,21 @@
package jdbernard.timestamper.core;
import java.io.IOException;
/**
*
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com})
*/
public class AuthenticationException extends IOException {
public AuthenticationException() { super(); }
public AuthenticationException(String message) { super(message); }
public AuthenticationException(Throwable t) { super(t); }
public AuthenticationException(String message, Throwable t) {
super(message, t);
}
}

View File

@ -0,0 +1,64 @@
package jdbernard.timestamper.core;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
/**
*
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com})
*/
public class FileTimelineSource extends TimelineSource {
private File file;
public FileTimelineSource(URI uri) {
super(uri);
this.file = new File(uri);
}
/** * {@inheritDoc } */
public Timeline read() throws IOException {
FileInputStream fin = new FileInputStream(file);
Timeline t = StreamBasedTimelineSource.readFromStream(fin);
fin.close();
return t;
}
public Timeline readWithComments(OutputStream commentStream)
throws IOException {
FileInputStream fin = new FileInputStream(file);
Timeline t = StreamBasedTimelineSource.readFromStream(fin, commentStream);
fin.close();
return t;
}
/** * {@inheritDoc } */
public void persist(Timeline t) throws IOException {
FileOutputStream fout = new FileOutputStream(file);
StreamBasedTimelineSource.writeToStream(fout, t);
fout.close();
}
public boolean isAuthenticated() {
File dir = file.getParentFile();
return (dir.canRead() && dir.canWrite());
}
public void authenticate() throws AuthenticationException {
File dir = file.getParentFile();
if (!dir.canRead())
throw new AuthenticationException("This FileTimelineSource does not"
+ " have read access to the parent directory for the "
+ "given file (" + file.getAbsolutePath() + ".");
if (!dir.canWrite())
throw new AuthenticationException("This FileTimelineSource does not"
+ " have write access to the parent directory for the "
+ "given file (" + file.getAbsolutePath() + ".");
}
}

View File

@ -0,0 +1,203 @@
package jdbernard.timestamper.core;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.text.ParseException;
import java.util.Date;
import java.util.Scanner;
/**
*
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com})
*/
public class StreamBasedTimelineSource extends TimelineSource {
private static final int lineWrap = 78;
private static enum ReadingState {
NewMarker,
StartMark,
ReadMark,
StartNotes,
ReadNotes,
EndMarker
};
private InputStream in;
private OutputStream out;
private ByteArrayOutputStream comments;
public StreamBasedTimelineSource(InputStream inStream,
OutputStream outStream) {
super(null);
this.in = inStream;
this.out = outStream;
this.comments = new ByteArrayOutputStream();
}
/** {@inheritDoc } */
public Timeline read() throws IOException {
return readFromStream(in, comments);
}
/** {@inheritDoc } */
public void persist(Timeline t) throws IOException {
writeToStream(out, t);
}
public void authenticate() throws AuthenticationException { }
public boolean isAuthenticated() { return true; }
/**
* Allows a user to extract the comments from the last parsed file.
* @return a <code>byte[]</code> representing the portions of the file
* which were comments.
*/
public byte[] getCommentBytes() {
return comments.toByteArray();
}
/**
* Write the a representation of the timeline to a stream. This method
* flushes the stream after it finishes writing but does not close the
* stream.
* @param stream An open stream to write the timeline representation to.
* @param timeline The timeline to write.
* @throws java.io.IOException
*/
public static void writeToStream(OutputStream stream, Timeline timeline)
throws IOException {
Writer out = new OutputStreamWriter(stream);
for (TimelineMarker tm : timeline) {
// write timestamp
out.write(Timeline.longFormat.format(tm.getTimestamp()) + "\n");
// write mark
String mark = tm.getMark().replace('\n', '\u0000');
if (mark.length() < lineWrap) out.write(mark + "\n");
else {
// wrap lines if neccessary
int i;
for (i = 0; (i + lineWrap) < mark.length(); i+=lineWrap)
out.write(mark.substring(i, i+lineWrap) + "\\\n");
if (i < mark.length())
out.write(mark.substring(i, mark.length()) + "\n");
}
// write notes
String notes = tm.getNotes().replace('\n', '\u0000');
if (notes.length() < lineWrap) out.write(notes + "\n");
else {
// wrap lines if neccessary
int i;
for (i = 0; (i + lineWrap) < notes.length(); i+=lineWrap)
out.write(notes.substring(i, i+lineWrap) + "\\\n");
if (i < notes.length())
out.write(notes.substring(i, notes.length()) + "\n");
}
out.write("\n");
}
out.flush();
}
/**
* Create a <b>Timeline</b> instance from a given stream.
* @param stream The stream to read from.
* @return A new <b>Timeline</b> instance specified by the input stream.
* @throws java.io.IOException
* @throws java.io.FileNotFoundException
*/
public static Timeline readFromStream(InputStream stream)
throws IOException {
return readFromStream(stream, null);
}
/**
* Create a <b>Timeline</b> instance from a given stream.
* @param stream The stream to read from.
* @param commentStream A stream to write comments found in the file. This
* parameter may be <b>null</b>, in which case comments are ignored.
* @return A new <b>Timeline</b> instance specified by the input stream.
* @throws java.io.IOException
* @throws java.io.FileNotFoundException
*/
public static Timeline readFromStream(InputStream stream,
OutputStream commentStream) throws IOException {
Scanner in = new Scanner(stream);
Timeline timeline = new Timeline();
PrintWriter commentsWriter = commentStream == null ?
null : new PrintWriter(commentStream);
ReadingState readingState = ReadingState.NewMarker;
Date d = null;
StringBuilder mark = null;
StringBuilder notes = null;
String line;
int lineNumber = 0;
while (in.hasNextLine()) {
line = in.nextLine();
lineNumber++;
// line is a comment
if (line.startsWith("#")) {
if (commentsWriter != null) commentsWriter.println(line);
continue; // don't parse this line as part of the timeline
}
switch (readingState) {
case NewMarker:
try { d = Timeline.longFormat.parse(line); }
catch (ParseException pe) {
throw new IOException("Error parsing timeline file at line "
+ lineNumber + ": expected a new marker date but could not parse"
+ " the date. Error: " + pe.getLocalizedMessage());
}
readingState = ReadingState.StartMark;
break;
case StartMark:
mark = new StringBuilder();
// fall through to ReadMark
case ReadMark:
if (line.endsWith("\\")) {
readingState = ReadingState.ReadMark;
line = line.substring(0, line.length() - 1);
}
else readingState = ReadingState.StartNotes;
mark.append(line);
break;
case StartNotes:
notes = new StringBuilder();
// fall through to ReadNotes
case ReadNotes:
if (line.endsWith("\\")) {
readingState = ReadingState.ReadNotes;
line = line.substring(0, line.length() - 1);
}
else readingState = ReadingState.EndMarker;
notes.append(line);
break;
case EndMarker:
String sMark = mark.toString().replace('\u0000', '\n');
String sNotes = notes.toString().replace('\u0000', '\n');
timeline.addMarker(d, sMark, sNotes);
readingState = ReadingState.NewMarker;
}
}
return timeline;
}
}

View File

@ -0,0 +1,129 @@
package jdbernard.timestamper.core;
import java.io.IOException;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
/**
*
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com})
*/
public class SyncTarget {
protected final TimelineSource source;
protected final Timeline localTimeline;
protected final String name;
protected Timer syncTimer;
protected SyncTask syncTask;
protected long syncInterval= 30 * 60 * 1000; // 30 minutes converted to ms
protected boolean pushEnabled = true;
protected boolean pullEnabled = true;
protected boolean syncOnExit = true;
protected class SyncTask extends TimerTask {
@Override public void run() {
synchronized(this) {
try { SyncTarget.this.sync(); }
catch (IOException ioe) { /* TODO */ }
}}}
protected SyncTarget(String name, TimelineSource source,
Timeline localTimeline) {
this.name = name;
this.source = source;
this.localTimeline = localTimeline;
syncTimer = new Timer(source.toString() + " sync-timer");
syncTask = new SyncTask();
syncTimer.schedule(syncTask, syncInterval, syncInterval);
}
/**
* Sync the local timeline with the remote timeline represented by this
* SyncTarget object.
* @return <b>true</b> if the two timelines were out of sync and have
* been put into synch, <b>false</b> if the timelines were already in
* sync and no action was requried.
* @throws IOException if there is an error communicating with the remote
* timeline. This includes AuthenticationException.
*/
protected boolean sync() throws IOException {
assert (pullEnabled || pushEnabled);
Timeline remoteTimeline;
boolean syncPerformed = false;
// make sure we're authenticated to whatever source we're using
if (!SyncTarget.this.source.isAuthenticated()) {
SyncTarget.this.source.authenticate();
}
// try to copy the remote timeline locally
remoteTimeline = SyncTarget.this.source.read();
// if we are pulling markers from the remote line
if (SyncTarget.this.pullEnabled) {
// get all markers in the remote timeline not in the local one
Collection<TimelineMarker> diffFromRemote =
remoteTimeline.difference(localTimeline);
if (diffFromRemote.size() != 0) {
// add all markers in the remote tline to the local one
localTimeline.addAll(diffFromRemote);
syncPerformed = true;
}
}
// if we are pushing markers to the remote timeline
if (SyncTarget.this.pushEnabled) {
// get all markers in the local timeline but not in the remote
Collection<TimelineMarker> diffFromLocal =
localTimeline.difference(remoteTimeline);
if (diffFromLocal.size() != 0) {
// add the difference to the remote timeline
remoteTimeline.addAll(diffFromLocal);
syncPerformed = true;
}
}
return syncPerformed;
}
public String getName() { return name; }
public TimelineSource getSource() { return source; }
public synchronized void setSyncInterval(long syncInterval) {
this.syncInterval= syncInterval;
syncTask.cancel();
syncTask = new SyncTask();
syncTimer.purge();
syncTimer.schedule(syncTask, syncInterval, syncInterval);
}
public long getSyncInterval() { return syncInterval; }
public synchronized void enablePush(boolean enablePush) {
this.pullEnabled = enablePush;
}
public boolean isPushEnabled() { return pushEnabled; }
public synchronized void enablePull(boolean enablePull) {
this.pullEnabled = enablePull;
}
public boolean isPullEnabled() { return pullEnabled; }
public synchronized void enableSyncOnExit(boolean syncOnExit) {
this.syncOnExit = syncOnExit;
}
public boolean isSyncOnExitEnabled() { return syncOnExit; }
}

View File

@ -0,0 +1,95 @@
package jdbernard.timestamper.core;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.TreeSet;
/**
* @author Jonathan Bernard {@literal <jdbernard@gmail.com>}
* 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
* mark.
*/
public class Timeline implements Iterable<TimelineMarker> {
public static SimpleDateFormat shortFormat = new SimpleDateFormat("HH:mm:ss");
public static SimpleDateFormat longFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
private TreeSet<TimelineMarker> timelineList;
public Timeline() {
timelineList = new TreeSet<TimelineMarker>();
}
public void addMarker(Date timestamp, String name, String notes) {
timelineList.add(new TimelineMarker(timestamp, name, notes));
}
public String getLastName(Date timestamp) {
TimelineMarker lastMarker = getLastMarker(timestamp);
return (lastMarker == null ?
"No previous marker." :
lastMarker.getMark());
}
public String getLastNotes(Date timestamp) {
TimelineMarker lastMarker = getLastMarker(timestamp);
return (lastMarker == null ?
"No previous marker." :
lastMarker.getNotes());
}
public TimelineMarker getLastMarker(Date timestamp) {
TimelineMarker lastMarker = null;
for (TimelineMarker tm : timelineList) {
if (tm.getTimestamp().after(timestamp))
break;
lastMarker = tm;
}
return lastMarker;
}
public void removeMarker(TimelineMarker marker) {
timelineList.remove(marker);
}
public Iterator<TimelineMarker> iterator() {
return timelineList.iterator();
}
/**
* Return the difference of the this timeline relative to another timeline.
* More specifically, return the set of all
* {@link jdbernard.timestamper.core.TimelineMarker}s that are present in
* the <b>this</b> timeline but not present in the given timeline.
* timeline.
* @param t
* @return A collection representing the TimelineMarkers present in
* <b>this</b> timeline but not in the given timeline.
*/
public Collection<TimelineMarker> difference(Timeline t) {
TreeSet<TimelineMarker> difference = new TreeSet<TimelineMarker>();
for (TimelineMarker tm : timelineList) {
if (!t.timelineList.contains(tm))
difference.add(tm);
}
return difference;
}
public void addAll(Timeline t) {
for (TimelineMarker tm : t) {
if (!timelineList.contains(tm))
timelineList.add(tm);
}
}
public void addAll(Collection<TimelineMarker> c) {
timelineList.addAll(c);
}
}

View File

@ -0,0 +1,63 @@
package jdbernard.timestamper.core;
import java.util.Date;
/**
* @author Jonathan Bernard {@literal <jdbernard@gmail.com>}
* This represents a marker on the timeline.
* The date of the marker and the mark cannot be changed once assigned.
*/
public class TimelineMarker implements Comparable<TimelineMarker> {
private final Date timestamp;
private final String mark;
private String notes;
public TimelineMarker(Date timestamp, String mark, String notes) {
if (timestamp == null || mark == null)
throw new IllegalArgumentException("Null timestamp or mark"
+ " is not permitted.");
this.timestamp = timestamp;
this.mark = mark;
this.notes = notes;
}
public Date getTimestamp() { return timestamp; }
public String getMark() { return mark; }
public String getNotes() { return notes; }
public void setNotes(String notes) { this.notes = notes; }
@Override
public int compareTo(TimelineMarker that) {
if (that == null) return Integer.MAX_VALUE;
int val = this.timestamp.compareTo(that.timestamp);
if (val == 0) val = this.mark.compareTo(that.mark);
return val;
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof TimelineMarker)) return false;
TimelineMarker that = (TimelineMarker) o;
return this.timestamp.equals(that.timestamp) &&
this.mark.equals(that.mark);
}
@Override
public int hashCode() {
int hash = 7;
hash = 61 * hash + (this.timestamp != null ? this.timestamp.hashCode() : 0);
hash = 61 * hash + (this.mark != null ? this.mark.hashCode() : 0);
return hash;
}
}

View File

@ -1,13 +1,17 @@
package jdbernard.timestamper.core;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@ -16,145 +20,144 @@ import java.util.StringTokenizer;
*/
public class TimelineProperties {
public static final String LOCAL_TIMELINE_URI = "timeline.uri";
public static final String REMOTE_TIMELINE_BASE = "remote.timeline.";
private static final Pattern remoteTimelinePropPattern =
Pattern.compile("\\Q" + REMOTE_TIMELINE_BASE + "\\E([^\\s\\.=]+?)[\\.=].*");
private File propertyFile;
private Timeline timeline;
private TimelineSource timelineSource;
private LinkedList<SyncTarget> syncTargets = new LinkedList<SyncTarget>();
public TimelineProperties(InputStream in) throws IOException {
parseConfig(in);
}
public TimelineProperties() {
this.propertyFile = new File("timeline.default.properties");
Properties config = new Properties();
private static enum ReadingState {
LocalTimelineDef, // expecting LOCAL TIMELINE definition
NewSync, // expecting a SYNC TO def
NewSyncOrOptions // either a NewDef, or a block of options
}
File timelineFile = new File("timeline.default.txt");
URI timelineURI = timelineFile.toURI();
private void parseConfig(InputStream is) throws IOException {
Scanner in = new Scanner(is);
URI sourceURI;
// read the information for the local timeline
if (!nextExtendedToken(in, "LOCAL TIMELINE:")) {
throw new IOException("Missing 'LOCAL TIMELINE:' definition."
+ " This must be the first line of the options file.");
timeline = new Timeline();
timelineSource = TimelineSourceFactory.newInstance(timelineURI);
try { timelineSource.persist(timeline); }
catch (IOException ioe) {
// TODO
}
// parse the source URI
timelineSource = parseSource(in);
config.setProperty(LOCAL_TIMELINE_URI, timelineURI.toString());
// load timeline from source
timeline = timelineSource.read(); // TODO: authentication
// add as many Syncs as we find
for (SyncTarget st = parseSync(in); st != null; st = parseSync(in))
syncTargets.add(st);
}
private SyncTarget parseSync(Scanner in) throws IOException {
SyncTarget st;
String token;
if (!nextExtendedToken(in, "SYNC WITH:")) { return null; }
st = new SyncTarget(parseSource(in), timeline);
while (true) {
if (nextExtendedToken(in, "PULL ONLY")) {
st.enablePull(true);
st.enablePush(false);
}
else if (nextExtendedToken(in, "PUSH ONLY")) {
st.enablePull(false);
st.enablePush(true);
}
else if (nextExtendedToken(in, "PUSH AND PULL") ||
nextExtendedToken(in, "PULL AND PUSH")) {
st.enablePull(true);
st.enablePush(true);
}
else if (nextExtendedToken(in, "SYNC ON EXIT")) {
st.enableSyncOnExit(true);
}
else if (nextExtendedToken(in, "UPDATE EVERY")) {
long updateAmount = 1;
// parse the time amount if present
token = in.next("\\d+");
if (token != null) { updateAmount = Long.parseLong(token); }
token = in.next();
if ("seconds".startsWith(token.toLowerCase()))
updateAmount *= 1000;
else if ("minutes".startsWith(token.toLowerCase()))
updateAmount *= 1000 * 60;
else if ("hours".startsWith(token.toLowerCase()))
updateAmount *= 1000 * 60 * 60;
else if ("days".startsWith(token.toLowerCase()))
updateAmount *= 1000 * 60 * 60 * 24;
else if ("weeks".startsWith(token.toLowerCase()))
updateAmount *= 1000 * 60 * 60 * 24 * 7;
else throw new IOException("'" + token + "' is not a supported"
+ " measure of time. The supported measures are "
+ "'seconds', 'minutes', 'hours', 'days', and 'weeks'.");
}
else return st;
try { config.store(new FileOutputStream(propertyFile), ""); }
catch (IOException ioe) {
// TODO
}
}
private TimelineSource parseSource(Scanner in) throws IOException {
public TimelineProperties(File propertyFile) throws IOException {
String strURI;
URI sourceURI;
URI timelineURI;
if (!in.hasNext()) {
throw new IOException("Expected the URI of a timeline source, but "
+ "ran out of input.");
this.propertyFile = propertyFile;
Properties config = new Properties();
try {
config.load(new InputStreamReader(new FileInputStream(propertyFile)));
} catch (IOException ioe) {
// TODO
}
strURI = in.next();
try { sourceURI = new URI(strURI); }
catch (URISyntaxException urise) {
throw new IOException("The source link '" + strURI + "' is not a"
+ " valid URI.", urise);
}
return TimelineSourceFactory.newInstance(sourceURI);
}
private static boolean nextExtendedToken(Scanner in, String expectedToken)
throws IOException {
StringTokenizer st = new StringTokenizer(expectedToken);
if (st.countTokens() < 1 || !in.hasNext()) { return false; }
// read one token
String curToken = st.nextToken();
String inToken = in.next("(?i)\\Q" + curToken + "\\E");
if (inToken == null) return false;
while (st.hasMoreTokens()) {
curToken = st.nextToken();
if (!in.hasNext()) {
throw new IOException("Ran out of input while expecting '"
+ curToken + "' from '" + expectedToken + "'.");
}
inToken = in.next("(?i)\\Q" + curToken + "\\E");
if (inToken == null) {
throw new IOException("Found '" + in.next() + "' but expected '"
+ curToken + "' from '" + expectedToken + "'.");
// load local timeline
strURI = config.getProperty(LOCAL_TIMELINE_URI, "");
if ("".equals(strURI)) {
timelineURI = new File("timeline.default.txt").toURI();
} else {
try { timelineURI = new URI(strURI); }
catch (URISyntaxException urise) {
throw new IOException("Unable to load the timeline: the timeline "
+ "URI is invalid.", urise);
}
}
return true;
timelineSource = TimelineSourceFactory.newInstance(timelineURI);
timeline = timelineSource.read();
// search keys for remote timeline entries
for (Object keyObj : config.keySet()) {
if (!(keyObj instanceof String)) continue;
String key = (String) keyObj;
String stName;
String remoteBase;
SyncTarget st;
Matcher m = remoteTimelinePropPattern.matcher(key);
if (!m.matches()) continue;
stName = m.group(1);
remoteBase = REMOTE_TIMELINE_BASE + stName;
strURI = config.getProperty(remoteBase + ".uri", "");
try { timelineURI = new URI(strURI); }
catch (URISyntaxException urise) { /* TODO */ }
// add a new SyncTarget to the list
st = new SyncTarget(stName, TimelineSourceFactory
.newInstance(timelineURI), timeline);
syncTargets.add(st);
// check for synch options
st.enablePull(Boolean.parseBoolean(
config.getProperty(remoteBase + ".pull", "true")));
st.enablePush(Boolean.parseBoolean(
config.getProperty(remoteBase + ".push", "true")));
st.enableSyncOnExit(Boolean.parseBoolean(
config.getProperty(remoteBase + ".sync-on-exit", "true")));
st.setSyncInterval(Long.parseLong(
config.getProperty(remoteBase + ".update-interval",
"1800000"))); // thirty minutes
}
}
public void writeToStream(OutputStream out) throws IOException {
public void save() throws IOException {
Properties config = new Properties();
timelineSource.persist(timeline);
config.setProperty(LOCAL_TIMELINE_URI,
timelineSource.getURI().toString());
for (SyncTarget st : syncTargets) {
String remoteBase = REMOTE_TIMELINE_BASE + st.getName();
config.setProperty(remoteBase + ".uri",
st.getSource().getURI().toString());
config.setProperty(remoteBase + ".pull",
Boolean.toString(st.isPullEnabled()));
config.setProperty(remoteBase + ".push",
Boolean.toString(st.isPushEnabled()));
config.setProperty(remoteBase + ".sync-on-exit",
Boolean.toString(st.isSyncOnExitEnabled()));
config.setProperty(remoteBase + ".update-interval",
Long.toString(st.getSyncInterval()));
}
try {
config.store(new FileOutputStream(propertyFile), "");
} catch (IOException ioe) {
// TODO
}
}
public void save(File newFile) throws IOException {
propertyFile = newFile;
save();
}
public Timeline getTimeline() { return timeline; }
public void setTimelineSource(TimelineSource newSource) {
this.timelineSource = newSource;
}
public TimelineSource getTimelineSource() { return timelineSource; }
public Collection<SyncTarget> getSyncTargets() { return syncTargets; }
}

View File

@ -0,0 +1,25 @@
package jdbernard.timestamper.core;
import java.io.IOException;
import java.net.URI;
/**
*
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com})
*/
public abstract class TimelineSource {
protected final URI uri;
public TimelineSource(URI uri) { this.uri = uri; }
public abstract Timeline read() throws IOException;
public abstract void persist(Timeline t) throws IOException;
public abstract boolean isAuthenticated();
public abstract void authenticate() throws AuthenticationException;
public URI getURI() {
return uri;
}
}

View File

@ -0,0 +1,38 @@
package jdbernard.timestamper.core;
import java.io.File;
import java.net.URI;
/**
*
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com})
*/
public class TimelineSourceFactory {
public static TimelineSource newInstance(URI uri) {
// File based
if ("file".equalsIgnoreCase(uri.getScheme())) {
return new FileTimelineSource(uri);
}
// Twitter
else if ("http".equalsIgnoreCase(uri.getScheme()) &&
("twitter.com".equalsIgnoreCase(uri.getHost())
|| "www.twitter.com".equalsIgnoreCase(uri.getHost()))) {
throw new UnsupportedOperationException("Twitter based timeline "
+ "sources are not yet supported.");
}
// SSH
else if ("ssh".equalsIgnoreCase(uri.getScheme())) {
throw new UnsupportedOperationException("SSH based timeline sources"
+ " are not yet supported.");
}
// unknown
else {
throw new UnsupportedOperationException("Timeline sources for the"
+ " " + uri.getScheme() + " are not currently supported.");
}
}
}

View File

@ -0,0 +1,34 @@
package jdbernard.timestamper.core;
import java.io.IOException;
import java.net.URI;
/**
*
* @author Jonathan Bernard ({@literal jonathan.bernard@gemalto.com})
*/
public class TwitterTimelineSource extends TimelineSource {
private String username;
private char[] password;
public TwitterTimelineSource(URI uri) {
super(uri);
}
public Timeline read() throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
public void persist(Timeline t) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean isAuthenticated() {
throw new UnsupportedOperationException("Not supported yet.");
}
public void authenticate() throws AuthenticationException {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@ -12,6 +12,7 @@
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="2"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>

View File

@ -4,7 +4,7 @@
* Created on October 19, 2008, 3:14 PM
*/
package jdbernard.timestamper;
package jdbernard.timestamper.gui;
import java.awt.Frame;
import java.awt.Point;
@ -56,7 +56,7 @@ public class AboutDialog extends JDialog implements MouseMotionListener {
});
aboutPanel.addMouseMotionListener(this);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.TimeStamperApp.class).getContext().getResourceMap(AboutDialog.class);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.gui.TimeStamperApp.class).getContext().getResourceMap(AboutDialog.class);
iconLabel.setIcon(resourceMap.getIcon("iconLabel.icon")); // NOI18N
iconLabel.setText(resourceMap.getString("iconLabel.text")); // NOI18N
iconLabel.setName("iconLabel"); // NOI18N

View File

@ -12,6 +12,7 @@
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="2"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>

View File

@ -4,7 +4,7 @@
* Created on September 3, 2008, 4:53 PM
*/
package jdbernard.timestamper;
package jdbernard.timestamper.gui;
import java.awt.Font;
import java.awt.Point;
@ -61,7 +61,7 @@ public class NotesDialog extends JDialog implements MouseMotionListener {
setUndecorated(true);
mainPanel.setBorder(javax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2, new java.awt.Color(0, 0, 0)));
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.TimeStamperApp.class).getContext().getResourceMap(NotesDialog.class);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.gui.TimeStamperApp.class).getContext().getResourceMap(NotesDialog.class);
mainPanel.setToolTipText(resourceMap.getString("mainPanel.toolTipText")); // NOI18N
mainPanel.setName("mainPanel"); // NOI18N
mainPanel.addMouseListener(new java.awt.event.MouseAdapter() {

View File

@ -1,6 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.6" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<NonVisualComponents>
<Container class="jdbernard.timestamper.gui.TimelineDayDisplay" name="dayDisplay">
<Properties>
<Property name="name" type="java.lang.String" value="dayDisplay" noResource="true"/>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="100" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="100" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container>
</NonVisualComponents>
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="name" type="java.lang.String" value="Form" noResource="true"/>
@ -12,6 +32,7 @@
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="2"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
@ -48,6 +69,8 @@
<AuxValues>
<AuxValue name="JavaCodeGenerator_ListenersCodePost" type="java.lang.String" value="mainPanel.addMouseMotionListener(this);"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="detailPanel">
<Properties>
@ -179,7 +202,7 @@
<Component class="javax.swing.JButton" name="prevWeekButton">
<Properties>
<Property name="action" type="javax.swing.Action" editor="org.netbeans.modules.swingapp.ActionEditor">
<action class="jdbernard.timestamper.PunchcardDisplayDialog" id="previousWeek" methodName="previousWeek"/>
<action class="jdbernard.timestamper.gui.PunchcardDisplayDialog" id="previousWeek" methodName="previousWeek"/>
</Property>
<Property name="hideActionText" type="boolean" value="true"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
@ -196,7 +219,7 @@
<Component class="javax.swing.JButton" name="prevDayButton">
<Properties>
<Property name="action" type="javax.swing.Action" editor="org.netbeans.modules.swingapp.ActionEditor">
<action class="jdbernard.timestamper.PunchcardDisplayDialog" id="previousDay" methodName="previousDay"/>
<action class="jdbernard.timestamper.gui.PunchcardDisplayDialog" id="previousDay" methodName="previousDay"/>
</Property>
<Property name="hideActionText" type="boolean" value="true"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
@ -213,7 +236,7 @@
<Component class="javax.swing.JButton" name="currentDayButton">
<Properties>
<Property name="action" type="javax.swing.Action" editor="org.netbeans.modules.swingapp.ActionEditor">
<action class="jdbernard.timestamper.PunchcardDisplayDialog" id="currentDay" methodName="currentDay"/>
<action class="jdbernard.timestamper.gui.PunchcardDisplayDialog" id="currentDay" methodName="currentDay"/>
</Property>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[0, 2, 0, 2]"/>
@ -229,7 +252,7 @@
<Component class="javax.swing.JButton" name="nextDayButton">
<Properties>
<Property name="action" type="javax.swing.Action" editor="org.netbeans.modules.swingapp.ActionEditor">
<action class="jdbernard.timestamper.PunchcardDisplayDialog" id="nextDay" methodName="nextDay"/>
<action class="jdbernard.timestamper.gui.PunchcardDisplayDialog" id="nextDay" methodName="nextDay"/>
</Property>
<Property name="hideActionText" type="boolean" value="true"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
@ -246,7 +269,7 @@
<Component class="javax.swing.JButton" name="nextWeekButton">
<Properties>
<Property name="action" type="javax.swing.Action" editor="org.netbeans.modules.swingapp.ActionEditor">
<action class="jdbernard.timestamper.PunchcardDisplayDialog" id="nextWeek" methodName="nextWeek"/>
<action class="jdbernard.timestamper.gui.PunchcardDisplayDialog" id="nextWeek" methodName="nextWeek"/>
</Property>
<Property name="hideActionText" type="boolean" value="true"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
@ -265,7 +288,7 @@
<Component class="javax.swing.JButton" name="newMarkerButton">
<Properties>
<Property name="action" type="javax.swing.Action" editor="org.netbeans.modules.swingapp.ActionEditor">
<action class="jdbernard.timestamper.PunchcardDisplayDialog" id="newMarker" methodName="newMarker"/>
<action class="jdbernard.timestamper.gui.PunchcardDisplayDialog" id="newMarker" methodName="newMarker"/>
</Property>
<Property name="name" type="java.lang.String" value="newMarkerButton" noResource="true"/>
</Properties>
@ -278,7 +301,7 @@
<Component class="javax.swing.JButton" name="deleteMarkerButton">
<Properties>
<Property name="action" type="javax.swing.Action" editor="org.netbeans.modules.swingapp.ActionEditor">
<action class="jdbernard.timestamper.PunchcardDisplayDialog" id="deleteMarker" methodName="deleteMarker"/>
<action class="jdbernard.timestamper.gui.PunchcardDisplayDialog" id="deleteMarker" methodName="deleteMarker"/>
</Property>
<Property name="name" type="java.lang.String" value="deleteMarkerButton" noResource="true"/>
</Properties>
@ -291,7 +314,7 @@
<Component class="javax.swing.JButton" name="saveMarkerChanges">
<Properties>
<Property name="action" type="javax.swing.Action" editor="org.netbeans.modules.swingapp.ActionEditor">
<action class="jdbernard.timestamper.PunchcardDisplayDialog" id="saveMarkerChanges" methodName="saveMarkerChanges"/>
<action class="jdbernard.timestamper.gui.PunchcardDisplayDialog" id="saveMarkerChanges" methodName="saveMarkerChanges"/>
</Property>
<Property name="name" type="java.lang.String" value="saveMarkerChanges" noResource="true"/>
</Properties>

View File

@ -4,8 +4,9 @@
* Created on October 16, 2008, 4:37 PM
*/
package jdbernard.timestamper;
package jdbernard.timestamper.gui;
import jdbernard.timestamper.core.TimelineMarker;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
@ -64,8 +65,8 @@ implements MouseMotionListener, ChangeListener {
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
dayDisplay = new jdbernard.timestamper.gui.TimelineDayDisplay();
mainPanel = new javax.swing.JPanel();
dayDisplay = new jdbernard.timestamper.TimelineDayDisplay();
detailPanel = new javax.swing.JPanel();
textPane = new javax.swing.JPanel();
markTextField = new javax.swing.JTextField();
@ -84,6 +85,19 @@ implements MouseMotionListener, ChangeListener {
deleteMarkerButton = new javax.swing.JButton();
saveMarkerChanges = new javax.swing.JButton();
dayDisplay.setName("dayDisplay"); // NOI18N
javax.swing.GroupLayout dayDisplayLayout = new javax.swing.GroupLayout(dayDisplay);
dayDisplay.setLayout(dayDisplayLayout);
dayDisplayLayout.setHorizontalGroup(
dayDisplayLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 100, Short.MAX_VALUE)
);
dayDisplayLayout.setVerticalGroup(
dayDisplayLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 100, Short.MAX_VALUE)
);
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setName("Form"); // NOI18N
setUndecorated(true);
@ -97,26 +111,12 @@ implements MouseMotionListener, ChangeListener {
});
mainPanel.addMouseMotionListener(this);
dayDisplay.setName("dayDisplay"); // NOI18N
dayDisplay.setPreferredSize(new java.awt.Dimension(100, 100));
javax.swing.GroupLayout dayDisplayLayout = new javax.swing.GroupLayout(dayDisplay);
dayDisplay.setLayout(dayDisplayLayout);
dayDisplayLayout.setHorizontalGroup(
dayDisplayLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 405, Short.MAX_VALUE)
);
dayDisplayLayout.setVerticalGroup(
dayDisplayLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 339, Short.MAX_VALUE)
);
detailPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
detailPanel.setName("detailPanel"); // NOI18N
textPane.setName("textPane"); // NOI18N
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.TimeStamperApp.class).getContext().getResourceMap(PunchcardDisplayDialog.class);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.gui.TimeStamperApp.class).getContext().getResourceMap(PunchcardDisplayDialog.class);
markTextField.setText(resourceMap.getString("markTextField.text")); // NOI18N
markTextField.setName("markTextField"); // NOI18N
@ -175,7 +175,7 @@ implements MouseMotionListener, ChangeListener {
gridBagConstraints.weightx = 1.0;
datePanel.add(dateLabel, gridBagConstraints);
javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.TimeStamperApp.class).getContext().getActionMap(PunchcardDisplayDialog.class, this);
javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.gui.TimeStamperApp.class).getContext().getActionMap(PunchcardDisplayDialog.class, this);
prevWeekButton.setAction(actionMap.get("previousWeek")); // NOI18N
prevWeekButton.setHideActionText(true);
prevWeekButton.setMargin(new java.awt.Insets(0, 2, 0, 2));
@ -270,26 +270,7 @@ implements MouseMotionListener, ChangeListener {
.addComponent(textPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
mainPanel.setLayout(mainPanelLayout);
mainPanelLayout.setHorizontalGroup(
mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(dayDisplay, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 405, Short.MAX_VALUE)
.addComponent(detailPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
mainPanelLayout.setVerticalGroup(
mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, mainPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(dayDisplay, javax.swing.GroupLayout.DEFAULT_SIZE, 339, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(detailPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
mainPanel.add(detailPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
@ -315,7 +296,7 @@ private void mainPanelMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:e
private javax.swing.JButton currentDayButton;
private javax.swing.JLabel dateLabel;
private javax.swing.JPanel datePanel;
private jdbernard.timestamper.TimelineDayDisplay dayDisplay;
private jdbernard.timestamper.gui.TimelineDayDisplay dayDisplay;
private javax.swing.JButton deleteMarkerButton;
private javax.swing.JPanel detailPanel;
private javax.swing.JPanel mainPanel;
@ -418,7 +399,7 @@ private void mainPanelMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:e
public void stateChanged(ChangeEvent e) {
if (e.getSource() == dayDisplay) {
Timeline.TimelineMarker marker = dayDisplay.getSelectedTimelineMarker();
TimelineMarker marker = dayDisplay.getSelectedTimelineMarker();
if (marker == null) {
timestampDateChooser.setDate(null);

View File

@ -14,6 +14,7 @@
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="2"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>

View File

@ -2,7 +2,7 @@
* TimeStamperAboutBox.java
*/
package jdbernard.timestamper;
package jdbernard.timestamper.gui;
import org.jdesktop.application.Action;
@ -37,13 +37,13 @@ public class TimeStamperAboutBox extends javax.swing.JDialog {
javax.swing.JLabel appDescLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.TimeStamperApp.class).getContext().getResourceMap(TimeStamperAboutBox.class);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.gui.TimeStamperApp.class).getContext().getResourceMap(TimeStamperAboutBox.class);
setTitle(resourceMap.getString("title")); // NOI18N
setModal(true);
setName("aboutBox"); // NOI18N
setResizable(false);
javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.TimeStamperApp.class).getContext().getActionMap(TimeStamperAboutBox.class, this);
javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.gui.TimeStamperApp.class).getContext().getActionMap(TimeStamperAboutBox.class, this);
closeButton.setAction(actionMap.get("closeAboutBox")); // NOI18N
closeButton.setName("closeButton"); // NOI18N

View File

@ -2,7 +2,7 @@
* TimeStamperApp.java
*/
package jdbernard.timestamper;
package jdbernard.timestamper.gui;
import java.io.File;
import java.io.FileInputStream;
@ -16,6 +16,7 @@ import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import jdbernard.timestamper.core.TimelineProperties;
import org.jdesktop.application.Application;
import org.jdesktop.application.SingleFrameApplication;
@ -25,16 +26,14 @@ import org.jdesktop.application.SingleFrameApplication;
public class TimeStamperApp extends SingleFrameApplication
implements Application.ExitListener {
private Timeline activeTimeline;
private String currentTimelineFile;
private TimelineProperties timelineProperties;
private Logger log;
private Properties config;
public TimeStamperApp() {
super();
// set defaults for fields
activeTimeline = new Timeline();
timelineProperties = new TimelineProperties();
// set up logger
log = Logger.getLogger("jdbernard.timestamper");
@ -71,25 +70,14 @@ implements Application.ExitListener {
}
// load the last used timeline
loadTimeline(config.getProperty("lastUsedTimelineFilename"));
// TODO: fix
loadTimelineProperties(config.getProperty("lastUsedTimelineProperties", "timeline.default.properties"));
}
/**
*
*/
@Override protected void initialize(String[] args) {
if (args.length > 0) {
File inFile = new File(args[0]);
if (!inFile.exists())
try { inFile.createNewFile(); }
catch (IOException ioe) {
log.warning("No file '" + args[0] + " exists and an error"
+ " occurred trying to create it.");
}
currentTimelineFile = args[0];
loadTimeline(currentTimelineFile);
}
}
/**
@ -124,48 +112,45 @@ implements Application.ExitListener {
launch(TimeStamperApp.class, args);
}
public void saveTimeline() {
saveTimelineToFile(currentTimelineFile);
public TimelineProperties getTimelineProperties() {
return timelineProperties;
}
public void saveTimelineToFile(String filename) {
if (filename == null || filename.equals(""))
filename = "default-timeline.txt";
public void saveTimelineProperties() {
try {
Timeline.writeToFile(filename, activeTimeline);
currentTimelineFile = filename;
timelineProperties.save();
} catch (IOException ioe) {
log.warning("Could not save timeline file: "
+ ioe.getLocalizedMessage());
log.warning("Could not save the timeline to <"
+ timelineProperties.getTimelineSource().getURI().toString()
+ ">:" + ioe.getLocalizedMessage());
}
}
public void saveTimelinePropertiesAs(File propertyFile) {
try { timelineProperties.save(propertyFile); }
catch (IOException ioe) {
// TODO
}
}
public void loadTimeline(String filename) {
if (filename == null || filename.equals("")) {
activeTimeline = new Timeline();
return;
}
try {
activeTimeline = Timeline.readFromFile(filename);
currentTimelineFile = filename;
} catch (IOException ioe) {
log.warning("Could not load from the file: " +
ioe.getLocalizedMessage());
}
public void loadTimelineProperties(String filename) {
loadTimelineProperties(new File(filename));
}
public Timeline getActiveTimeline() {
return activeTimeline;
public void loadTimelineProperties(File propertyFile) {
try { timelineProperties = new TimelineProperties(propertyFile); }
catch (IOException ioe) {
// TODO
}
}
@Override
public void willExit(EventObject e) {
saveTimeline();
saveTimelineProperties();
config.setProperty("lastUsedTimelineFilename", currentTimelineFile);
config.setProperty("lastUsedTimelineProperties",
timelineProperties.getTimelineSource().getURI().toString());
try {
FileOutputStream out = new FileOutputStream("timestamper.config");
config.store(out, "");

View File

@ -267,6 +267,7 @@
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="2"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>

View File

@ -2,8 +2,10 @@
* TimeStamperView.java
*/
package jdbernard.timestamper;
package jdbernard.timestamper.gui;
import jdbernard.timestamper.core.TimelineMarker;
import jdbernard.timestamper.core.Timeline;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
@ -16,7 +18,6 @@ import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.ButtonGroup;
import javax.swing.JFileChooser;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
@ -24,7 +25,6 @@ import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import sun.security.jca.GetInstance;
/**
* The application's main frame.
@ -129,11 +129,11 @@ ChangeListener {
mainPanel.addMouseMotionListener(this);
currentTaskLabel.setFont(currentTaskLabel.getFont().deriveFont(currentTaskLabel.getFont().getStyle() | java.awt.Font.BOLD, currentTaskLabel.getFont().getSize()+2));
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.TimeStamperApp.class).getContext().getResourceMap(TimeStamperView.class);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.gui.TimeStamperApp.class).getContext().getResourceMap(TimeStamperView.class);
currentTaskLabel.setText(resourceMap.getString("currentTaskLabel.text")); // NOI18N
currentTaskLabel.setName("currentTaskLabel"); // NOI18N
javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.TimeStamperApp.class).getContext().getActionMap(TimeStamperView.class, this);
javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(jdbernard.timestamper.gui.TimeStamperApp.class).getContext().getActionMap(TimeStamperView.class, this);
exitButton.setAction(actionMap.get("quit")); // NOI18N
exitButton.setBorder(null);
exitButton.setContentAreaFilled(false);
@ -269,7 +269,8 @@ ChangeListener {
private void taskTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_taskTextFieldKeyReleased
if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
Timeline t = ((TimeStamperApp) getApplication()).getActiveTimeline();
Timeline t = ((TimeStamperApp) getApplication())
.getTimelineProperties().getTimeline();
Date d = new Date();
t.addMarker(d, taskTextField.getText(), "No comments.");
startTimeLabel.setText(Timeline.shortFormat.format(d));
@ -312,8 +313,9 @@ ChangeListener {
}
public void setNotesForActiveTask(String notes) {
Timeline t = ((TimeStamperApp) getApplication()).getActiveTimeline();
Timeline.TimelineMarker tm = t.getLastMarker(new Date());
Timeline t = ((TimeStamperApp) getApplication())
.getTimelineProperties().getTimeline();
TimelineMarker tm = t.getLastMarker(new Date());
if (tm == null) return;
tm.setNotes(notes);
}
@ -472,7 +474,7 @@ ChangeListener {
@Action
public void saveTimeline() {
((TimeStamperApp) getApplication()).saveTimeline();
((TimeStamperApp) getApplication()).saveTimelineProperties();
}
@Action
@ -480,8 +482,8 @@ ChangeListener {
if (fileChooser.showSaveDialog(getFrame())!=JFileChooser.APPROVE_OPTION)
return;
((TimeStamperApp) getApplication()).saveTimelineToFile(
fileChooser.getSelectedFile().getAbsolutePath());
((TimeStamperApp) getApplication()).saveTimelinePropertiesAs(
fileChooser.getSelectedFile());
}
@Action
@ -489,15 +491,16 @@ ChangeListener {
if (fileChooser.showOpenDialog(getFrame())!=JFileChooser.APPROVE_OPTION)
return;
((TimeStamperApp) getApplication()).loadTimeline(
((TimeStamperApp) getApplication()).loadTimelineProperties(
fileChooser.getSelectedFile().getAbsolutePath());
fireChangeEvent();
}
public void refreshDialog() {
Timeline t = ((TimeStamperApp) getApplication()).getActiveTimeline();
Timeline.TimelineMarker lastMarker = t.getLastMarker(new Date());
Timeline t = ((TimeStamperApp) getApplication())
.getTimelineProperties().getTimeline();
TimelineMarker lastMarker = t.getLastMarker(new Date());
if (lastMarker != null) {
mostRecentTask = lastMarker.getTimestamp();

View File

@ -2,8 +2,10 @@
* Author: Jonathan Bernard - jonathan.bernard@gemalto.com
*/
package jdbernard.timestamper;
package jdbernard.timestamper.gui;
import jdbernard.timestamper.core.TimelineMarker;
import jdbernard.timestamper.core.Timeline;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
@ -31,7 +33,7 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
ChangeListener {
private class MarkerDisplayEntry {
public Timeline.TimelineMarker marker;
public TimelineMarker marker;
public float relY;
public float relHeight;
public Rectangle2D markBounds;
@ -186,7 +188,7 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
private ArrayList<MarkerDisplayEntry> markerEntries;
private ArrayList<TimeLegendEntry> timeLegendLocations;
private Timeline.TimelineMarker currentMarker;
private TimelineMarker currentMarker;
private ArrayList<ChangeListener> changeListeners = new ArrayList<ChangeListener>();
private Point lastMousePress;
@ -318,18 +320,20 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
return selectedOpaque;
}
public Timeline.TimelineMarker getSelectedTimelineMarker() {
public TimelineMarker getSelectedTimelineMarker() {
return currentMarker;
}
public void addMarker(Date timestamp, String mark, String notes) {
Timeline timeline = TimeStamperApp.getApplication().getActiveTimeline();
Timeline timeline = TimeStamperApp.getApplication()
.getTimelineProperties().getTimeline();
timeline.addMarker(timestamp, mark, notes);
updateMarkers(getGraphics());
}
public void deleteSelectedMarker() {
Timeline timeline = TimeStamperApp.getApplication().getActiveTimeline();
Timeline timeline = TimeStamperApp.getApplication()
.getTimelineProperties().getTimeline();
timeline.removeMarker(currentMarker);
updateMarkers(getGraphics());
}
@ -348,7 +352,8 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
*/
private void updateMarkers(Graphics g) {
Timeline timeline = TimeStamperApp.getApplication().getActiveTimeline();
Timeline timeline = TimeStamperApp.getApplication()
.getTimelineProperties().getTimeline();
Insets insets = this.getInsets();
Rectangle bounds = this.getBounds();
Rectangle canvasBounds = new Rectangle(insets.left, insets.top,
@ -396,7 +401,7 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
// get all relevant markers starting from the marker just before the
// visible start of the display
Timeline.TimelineMarker tm = timeline.getLastMarker(rangeStartDate);
TimelineMarker tm = timeline.getLastMarker(rangeStartDate);
// If there is no previous marker
if (tm == null)
@ -408,11 +413,11 @@ public class TimelineDayDisplay extends JComponent implements MouseListener,
// Now we want to step through the timeline, capturing all markers
// between the visible ranges.
Iterator<Timeline.TimelineMarker> itr = timeline.iterator();
Iterator<TimelineMarker> itr = timeline.iterator();
while (!itr.next().equals(tm));
ArrayList<Timeline.TimelineMarker> markers = new ArrayList<Timeline.TimelineMarker>();
ArrayList<TimelineMarker> markers = new ArrayList<TimelineMarker>();
while (rangeEndDate.after(tm.getTimestamp())) {
markers.add(tm);
if (itr.hasNext()) tm = itr.next();

View File

@ -0,0 +1,42 @@
markTextField.text=
previousWeek.Action.text=<<
previousWeek.Action.shortDescription=Go back one week.
dateLabel.text=date
#NOI18N
dateLabel.background=255, 255, 153
prevWeekButton.text=jButton1
previousWeek.Action.icon=icons/media-seek-backward.png
previousWeek.Action.smallIcon=icons/media-seek-backward.png
prevDayButton.text=jButton1
previousDay.Action.smallIcon=icons/media-playback-reverse.png
previousDay.Action.icon=icons/media-playback-reverse.png
previousDay.Action.shortDescription=Go back one day.
previousDay.Action.text=Previous Day
currentDay.Action.text=Today
currentDay.Action.shortDescription=Go to today's date.
jButton2.text=jButton2
nextDay.Action.smallIcon=icons/media-playback-start.png
nextDay.Action.icon=icons/media-playback-start.png
nextDay.Action.shortDescription=Go one day forward.
nextDay.Action.text=Next Day
nextWeekButton.text=jButton3
nextWeek.Action.text=Next Week
nextWeek.Action.smallIcon=icons/media-seek-forward.png
nextWeek.Action.icon=icons/media-seek-forward.png
nextWeek.Action.shortDescription=Go one week forward.
newMarkerButton.text=jButton1
newMarker.Action.smallIcon=icons/16-square-green-add.png
newMarker.Action.icon=icons/16-square-green-add.png
newMarker.Action.shortDescription=Create a new timestamp mark.
newMarker.Action.text=New Marker
deleteMarkerButton.text=jButton1
deleteMarker.Action.text=Delete Marker
deleteMarker.Action.shortDescription=Delete a timestamp mark.
deleteMarker.Action.smallIcon=icons/16-square-red-delete.png
deleteMarker.Action.icon=icons/16-square-red-delete.png
saveMarkerChanges.text=jButton1
saveMarkerChanges.Action.smallIcon=icons/document-save-16x16.png
saveMarkerChanges.Action.icon=icons/document-save-16x16.png
saveMarkerChanges.Action.shortDescription=Save timeline marker changes.
saveMarkerChanges.Action.text=Save Changes
timestampDateChooser.dateFormatString=MMM d, yyyy HH:mm

View File

@ -9,9 +9,9 @@ Application.description = Simple application used to track activities throughout
Application.vendorId = Jonathan Bernard
Application.id = TimeStamper
Application.lookAndFeel = system
Application.icon=/jdbernard/timestamper/resources/icons/appointment-new-32x32.png
Application.icon=icons/appointment-new-32x32.png
quit.Action.text=Exit
quit.Action.accelerator=ctrl pressed Q
quit.Action.smallIcon=/jdbernard/timestamper/resources/icons/16-em-cross.png
quit.Action.icon=/jdbernard/timestamper/resources/icons/16-em-cross.png
quit.Action.smallIcon=icons/16-em-cross.png
quit.Action.icon=icons/16-em-cross.png
quit.Action.shortDescription=Exit the application

View File

@ -14,36 +14,36 @@ totalTimeNow.text=3day 2hr 50min 25sec
#NOI18N
totalTimeNow.foreground=0, 153, 0
notesButton.text=jButton1
editNotes.Action.smallIcon=/jdbernard/timestamper/resources/icons/16-em-pencil.png
editNotes.Action.icon=/jdbernard/timestamper/resources/icons/16-em-pencil.png
editNotes.Action.smallIcon=icons/16-em-pencil.png
editNotes.Action.icon=icons/16-em-pencil.png
editNotes.Action.shortDescription=Edit notes for this task
editNotes.Action.text=Show Notes...
jButton1.text=
showOptionsMenu.Action.text=Options menu
showOptionsMenu.Action.shortDescription=Show the application's options menu.
showOptionsMenu.Action.smallIcon=/jdbernard/timestamper/resources/icons/16-tool-a.png
showOptionsMenu.Action.icon=/jdbernard/timestamper/resources/icons/16-tool-a.png
showOptionsMenu.Action.smallIcon=icons/16-tool-a.png
showOptionsMenu.Action.icon=icons/16-tool-a.png
saveTimelineMenuItem.text=Item
saveTimeline.Action.text=Save Timeline...
saveTimeline.Action.smallIcon=/jdbernard/timestamper/resources/icons/document-save-16x16.png
saveTimeline.Action.icon=/jdbernard/timestamper/resources/icons/document-save-16x16.png
saveTimeline.Action.smallIcon=icons/document-save-16x16.png
saveTimeline.Action.icon=icons/document-save-16x16.png
saveTimeline.Action.shortDescription=Save all actions to a timeline file.
saveTimelineAs.Action.smallIcon=/jdbernard/timestamper/resources/icons/document-save-as-16x16.png
saveTimelineAs.Action.icon=/jdbernard/timestamper/resources/icons/document-save-as-16x16.png
saveTimelineAs.Action.smallIcon=icons/document-save-as-16x16.png
saveTimelineAs.Action.icon=icons/document-save-as-16x16.png
saveTimelineAs.Action.shortDescription=Save actions to a new timeline file.
saveTimelineAs.Action.text=Save Timeline As...
loadTimelineMenuItem.text=Item
loadTimeline.Action.text=Load Timeline...
loadTimeline.Action.shortDescription=Load a set of actions from a timeline file
loadTimeline.Action.smallIcon=/jdbernard/timestamper/resources/icons/document-open-16x16.png
loadTimeline.Action.icon=/jdbernard/timestamper/resources/icons/document-open-16x16.png
loadTimeline.Action.smallIcon=icons/document-open-16x16.png
loadTimeline.Action.icon=icons/document-open-16x16.png
currentTimeLabel.text=12:00:00
#NOI18N
currentTimeLabel.foreground=204, 0, 0
exit.Action.text=Exit
exit.Action.smallIcon=/jdbernard/timestamper/resources/icons/16-em-cross.png
exit.Action.smallIcon=icons/16-em-cross.png
exit.Action.shortDescription=Close the application.
exit.Action.icon=/jdbernard/timestamper/resources/icons/16-em-cross.png
exit.Action.icon=icons/16-em-cross.png
notesButton2.text=jToggleButton1
#NOI18N
optionsButton.rolloverIcon=icons/16-tool-a-hover.png
@ -53,8 +53,8 @@ showNotesDialogCheckBoxMenuItem.text=CheckBox
showPunchcardCheckBoxMenuItem.text=CheckBox
showPunchcard.Action.shortDescription=View the timeline
showPunchcard.Action.text=Show Timeline...
showPunchcard.Action.smallIcon=/jdbernard/timestamper/resources/icons/16-file-archive.png
showPunchcard.Action.icon=/jdbernard/timestamper/resources/icons/16-file-archive.png
showPunchcard.Action.smallIcon=icons/16-file-archive.png
showPunchcard.Action.icon=icons/16-file-archive.png
editNotes.Action.accelerator=ctrl pressed N
showPunchcard.Action.accelerator=ctrl pressed T
showAboutCheckBoxMenuItem.text=CheckBox

View File

@ -1,42 +0,0 @@
markTextField.text=
previousWeek.Action.text=<<
previousWeek.Action.shortDescription=Go back one week.
dateLabel.text=date
#NOI18N
dateLabel.background=255, 255, 153
prevWeekButton.text=jButton1
previousWeek.Action.icon=/jdbernard/timestamper/resources/icons/media-seek-backward.png
previousWeek.Action.smallIcon=/jdbernard/timestamper/resources/icons/media-seek-backward.png
prevDayButton.text=jButton1
previousDay.Action.smallIcon=/jdbernard/timestamper/resources/icons/media-playback-reverse.png
previousDay.Action.icon=/jdbernard/timestamper/resources/icons/media-playback-reverse.png
previousDay.Action.shortDescription=Go back one day.
previousDay.Action.text=Previous Day
currentDay.Action.text=Today
currentDay.Action.shortDescription=Go to today's date.
jButton2.text=jButton2
nextDay.Action.smallIcon=/jdbernard/timestamper/resources/icons/media-playback-start.png
nextDay.Action.icon=/jdbernard/timestamper/resources/icons/media-playback-start.png
nextDay.Action.shortDescription=Go one day forward.
nextDay.Action.text=Next Day
nextWeekButton.text=jButton3
nextWeek.Action.text=Next Week
nextWeek.Action.smallIcon=/jdbernard/timestamper/resources/icons/media-seek-forward.png
nextWeek.Action.icon=/jdbernard/timestamper/resources/icons/media-seek-forward.png
nextWeek.Action.shortDescription=Go one week forward.
newMarkerButton.text=jButton1
newMarker.Action.smallIcon=/jdbernard/timestamper/resources/icons/16-square-green-add.png
newMarker.Action.icon=/jdbernard/timestamper/resources/icons/16-square-green-add.png
newMarker.Action.shortDescription=Create a new timestamp mark.
newMarker.Action.text=New Marker
deleteMarkerButton.text=jButton1
deleteMarker.Action.text=Delete Marker
deleteMarker.Action.shortDescription=Delete a timestamp mark.
deleteMarker.Action.smallIcon=/jdbernard/timestamper/resources/icons/16-square-red-delete.png
deleteMarker.Action.icon=/jdbernard/timestamper/resources/icons/16-square-red-delete.png
saveMarkerChanges.text=jButton1
saveMarkerChanges.Action.smallIcon=/jdbernard/timestamper/resources/icons/document-save-16x16.png
saveMarkerChanges.Action.icon=/jdbernard/timestamper/resources/icons/document-save-16x16.png
saveMarkerChanges.Action.shortDescription=Save timeline marker changes.
saveMarkerChanges.Action.text=Save Changes
timestampDateChooser.dateFormatString=MMM d, yyyy HH:mm