com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable.java Source code

Java tutorial

Introduction

Here is the source code for com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable.java

Source

/*
 * Copyright 2010 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.web.bindery.autobean.gwt.client.impl;

import com.google.web.bindery.autobean.shared.Splittable;
import com.google.web.bindery.autobean.shared.impl.HasSplittable;
import com.google.web.bindery.autobean.shared.impl.StringQuoter;
import com.google.gwt.core.client.GwtScriptOnly;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsonUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Implements the EntityCodex.Splittable interface using a raw JavaScriptObject.
 * <p>
 * A string value represented by a JsoSplittable can't use the string object
 * directly, since {@code String.prototype} is overridden, so instead a
 * temporary wrapper object is used to encapsulate the string data.
 */
@GwtScriptOnly
public final class JsoSplittable extends JavaScriptObject implements Splittable, HasSplittable {
    private static boolean stringifyFastTested;
    private static boolean stringifyFastResult;

    public static native JsoSplittable create() /*-{
                                                return {};
                                                }-*/;

    public static Splittable create(boolean value) {
        return create0(value);
    }

    public static Splittable create(double value) {
        return create0(value);
    }

    public static Splittable create(String value) {
        return create0(value);
    }

    public static native JsoSplittable createIndexed() /*-{
                                                       return [];
                                                       }-*/;

    public static native JsoSplittable nullValue() /*-{
                                                   return null;
                                                   }-*/;

    private static native Splittable create0(boolean object) /*-{
                                                             return Boolean(object);
                                                             }-*/;

    private static native Splittable create0(double object) /*-{
                                                            return Number(object);
                                                            }-*/;

    private static native Splittable create0(String object) /*-{
                                                            return {
                                                            __s : object
                                                            };
                                                            }-*/;

    private static native boolean isUnwrappedString(JavaScriptObject obj) /*-{
                                                                          return Object.prototype.toString.call(obj) == '[object String]';
                                                                          }-*/;

    private static boolean stringifyFastSupported() {
        if (stringifyFastTested) {
            return stringifyFastResult;
        }
        stringifyFastTested = true;
        return stringifyFastResult = stringifyFastSupported0();
    }

    /**
     * Test that the JSON api is available and that it does not add function
     * objects to the output. The test for function objects is for old versions of
     * Safari.
     */
    private static native boolean stringifyFastSupported0() /*-{
                                                            return $wnd.JSON && $wnd.JSON.stringify && $wnd.JSON.stringify({
                                                            b : function() {
                                                            }
                                                            }) == '{}';
                                                            }-*/;

    protected JsoSplittable() {
    };

    public native boolean asBoolean() /*-{
                                      return this == true;
                                      }-*/;

    public native double asNumber() /*-{
                                    return Number(this);
                                    }-*/;

    public void assign(Splittable parent, int index) {
        if (isString()) {
            assign0(parent, index, asString());
        } else {
            assign0(parent, index, this);
        }
    }

    public void assign(Splittable parent, String index) {
        if (isString()) {
            assign0(parent, index, asString());
        } else {
            assign0(parent, index, this);
        }
    }

    public native String asString() /*-{
                                    return this.__s;
                                    }-*/;

    public Splittable deepCopy() {
        return StringQuoter.split(getPayload());
    }

    public JsoSplittable get(int index) {
        return getRaw(index);
    }

    public JsoSplittable get(String key) {
        return getRaw(key);
    }

    public String getPayload() {
        if (isString()) {
            return JsonUtils.escapeValue(asString());
        }
        if (stringifyFastSupported()) {
            return stringifyFast();
        }
        return stringifySlow();
    }

    public List<String> getPropertyKeys() {
        List<String> toReturn = new ArrayList<String>();
        getPropertyKeys0(toReturn);
        return Collections.unmodifiableList(toReturn);
    }

    public native Object getReified(String key) /*-{
                                                return this.__reified && this.__reified[':' + key];
                                                }-*/;

    public Splittable getSplittable() {
        return this;
    }

    public native boolean isBoolean() /*-{
                                      return Object.prototype.toString.call(this) == '[object Boolean]';
                                      }-*/;

    public native boolean isFunction() /*-{
                                       return Object.prototype.toString.call(this) == '[object Function]';
                                       }-*/;

    public native boolean isIndexed() /*-{
                                      return Object.prototype.toString.call(this) == '[object Array]';
                                      }-*/;

    public boolean isKeyed() {
        return this != NULL && !isString() && !isIndexed() && !isFunction();
    }

    public native boolean isNull(int index) /*-{
                                            return this[index] == null;
                                            }-*/;

    public native boolean isNull(String key) /*-{
                                             return this[key] == null;
                                             }-*/;

    public native boolean isNumber() /*-{
                                     return Object.prototype.toString.call(this) == '[object Number]';
                                     }-*/;

    public native boolean isReified(String key) /*-{
                                                return !!(this.__reified && this.__reified.hasOwnProperty(':' + key));
                                                }-*/;

    /**
     * Returns whether or not the current object is a string-carrier.
     */
    public native boolean isString() /*-{
                                     return this && this.__s != null;
                                     }-*/;

    public native boolean isUndefined(String key) /*-{
                                                  return this[key] === undefined;
                                                  }-*/;

    public native void setReified(String key, Object object) /*-{
                                                             // Use a function object so native JSON.stringify will ignore
                                                             (this.__reified || (this.__reified = function() {
                                                             }))[':' + key] = object;
                                                             }-*/;

    public native void setSize(int size) /*-{
                                         this.length = size;
                                         }-*/;

    public native int size() /*-{
                             return this.length;
                             }-*/;

    private native void assign0(Splittable parent, int index, Splittable value) /*-{
                                                                                parent[index] = value;
                                                                                }-*/;

    private native void assign0(Splittable parent, int index, String value) /*-{
                                                                            parent[index] = value;
                                                                            }-*/;

    private native void assign0(Splittable parent, String index, Splittable value) /*-{
                                                                                   parent[index] = value;
                                                                                   }-*/;

    private native void assign0(Splittable parent, String index, String value) /*-{
                                                                               parent[index] = value;
                                                                               }-*/;

    private native void getPropertyKeys0(List<String> list) /*-{
                                                            for (key in this) {
                                                            if (this.hasOwnProperty(key)) {
                                                            list.@java.util.List::add(Ljava/lang/Object;)(key);
                                                            }
                                                            }
                                                            }-*/;

    private native JsoSplittable getRaw(int index) /*-{
                                                   _ = this[index];
                                                   if (_ == null) {
                                                   return null;
                                                   }
                                                   if (@com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable::isUnwrappedString(*)(_)) {
                                                   return @com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable::create(Ljava/lang/String;)(_);
                                                   }
                                                   return Object(_);
                                                   }-*/;

    private native JsoSplittable getRaw(String index) /*-{
                                                      _ = this[index];
                                                      if (_ == null) {
                                                      return null;
                                                      }
                                                      if (@com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable::isUnwrappedString(*)(_)) {
                                                      return @com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable::create(Ljava/lang/String;)(_);
                                                      }
                                                      return Object(_);
                                                      }-*/;

    /**
     * The test for {@code $H} removes the key in the emitted JSON, however making
     * a similar test for {@code __reified} causes the key to be emitted with an
     * explicit {@code null} value.
     */
    private native String stringifyFast() /*-{
                                          return $wnd.JSON.stringify(this, function(key, value) {
                                          if (key == "$H") {
                                          return;
                                          }
                                          return value;
                                          });
                                          }-*/;

    private String stringifySlow() {
        StringBuilder sb = new StringBuilder();
        stringifySlow(sb);
        return sb.toString();
    }

    private void stringifySlow(StringBuilder sb) {
        if (this == NULL) {
            sb.append("null");
            return;
        }
        if (isBoolean()) {
            sb.append(asBoolean());
            return;
        }
        if (isNumber()) {
            sb.append(asNumber());
            return;
        }
        if (isString()) {
            sb.append(JsonUtils.escapeValue(asString()));
            return;
        }
        if (isIndexed()) {
            sb.append("[");
            for (int i = 0, j = size(); i < j; i++) {
                if (i > 0) {
                    sb.append(",");
                }
                get(i).stringifySlow(sb);
            }
            sb.append("]");
            return;
        }

        sb.append("{");
        boolean needsComma = false;
        for (String key : getPropertyKeys()) {
            if (needsComma) {
                sb.append(",");
            } else {
                needsComma = true;
            }
            JsoSplittable value = get(key);
            if (!value.isFunction()) {
                if ("$H".equals(key)) {
                    // Ignore hashcode
                    continue;
                }
                sb.append(JsonUtils.escapeValue(key));
                sb.append(":");
                value.stringifySlow(sb);
            }
        }
        sb.append("}");
    }
}