json/src/main/java/org/netbeans/html/json/impl/JSON.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 16 Dec 2013 16:59:43 +0100
branchnetbeans
changeset 362 92fb71afdc0e
parent 358 json/src/main/java/org/apidesign/html/json/impl/JSON.java@80702021b851
child 365 5c93ad8c7a15
permissions -rw-r--r--
Moving implementation classes into org.netbeans.html namespace
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package org.netbeans.html.json.impl;
    44 
    45 import java.io.IOException;
    46 import java.io.InputStream;
    47 import java.util.HashMap;
    48 import java.util.Map;
    49 import net.java.html.BrwsrCtx;
    50 import org.apidesign.html.context.spi.Contexts;
    51 import org.apidesign.html.json.spi.FunctionBinding;
    52 import org.apidesign.html.json.spi.JSONCall;
    53 import org.apidesign.html.json.spi.PropertyBinding;
    54 import org.apidesign.html.json.spi.Technology;
    55 import org.apidesign.html.json.spi.Transfer;
    56 import org.apidesign.html.json.spi.WSTransfer;
    57 
    58 /**
    59  *
    60  * @author Jaroslav Tulach <jtulach@netbeans.org>
    61  */
    62 public final class JSON {
    63     private JSON() {
    64     }
    65 
    66     static Technology<?> findTechnology(BrwsrCtx c) {
    67         Technology<?> t = Contexts.find(c, Technology.class);
    68         return t == null ? EmptyTech.EMPTY : t;
    69     }
    70 
    71     static Transfer findTransfer(BrwsrCtx c) {
    72         Transfer t = Contexts.find(c, Transfer.class);
    73         return t == null ? EmptyTech.EMPTY : t;
    74     }
    75 
    76     static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
    77         WSTransfer<?> t = Contexts.find(c, WSTransfer.class);
    78         return t == null ? EmptyTech.EMPTY : t;
    79     }
    80     
    81     public static void runInBrowser(BrwsrCtx c, Runnable runnable) {
    82         findTechnology(c).runSafe(runnable);
    83     }
    84     
    85     public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
    86         Transfer t = findTransfer(c);
    87         t.extract(value, props, values);
    88     }
    89     
    90     private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
    91         if (prop == null) return obj;
    92         
    93         String[] arr = { prop };
    94         Object[] val = { null };
    95         extract(c, obj, arr, val);
    96         return val[0];
    97     }
    98 
    99     public static Object toJSON(Object value) {
   100         if (value == null) {
   101             return "null";
   102         }
   103         if (value instanceof Enum) {
   104             value = value.toString();
   105         }
   106         if (value instanceof String) {
   107             String s = (String)value;
   108             int len = s.length();
   109             StringBuilder sb = new StringBuilder(len + 10);
   110             sb.append('"');
   111             for (int i = 0; i < len; i++) {
   112                 char ch = s.charAt(i);
   113                 switch (ch) {
   114                     case '\"': sb.append("\\\""); break;
   115                     case '\n': sb.append("\\n"); break;
   116                     case '\r': sb.append("\\r"); break;
   117                     case '\t': sb.append("\\t"); break;
   118                     case '\\': sb.append("\\\\"); break;
   119                     default: sb.append(ch);
   120                 }
   121             }
   122             sb.append('"');
   123             return sb.toString();
   124         }
   125         return value.toString();
   126     }
   127 
   128     public static String toString(BrwsrCtx c, Object obj, String prop) {
   129         obj = getProperty(c, obj, prop);
   130         return obj instanceof String ? (String)obj : null;
   131     }
   132     public static Number toNumber(BrwsrCtx c, Object obj, String prop) {
   133         obj = getProperty(c, obj, prop);
   134         if (!(obj instanceof Number)) {
   135             obj = Double.NaN;
   136         }
   137         return (Number)obj;
   138     }
   139     public static <M> M toModel(BrwsrCtx c, Class<M> aClass, Object data, Object object) {
   140         Technology<?> t = findTechnology(c);
   141         Object o = t.toModel(aClass, data);
   142         return aClass.cast(o);
   143     }
   144     
   145     public static boolean isSame(int a, int b) {
   146         return a == b;
   147     }
   148     
   149     public static boolean isSame(double a, double b) {
   150         return a == b;
   151     }
   152     
   153     public static boolean isSame(Object a, Object b) {
   154         if (a == b) {
   155             return true;
   156         }
   157         if (a == null || b == null) {
   158             return false;
   159         }
   160         return a.equals(b);
   161     }
   162     
   163     public static int hashPlus(Object o, int h) {
   164         return o == null ? h : h ^ o.hashCode();
   165     }
   166 
   167     public static <T> T extractValue(Class<T> type, Object val) {
   168         if (Number.class.isAssignableFrom(type)) {
   169             val = numberValue(val);
   170         }
   171         if (Boolean.class == type) {
   172             val = boolValue(val);
   173         }
   174         if (String.class == type) {
   175             val = stringValue(val);
   176         }
   177         if (Character.class == type) {
   178             val = charValue(val);
   179         }
   180         if (Integer.class == type) {
   181             val = val instanceof Number ? ((Number)val).intValue() : 0;
   182         }
   183         if (Long.class == type) {
   184             val = val instanceof Number  ? ((Number)val).longValue() : 0;
   185         }
   186         if (Short.class == type) {
   187             val = val instanceof Number ? ((Number)val).shortValue() : 0;
   188         }
   189         if (Byte.class == type) {
   190             val = val instanceof Number ? ((Number)val).byteValue() : 0;
   191         }        
   192         if (Double.class == type) {
   193             val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
   194         }
   195         if (Float.class == type) {
   196             val = val instanceof Number ? ((Number)val).floatValue() : Float.NaN;
   197         }
   198         return type.cast(val);
   199     }
   200     
   201     protected static boolean isNumeric(Object val) {
   202         return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
   203     }
   204     
   205     public static String stringValue(Object val) {
   206         if (val instanceof Boolean) {
   207             return ((Boolean)val ? "true" : "false");
   208         }
   209         if (isNumeric(val)) {
   210             return Long.toString(((Number)val).longValue());
   211         }
   212         if (val instanceof Float) {
   213             return Float.toString((Float)val);
   214         }
   215         if (val instanceof Double) {
   216             return Double.toString((Double)val);
   217         }
   218         return (String)val;
   219     }
   220 
   221     public static Number numberValue(Object val) {
   222         if (val instanceof String) {
   223             try {
   224                 return Double.valueOf((String)val);
   225             } catch (NumberFormatException ex) {
   226                 return Double.NaN;
   227             }
   228         }
   229         if (val instanceof Boolean) {
   230             return (Boolean)val ? 1 : 0;
   231         }
   232         return (Number)val;
   233     }
   234 
   235     public static Character charValue(Object val) {
   236         if (val instanceof Number) {
   237             return Character.toChars(numberValue(val).intValue())[0];
   238         }
   239         if (val instanceof Boolean) {
   240             return (Boolean)val ? (char)1 : (char)0;
   241         }
   242         if (val instanceof String) {
   243             String s = (String)val;
   244             return s.isEmpty() ? (char)0 : s.charAt(0);
   245         }
   246         return (Character)val;
   247     }
   248     
   249     public static Boolean boolValue(Object val) {
   250         if (val instanceof String) {
   251             return Boolean.parseBoolean((String)val);
   252         }
   253         if (val instanceof Number) {
   254             return numberValue(val).doubleValue() != 0.0;
   255         }
   256     
   257         return Boolean.TRUE.equals(val);
   258     }
   259     
   260     public static void loadJSON(
   261         BrwsrCtx c, RcvrJSON callback,
   262         String urlBefore, String urlAfter, String method,
   263         Object data
   264     ) {
   265         JSONCall call = PropertyBindingAccessor.createCall(c, callback, urlBefore, urlAfter, method, data);
   266         Transfer t = findTransfer(c);
   267         t.loadJSON(call);
   268     }
   269     public static WS openWS(
   270         BrwsrCtx c, RcvrJSON r, String url, Object data
   271     ) {
   272         WS ws = WSImpl.create(findWSTransfer(c), r);
   273         ws.send(c, url, data);
   274         return ws;
   275     }
   276     
   277     public static abstract class WS {
   278         private WS() {
   279         }
   280         
   281         public abstract void send(BrwsrCtx ctx, String url, Object model);
   282     }
   283     
   284     private static final class WSImpl<Socket> extends WS {
   285 
   286         private final WSTransfer<Socket> trans;
   287         private final RcvrJSON rcvr;
   288         private Socket socket;
   289         private String prevURL;
   290 
   291         private WSImpl(WSTransfer<Socket> trans, RcvrJSON rcvr) {
   292             this.trans = trans;
   293             this.rcvr = rcvr;
   294         }
   295         
   296         static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
   297             return new WSImpl<Socket>(t, r);
   298         }
   299 
   300         @Override
   301         public void send(BrwsrCtx ctx, String url, Object data) {
   302             Socket s = socket;
   303             if (s == null) {
   304                 if (data != null) {
   305                     throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
   306                 }
   307                 JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, url, null, "WebSocket", null);
   308                 socket = trans.open(url, call);
   309                 prevURL = url;
   310                 return;
   311             }
   312             if (data == null) {
   313                 trans.close(s);
   314                 socket = null;
   315                 return;
   316             }
   317             if (!prevURL.equals(url)) {
   318                 throw new IllegalStateException(
   319                     "Can't call to different URL " + url + " was: " + prevURL + "!"
   320                     + " Close the socket by calling it will null data first!"
   321                 );
   322             }
   323             JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, prevURL, null, "WebSocket", data);
   324             trans.send(s, call);
   325         }
   326         
   327     }
   328     
   329     private static final Map<Class,FromJSON<?>> froms;
   330     static {
   331         Map<Class,FromJSON<?>> m = new HashMap<Class,FromJSON<?>>();
   332         froms = m;
   333     }
   334     public static void register(FromJSON<?> from) {
   335         froms.put(from.factoryFor(), from);
   336     }
   337     
   338     public static boolean isModel(Class<?> clazz) {
   339         return findFrom(clazz) != null; 
   340     }
   341     
   342     private static FromJSON<?> findFrom(Class<?> clazz) {
   343         for (int i = 0; i < 2; i++) {
   344             FromJSON<?> from = froms.get(clazz);
   345             if (from == null) {
   346                 initClass(clazz);
   347             } else {
   348                 return from;
   349             }
   350         }
   351         return null;
   352     }
   353     
   354     public static <Model> Model bindTo(Model model, BrwsrCtx c) {
   355         FromJSON<?> from = findFrom(model.getClass());
   356         if (from == null) {
   357             throw new IllegalArgumentException();
   358         }
   359         return (Model) from.cloneTo(model, c);
   360     }
   361     
   362     public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data) 
   363     throws IOException {
   364         Transfer tr = findTransfer(c);
   365         return read(c, modelClazz, tr.toJSON((InputStream)data));
   366     }
   367     public static <T> T read(BrwsrCtx c, Class<T> modelClazz, Object data) {
   368         if (data == null) {
   369             return null;
   370         }
   371         if (modelClazz == String.class) {
   372             return modelClazz.cast(data.toString());
   373         }
   374         for (int i = 0; i < 2; i++) {
   375             FromJSON<?> from = froms.get(modelClazz);
   376             if (from == null) {
   377                 initClass(modelClazz);
   378             } else {
   379                 return modelClazz.cast(from.read(c, data));
   380             }
   381         }
   382         throw new NullPointerException();
   383     }
   384     static void initClass(Class<?> modelClazz) {
   385         try {
   386             // try to resolve the class
   387             ClassLoader l;
   388             try {
   389                 l = modelClazz.getClassLoader();
   390             } catch (SecurityException ex) {
   391                 l = null;
   392             }
   393             if (l != null) {
   394                 Class.forName(modelClazz.getName(), true, l);
   395             }
   396             modelClazz.newInstance();
   397         } catch (Exception ex) {
   398             // ignore and try again
   399         }
   400     }
   401     
   402     private static final class EmptyTech
   403     implements Technology<Object>, Transfer, WSTransfer<Void> {
   404         private static final EmptyTech EMPTY = new EmptyTech();
   405 
   406         @Override
   407         public Object wrapModel(Object model) {
   408             return model;
   409         }
   410 
   411         @Override
   412         public void valueHasMutated(Object data, String propertyName) {
   413         }
   414 
   415         @Override
   416         public void bind(PropertyBinding b, Object model, Object data) {
   417         }
   418 
   419         @Override
   420         public void expose(FunctionBinding fb, Object model, Object d) {
   421         }
   422 
   423         @Override
   424         public void applyBindings(Object data) {
   425         }
   426 
   427         @Override
   428         public Object wrapArray(Object[] arr) {
   429             return arr;
   430         }
   431 
   432         @Override
   433         public void extract(Object obj, String[] props, Object[] values) {
   434             for (int i = 0; i < values.length; i++) {
   435                 values[i] = null;
   436             }
   437         }
   438 
   439         @Override
   440         public void loadJSON(JSONCall call) {
   441             call.notifyError(new UnsupportedOperationException());
   442         }
   443 
   444         @Override
   445         public <M> M toModel(Class<M> modelClass, Object data) {
   446             return modelClass.cast(data);
   447         }
   448 
   449         @Override
   450         public Object toJSON(InputStream is) throws IOException {
   451             throw new IOException("Not supported");
   452         }
   453 
   454         @Override
   455         public synchronized void runSafe(Runnable r) {
   456             r.run();
   457         }
   458 
   459         @Override
   460         public Void open(String url, JSONCall onReply) {
   461             onReply.notifyError(new UnsupportedOperationException("WebSockets not supported!"));
   462             return null;
   463         }
   464 
   465         @Override
   466         public void send(Void socket, JSONCall data) {
   467         }
   468 
   469         @Override
   470         public void close(Void socket) {
   471         }
   472     }
   473     
   474 }