Package com.sun.sgs.impl.util

Source Code of com.sun.sgs.impl.util.BindingKeyedMapImpl$EntrySet

/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.sun.sgs.impl.util;

import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.NameNotBoundException;
import com.sun.sgs.app.ObjectNotFoundException;
import com.sun.sgs.app.util.ManagedSerializable;
import com.sun.sgs.impl.sharedutil.Objects;
import com.sun.sgs.service.DataService;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Map.Entry;
import java.util.Set;

/**
* An implementation of a persistent {@code Map} that uses service
* bindings in the data service to store key/value pairs.  This map does
* not permit {@code null} keys or values.
*
* <p>Note: This map is parameterized by value type only.  A {@code String}
* is the only valid key type for a {@code BindingKeyedMap}.
*
* <p>A value is stored in the data service using its associated key (a
* String) as a suffix to the {@code keyPrefix} specified during
* construction.  All values must implement {@code Serializable}.  If a
* value implements {@code Serializable}, but does not implement {@link
* ManagedObject}, the value will be wrapped in an instance of {@code
* ManagedSerializable} when storing it in the data service.  Note: users of
* this map must use this map's APIs to avoid leaking wrappers for
* non-managed, serializable objects.
*
* <p>Instances of {@code BindingKeyedMap} as well as its associated
* iterators are serializable, but not managed, objects.
*
* @param  <V> the type for the map's values
*/
public class BindingKeyedMapImpl<V>
    extends AbstractMap<String, V>
    implements BindingKeyedMap<V>, Serializable
{
    /** The serialVersionUID for this class. */
    private static final long serialVersionUID = 1L;

    /** The key prefix. */
    private final String keyPrefix;

    /**
     * Constructs an instance with the specified {@code keyPrefix}.
     *
     * @param  keyPrefix a key prefix
     * @throws  IllegalArgumentException if {@code keyPrefix} is empty
     */
    BindingKeyedMapImpl(String keyPrefix) {
  if (keyPrefix == null) {
      throw new NullPointerException("null keyPrefix");
  } else if (keyPrefix.isEmpty()) {
      throw new IllegalArgumentException("empty keyPrefix");
  }
  this.keyPrefix = keyPrefix;
    }

    /* -- Override AbstractMap methods -- */

    /** {@inheritDoc} */
    public boolean isEmpty() {
  return isEmptyInternal(keyPrefix);
    }
   
    /** {@inheritDoc} */
    public V put(String key, V value) {
  Objects.checkNull("key", key);
  checkSerializable("value", value);

  String bindingName = getBindingName(key);
  V previousValue = get(key);
  if (previousValue != null) {
      removeValue(bindingName);
  }

  // Store key/value pair.
  putKeyValue(bindingName, value);
 
  return previousValue;
    }

    /** {@inheritDoc} */
    public V get(Object key) {
  checkKey("key", key);
  String bindingName = getBindingName((String) key);
  V value = null;
  try {
      value = getValue(bindingName);
  } catch (NameNotBoundException e) {
  }
  return value;
    }

    /** {@inheritDoc} */
    public boolean containsKey(Object key) {
  checkKey("key", key);
  return containsKeyInternal(getBindingName((String) key));
    }

    /** {@inheritDoc} */
    public boolean containsValue(Object value) {
  Objects.checkNull("value", value);
  Iterator iter = new ValueIterator<V>(keyPrefix);
  while (iter.hasNext()) {
      try {
    if (value.equals(iter.next())) {
        return true;
    }
      } catch (ObjectNotFoundException e) {
      }
  }
  return false;
    }
   
    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    public V remove(Object key) {
  checkKey("key", key);
  DataService dataService = BindingKeyedCollectionsImpl.getDataService();
  String bindingName = getBindingName((String) key);
  V value = null;
  try {
      ManagedObject v = dataService.getServiceBinding(bindingName);
      if (v instanceof Wrapper) {
    value = (V) ((Wrapper) v).get();
    dataService.removeObject(v);
      } else {
    value = (V) v;
      }
      dataService.removeServiceBinding(bindingName);
  } catch (NameNotBoundException e) {
  }
  return value;
    }

    /** {@inheritDoc} */
    public void clear() {
  clearInternal(keyPrefix);
    }

    /** {@inheritDoc} */
    public int size() {
  return sizeInternal(keyPrefix);
    }

    /** {@inheritDoc} */
    public Set<Entry<String, V>> entrySet() {
  return new EntrySet<V>(keyPrefix);
    }

    /**
     * A serializable {@code Set} for this map's entries.
     */
    private static final class EntrySet<V>
  extends AbstractSet<Entry<String, V>>
  implements Serializable
    {
  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  /** The key prefix. */
        private final String keyPrefix;

  /**
   * Constructs an instance with the specified {@code keyPrefix}.
   *
   * @param keyPrefix a key prefix
   */
        EntrySet(String keyPrefix) {
      this.keyPrefix = keyPrefix;
  }

  /** {@inheritDoc} */
        public Iterator<Entry<String, V>> iterator() {
            return new EntryIterator<V>(keyPrefix);
  }

  /** {@inheritDoc} */
  public boolean isEmpty() {
      return isEmptyInternal(keyPrefix);
  }

  /** {@inheritDoc} */
  public int size() {
      return sizeInternal(keyPrefix);
  }

  /** {@inheritDoc} */ 
  public void clear() {
      clearInternal(keyPrefix);
  }
 
  /** {@inheritDoc} */
  @SuppressWarnings("unchecked")
  public boolean contains(Object o) {
      Entry<String, V> entry = (Entry<String, V>) o;
      return containsKeyInternal(keyPrefix + entry.getKey());
  }
 
  /** {@inheritDoc} */
  @SuppressWarnings("unchecked")
  public boolean remove(Object o) {
      Entry<String, V> entry = (Entry<String, V>) o;
      return removeOverrideInternal(keyPrefix + entry.getKey());
  }
    }

    /** {@inheritDoc} */
    public Set<String> keySet() {
        return new KeySet<V>(keyPrefix);
    }
   
    /**
     * A serializable {@code Set} for this map's entries.
     */
    private static final class KeySet<V>
  extends AbstractSet<String>
  implements Serializable
    {
  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  /** The key prefix. */
        private final String keyPrefix;

  /**
   * Constructs an instance with the specified {@code keyPrefix}.
   *
   * @param keyPrefix a key prefix
   */
        KeySet(String keyPrefix) {
      this.keyPrefix = keyPrefix;
  }

  /** {@inheritDoc} */
        public Iterator<String> iterator() {
            return new KeyIterator<V>(keyPrefix);
  }

  /** {@inheritDoc} */
  public boolean isEmpty() {
      return isEmptyInternal(keyPrefix);
  }

  /** {@inheritDoc} */
  public int size() {
      return sizeInternal(keyPrefix);
  }

  /** {@inheritDoc} */ 
  public void clear() {
      clearInternal(keyPrefix);
  }
 
  /** {@inheritDoc} */
  public boolean contains(Object o) {
      return containsKeyInternal(keyPrefix + (String) o);
  }

  /** {@inheritDoc} */
  public boolean remove(Object o) {
      return removeOverrideInternal(keyPrefix + (String) o);
  }
    }

    /** {@inheritDoc} */
    public Collection<V> values() {
        return new Values<V>(keyPrefix);
    }
   
    /**
     * A serializable {@code Collection} for this map's values.
     */
    private static final class Values<V>
  extends AbstractCollection<V>
  implements Serializable
    {
  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  /** The key prefix. */
        private final String keyPrefix;

  /**
   * Constructs an instance with the specified {@code keyPrefix}.
   *
   * @param keyPrefix a key prefix
   */
        Values(String keyPrefix) {
      this.keyPrefix = keyPrefix;
  }

  /** {@inheritDoc} */
        public Iterator<V> iterator() {
            return new ValueIterator<V>(keyPrefix);
  }

  /** {@inheritDoc} */
  public boolean isEmpty() {
      return isEmptyInternal(keyPrefix);
  }

  /** {@inheritDoc} */
  public int size() {
      return sizeInternal(keyPrefix);
  }

  /** {@inheritDoc} */ 
  public void clear() {
      clearInternal(keyPrefix);
  }
    }
    /**
     * An abstract iterator for obtaining this map's entries.
     * values.
     */
    private abstract static class AbstractIterator<E, V>
  implements Iterator<E>, Serializable
    {
  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  /** The data service. */
  private transient DataService dataService;
 
  /** The prefix for keys. */
  private final String prefix;

  /** The key used to look up the next service bound name; or null. */
  private String key;

  /** The key returned by {@code next}, or null. */
  private String keyReturnedByNext;
 
  /** The name fetched in the {@code hasNext} method, which
   * is only valid if {@code hasNext} returns {@code true}. */
  private String nextName;
 
  /**
   * Constructs an instance of this class with the specified
   * {@code keyPrefix}.
   */
  AbstractIterator(String prefix) {
      this.prefix = prefix;
      this.key = prefix;
      dataService = BindingKeyedCollectionsImpl.getDataService();
  }

  /** {@inheritDoc} */
  public boolean hasNext() {
      if (key == null) {
    return false;
      }
      if (nextName != null) {
    return true;
      }
      String name = dataService.nextServiceBoundName(key);
      if (name != null && name.startsWith(prefix)) {
    nextName = name;
    return true;
      } else {
    key = null;
    return false;
      }
  }
 
  /** {@inheritDoc} */
  public abstract E next();
 
  /** {@inheritDoc} */
  public void remove() {
      if (keyReturnedByNext == null) {
    throw new IllegalStateException();
      }
      removeOverrideInternal(keyReturnedByNext);
      keyReturnedByNext = null;
  }

  /**
   * Returns the next entry or throws {@code NoSuchElementException} if
   * there is no next entry.
   */
  Entry<String, V> nextEntry() {
      try {
    if (!hasNext()) {
        throw new NoSuchElementException();
    }
    keyReturnedByNext = nextName;
    key = nextName;
    return new KeyValuePair<V>(
        prefix,
        keyReturnedByNext.substring(prefix.length()));
      } finally {
    nextName = null;
      }
  }

  /**
   * Returns the next key or throws {@code NoSuchElementException} if
   * there is no next key.
   */
  String nextKey() {
      try {
    if (!hasNext()) {
        throw new NoSuchElementException();
    }
    keyReturnedByNext = nextName;
    key = nextName;
    return keyReturnedByNext.substring(prefix.length());
      } finally {
    nextName = null;
      }
  }

  /**
   * Returns the next value or throws {@code NoSuchElementException} if
   * there is no next value.
   */
  V nextValue() {
      try {
    if (!hasNext()) {
        throw new NoSuchElementException();
    }
    keyReturnedByNext = nextName;
    key = nextName;
    return getValue(keyReturnedByNext);
      } finally {
    nextName = null;
      }
  }

  private void readObject(ObjectInputStream s)
      throws IOException, ClassNotFoundException
  {
      s.defaultReadObject();
      dataService = BindingKeyedCollectionsImpl.getDataService();
  }
 
  @SuppressWarnings("unchecked")
  private V getValue(String bindingName) {
      ManagedObject v = dataService.getServiceBinding(bindingName);
      return
    v instanceof Wrapper ?
    (V) ((Wrapper) v).get() :
    (V) v;
  }
    }

    /**
     * An iterator over the entry set
     */
    private static final class EntryIterator<V>
            extends AbstractIterator<Entry<String, V>, V>
    {
  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  /**
   * Constructs an instance with the given {@code keyPrefix}.
   *
   * @param keyPrefix a key prefix.
   */
        EntryIterator(String keyPrefix) {
      super(keyPrefix);
  }

  /**
   * {@inheritDoc}
   */
        public Entry<String, V> next() {
      return nextEntry();
  }
    }

    /**
     * An iterator over the keys in the map.
     */
    private static final class KeyIterator<V>
            extends AbstractIterator<String, V>
    {
  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  /**
   * Constructs an instance with the given {@code keyPrefix}.
   *
   * @param keyPrefix a key prefix.
   */
        KeyIterator(String keyPrefix) {
      super(keyPrefix);
  }

  /**
   * {@inheritDoc}
   */
  public String next() {
      return nextKey();
  }
    }

    /**
     * An iterator over the values in the tree.
     */
    static final class ValueIterator<V>
            extends AbstractIterator<V, V>
    {
  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  /**
   * Constructs an instance with the given {@code keyPrefix}.
   *
   * @param keyPrefix a key prefix.
   */
        ValueIterator(String keyPrefix) {
      super(keyPrefix);
  }

  /**
   * {@inheritDoc}
   */
  public V next() {
      return nextValue();
  }
    }

    /* -- Implement BindingKeyedMap -- */

    /** {@inheritDoc} */
    public String getKeyPrefix() {
  return keyPrefix;
    }

    /** {@inheritDoc} */
    public boolean putOverride(String key, V value) {
  Objects.checkNull("key", key);
  checkSerializable("value", value);
  String bindingName = getBindingName(key);
  boolean previouslyMapped = containsKeyInternal(bindingName);
  if (previouslyMapped) {
      try {
    removeValue(bindingName);
      } catch (ObjectNotFoundException e) {
      }
  }
  putKeyValue(getBindingName(key), value);
  return previouslyMapped;
    }

    /** {@inheritDoc} */
    public boolean removeOverride(String key) {
  Objects.checkNull("key", key);
  return removeOverrideInternal(getBindingName(key));
    }
   
    /* -- Private classes and methods. -- */

    /**
     * A wrapper for a serializable, but not managed, object.
     */
    private static final class Wrapper<V> extends ManagedSerializable<V> {

  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  Wrapper(V obj) {
      super(obj);
  }
    }

    /**
     * A serializable {@code Entry} used in entry sets for this map.
     */
    private static final class KeyValuePair<V>
  implements Entry<String, V>, Serializable
    {
  /** The serialVersionUID for this class. */
  private static final long serialVersionUID = 1L;

  private final String prefix;
  private final String k;

  KeyValuePair(String prefix, String key) {
      this.prefix = prefix;
      this.k = key;
  }
 
  /** {@inheritDoc} */
  public String getKey() {
      return k;
  }

  /** {@inheritDoc} */
  public V getValue() {
      return getValue(prefix + k);
  }

  /** {@inheritDoc} */
  public V setValue(V value) {
      checkSerializable("value", value);
      String bindingName = prefix + k;
      V previousValue =  getValue(bindingName);
      if (previousValue != null) {
    removeValue(bindingName);
      }
      putKeyValue(bindingName, value);
      return previousValue;
  }

  /** {@inheritDoc} */
  public int hashCode() {
            return getKey().hashCode() ^ getValue().hashCode();
  }

  /** {@inheritDoc} */
  public boolean equals(Object o) {
      if (o instanceof Entry) {
    Entry entry = (Entry) o;
    Object entryKey = entry.getKey();
    Object entryValue = entry.getValue();
    return
        entryKey != null && getKey().equals(entryKey) &&
        entryValue != null && getValue().equals(entryValue);
      } else {
    return false;
      }
  }

  /** {@inheritDoc} */
  public String toString() {
      return k + "=" + getValue().toString();
  }
 
  @SuppressWarnings("unchecked")
  private V getValue(String bindingName) {
      try {
    ManagedObject v = BindingKeyedCollectionsImpl.getDataService().
        getServiceBinding(bindingName);
    return
        v instanceof Wrapper ?
        (V) ((Wrapper) v).get() :
        (V) v;
      } catch (NameNotBoundException e) {
    throw new IllegalStateException("entry has been removed");
      }
  }
    }

    /**
     * Returns the binding name for the specified key name (i.e.,
     * adds the key prefix to the key name).
     *
     * @param  keyName a key name
     * @return  a binding name
     */
    private String getBindingName(String keyName) {
  return keyPrefix + keyName;
    }
 
    /**
     * Returns {@code true} if there is no key with the given {@code keyPrefix},
     * or the first key with the {@code keyPrefix} is the key stop.
     *
     * @param  keyPrefix a key prefix
     * @returns  {@code true} if the {@code keyPrefix} corresponds to an
     *    empty map
     */
    private static boolean isEmptyInternal(String keyPrefix) {
  DataService dataService = BindingKeyedCollectionsImpl.getDataService();
  String key = dataService.nextServiceBoundName(keyPrefix);
  return key == null || !key.startsWith(keyPrefix);
    }

    /**
     * Returns the size of the map with the specified {@code keyPrefix}.
     *
     * @param  keyPrefix a key prefix
     * @return  the size of the map
     */
    private static int sizeInternal(String keyPrefix) {
  int size = 0;
  Iterator<String> iter = new KeyIterator<Object>(keyPrefix);
  while (iter.hasNext()) {
      iter.next();
      size++;
  }
  return size; 
    }

    /**
     * Clears the map with the specified {@code keyPrefix}.
     */
    private static void clearInternal(String keyPrefix) {
  Iterator<String> iter = new KeyIterator<Object>(keyPrefix);
  while (iter.hasNext()) {
      iter.next();
      iter.remove();
  }
    }

    /**
     * Returns {@code true} if a service binding with the specified
     * {@code bindingName} exists.
     */
    private static boolean containsKeyInternal(String bindingName) {
  DataService dataService = BindingKeyedCollectionsImpl.getDataService();
  boolean containsKey = false;
  try {
      dataService.getServiceBinding(bindingName);
      containsKey = true;
  } catch (NameNotBoundException e) {
  } catch (ObjectNotFoundException e) {
      containsKey = true;
  }
  return containsKey;
    }

    /**
     * If a service binding with the specified {@code bindingName} exists,
     * removes the binding (and the wrapper for the associated value if
     * applicable) and returns {@code true}.  Otherwise, returns
     * {@code false}.
     */
    private static boolean removeOverrideInternal(String bindingName) {
  boolean previouslyMapped = containsKeyInternal(bindingName);
  if (previouslyMapped) {
      DataService dataService =
    BindingKeyedCollectionsImpl.getDataService();
      try {
    removeValue(bindingName);
      } catch (ObjectNotFoundException ignore) {
      }
      dataService.removeServiceBinding(bindingName);
  }
  return previouslyMapped;
    }

    /**
     * Removes the wrapper (if applicable) for the value associated with
     * the specified {@code bindingName}.
     *
     * @throws  NameNotBoundException if the service binding does not exist
     * @throws  ObjectNotFoundException if the value associated with the
     *    specified {@code bindingName} has been removed
     */
    private static void removeValue(String bindingName) {
  DataService dataService =
      BindingKeyedCollectionsImpl.getDataService();
  ManagedObject v = dataService.getServiceBinding(bindingName);
  if (v instanceof Wrapper) {
      dataService.removeObject(v);
  }
    }
   
    /**
     * Puts the specified {@code key}/{@code value} pair in this map,
     * wrapping the value if the value does not implement {@code
     * ManagedObject}.  The caller is responsible for removing the wrapper
     * for the old value, if applicable.
     *
     * @param  key a key
     * @param  value a value
     */
    private static void putKeyValue(String bindingName, Object value) {
  assert value != null && value instanceof Serializable;
  ManagedObject v =
      value instanceof ManagedObject ?
      (ManagedObject) value :
      new Wrapper<Object>(value);
  BindingKeyedCollectionsImpl.getDataService().
      setServiceBinding(bindingName, v);
    }

    /**
     * Returns the value associated with the specified {@code bindingName}.
     * removing the wrapper if applicable.
     */
    @SuppressWarnings("unchecked")
    private V getValue(String bindingName) {
  ManagedObject v =
      BindingKeyedCollectionsImpl.getDataService().
          getServiceBinding(bindingName);
  return
      v instanceof Wrapper ?
      (V) ((Wrapper) v).get() :
      (V) v;
    }
   
    /**
     * Throws {@code IllegalArgumentException} of {@code obj} is not
     * serializable.
     */
    private static void checkSerializable(String name, Object obj) {
  Objects.checkNull(name, obj);
  if (!(obj instanceof Serializable)) {
      throw new IllegalArgumentException(name + " not serializable");
  }
    }

    /**
     * Throws {@code ClassCastException} if (@code key) is not an instance
     * of {@code String}.
     */
    private static void checkKey(String keyName, Object key) {
  Objects.checkNull(keyName, key);
  if (!(key instanceof String)) {
      throw new ClassCastException(
    "key is not an instance of String: " +
    key.getClass().getName());
  }
    }
}
TOP

Related Classes of com.sun.sgs.impl.util.BindingKeyedMapImpl$EntrySet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.