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  (r‚vision 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 (r‚vision 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        
(r‚vision 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 (r‚vision 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   (r‚vision 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   (r‚vision 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 
(r‚vision 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   (r‚vision 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    (r‚vision 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   (r‚vision 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

Re: Java 1.4 & Java 5 compatibility

Kirill Grouchnikov 10/08/2009

Re: Java 1.4 & Java 5 compatibility

Emmanuel Bourg 10/08/2009

Re: Java 1.4 & Java 5 compatibility

Kirill Grouchnikov 10/09/2009
  • Mysql
  • Glassfish
  • Jruby
  • Rails
  • Nblogo
Terms of Use; Privacy Policy;
© 2010, Oracle Corporation and/or its affiliates
(revision 20100312.9442df5)
 
 
loading
Please Confirm