Java 1.4 & Java 5 compatibility
- From: Emmanuel Bourg <ebourg@apache.org>
- To: dev@trident.kenai.com
- Subject: Java 1.4 & Java 5 compatibility
- Date: Tue, 06 Oct 2009 16:38:12 +0200
Hi,
I started playing with the Trident API a few days ago and it seems well thought out, thank you for the work Kirill.
Currently Trident requires Java 6, I investigated how to downgrade this requirement to work on Java 1.4 and Java 5 (I'm maintaining a Swing application that runs on OS X 10.3.9). Here are the steps I followed if anyone is interested to create a backport.
The Java 5 compatibility is easy to get, the only Java 6 feature in the current code is the TimelineSwingWorker class which is optional. It can either be removed or reimplemented with the SwingWorker API from jdesktop. Also the compilation target must be changed to 1.5 in the build.properties file.
The Java 1.4 compatibility requires a little more work:
- turn the RunOnUIThread annotation into a standard interface
- replace the System.nanoTime() calls by Utils.nanoTime() from backport-util-concurrent
- turn the enums into standard classes with static fields
- replace the calls to Class.getCanonicalName()
- post process the jar with Retroweaver
Beyond the binary compatibility I don't know if Trident relies on behaviors specific to Java 6, but at least the backport worked fine for simple animations on Java 1.4. The backport adds a dependency on backport-util-concurrent.jar (~300k). Changing the enums is optional if you don't mind depending on the Retroweaver runtime (~180k). Retroweaver provides also a replacement for System.nanoTime(), but the alternative from backport-util-concurrent is more accurate.
Emmanuel Bourg
Index: src/org/pushingpixels/trident/TimelinePropertyBuilder.java
===================================================================
--- src/org/pushingpixels/trident/TimelinePropertyBuilder.java (rvision 82)
+++ src/org/pushingpixels/trident/TimelinePropertyBuilder.java (copie de
travail)
@@ -242,7 +242,7 @@
} catch (Throwable exc) {
System.err.println("Exception
occurred in updating field '"
+ this.fieldName + "'
of object "
- +
this.object.getClass().getCanonicalName()
+ +
this.object.getClass().getName()
+ " at timeline
position " + timelinePosition);
exc.printStackTrace();
}
@@ -275,7 +275,7 @@
} catch (Throwable exc) {
System.err.println("Exception
occurred in updating field '"
+ this.fieldName + "'
of object "
- +
this.object.getClass().getCanonicalName()
+ +
this.object.getClass().getName()
+ " at timeline
position " + timelinePosition);
exc.printStackTrace();
}
Index: src/org/pushingpixels/trident/Timeline.java
===================================================================
--- src/org/pushingpixels/trident/Timeline.java (rvision 82)
+++ src/org/pushingpixels/trident/Timeline.java (copie de travail)
@@ -105,14 +105,77 @@
private int doneCount;
- public enum RepeatBehavior {
- LOOP, REVERSE
- }
+ public static class RepeatBehavior {
+ private String name;
+ public static RepeatBehavior LOOP = new RepeatBehavior("LOOP");
+ public static RepeatBehavior REVERSE = new RepeatBehavior("REVERSE");
- public enum TimelineState {
- IDLE, READY, PLAYING_FORWARD, PLAYING_REVERSE, SUSPENDED,
CANCELLED, DONE
- }
+ public RepeatBehavior(String name) {
+ this.name = name;
+ }
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ RepeatBehavior that = (RepeatBehavior) o;
+
+ if (!name.equals(that.name)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+ }
+
+ public static class TimelineState {
+ private String name;
+ public static final TimelineState IDLE = new TimelineState("IDLE");
+ public static final TimelineState READY = new TimelineState("READY");
+ public static final TimelineState PLAYING_FORWARD = new
TimelineState("PLAYING_FORWARD");
+ public static final TimelineState PLAYING_REVERSE = new
TimelineState("PLAYING_REVERSE");
+ public static final TimelineState SUSPENDED = new
TimelineState("SUSPENDED");
+ public static final TimelineState CANCELLED = new
TimelineState("CANCELLED");
+ public static final TimelineState DONE = new TimelineState("DONE");
+
+ public TimelineState(String name) {
+ this.name = name;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TimelineState that = (TimelineState) o;
+
+ if (!name.equals(that.name)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+ }
+
// protected abstract class AbstractFieldInfo<T> {
// protected Object object;
//
@@ -295,8 +358,8 @@
}
}
- @RunOnUIThread
- private class UISetter extends Setter {
+ //@RunOnUIThread
+ private class UISetter extends Setter implements RunOnUIThread {
}
class Chain implements TimelineCallback {
@@ -325,8 +388,8 @@
boolean shouldRunOnUIThread = false;
Class<?> clazz = callback.getClass();
while ((clazz != null) &&
!shouldRunOnUIThread) {
- shouldRunOnUIThread = clazz
-
.isAnnotationPresent(RunOnUIThread.class);
+ shouldRunOnUIThread =
+
RunOnUIThread.class.isAssignableFrom(clazz);
clazz = clazz.getSuperclass();
}
if (shouldRunOnUIThread
@@ -358,8 +421,8 @@
boolean shouldRunOnUIThread = false;
Class<?> clazz = callback.getClass();
while ((clazz != null) &&
!shouldRunOnUIThread) {
- shouldRunOnUIThread = clazz
-
.isAnnotationPresent(RunOnUIThread.class);
+ shouldRunOnUIThread =
+
RunOnUIThread.class.isAssignableFrom(clazz);
clazz = clazz.getSuperclass();
}
if (shouldRunOnUIThread
@@ -465,7 +528,7 @@
public final <T> void addPropertyToInterpolate(
TimelinePropertyBuilder<T> propertyBuilder) {
-
this.propertiesToInterpolate.add(propertyBuilder.getFieldInfo(this));
+ this.propertiesToInterpolate.add((AbstractFieldInfo)
propertyBuilder.getFieldInfo(this));
}
public final <T> void addPropertyToInterpolate(String propName,
Index: src/org/pushingpixels/trident/swing/TimelineSwingWorker.java
===================================================================
--- src/org/pushingpixels/trident/swing/TimelineSwingWorker.java
(rvision 82)
+++ src/org/pushingpixels/trident/swing/TimelineSwingWorker.java
(copie de travail)
@@ -29,7 +29,7 @@
*/
package org.pushingpixels.trident.swing;
-import javax.swing.SwingWorker;
+import org.jdesktop.swingworker.SwingWorker;
import org.pushingpixels.trident.TimelineScenario;
Index: src/org/pushingpixels/trident/TimelineScenario.java
===================================================================
--- src/org/pushingpixels/trident/TimelineScenario.java (rvision 82)
+++ src/org/pushingpixels/trident/TimelineScenario.java (copie de travail)
@@ -50,10 +50,39 @@
boolean isLooping;
- public enum TimelineScenarioState {
- DONE, PLAYING, IDLE, SUSPENDED
- }
+ public static class TimelineScenarioState {
+ private String name;
+ public static final TimelineScenarioState DONE = new
TimelineScenarioState("DONE");
+ public static final TimelineScenarioState PLAYING = new
TimelineScenarioState("PLAYING");
+ public static final TimelineScenarioState IDLE = new
TimelineScenarioState("IDLE");
+ public static final TimelineScenarioState SUSPENDED = new
TimelineScenarioState("SUSPENDED");
+ public TimelineScenarioState(String name) {
+ this.name = name;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TimelineScenarioState that = (TimelineScenarioState) o;
+
+ if (!name.equals(that.name)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+ }
+
class Chain implements TimelineScenarioCallback {
private List<TimelineScenarioCallback> callbacks;
Index: src/org/pushingpixels/trident/TimelineEngine.java
===================================================================
--- src/org/pushingpixels/trident/TimelineEngine.java (rvision 82)
+++ src/org/pushingpixels/trident/TimelineEngine.java (copie de travail)
@@ -35,6 +35,7 @@
import org.pushingpixels.trident.Timeline.TimelineState;
import org.pushingpixels.trident.TimelineScenario.TimelineScenarioState;
import org.pushingpixels.trident.callback.RunOnUIThread;
+import edu.emory.mathcs.backport.java.util.concurrent.helpers.Utils;
/**
* The Trident timeline engine. This is the main entry point to play
@@ -66,11 +67,42 @@
private Map<TimelineScenario, Runnable> waitingTimelineScenariosMap;
- enum TimelineOperationKind {
- PLAY, CANCEL, RESUME, SUSPEND, ABORT
- }
+ static class TimelineOperationKind {
- class TimelineOperation {
+ private String name;
+ public static final TimelineOperationKind PLAY = new
TimelineOperationKind("PLAY");
+ public static final TimelineOperationKind CANCEL = new
TimelineOperationKind("CANCEL");
+ public static final TimelineOperationKind RESUME = new
TimelineOperationKind("RESUME");
+ public static final TimelineOperationKind SUSPEND = new
TimelineOperationKind("SUSPEND");
+ public static final TimelineOperationKind ABORT = new
TimelineOperationKind("ABORT");
+
+ TimelineOperationKind(String name) {
+ this.name = name;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TimelineOperationKind that = (TimelineOperationKind) o;
+
+ if (!name.equals(that.name)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+ }
+
+ class TimelineOperation {
public TimelineOperationKind operationKind;
Runnable operationRunnable;
@@ -210,7 +242,7 @@
public final void run() {
TridentConfig.PulseSource pulseSource =
TridentConfig.getInstance()
.getPulseSource();
- lastIterationTimeStamp = System.nanoTime();
+ lastIterationTimeStamp = Utils.nanoTime();
while (true) {
pulseSource.waitUntilNextPulse();
updateTimelines();
@@ -332,16 +364,13 @@
// run operations in the order that
they have arrived
// from the application after the
last CANCEL
for (TimelineOperation operation :
toRun) {
- switch
(operation.operationKind) {
- case PLAY:
+ if
(TimelineOperationKind.PLAY.equals(operation.operationKind)) {
operation.operationRunnable.run();
- break;
- case SUSPEND:
+ } else if
(TimelineOperationKind.SUSPEND.equals(operation.operationKind)) {
this.suspendTimeline(waiting);
- break;
- case RESUME:
+ } else if
(TimelineOperationKind.RESUME.equals(operation.operationKind)) {
this.resumeTimeline(waiting);
- }
+ }
}
}
}
@@ -354,11 +383,11 @@
if ((this.runningTimelines.size() == 0)
&& (this.runningScenarios.size() ==
0)) {
- this.lastIterationTimeStamp =
System.nanoTime();
+ this.lastIterationTimeStamp =
Utils.nanoTime();
return;
}
- long passedSinceLastIteration = (System.nanoTime() -
this.lastIterationTimeStamp) / 1000000;
+ long passedSinceLastIteration = (Utils.nanoTime() -
this.lastIterationTimeStamp) / 1000000;
if (passedSinceLastIteration < 0) {
// ???
passedSinceLastIteration = 0;
@@ -374,10 +403,10 @@
for (Iterator<Timeline> itTimeline =
this.runningTimelines
.iterator(); itTimeline.hasNext();) {
Timeline timeline = itTimeline.next();
- if (timeline.getState() ==
TimelineState.SUSPENDED)
+ if
(TimelineState.SUSPENDED.equals(timeline.getState()))
continue;
- if (timeline.getState() ==
TimelineState.READY) {
+ if
(TimelineState.READY.equals(timeline.getState())) {
if ((timeline.timeUntilPlay -
passedSinceLastIteration) > 0) {
// still needs to wait in the
READY state
timeline.timeUntilPlay -=
passedSinceLastIteration;
@@ -400,9 +429,8 @@
}
// Component comp = entry.getKey();
- // at this point, the timeline must be playing
- switch (timeline.getState()) {
- case PLAYING_FORWARD:
+ // at this point, the timeline must be playing
+ if
(TimelineState.PLAYING_FORWARD.equals(timeline.getState())) {
timeline.durationFraction =
timeline.durationFraction
+ (float)
passedSinceLastIteration
/ (float)
timeline.duration;
@@ -450,8 +478,7 @@
itTimeline.remove();
}
}
- break;
- case PLAYING_REVERSE:
+ } else if
(TimelineState.PLAYING_REVERSE.equals(timeline.getState())) {
timeline.durationFraction =
timeline.durationFraction
- (float)
passedSinceLastIteration
/ (float)
timeline.duration;
@@ -497,8 +524,7 @@
itTimeline.remove();
}
}
- break;
- default:
+ } else {
throw new
IllegalStateException("Timeline cannot be in "
+ timeline.getState()
+ " state");
}
@@ -556,7 +582,7 @@
// System.err.println("Periodic update done");
// this.nothingTracked =
(this.runningTimelines.size() == 0);
- this.lastIterationTimeStamp = System.nanoTime();
+ this.lastIterationTimeStamp = Utils.nanoTime();
}
}
@@ -571,8 +597,8 @@
boolean shouldRunOnUIThread = false;
Class<?> clazz = timeline.callback.getClass();
while ((clazz != null) &&
!shouldRunOnUIThread) {
- shouldRunOnUIThread = clazz
-
.isAnnotationPresent(RunOnUIThread.class);
+ shouldRunOnUIThread =
+
RunOnUIThread.class.isAssignableFrom(clazz);
clazz = clazz.getSuperclass();
}
if (shouldRunOnUIThread &&
(timeline.uiToolkitHandler != null)) {
@@ -611,8 +637,8 @@
boolean shouldRunOnUIThread = false;
Class<?> clazz = timeline.callback.getClass();
while ((clazz != null) &&
!shouldRunOnUIThread) {
- shouldRunOnUIThread = clazz
-
.isAnnotationPresent(RunOnUIThread.class);
+ shouldRunOnUIThread =
+
RunOnUIThread.class.isAssignableFrom(clazz);
clazz = clazz.getSuperclass();
}
if (shouldRunOnUIThread &&
(timeline.uiToolkitHandler != null)) {
Index: src/org/pushingpixels/trident/callback/RunOnUIThread.java
===================================================================
--- src/org/pushingpixels/trident/callback/RunOnUIThread.java (rvision 82)
+++ src/org/pushingpixels/trident/callback/RunOnUIThread.java (copie de
travail)
@@ -36,7 +36,7 @@
*
* @author Kirill Grouchnikov
*/
-@Target(ElementType.TYPE)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface RunOnUIThread {
+//@Target(ElementType.TYPE)
+//@Retention(RetentionPolicy.RUNTIME)
+public interface RunOnUIThread {
}
\ No newline at end of file
Index:
src/org/pushingpixels/trident/callback/UIThreadTimelineCallbackAdapter.java
===================================================================
---
src/org/pushingpixels/trident/callback/UIThreadTimelineCallbackAdapter.java
(rvision 82)
+++
src/org/pushingpixels/trident/callback/UIThreadTimelineCallbackAdapter.java
(copie de travail)
@@ -35,6 +35,6 @@
*
* @author Kirill Grouchnikov
*/
-@RunOnUIThread
-public class UIThreadTimelineCallbackAdapter extends TimelineCallbackAdapter
{
+//@RunOnUIThread
+public class UIThreadTimelineCallbackAdapter extends TimelineCallbackAdapter
implements RunOnUIThread {
}
Index: src/org/pushingpixels/trident/swt/SWTRepaintCallback.java
===================================================================
--- src/org/pushingpixels/trident/swt/SWTRepaintCallback.java (rvision 82)
+++ src/org/pushingpixels/trident/swt/SWTRepaintCallback.java (copie de
travail)
@@ -35,8 +35,8 @@
import org.pushingpixels.trident.callback.RunOnUIThread;
import org.pushingpixels.trident.callback.TimelineCallback;
-@RunOnUIThread
-public class SWTRepaintCallback implements TimelineCallback {
+//@RunOnUIThread
+public class SWTRepaintCallback implements TimelineCallback, RunOnUIThread {
private Control control;
private Rectangle rect;
Index: build.properties
===================================================================
--- build.properties (rvision 82)
+++ build.properties (copie de travail)
@@ -1,6 +1,6 @@
jdk.home=C:/Program Files/Java/jdk1.6.0_16
-javac.source=1.6
-javac.target=1.6
+javac.source=1.5
+javac.target=1.5
javac.encoding=ISO-8859-1
javac.debug=on
javac.generate.no.warnings=off
Index: build.xml
===================================================================
--- build.xml (rvision 82)
+++ build.xml (copie de travail)
@@ -58,7 +58,8 @@
<path id="trident.module.classpath">
<path refid="${module.jdk.classpath.trident}" />
- </path>
+ <fileset dir="lib" includes="*.jar"/>
+ </path>
<patternset id="excluded.from.module.trident" />
@@ -187,5 +188,16 @@
<target name="clean" depends="clean.module.trident"
description="cleanup all" />
- <target name="all" depends="timestamp, init, clean,
compile.module.trident, jar, compile.module.trident.test, jar-tst"
description="build all" />
-</project>
\ No newline at end of file
+ <target name="all" depends="timestamp, init, clean,
compile.module.trident, jar, compile.module.trident.test, jar-tst,
retroweaver" description="build all" />
+
+ <target name="retroweaver">
+ <taskdef name="retroweaver"
classname="net.sourceforge.retroweaver.ant.RetroWeaverTask">
+ <classpath>
+ <fileset dir="lib" includes="*.jar"/>
+ </classpath>
+ </taskdef>
+ <retroweaver inputjar="drop/trident.jar"
outputjar="drop/trident-java14.jar" stripAttributes="true" verbose="true"/>
+
+ </target>
+
+</project>
|
Java 1.4 & Java 5 compatibility |
Emmanuel Bourg | 10/06/2009 |
| Kirill Grouchnikov | 10/08/2009 | |
| Emmanuel Bourg | 10/08/2009 | |
| Kirill Grouchnikov | 10/09/2009 |






