/* * Copyright 2011 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.shared.impl; import com.google.web.bindery.autobean.shared.Splittable; import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder; import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * A Map implementation for regular JSON maps with value-type keys. * * @param <K> the key type * @param <V> the value type */ public class SplittableSimpleMap<K, V> implements Map<K, V>, HasSplittable { private final Splittable data; private final Coder keyCoder; private final EncodeState state; private final Coder valueCoder; /** * Don't hang the reified data from {@link #data} since we can't tell the * __reified field from the actual data. */ private Splittable reified = StringQuoter.createSplittable(); public SplittableSimpleMap(Splittable data, Coder keyCoder, Coder valueCoder, EncodeState state) { this.data = data; this.keyCoder = keyCoder; this.state = state; this.valueCoder = valueCoder; } public void clear() { for (String key : data.getPropertyKeys()) { Splittable.NULL.assign(data, key); reified.setReified(key, null); } } public boolean containsKey(Object key) { String encodedKey = encodedKey(key); return !data.isUndefined(encodedKey) || reified.isReified(encodedKey); } public boolean containsValue(Object value) { return values().contains(value); } public Set<java.util.Map.Entry<K, V>> entrySet() { return new AbstractSet<Map.Entry<K, V>>() { final List<String> keys = data.getPropertyKeys(); @Override public Iterator<java.util.Map.Entry<K, V>> iterator() { return new Iterator<Map.Entry<K, V>>() { Iterator<String> keyIterator = keys.iterator(); String encodedKey; public boolean hasNext() { return keyIterator.hasNext(); } public java.util.Map.Entry<K, V> next() { encodedKey = keyIterator.next(); return new Map.Entry<K, V>() { @SuppressWarnings("unchecked") final K key = (K) keyCoder.decode(state, StringQuoter.split(StringQuoter .quote(encodedKey))); @SuppressWarnings("unchecked") final V value = (V) valueCoder.decode(state, data.get(encodedKey)); public K getKey() { return key; } public V getValue() { return value; } public V setValue(V newValue) { return put(key, newValue); } }; } public void remove() { Splittable.NULL.assign(data, encodedKey); reified.setReified(encodedKey, null); } }; } @Override public int size() { return keys.size(); } }; } public V get(Object key) { String encodedKey = encodedKey(key); return getRaw(encodedKey); } public Splittable getSplittable() { return data; } public boolean isEmpty() { return data.getPropertyKeys().isEmpty(); } public Set<K> keySet() { return new AbstractSet<K>() { final List<String> keys = data.getPropertyKeys(); @Override public Iterator<K> iterator() { return new Iterator<K>() { final Iterator<String> it = keys.iterator(); String lastEncodedKey; public boolean hasNext() { return it.hasNext(); } public K next() { lastEncodedKey = it.next(); @SuppressWarnings("unchecked") K toReturn = (K) keyCoder.decode(state, StringQuoter.split(StringQuoter.quote(lastEncodedKey))); return toReturn; } public void remove() { Splittable.NULL.assign(data, lastEncodedKey); reified.setReified(lastEncodedKey, null); } }; } @Override public int size() { return keys.size(); } }; } public V put(K key, V value) { V toReturn = get(key); String encodedKey = encodedKey(key); reified.setReified(encodedKey, value); Splittable encodedValue = valueCoder.extractSplittable(state, value); if (encodedValue == null) { // External datastructure reified.setReified(AbstractAutoBean.UNSPLITTABLE_VALUES_KEY, true); } else { encodedValue.assign(data, encodedKey); } return toReturn; } public void putAll(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } public V remove(Object key) { V toReturn = get(key); String encodedKey = encodedKey(key); reified.setReified(encodedKey, null); Splittable.NULL.assign(data, encodedKey); return toReturn; } public int size() { return data.getPropertyKeys().size(); } public Collection<V> values() { return new AbstractCollection<V>() { final List<String> keys = data.getPropertyKeys(); @Override public Iterator<V> iterator() { return new Iterator<V>() { final Iterator<String> it = keys.iterator(); String lastEncodedKey; public boolean hasNext() { return it.hasNext(); } public V next() { lastEncodedKey = it.next(); return getRaw(lastEncodedKey); } public void remove() { Splittable.NULL.assign(data, lastEncodedKey); reified.setReified(lastEncodedKey, null); } }; } @Override public int size() { return keys.size(); } }; } private String encodedKey(Object key) { return keyCoder.extractSplittable(state, key).asString(); } private V getRaw(String encodedKey) { if (reified.isReified(encodedKey)) { @SuppressWarnings("unchecked") V toReturn = (V) reified.getReified(encodedKey); return toReturn; } // Both undefined or an explicit null should return null here if (data.isNull(encodedKey)) { return null; } Splittable value = data.get(encodedKey); @SuppressWarnings("unchecked") V toReturn = (V) valueCoder.decode(state, value); reified.setReified(encodedKey, toReturn); return toReturn; } }