/* * 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.JsArrayString; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /** * A Map from String to Object implementation over a Javascript Object. * * @param <T> The type of the map values * * @author Danilo Reinert */ public class LightMap<T> implements Map<String, T> { JsMap<T> innerMap = JsMap.create(); @Override public int size() { return innerMap.size(); } @Override public boolean isEmpty() { return innerMap.size() <= 0; } @Override public boolean containsKey(Object o) { checkNotNull(o); assert o instanceof String : "Key should be of type String"; String key = (String) o; return innerMap.contains(key); } @Override public boolean containsValue(Object o) { checkNotNull(o); JsArrayString keys = innerMap.keys(); for (int i = 0; i < keys.length(); i++) { String key = keys.get(i); T t = innerMap.get(key); if (o.equals(t)) return true; } return false; } @Override public T get(Object o) { checkNotNull(o); assert o instanceof String : "Key should be of type String"; String key = (String) o; return innerMap.get(key); } @Override public T put(String s, T t) { checkNotNull(s); checkNotNull(t); T old = innerMap.get(s); innerMap.put(s, t); return old; } @Override public T remove(Object o) { checkNotNull(o); assert o instanceof String : "Key should be of type String"; final String key = (String) o; T t = innerMap.get(key); innerMap.remove(key); return t; } @Override public void putAll(Map<? extends String, ? extends T> map) { final Set<? extends String> keySet = map.keySet(); for (String s : keySet) { put(s, map.get(s)); } } @Override public void clear() { innerMap.clear(); } @Override public Set<String> keySet() { return new KeySet<>(this); } @Override public Collection<T> values() { return new ValueArray<>(this); } @Override public Set<Entry<String, T>> entrySet() { return new EntrySet<>(this); } private void checkNotNull(Object o) { if (o == null) throw new NullPointerException("This map does not support null values"); } private static class JsEntry<T> implements Entry<String, T> { private final LightMap<T> map; private final String key; private JsEntry(LightMap<T> map, String key) { this.map = map; this.key = key; } @Override public String getKey() { return key; } @Override public T getValue() { return map.get(key); } @Override public T setValue(T t) { return map.put(key, t); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Entry)) { return false; } final Entry<String, T> entry = (Entry<String, T>) o; if (!key.equals(entry.getKey())) { return false; } if (!getValue().equals(entry.getValue())) { return false; } return true; } @Override public int hashCode() { int result = getValue().hashCode(); result = 31 * result + key.hashCode(); return result; } } private static class KeySet<T> extends JsArraySet<String> { private final LightMap<T> map; private KeySet(LightMap<T> map) { super(map.innerMap.keys()); this.map = map; } @Override public void clear() { super.clear(); map.clear(); } @Override public boolean remove(Object o) { return super.remove(o) && map.remove(o) != null; } @Override public boolean add(String s) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection<? extends String> strings) { throw new UnsupportedOperationException(); } } private static class ValueArray<T> extends JsArrayList<T> { private final LightMap<T> map; private ValueArray(LightMap<T> map) { super(map.innerMap.values()); this.map = map; } @Override public void clear() { super.clear(); map.clear(); } @Override @SuppressWarnings("unchecked") public boolean remove(Object o) { return super.remove(o) && map.remove(map.innerMap.keyOf((T) o)) != null; } @Override public boolean add(T t) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection<? extends T> c) { throw new UnsupportedOperationException(); } @Override public boolean addAll(int i, Collection<? extends T> c) { throw new UnsupportedOperationException(); } } private static class EntrySet<T> extends AbstractSet<Entry<String, T>> { private final LightMap<T> map; private EntrySet(LightMap<T> map) { this.map = map; } @Override public void clear() { map.clear(); } @Override public int size() { return map.size(); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public boolean contains(Object o) { Entry<String, T> entry = (Entry<String, T>) o; return map.containsKey(entry.getKey()); } @Override public Iterator<Entry<String, T>> iterator() { return new Itr(); } @Override public boolean add(Entry<String, T> tJsEntry) { throw new UnsupportedOperationException(); } @Override public boolean remove(Object o) { Entry<String, T> entry = (Entry<String, T>) o; return map.remove(entry.getKey()) != null; } @Override public boolean addAll(Collection<? extends Entry<String, T>> entries) { throw new UnsupportedOperationException(); } private class Itr implements Iterator<Entry<String, T>> { private JsArrayString keys = map.innerMap.keys(); private int cursor; // index of next element to return private int lastRet = -1; // index of last element returned; -1 if no such public boolean hasNext() { return cursor != keys.length(); } @Override public Entry<String, T> next() { int i = cursor; if (i >= keys.length()) throw new NoSuchElementException(); cursor = i + 1; return new JsEntry<>(map, keys.get(lastRet = i)); } @Override public void remove() { if (lastRet < 0) throw new IllegalStateException(); map.remove(keys.get(lastRet)); cursor = lastRet; lastRet = -1; } } } }