/*
* Copyright 2014 Grow Bit
*
* 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 org.turbogwt.core.collections;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
/**
* Map of String to Object implemented on a JavaScriptObject.
*
* @param <T> Type of mapped values
*
* @author Danilo Reinert
*/
public class JsMap<T> extends JavaScriptObject {
protected JsMap() {
}
public static native <T> JsMap<T> create() /*-{
var o = {};
Object.defineProperties(o, {__props__: {enumerable: false, writable: true, value: []}});
return o;
}-*/;
public final native void clear() /*-{
// Although not fast, it's safer than creating new objects
for (var key in this) delete this[key];
Object.defineProperties(this, {__props__: {enumerable: false, writable: true, value: []}});
}-*/;
public final native T get(String key) /*-{
return this[key];
}-*/;
public final void put(String key, T value) {
checkNotNull(key);
checkNotNull(value);
set0(key, value);
}
public final native boolean contains(String key) /*-{
return (key in this);
}-*/;
public final native void remove(String key) /*-{
if (!this.__props__)
Object.defineProperties(this, {__props__: {enumerable: false, writable: true, value: Object.keys(this)}});
var i = this.__props__.indexOf(key);
if (~i) {
this.__props__.splice(i, 1);
delete this[key];
}
}-*/;
public final native int size() /*-{
if (!this.__props__)
Object.defineProperties(this, {__props__: {enumerable: false, writable: true, value: Object.keys(this)}});
return this.__props__.length;
}-*/;
public final String keyOf(T t) {
checkNotNull(t);
return keyOf0(t);
}
public final native JsArrayString keys() /*-{
return this.__props__;
}-*/;
public final native JsArray<T> values() /*-{
var values = [];
for (var key in this) values.push(this[key]);
return values;
}-*/;
private void checkNotNull(Object o) {
if (o == null) throw new NullPointerException("This map does not accept null keys or values.");
}
private native String keyOf0(T t) /*-{
for (var key in this) {
// CHECKSTYLE:OFF
if (@org.turbogwt.core.collections.JsMap::equalsBridge(Ljava/lang/Object;Ljava/lang/Object;)(t, this[key]))
return key;
// CHECKSTYLE:ON
}
return null;
}-*/;
private native void set0(String key, T value) /*-{
if (!this[key]) {
if (!this.__props__)
Object.defineProperties(this, {__props__: {enumerable: false, writable: true,
value: Object.keys(this)}});
this.__props__.push(key);
}
this[key] = value;
}-*/;
/**
* Bridge method from JSNI that keeps us from having to make polymorphic calls
* in JSNI. By putting the polymorphism in Java code, the compiler can do a
* better job of optimizing in most cases. (Copied from GWT source)
*/
@SuppressWarnings("unused")
private static boolean equalsBridge(Object o1, Object o2) {
return o1.equals(o2);
}
}