Actual communication with JavaScript
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 09 Jul 2013 09:09:13 +0200
changeset 181c09d00808cab
parent 180 a34250189f24
child 182 99c8145d1e4f
Actual communication with JavaScript
geo/pom.xml
geo/src/main/java/net/java/html/geo/Position.java
geo/src/main/java/org/apidesign/html/geo/impl/JsG.java
geo/src/test/java/net/java/html/geo/OnLocationTest.java
     1.1 --- a/geo/pom.xml	Tue Jul 09 08:08:36 2013 +0200
     1.2 +++ b/geo/pom.xml	Tue Jul 09 09:09:13 2013 +0200
     1.3 @@ -37,5 +37,11 @@
     1.4        <artifactId>org-openide-util-lookup</artifactId>
     1.5        <type>jar</type>
     1.6      </dependency>
     1.7 +    <dependency>
     1.8 +      <groupId>org.apidesign.html</groupId>
     1.9 +      <artifactId>net.java.html.boot</artifactId>
    1.10 +      <version>0.4-SNAPSHOT</version>
    1.11 +      <type>jar</type>
    1.12 +    </dependency>
    1.13    </dependencies>
    1.14  </project>
     2.1 --- a/geo/src/main/java/net/java/html/geo/Position.java	Tue Jul 09 08:08:36 2013 +0200
     2.2 +++ b/geo/src/main/java/net/java/html/geo/Position.java	Tue Jul 09 09:09:13 2013 +0200
     2.3 @@ -20,32 +20,82 @@
     2.4   */
     2.5  package net.java.html.geo;
     2.6  
     2.7 -/**
     2.8 +import java.util.logging.Level;
     2.9 +import java.util.logging.Logger;
    2.10 +import org.apidesign.html.geo.impl.JsG;
    2.11 +
    2.12 +/** Class that represents a geolocation position provided as a callback
    2.13 + * to {@link Handle#onLocation(net.java.html.geo.Position)} method. The
    2.14 + * class getters mimic closely the structure of the position object as
    2.15 + * specified by <a href="http://www.w3.org/TR/2012/PR­geolocation­API­20120510/">
    2.16 + * W3C's Geolocation API</a>.
    2.17   *
    2.18   * @author Jaroslav Tulach <jtulach@netbeans.org>
    2.19   */
    2.20  public final class Position {
    2.21 -    Position() {
    2.22 +    static final Logger LOG = Logger.getLogger(Position.class.getName());
    2.23 +    private final long timestamp;
    2.24 +    private final Coordinates coords;
    2.25 +
    2.26 +    Position(Object position) {
    2.27 +        Object obj = JsG.get(position, "timestamp");
    2.28 +        timestamp = obj instanceof Number ? ((Number)obj).longValue() : 0L;
    2.29 +        coords = new Coordinates(JsG.get(position, "coords"));
    2.30 +    }
    2.31 +    
    2.32 +    /** The actual location of the position.
    2.33 +     * @return non-null coordinates
    2.34 +     */
    2.35 +    public Coordinates getCoords() {
    2.36 +        return coords;
    2.37 +    }
    2.38 +    
    2.39 +    /** The time when the position has been recorded.
    2.40 +     * @return time in milliseconds since era (e.g. Jan 1, 1970).
    2.41 +     */
    2.42 +    public long getTimestamp() {
    2.43 +        return timestamp;
    2.44      }
    2.45  
    2.46 -    public Coordinates getCoords() {
    2.47 -        return null;
    2.48 -    }
    2.49 -    
    2.50 -    public long getTimestamp() {
    2.51 -        return 0L;
    2.52 -    }
    2.53 -    
    2.54 +    /** Actual location of a {@link Position}. 
    2.55 +     *  Mimics closely <a href="http://www.w3.org/TR/2012/PR­geolocation­API­20120510/">
    2.56 +     * W3C's Geolocation API</a>.
    2.57 +     */
    2.58      public static final class Coordinates {
    2.59 -        private double latitude;
    2.60 -        private double longitude;
    2.61 -        private double accuracy;
    2.62 +        private final Object data;
    2.63  
    2.64 -        private Double altitude;
    2.65 -        private Double altitudeAccuracy;
    2.66 -        private Double heading;
    2.67 -        private Double speed;
    2.68 -    }
    2.69 +        Coordinates(Object data) {
    2.70 +            this.data = data;
    2.71 +        }
    2.72 +        
    2.73 +        public double getLatitude() {
    2.74 +            return ((Number)JsG.get(data, "latitude")).doubleValue(); // NOI18N
    2.75 +        }
    2.76 +
    2.77 +        public double getLongitude() {
    2.78 +            return ((Number)JsG.get(data, "longitude")).doubleValue(); // NOI18N
    2.79 +        }
    2.80 +
    2.81 +        public double getAccuracy() {
    2.82 +            return ((Number)JsG.get(data, "accuracy")).doubleValue(); // NOI18N
    2.83 +        }
    2.84 +        
    2.85 +        public Double getAltitude() {
    2.86 +            return (Double)JsG.get(data, "altitude"); // NOI18N
    2.87 +        }
    2.88 +        
    2.89 +        public Double getAltitudeAccuracy() {
    2.90 +            return (Double)JsG.get(data, "altitudeAccuracy"); // NOI18N
    2.91 +        }
    2.92 +        
    2.93 +        public Double getHeading() {
    2.94 +            return (Double)JsG.get(data, "heading"); // NOI18N
    2.95 +        }
    2.96 +        
    2.97 +        public Double getSpeed() {
    2.98 +            return (Double)JsG.get(data, "speed"); // NOI18N
    2.99 +        }
   2.100 +    } // end of Coordinates
   2.101  
   2.102      /** Rather than subclassing this class directly consider using {@link OnLocation}
   2.103       * annotation. Such annotation will generate a subclass for you automatically
   2.104 @@ -53,11 +103,11 @@
   2.105       * which can be used to obtain instance of this class.
   2.106       */
   2.107      public static abstract class Handle {
   2.108 -
   2.109          private final boolean oneTime;
   2.110          private boolean enableHighAccuracy;
   2.111          private long timeout;
   2.112          private long maximumAge;
   2.113 +        volatile JsH handle;
   2.114  
   2.115          /** Creates new instance of this handle.
   2.116           * 
   2.117 @@ -87,7 +137,7 @@
   2.118           * W3C's Geolocation API</a>. By default the mode is disabled.
   2.119           * @param enable <code>true</code> or <code>false</code>
   2.120           */
   2.121 -        public void setHighAccuracy(boolean enable) {
   2.122 +        public final void setHighAccuracy(boolean enable) {
   2.123              this.enableHighAccuracy = enable;
   2.124          }
   2.125  
   2.126 @@ -95,7 +145,7 @@
   2.127           * By default infinity.
   2.128           * @param timeout time in milliseconds to wait for a result.
   2.129           */
   2.130 -        public void setTimeout(long timeout) {
   2.131 +        public final void setTimeout(long timeout) {
   2.132              this.timeout = timeout;
   2.133          }
   2.134  
   2.135 @@ -103,20 +153,74 @@
   2.136           * returned. By default maximum age is set to zero.
   2.137           * @param age time in milliseconds of acceptable cached results
   2.138           */
   2.139 -        public void setMaximumAge(long age) {
   2.140 +        public final void setMaximumAge(long age) {
   2.141              this.maximumAge = age;
   2.142          }
   2.143  
   2.144          /** Initializes the <em>query</em> or <em>watch</em> request(s) and
   2.145 -         * returns immediately.
   2.146 +         * returns immediately. Has no effect if the query has already been
   2.147 +         * started.
   2.148           */
   2.149 -        public void start() {
   2.150 +        public final void start() {
   2.151 +            if (handle != null) {
   2.152 +                return;
   2.153 +            }
   2.154 +            handle = new JsH();
   2.155 +            handle.start();
   2.156          }
   2.157  
   2.158          /** Stops all pending requests. After this call no further callbacks
   2.159 -         * can be obtained.
   2.160 +         * can be obtained. Does nothing if no query or watch was in progress.
   2.161           */
   2.162 -        public void stop() {
   2.163 +        public final void stop() {
   2.164 +            JsH h = handle;
   2.165 +            if (h == null) {
   2.166 +                return;
   2.167 +            }
   2.168 +            handle = null;
   2.169 +            h.stop();
   2.170 +        }
   2.171 +
   2.172 +        private final class JsH extends JsG {
   2.173 +            long watch;
   2.174 +            
   2.175 +            @Override
   2.176 +            public void onLocation(Object position) {
   2.177 +                if (handle != this) {
   2.178 +                    return;
   2.179 +                }
   2.180 +                if (oneTime) {
   2.181 +                    stop();
   2.182 +                }
   2.183 +                try {
   2.184 +                    Handle.this.onLocation(new Position(position));
   2.185 +                } catch (Throwable ex) {
   2.186 +                    LOG.log(Level.SEVERE, null, ex);
   2.187 +                }
   2.188 +            }
   2.189 +
   2.190 +            @Override
   2.191 +            public void onError(Object error) {
   2.192 +                if (handle != this) {
   2.193 +                    return;
   2.194 +                }
   2.195 +                if (oneTime) {
   2.196 +                    stop();
   2.197 +                }
   2.198 +                try {
   2.199 +                    Handle.this.onError(new Exception());
   2.200 +                } catch (Throwable ex) {
   2.201 +                    LOG.log(Level.SEVERE, null, ex);
   2.202 +                }
   2.203 +            }
   2.204 +
   2.205 +            final void start() {
   2.206 +                watch = start(oneTime, enableHighAccuracy, timeout, maximumAge);
   2.207 +            }
   2.208 +
   2.209 +            protected final void stop() {
   2.210 +                super.stop(watch);
   2.211 +            }
   2.212          }
   2.213      }
   2.214  }
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/geo/src/main/java/org/apidesign/html/geo/impl/JsG.java	Tue Jul 09 09:09:13 2013 +0200
     3.3 @@ -0,0 +1,80 @@
     3.4 +/**
     3.5 + * HTML via Java(tm) Language Bindings
     3.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     3.7 + *
     3.8 + * This program is free software: you can redistribute it and/or modify
     3.9 + * it under the terms of the GNU General Public License as published by
    3.10 + * the Free Software Foundation, version 2 of the License.
    3.11 + *
    3.12 + * This program is distributed in the hope that it will be useful,
    3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.15 + * GNU General Public License for more details. apidesign.org
    3.16 + * designates this particular file as subject to the
    3.17 + * "Classpath" exception as provided by apidesign.org
    3.18 + * in the License file that accompanied this code.
    3.19 + *
    3.20 + * You should have received a copy of the GNU General Public License
    3.21 + * along with this program. Look for COPYING file in the top folder.
    3.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    3.23 + */
    3.24 +package org.apidesign.html.geo.impl;
    3.25 +
    3.26 +import net.java.html.js.JavaScriptBody;
    3.27 +
    3.28 +/** Implementation class to deal with browser's <code>navigator.geolocation</code> 
    3.29 + * object.
    3.30 + *
    3.31 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.32 + */
    3.33 +public abstract class JsG {
    3.34 +    protected JsG() {
    3.35 +        if (!getClass().getName().equals("net.java.html.geo.Position$Handle$JsH")) {
    3.36 +            throw new IllegalStateException();
    3.37 +        }
    3.38 +    }
    3.39 +    
    3.40 +    public abstract void onLocation(Object position);
    3.41 +    public abstract void onError(Object error);
    3.42 +
    3.43 +    @JavaScriptBody(
    3.44 +        args = { "onlyOnce", "enableHighAccuracy", "timeout", "maximumAge" }, 
    3.45 +        javacall = true, 
    3.46 +        body = 
    3.47 +        "var self = this;" +
    3.48 +        "var ok = function (position) {" +
    3.49 +        "  self.@org.apidesign.html.geo.impl.GeoImpl::onLocation(Ljava/lang/Object;)(position);" +
    3.50 +        "};" +
    3.51 +        "var fail = function (error) {" +
    3.52 +        "  self.@org.apidesign.html.geo.impl.GeoImpl::onError(Ljava/lang/Object;)(error);" +
    3.53 +        "};" +
    3.54 +        "var options = {};" +
    3.55 +        "options.enableHighAccuracy = enableHighAccuracy;" +
    3.56 +        "if (timeout >= 0) options.timeout = timeout;" +
    3.57 +        "if (maximumAge >= 0) options.maximumAge = maximumAge;" +
    3.58 +        "if (onlyOnce) {" +
    3.59 +        "  navigator.geolocation.getCurrentPosition(ok, fail);" +
    3.60 +        "  return 0;" +
    3.61 +        "} else {" +
    3.62 +        "  return navigator.geolocation.watchPosition(ok, fail);" +
    3.63 +        "}"
    3.64 +    )
    3.65 +    protected long start(
    3.66 +        boolean onlyOnce, 
    3.67 +        boolean enableHighAccuracy,
    3.68 +        long timeout,
    3.69 +        long maximumAge
    3.70 +    ) {
    3.71 +        return -1;
    3.72 +    }
    3.73 +    
    3.74 +    @JavaScriptBody(args = { "watch" }, body = "navigator.geolocation.clearWatch(watch);")
    3.75 +    protected void stop(long watch) {
    3.76 +    }
    3.77 +
    3.78 +    @JavaScriptBody(args = { "self", "property" }, body = "return self[property];")
    3.79 +    public static Object get(Object self, String property) {
    3.80 +        return null;
    3.81 +    }
    3.82 +
    3.83 +}
     4.1 --- a/geo/src/test/java/net/java/html/geo/OnLocationTest.java	Tue Jul 09 08:08:36 2013 +0200
     4.2 +++ b/geo/src/test/java/net/java/html/geo/OnLocationTest.java	Tue Jul 09 09:09:13 2013 +0200
     4.3 @@ -44,7 +44,7 @@
     4.4      @Test public void onLocationHandleCallback() throws Throwable {
     4.5          net.java.html.geo.Position.Handle h = OnLocationHandle.createQuery();
     4.6          cnt = 0;
     4.7 -        h.onLocation(new Position());
     4.8 +        h.onLocation(new Position(null));
     4.9          assertEquals(cnt, 1, "The callback has been made");
    4.10      }
    4.11  
    4.12 @@ -82,7 +82,7 @@
    4.13      @Test public void onInstanceCallback() throws Throwable {
    4.14          OnLocationTest t = new OnLocationTest();
    4.15          net.java.html.geo.Position.Handle h = InstanceHandle.createWatch(t);
    4.16 -        h.onLocation(new Position());
    4.17 +        h.onLocation(new Position(null));
    4.18          assertEquals(t.instCnt, 1, "One callback made");
    4.19      }
    4.20