geo/src/main/java/net/java/html/geo/Position.java
author Jaroslav Tulach <jtulach@netbeans.org>
Sat, 02 Aug 2014 12:59:31 +0200
changeset 790 30f20d9c0986
parent 727 66f03ad8f00f
child 842 ebb2428bb7e1
permissions -rw-r--r--
Fixing Javadoc to succeed on JDK8
jaroslav@172
     1
/**
jaroslav@358
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jaroslav@172
     3
 *
jaroslav@551
     4
 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
jaroslav@172
     5
 *
jaroslav@358
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
jaroslav@358
     7
 * Other names may be trademarks of their respective owners.
jaroslav@172
     8
 *
jaroslav@358
     9
 * The contents of this file are subject to the terms of either the GNU
jaroslav@358
    10
 * General Public License Version 2 only ("GPL") or the Common
jaroslav@358
    11
 * Development and Distribution License("CDDL") (collectively, the
jaroslav@358
    12
 * "License"). You may not use this file except in compliance with the
jaroslav@358
    13
 * License. You can obtain a copy of the License at
jaroslav@358
    14
 * http://www.netbeans.org/cddl-gplv2.html
jaroslav@358
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
jaroslav@358
    16
 * specific language governing permissions and limitations under the
jaroslav@358
    17
 * License.  When distributing the software, include this License Header
jaroslav@358
    18
 * Notice in each file and include the License file at
jaroslav@358
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
jaroslav@358
    20
 * particular file as subject to the "Classpath" exception as provided
jaroslav@358
    21
 * by Oracle in the GPL Version 2 section of the License file that
jaroslav@358
    22
 * accompanied this code. If applicable, add the following below the
jaroslav@358
    23
 * License Header, with the fields enclosed by brackets [] replaced by
jaroslav@358
    24
 * your own identifying information:
jaroslav@358
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
jaroslav@358
    26
 *
jaroslav@358
    27
 * Contributor(s):
jaroslav@358
    28
 *
jaroslav@358
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
jaroslav@551
    30
 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
jaroslav@358
    31
 *
jaroslav@358
    32
 * If you wish your version of this file to be governed by only the CDDL
jaroslav@358
    33
 * or only the GPL Version 2, indicate your decision by adding
jaroslav@358
    34
 * "[Contributor] elects to include this software in this distribution
jaroslav@358
    35
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
jaroslav@358
    36
 * single choice of license, a recipient has the option to distribute
jaroslav@358
    37
 * your version of this file under either the CDDL, the GPL Version 2 or
jaroslav@358
    38
 * to extend the choice of license to its licensees as provided above.
jaroslav@358
    39
 * However, if you add GPL Version 2 code and therefore, elected the GPL
jaroslav@358
    40
 * Version 2 license, then the option applies only if the new code is
jaroslav@358
    41
 * made subject to such option by the copyright holder.
jaroslav@172
    42
 */
jaroslav@172
    43
package net.java.html.geo;
jaroslav@172
    44
jaroslav@181
    45
import java.util.logging.Level;
jaroslav@181
    46
import java.util.logging.Logger;
jaroslav@362
    47
import org.netbeans.html.geo.impl.JsG;
jaroslav@181
    48
jaroslav@181
    49
/** Class that represents a geolocation position provided as a callback
jaroslav@181
    50
 * to {@link Handle#onLocation(net.java.html.geo.Position)} method. The
jaroslav@181
    51
 * class getters mimic closely the structure of the position object as
jaroslav@628
    52
 * specified by <a href="http://www.w3.org/TR/geolocation-API/">
jaroslav@181
    53
 * W3C's Geolocation API</a>.
jaroslav@172
    54
 *
jtulach@790
    55
 * @author Jaroslav Tulach
jaroslav@172
    56
 */
jaroslav@172
    57
public final class Position {
jaroslav@181
    58
    static final Logger LOG = Logger.getLogger(Position.class.getName());
jaroslav@181
    59
    private final long timestamp;
jaroslav@181
    60
    private final Coordinates coords;
jaroslav@181
    61
jaroslav@181
    62
    Position(Object position) {
jaroslav@181
    63
        Object obj = JsG.get(position, "timestamp");
jaroslav@181
    64
        timestamp = obj instanceof Number ? ((Number)obj).longValue() : 0L;
jaroslav@181
    65
        coords = new Coordinates(JsG.get(position, "coords"));
jaroslav@181
    66
    }
jaroslav@181
    67
    
jaroslav@181
    68
    /** The actual location of the position.
jaroslav@181
    69
     * @return non-null coordinates
jaroslav@181
    70
     */
jaroslav@181
    71
    public Coordinates getCoords() {
jaroslav@181
    72
        return coords;
jaroslav@181
    73
    }
jaroslav@181
    74
    
jaroslav@181
    75
    /** The time when the position has been recorded.
jaroslav@181
    76
     * @return time in milliseconds since era (e.g. Jan 1, 1970).
jaroslav@181
    77
     */
jaroslav@181
    78
    public long getTimestamp() {
jaroslav@181
    79
        return timestamp;
jaroslav@172
    80
    }
jaroslav@172
    81
jaroslav@181
    82
    /** Actual location of a {@link Position}. 
jaroslav@628
    83
     *  Mimics closely <a href="http://www.w3.org/TR/geolocation-API/">
jaroslav@181
    84
     * W3C's Geolocation API</a>.
jaroslav@181
    85
     */
jaroslav@172
    86
    public static final class Coordinates {
jaroslav@181
    87
        private final Object data;
jaroslav@172
    88
jaroslav@181
    89
        Coordinates(Object data) {
jaroslav@181
    90
            this.data = data;
jaroslav@181
    91
        }
jaroslav@181
    92
        
jaroslav@712
    93
        /**
jaroslav@712
    94
         * @return geographic coordinate specified in decimal degrees.
jaroslav@712
    95
         */
jaroslav@181
    96
        public double getLatitude() {
jaroslav@181
    97
            return ((Number)JsG.get(data, "latitude")).doubleValue(); // NOI18N
jaroslav@181
    98
        }
jaroslav@181
    99
jaroslav@712
   100
        /**
jaroslav@712
   101
         * @return geographic coordinate specified in decimal degrees.
jaroslav@712
   102
         */
jaroslav@181
   103
        public double getLongitude() {
jaroslav@181
   104
            return ((Number)JsG.get(data, "longitude")).doubleValue(); // NOI18N
jaroslav@712
   105
        } 
jaroslav@181
   106
jaroslav@712
   107
        /**
jaroslav@712
   108
         * The accuracy attribute denotes the accuracy level of the latitude 
jaroslav@712
   109
         * and longitude coordinates. It is specified in meters. 
jaroslav@712
   110
         * The value of the accuracy attribute must be a non-negative number.
jaroslav@712
   111
         * 
jaroslav@712
   112
         * @return accuracy in meters
jaroslav@712
   113
         */
jaroslav@181
   114
        public double getAccuracy() {
jaroslav@181
   115
            return ((Number)JsG.get(data, "accuracy")).doubleValue(); // NOI18N
jaroslav@181
   116
        }
jaroslav@181
   117
        
jaroslav@712
   118
        /** Denotes the height of the position, specified in meters above the ellipsoid. 
jaroslav@712
   119
         * If the implementation cannot provide altitude information, 
jaroslav@712
   120
         * the value of this attribute must be null.
jaroslav@712
   121
         * @return value in meters, may return null, if the information is not available 
jaroslav@712
   122
         */
jaroslav@181
   123
        public Double getAltitude() {
jaroslav@181
   124
            return (Double)JsG.get(data, "altitude"); // NOI18N
jaroslav@181
   125
        }
jaroslav@181
   126
        
jaroslav@712
   127
        /**  The altitude accuracy is specified in meters. 
jaroslav@712
   128
         * If the implementation cannot provide altitude information, 
jaroslav@712
   129
         * the value of this attribute must be null. Otherwise, the value 
jaroslav@712
   130
         * must be a non-negative real number.
jaroslav@712
   131
         * @return value in meters; may return null, if the information is not available 
jaroslav@712
   132
         */
jaroslav@181
   133
        public Double getAltitudeAccuracy() {
jaroslav@181
   134
            return (Double)JsG.get(data, "altitudeAccuracy"); // NOI18N
jaroslav@181
   135
        }
jaroslav@181
   136
        
jaroslav@628
   137
        /** @return may return null, if the information is not available */
jaroslav@181
   138
        public Double getHeading() {
jaroslav@181
   139
            return (Double)JsG.get(data, "heading"); // NOI18N
jaroslav@181
   140
        }
jaroslav@181
   141
        
jaroslav@628
   142
        /** @return may return null, if the information is not available */
jaroslav@181
   143
        public Double getSpeed() {
jaroslav@181
   144
            return (Double)JsG.get(data, "speed"); // NOI18N
jaroslav@181
   145
        }
jaroslav@181
   146
    } // end of Coordinates
jaroslav@175
   147
jaroslav@179
   148
    /** Rather than subclassing this class directly consider using {@link OnLocation}
jaroslav@179
   149
     * annotation. Such annotation will generate a subclass for you automatically
jaroslav@179
   150
     * with two static methods <code>createQuery</code> and <code>createWatch</code>
jaroslav@179
   151
     * which can be used to obtain instance of this class.
jaroslav@175
   152
     */
jaroslav@175
   153
    public static abstract class Handle {
jaroslav@175
   154
        private final boolean oneTime;
jaroslav@175
   155
        private boolean enableHighAccuracy;
jaroslav@175
   156
        private long timeout;
jaroslav@175
   157
        private long maximumAge;
jaroslav@181
   158
        volatile JsH handle;
jaroslav@175
   159
jaroslav@179
   160
        /** Creates new instance of this handle.
jaroslav@179
   161
         * 
jaroslav@179
   162
         * @param oneTime <code>true</code> if the handle represents one time 
jaroslav@179
   163
         *   <em>query</em>. <code>false</code> if it represents a <em>watch</em>
jaroslav@179
   164
         */
jaroslav@175
   165
        protected Handle(boolean oneTime) {
jaroslav@175
   166
            super();
jaroslav@175
   167
            this.oneTime = oneTime;
jaroslav@175
   168
        }
jaroslav@175
   169
jaroslav@179
   170
        /** Callback from the implementation when a (new) position has been
jaroslav@179
   171
         * received and identified
jaroslav@179
   172
         * @param p the position
jaroslav@179
   173
         * @throws Throwable if an exception is thrown, it will be logged by the system
jaroslav@179
   174
         */
jaroslav@178
   175
        protected abstract void onLocation(Position p) throws Throwable;
jaroslav@175
   176
jaroslav@179
   177
        /** Callback when an error occurs.
jaroslav@179
   178
         * @param ex the exception describing what went wrong
jaroslav@179
   179
         * @throws Throwable if an exception is thrown, it will be logged by the system
jaroslav@179
   180
         */
jaroslav@179
   181
        protected abstract void onError(Exception ex) throws Throwable;
jaroslav@193
   182
        
jaroslav@193
   183
        /** Check whether the location API is supported.
jaroslav@193
   184
         * @return true, if one can call {@link #start}.
jaroslav@193
   185
         */
jaroslav@193
   186
        public final boolean isSupported() {
jaroslav@193
   187
            return JsG.hasGeolocation();
jaroslav@193
   188
        }
jaroslav@175
   189
jaroslav@179
   190
        /** Turns on high accuracy mode as specified by the 
jaroslav@179
   191
         * <a href="http://www.w3.org/TR/2012/PR­geolocation­API­20120510/">
jaroslav@179
   192
         * W3C's Geolocation API</a>. By default the mode is disabled.
jaroslav@179
   193
         * @param enable <code>true</code> or <code>false</code>
jaroslav@179
   194
         */
jaroslav@181
   195
        public final void setHighAccuracy(boolean enable) {
jaroslav@175
   196
            this.enableHighAccuracy = enable;
jaroslav@175
   197
        }
jaroslav@175
   198
jaroslav@179
   199
        /** The amount of milliseconds to wait for a result.
jaroslav@179
   200
         * By default infinity.
jaroslav@179
   201
         * @param timeout time in milliseconds to wait for a result.
jaroslav@179
   202
         */
jaroslav@181
   203
        public final void setTimeout(long timeout) {
jaroslav@175
   204
            this.timeout = timeout;
jaroslav@175
   205
        }
jaroslav@175
   206
jaroslav@179
   207
        /** Sets maximum age of cached results which are acceptable to be
jaroslav@179
   208
         * returned. By default maximum age is set to zero.
jaroslav@179
   209
         * @param age time in milliseconds of acceptable cached results
jaroslav@179
   210
         */
jaroslav@181
   211
        public final void setMaximumAge(long age) {
jaroslav@175
   212
            this.maximumAge = age;
jaroslav@175
   213
        }
jaroslav@193
   214
        
jaroslav@179
   215
        /** Initializes the <em>query</em> or <em>watch</em> request(s) and
jaroslav@181
   216
         * returns immediately. Has no effect if the query has already been
jaroslav@193
   217
         * started. If a problem appears while starting the system,
jaroslav@193
   218
         * it is immediately reported via the {@link #onError(java.lang.Exception)}
jaroslav@193
   219
         * callback. For example, if the {@link #isSupported()} method
jaroslav@193
   220
         * returns <code>false</code> an IllegalStateException is created
jaroslav@193
   221
         * and sent to the {@link #onError(java.lang.Exception) callback} method.
jaroslav@179
   222
         */
jaroslav@181
   223
        public final void start() {
jaroslav@181
   224
            if (handle != null) {
jaroslav@181
   225
                return;
jaroslav@181
   226
            }
jaroslav@181
   227
            handle = new JsH();
jaroslav@193
   228
            try {
jaroslav@193
   229
                if (!isSupported()) {
jaroslav@193
   230
                    throw new IllegalStateException("geolocation API not supported");
jaroslav@193
   231
                }
jaroslav@193
   232
                handle.start();
jaroslav@193
   233
            } catch (Exception ex) {
jaroslav@193
   234
                try {
jaroslav@193
   235
                    onError(ex);
jaroslav@193
   236
                } catch (Throwable thr) {
jaroslav@193
   237
                    LOG.log(Level.INFO, "Problems delivering onError report", thr);
jaroslav@193
   238
                }
jaroslav@193
   239
            }
jaroslav@175
   240
        }
jaroslav@175
   241
jaroslav@179
   242
        /** Stops all pending requests. After this call no further callbacks
jaroslav@181
   243
         * can be obtained. Does nothing if no query or watch was in progress.
jaroslav@179
   244
         */
jaroslav@181
   245
        public final void stop() {
jaroslav@181
   246
            JsH h = handle;
jaroslav@181
   247
            if (h == null) {
jaroslav@181
   248
                return;
jaroslav@181
   249
            }
jaroslav@181
   250
            handle = null;
jaroslav@181
   251
            h.stop();
jaroslav@181
   252
        }
jaroslav@181
   253
jaroslav@181
   254
        private final class JsH extends JsG {
jaroslav@181
   255
            long watch;
jaroslav@181
   256
            
jaroslav@181
   257
            @Override
jaroslav@181
   258
            public void onLocation(Object position) {
jaroslav@181
   259
                if (handle != this) {
jaroslav@181
   260
                    return;
jaroslav@181
   261
                }
jaroslav@181
   262
                if (oneTime) {
jaroslav@181
   263
                    stop();
jaroslav@181
   264
                }
jaroslav@181
   265
                try {
jaroslav@181
   266
                    Handle.this.onLocation(new Position(position));
jaroslav@181
   267
                } catch (Throwable ex) {
jaroslav@181
   268
                    LOG.log(Level.SEVERE, null, ex);
jaroslav@181
   269
                }
jaroslav@181
   270
            }
jaroslav@181
   271
jaroslav@181
   272
            @Override
jtulach@724
   273
            public void onError(final String message, int code) {
jaroslav@181
   274
                if (handle != this) {
jaroslav@181
   275
                    return;
jaroslav@181
   276
                }
jaroslav@181
   277
                if (oneTime) {
jaroslav@181
   278
                    stop();
jaroslav@181
   279
                }
jaroslav@181
   280
                try {
jtulach@724
   281
                    final Exception err = new Exception(message + " errno: " + code) {
jtulach@724
   282
                        @Override
jtulach@724
   283
                        public String getLocalizedMessage() {
jtulach@724
   284
                            return message;
jtulach@724
   285
                        }
jtulach@724
   286
                    };
jtulach@724
   287
                    Handle.this.onError(err);
jaroslav@181
   288
                } catch (Throwable ex) {
jaroslav@181
   289
                    LOG.log(Level.SEVERE, null, ex);
jaroslav@181
   290
                }
jaroslav@181
   291
            }
jaroslav@181
   292
jaroslav@181
   293
            final void start() {
jaroslav@181
   294
                watch = start(oneTime, enableHighAccuracy, timeout, maximumAge);
jaroslav@181
   295
            }
jaroslav@181
   296
jaroslav@181
   297
            protected final void stop() {
jaroslav@181
   298
                super.stop(watch);
jaroslav@181
   299
            }
jaroslav@175
   300
        }
jaroslav@175
   301
    }
jaroslav@172
   302
}