package org.apache.commons.events.observable ; 

import java.util.SortedMap ; 
import java.util.Comparator ; 

/**
 * <p>
 * Decorates a <code>SortedMap</code> implementation with a <b>bound 
 * property</b> named &quot;collection&quot;.
 * </p>
 * <p>
 * Each modifying method call made on this <code>SortedMap</code> is 
 * handled as a change to the &quot;collection&quot; property.  This 
 * facility serves to notify subscribers of a change to the collection
 * but does not allow users the option of vetoing the change.  To 
 * gain the ability to veto the change, use a {@link ConstrainedSortedMap}
 * decorater.
 * </p>
 * <p>
 * Due to the fact that a Map offers several &quot;views&quot; of the 
 * same data, some confusion may arise as to what action caused a 
 * particular event.  For instance, entries in the map may be 
 * deleted by <code>Map.values().remove(object)</code>.  There is no
 * known method, short of digging into the implementation of a Map,
 * to determine <i>which</i> key-value pair was deleted by this operation,
 * particularly if there is more than one occurence of a specific 
 * value.  (Conversely, there is also no means of controlling which 
 * key-value pair is deleted by this method; therefore it's not a 
 * terribly smart thing to do.) 
 * </p>
 * <p> 
 * This implementation of a bound map makes no attempt to interpret 
 * events generated by these alternate views of the data.  It merely
 * ensures that registered listeners receive <i>all</i> events generated 
 * by <i>any</i> view of the Map's data.  It is left to the client
 * to interpret the events generated by their map's usage.
 * </p>
 * 
 * @see BoundMap
 * @since Commons Events 1.0
 * @author Bryce Nordgren / USDA Forest Service
 */
public class BoundSortedMap extends BoundMap implements SortedMap { 

    // Constructors
    //-----------------------------------------------------------------------
    protected BoundSortedMap(
        final SortedMap source, 
        final CollectionChangeEventFactory eventFactory) {

        super(source, eventFactory) ; 
    }

    protected BoundSortedMap(final SortedMap source) { 
        super(source);  
    }


    // Factory methods
    //-----------------------------------------------------------------------
    public static BoundSortedMap decorate(
        final SortedMap source, 
        final CollectionChangeEventFactory eventFactory) {

        return new BoundSortedMap(source, eventFactory) ; 
    }

    public static BoundSortedMap decorate(final SortedMap source) { 
        return new BoundSortedMap(source) ; 
    }
        
    // Utility methods
    //-----------------------------------------------------------------------
    /**
     * Typecast to the SortedMap interface...
     * @return a SortedMap
     */
    private SortedMap getSortedMap() {
        return (SortedMap)(getMap()) ; 
    }

    /**
     * Binds an unbound SortedMap returned by the decorated object.  
     * Ensures that this map and the newly bound map get each other's
     * events.
     */
    private SortedMap bindSortedMap(SortedMap unbound) { 
        // make a copy of the event factory
        CollectionChangeEventFactory factoryCopy = 
            (CollectionChangeEventFactory)(eventFactory.clone()) ; 

        // bind the subMap view
        BoundSortedMap boundMap = BoundSortedMap.decorate(
            unbound, factoryCopy) ; 

        // send "boundMap's" events to our listeners
        boundMap.addPropertyChangeListener(createEventRepeater()) ; 

        // send our events to "boundMap's" listeners
        addPropertyChangeListener(boundMap.createEventRepeater()) ; 

        return boundMap ; 
    }

    // SortedMap API (methods which do not change map contents.)
    //-----------------------------------------------------------------------
    public Comparator comparator() { 
        return getSortedMap().comparator() ; 
    }

    public Object firstKey() { 
        return getSortedMap().firstKey() ; 
    }

    public Object lastKey() { 
        return getSortedMap().lastKey() ; 
    }

    // Decorator methods
    //-----------------------------------------------------------------------
    public SortedMap subMap(Object fromKey, Object toKey) { 
        SortedMap subMap = getSortedMap().subMap(fromKey, toKey) ; 
        return bindSortedMap(subMap) ; 
    }

    public SortedMap headMap(Object toKey) {
        SortedMap subMap = getSortedMap().headMap(toKey) ; 
        return bindSortedMap(subMap) ; 
    }

    public SortedMap tailMap(Object fromKey) { 
        SortedMap subMap = getSortedMap().tailMap(fromKey) ; 
        return bindSortedMap(subMap) ; 
    }
}
