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

org.onebusaway.collections.FactoryMap Maven / Gradle / Ivy

/**
 * Copyright (C) 2011 Brian Ferris 
 *
 * 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.onebusaway.collections;

import java.io.Serializable;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 * A extension of {@link HashMap} that will automatically create a {@link Map}
 * key-value entry if a call to {@link #get(Object)} is made where the key is
 * not already present in the map.
 * 
 * Default map entries can be created by passing in an instance of
 * {@link IValueFactory} as an object factory (see
 * {@link #FactoryMap(IValueFactory)}).
 * 
 * Maps can also be created by passing a plain-old Java object. The class of the
 * Java object will be used to create new value instances on demand, as long the
 * class has a no-arg constructor (see {@link #FactoryMap(Object)}).
 * 
 * @author bdferris
 */
public class FactoryMap extends HashMap {

  private static final long serialVersionUID = 1L;

  private IValueFactory _valueFactory;

  /**
   * Object factory interface for creating a new value for a specified key for
   * use in {@link FactoryMap}
   * 
   * @author bdferris
   */
  public interface IValueFactory {
    public VF create(KF key);
  }

  /**
   * A convenience method for creating an instance of {@link FactoryMap} that
   * wraps an existing {@link Map} and has a specific default value. The default
   * value's class will be used to create new value instances as long as it has
   * a no-arg constructor.
   * 
   * @param map an existing map to wrap
   * @param defaultValue see {@link #FactoryMap(Object)} for discussion
   * @return a {@link Map} with factory-map behavior
   */
  public static  Map create(Map map, V defaultValue) {
    return new MapImpl(map, new ClassInstanceFactory(
        defaultValue.getClass()));
  }

  /**
   * A convenience method for creating an instance of {@link FactoryMap} that
   * wraps an existing {@link Map} and has a specific default value factory.
   * 
   * @param map an existing map to wrap
   * @param factory see {@link #FactoryMap(IValueFactory)} for discussion
   * @return a {@link Map} with factory-map behavior
   */
  public static  Map create(Map map,
      IValueFactory factory) {
    return new MapImpl(map, factory);
  }

  /**
   * A convenience method for creating an instance of {@link FactoryMap} that
   * wraps an existing {@link SortedMap} and has a specific default value. The
   * default value's class will be used to create new value instances as long as
   * it has a no-arg constructor.
   * 
   * @param map an existing sorted map to wrap
   * @param defaultValue see {@link #FactoryMap(Object)} for discussion
   * @return a {@link SortedMap} with factory-map behavior
   */
  public static  SortedMap createSorted(SortedMap map,
      V defaultValue) {
    return new SortedMapImpl(map, new ClassInstanceFactory(
        defaultValue.getClass()));
  }

  /**
   * A convenience method for creating an instance of {@link FactoryMap} that
   * wraps an existing {@link SortedMap} and has a specific default value
   * factory.
   * 
   * @param map an existing sorted map to wrap
   * @param factory see {@link #FactoryMap(IValueFactory)} for discussion
   * @return a {@link SortedMap} with factory-map behavior
   */
  public static  SortedMap createSorted(SortedMap map,
      IValueFactory factory) {
    return new SortedMapImpl(map, factory);
  }

  /**
   * A factory map constructor that accepts a default value instance. The
   * {@link Class} of the default value instance will be used to create new
   * default value instances as needed assuming the class has no-arg
   * constructor. New values will be created when calls are made to
   * {@link #get(Object)} and the specified key is not already present in the
   * map. Why do we accept an object instance instead of a class instance? It
   * makes it easier to handle cases where V is itself a parameterized type.
   * 
   * @param factoryInstance the {@link Class} of the instance will be used to
   *          create new values as needed
   */
  public FactoryMap(V factoryInstance) {
    this(new ClassInstanceFactory(factoryInstance.getClass()));
  }

  /**
   * A factory map constructor that accepts a {@link IValueFactory} default
   * value factory. The value factory will be called when calls are made to
   * {@link #get(Object)} and the specified key is not already present in the
   * map.
   * 
   * @param valueFactory the default value factory
   */
  public FactoryMap(IValueFactory valueFactory) {
    _valueFactory = valueFactory;
  }

  /**
   * Returns the value to which the specified key is mapped, or a default value
   * instance if the specified key is not present in the map. Subsequent clals
   * to {@link #get(Object)} with the same key will return the same value
   * instance.
   * 
   * @see Map#get(Object)
   * @see #put(Object, Object)
   */
  @SuppressWarnings("unchecked")
  @Override
  public V get(Object key) {
    if (!containsKey(key))
      put((K) key, createValue((K) key));
    return super.get(key);
  }

  private V createValue(K key) {
    return _valueFactory.create(key);
  }

  private static class ClassInstanceFactory implements
      IValueFactory, Serializable {

    private static final long serialVersionUID = 1L;

    private Class _valueClass;

    @SuppressWarnings({"rawtypes", "unchecked"})
    public ClassInstanceFactory(Class valueClass) {
      _valueClass = valueClass;
    }

    public V create(K key) {
      try {
        return _valueClass.newInstance();
      } catch (Exception e) {
        throw new IllegalStateException(e);
      }
    }
  }

  private static class MapImpl implements Map, Serializable {

    private static final long serialVersionUID = 1L;

    private Map _source;

    private IValueFactory _valueFactory;

    public MapImpl(Map source, IValueFactory valueFactory) {
      _source = source;
      _valueFactory = valueFactory;
    }

    public void clear() {
      _source.clear();
    }

    public boolean containsKey(Object key) {
      return _source.containsKey(key);
    }

    public boolean containsValue(Object value) {
      return _source.containsValue(value);
    }

    public Set> entrySet() {
      return _source.entrySet();
    }

    @SuppressWarnings("unchecked")
    public V get(Object key) {
      if (!containsKey(key))
        _source.put((K) key, createValue((K) key));
      return _source.get(key);
    }

    public boolean isEmpty() {
      return _source.isEmpty();
    }

    public Set keySet() {
      return _source.keySet();
    }

    public V put(K key, V value) {
      return _source.put(key, value);
    }

    public void putAll(Map t) {
      _source.putAll(t);
    }

    public V remove(Object key) {
      return _source.remove(key);
    }

    public int size() {
      return _source.size();
    }

    public Collection values() {
      return _source.values();
    }

    private V createValue(K key) {
      return _valueFactory.create(key);
    }
  }

  private static class SortedMapImpl extends MapImpl implements
      SortedMap {

    private static final long serialVersionUID = 1L;

    private SortedMap _source;

    public SortedMapImpl(SortedMap source,
        IValueFactory valueFactory) {
      super(source, valueFactory);
      _source = source;
    }

    public Comparator comparator() {
      return _source.comparator();
    }

    public K firstKey() {
      return _source.firstKey();
    }

    public SortedMap headMap(K toKey) {
      return _source.headMap(toKey);
    }

    public K lastKey() {
      return _source.lastKey();
    }

    public SortedMap subMap(K fromKey, K toKey) {
      return _source.subMap(fromKey, toKey);
    }

    public SortedMap tailMap(K fromKey) {
      return _source.tailMap(fromKey);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy