All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.emul.java.util.InternalJsStringMap Maven / Gradle / Ivy

There is a newer version: 2.7.0.vaadin7
Show newest version
/*
 * Copyright 2014 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 java.util;

import static com.google.gwt.core.shared.impl.InternalPreconditions.checkElement;
import static com.google.gwt.core.shared.impl.InternalPreconditions.checkState;

import com.google.gwt.core.client.JavaScriptObject;

import java.util.Map.Entry;

/**
 * A simple wrapper around JavaScriptObject to provide {@link java.util.Map}-like semantics where
 * the key type is string.
 * 

* Implementation notes: *

* String keys are mapped to their values via a JS associative map. String keys could collide with * intrinsic properties (like watch, constructor). To avoid that; {@link InternalJsStringMap}) uses * {@code Object.create(null)} so it doesn't inherit any properties. For legacy browsers where * {@code Object.create} is not available, {@link InternalJsStringMapLegacy} prepends each key with * a ':' while storing. */ class InternalJsStringMap { /** * String map implementation for browsers that doesn't support Object.create (IE8, FF3.6). */ static class InternalJsStringMapLegacy extends InternalJsStringMap { @Override native JavaScriptObject createMap() /*-{ return {}; }-*/; @Override public V get(String key) { return super.get(normalize(key)); } @Override public V put(String key, V value) { return super.put(normalize(key), value); } @Override public V remove(String key) { return super.remove(normalize(key)); } private String normalize(String key) { return ':' + key; } @Override public native boolean containsValue(Object value) /*-{ var map = this.@InternalJsStringMap::backingMap; for (var key in map) { // only keys that start with a colon ':' count if (key.charCodeAt(0) == 58) { var entryValue = map[key]; if (this.@InternalJsStringMap::equalsBridge(*)(value, entryValue)) { return true; } } } return false; }-*/; @Override public native Iterator> entries() /*-{ var list = this.@InternalJsStringMapLegacy::newEntryList()(); for (var key in this.@InternalJsStringMap::backingMap) { // only keys that start with a colon ':' count if (key.charCodeAt(0) == 58) { var entry = this.@InternalJsStringMap::newMapEntry(*)(key.substring(1)); list.@ArrayList::add(Ljava/lang/Object;)(entry); } } return list.@ArrayList::iterator()(); }-*/; /** * Returns a custom ArrayList so that we could intercept removal to forward into our map. */ private ArrayList> newEntryList() { return new ArrayList>() { @Override public Entry remove(int index) { Entry removed = super.remove(index); InternalJsStringMapLegacy.this.remove((String) removed.getKey()); return removed; } }; } } /** * String map implementation for browsers that includes __proto__ while iterating keys (i.e. * FireFox). See the Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=837630. */ static class InternalJsStringMapWithKeysWorkaround extends InternalJsStringMap { private static final String PROTO = "__proto__"; @Override public boolean containsValue(Object value) { V protoValue = get(PROTO); if (!isUndefined(protoValue) && equalsBridge(value, protoValue)) { return true; } return super.containsValue(value); } @Override protected String[] keys() { String[] keys = super.keys(); if (contains(PROTO)) { keys[keys.length] = PROTO; // safe in webmode } return keys; } } private final JavaScriptObject backingMap = createMap(); AbstractHashMap host; native JavaScriptObject createMap() /*-{ return Object.create(null); }-*/; public final boolean contains(String key) { return !isUndefined(get(key)); } public V get(String key) { return at(key); } public V put(String key, V value) { V oldValue = at(key); if (isUndefined(oldValue)) { host.elementAdded(); } set(key, toNullIfUndefined(value)); return oldValue; } public V remove(String key) { V value = at(key); if (!isUndefined(value)) { delete(key); host.elementRemoved(); } return value; } private native V at(String key) /*-{ return this.@InternalJsStringMap::backingMap[key]; }-*/; private native void set(String key, V value) /*-{ this.@InternalJsStringMap::backingMap[key] = value; }-*/; private native void delete(String key) /*-{ delete this.@InternalJsStringMap::backingMap[key]; }-*/; public native boolean containsValue(Object value) /*-{ var map = this.@InternalJsStringMap::backingMap; for (var key in map) { if (this.@InternalJsStringMap::equalsBridge(*)(value, map[key])) { return true; } } return false; }-*/; public Iterator> entries() { final String[] keys = keys(); return new Iterator>() { int i = 0, last = -1; @Override public boolean hasNext() { return i < keys.length; } @Override public Entry next() { checkElement(hasNext()); return newMapEntry(keys[last = i++]); } @Override public void remove() { checkState(last != -1); InternalJsStringMap.this.remove(keys[last]); last = -1; } }; } protected native String[] keys() /*-{ return Object.getOwnPropertyNames(this.@InternalJsStringMap::backingMap); }-*/; protected final Entry newMapEntry(final String key) { return new AbstractMapEntry() { @Override public K getKey() { return (K) key; } @Override public V getValue() { return get(key); } @Override public V setValue(V object) { return put(key, object); } }; } /** * 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. */ protected final boolean equalsBridge(Object value1, Object value2) { return host.equals(value1, value2); } private static T toNullIfUndefined(T value) { return isUndefined(value) ? null : value; } private static native boolean isUndefined(Object value) /*-{ return value === undefined; }-*/; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy