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

org.w3c.cci2.AbstractSparseArray Maven / Gradle / Ivy

There is a newer version: 2.18.10
Show newest version
/*
 * ====================================================================
 * Project:     openMDX/Core, http://www.openmdx.org/
 * Description: Abstract Sparse Array 
 * Owner:       OMEX AG, Switzerland, http://www.omex.ch
 * ====================================================================
 *
 * This software is published under the BSD license as listed below.
 * 
 * Copyright (c) 2006-2019, OMEX AG, Switzerland
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 * 
 * * Neither the name of the openMDX team nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * ------------------
 * 
 * This product includes software developed by other organizations as
 * listed in the NOTICE file.
 */
package org.w3c.cci2;

import java.io.Serializable;
import java.util.AbstractList;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

import org.openmdx.kernel.collection.InternalizedKeys;

/**
 * Abstract Sparse Array
 */
public abstract class AbstractSparseArray implements SparseArray {

    /**
     * Constructor
     */
    protected AbstractSparseArray() {
        super();
    }

    /**
     * This member caches the sparse array's list view
     */
    private transient List list = null;

    /**
     * This member caches the sparse array's entry set view
     */
    private transient Set> set = null;

    /**
     * The sorted map by which the sparse array will is backed
     */
    protected abstract SortedMap delegate();

    /**
     * Create a sub-array of the same concrete type
     * 
     * @param delegate the delegate
     * 
     * @return a concrete sub-array
     */
    protected abstract SparseArray subArray(
        SortedMap delegate
    );
    
    /*
     * (non-Javadoc)
     * 
     * @see java.util.AbstractMap#entrySet()
     */
    public Set> entrySet() {
        return this.set == null ? this.set = new AbstractSet>() {

            @Override
            public int size() {
                return AbstractSparseArray.this.delegate().size();
            }

            @Override
            public Iterator> iterator() {
                return new Iterator>() {

                    final Iterator> iterator = AbstractSparseArray.this.delegate().entrySet().iterator();

                    public boolean hasNext() {
                        return this.iterator.hasNext();
                    }

                    public Map.Entry next() {
                        return new Map.Entry() {

                            private final Map.Entry entry = iterator.next();

                            public Integer getKey() {
                                return this.entry.getKey();
                            }

                            public E getValue() {
                                return this.entry.getValue();
                            }

                            public E setValue(E value) {
                                if (value == null) {
                                    E old = this.entry.getValue();
                                    iterator.remove();
                                    return old;
                                } else {
                                    return this.entry.setValue(value);
                                }
                            }

                        };
                    }

                    public void remove() {
                        this.iterator.remove();
                    }

                };
            }

        } : this.set;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.TreeMap#put(java.lang.Object, java.lang.Object)
     */
    public E put(
        Integer key,
        E value
    ) {
        return value == null ? this.delegate().remove(key) : this.delegate().put(InternalizedKeys.internalize(key), value);
    }

    /**
     * A list backed up by the sparse array:
     * 
    *
  • its size() is the sparse array's lastKey() + 1 *
  • the sparse array's un-populated positions are represented as null values *
  • get(int) and set(int,E) operations are allowed for any index >= 0 *
  • clear() and add(E) are supported *
  • remove(int) and add(int,E) are not supported *
* * @return a List representing this SparseArray */ public List asList() { return this.list == null ? this.list = new AsList(this) : this.list; } /* * (non-Javadoc) * * @see java.util.SortedMap#comparator() */ public Comparator comparator() { return this.delegate().comparator(); } /* * (non-Javadoc) * * @see java.util.SortedMap#firstKey() */ public Integer firstKey() { return this.delegate().firstKey(); } /* * (non-Javadoc) * * @see java.util.SortedMap#lastKey() */ public Integer lastKey() { return this.delegate().lastKey(); } /** * Returns a view of the portion of this sparse array whose keys range from * fromKey, inclusive, to toKey, exclusive. (If * fromKey and toKey are equal, the returned sparse array * is empty.) The returned sparse array is backed by this sparse array, so * changes in the returned sparse array are reflected in this sparse array, * and vice-versa. The returned Map supports all optional map operations * that this sparse array supports. *

* * The map returned by this method will throw an * IllegalArgumentException if the user attempts to insert a key * outside the specified range. *

* * Note: this method always returns a half-open range (which * includes its low endpoint but not its high endpoint). If you need a * closed range (which includes both endpoints), and the key type * allows for calculation of the successor a given key, merely request the * subrange from lowEndpoint to successor(highEndpoint). * Sparse arrays are map whose keys are integers. * The following idiom obtains a view containing all of the key-value * mappings in m whose keys are between low and * high, inclusive: * *

    * 
    * Map sub = m.subMap(low, high + 1);
    * 
* * A similarly technique can be used to generate an open range * (which contains neither endpoint). The following idiom obtains a * view containing all of the key-value mappings in m whose keys * are between low and high, exclusive: * *
    * 
    * Map sub = m.subMap(low + 1, high);
    * 
* * @param fromKey * low endpoint (inclusive) of the subMap. * @param toKey * high endpoint (exclusive) of the subMap. * @return a view of the specified range within this sparse array. * * @throws ClassCastException * if fromKey and toKey * cannot be compared to one another using this map's comparator * (or, if the map has no comparator, using natural ordering). * Implementations may, but are not required to, throw this * exception if fromKey or toKey * cannot be compared to keys currently in the map. * @throws IllegalArgumentException * if fromKey is greater than * toKey; or if this map is itself a subMap, headMap, * or tailMap, and fromKey or toKey are not * within the specified range of the subMap, headMap, or tailMap. * @throws NullPointerException * if fromKey or toKey is * null and this sparse array does not tolerate * null keys. */ public SparseArray subMap( Integer fromKey, Integer toKey ) { return subArray( delegate().subMap(fromKey, toKey) ); } /** * Returns a view of the portion of this sparse array whose keys are greater * than or equal to fromKey. The returned sparse array is backed * by this sparse array, so changes in the returned sparse array are reflected * in this sparse array, and vice-versa. The returned map supports all * optional map operations that this sparse array supports. *

* * The map returned by this method will throw an * IllegalArgumentException if the user attempts to insert a key * outside the specified range. *

* * Note: this method always returns a view that contains its (low) * endpoint. If you need a view that does not contain this endpoint, and * the element type allows for calculation of the successor a given value, * merely request a tailMap bounded by successor(lowEndpoint). * Sparse arrays are map whose keys are integers. * The following idiom obtains a view containing all of the * key-value mappings in m whose keys are strictly greater than * low: * *

    * 
    * Map tail = m.tailMap(low + 1);
    * 
* * @param fromKey * low endpoint (inclusive) of the tailMap. * @return a view of the specified final range of this sparse array. * @throws ClassCastException * if fromKey is not compatible * with this map's comparator (or, if the map has no comparator, * if fromKey does not implement Comparable). * Implementations may, but are not required to, throw this * exception if fromKey cannot be compared to keys * currently in the map. * @throws IllegalArgumentException * if this map is itself a subMap, * headMap, or tailMap, and fromKey is not within the * specified range of the subMap, headMap, or tailMap. * @throws NullPointerException * if fromKey is null and * this sparse array does not tolerate null keys. */ public SparseArray tailMap(Integer fromKey) { return subArray(delegate().tailMap(fromKey)); } /** * Returns a view of the portion of this sparse array whose keys are * strictly less than toKey. The returned sparse array is backed by this * sparse array, so changes in the returned sparse array are reflected in this * sparse array, and vice-versa. The returned map supports all optional map * operations that this sparse array supports. *

* * The map returned by this method will throw an IllegalArgumentException * if the user attempts to insert a key outside the specified range. *

* * Note: this method always returns a view that does not contain its * (high) endpoint. If you need a view that does contain this endpoint, * and the key type allows for calculation of the successor a given * key, merely request a headMap bounded by successor(highEndpoint). * Sparse arrays are map whose keys are integers. * The following idiom obtains a view containing all of the * key-value mappings in m whose keys are less than or equal to * high: * *

    * 
    * Map head = m.headMap(high + 1);
    * 
* * @param toKey * high endpoint (exclusive) of the subMap. * @return a view of the specified initial range of this sparse array. * @throws ClassCastException * if toKey is not compatible * with this map's comparator (or, if the map has no comparator, * if toKey does not implement Comparable). * Implementations may, but are not required to, throw this * exception if toKey cannot be compared to keys * currently in the map. * @throws IllegalArgumentException * if this map is itself a subMap, * headMap, or tailMap, and toKey is not within the * specified range of the subMap, headMap, or tailMap. * @throws NullPointerException * if toKey is null and * this sparse array does not tolerate null keys. */ public SparseArray headMap(Integer toKey) { return subArray(delegate().headMap(toKey)); } /* * (non-Javadoc) * * @see java.lang.Iterable#iterator() */ public Iterator iterator() { return this.delegate().values().iterator(); } /* * (non-Javadoc) * * @see org.w3c.cci2.SparseArray#populationIterator() */ public ListIterator populationIterator() { return new ListIterator() { private final List list = new ArrayList(keySet()); private final ListIterator iterator = list.listIterator(); Integer current = null; public boolean hasNext() { return this.iterator.hasNext(); } public E next() { return get( current = this.iterator.next() ); } public void remove() { if (current == null) { throw new IllegalStateException(ILLEGAL_STATE); } else { this.iterator.remove(); put(this.current, null); this.current = null; } } public void add(E o) { if (current == null) { throw new IllegalStateException(ILLEGAL_STATE); } else { Integer i = Integer.valueOf(current.intValue() + 1); E e = get(i); if (e == null) { current = i; put(i, o); this.iterator.add(i); } else { throw new IllegalStateException(NO_SPACE); } } } public boolean hasPrevious() { return this.iterator.hasPrevious(); } public int nextIndex() { int i = this.iterator.nextIndex(); return i < list.size() ? this.list.get(i).intValue() : this.list.get(i - 1).intValue() + 1; } public E previous() { return get( current = this.iterator.previous() ); } public int previousIndex() { int i = this.iterator.previousIndex(); return i < 0 ? -1 : this.list.get(i).intValue(); } public void set(E o) { if (this.current == null) { throw new IllegalStateException(ILLEGAL_STATE); } else { put(this.current, o); } } }; } private static final String ILLEGAL_STATE = "Population iterator has no current element"; private static final String NO_SPACE = "Population iterator has no room to add an element"; /* * (non-Javadoc) * * @see java.util.Map#clear() */ public void clear() { this.delegate().clear(); } /* * (non-Javadoc) * * @see java.util.Map#containsKey(java.lang.Object) */ public boolean containsKey(Object key) { return this.delegate().containsKey(key); } /* * (non-Javadoc) * * @see java.util.Map#containsValue(java.lang.Object) */ public boolean containsValue(Object value) { return this.delegate().containsValue(value); } /* * (non-Javadoc) * * @see java.util.Map#get(java.lang.Object) */ public E get(Object key) { return this.delegate().get(key); } /* * (non-Javadoc) * * @see java.util.Map#isEmpty() */ public boolean isEmpty() { return this.delegate().isEmpty(); } /* * (non-Javadoc) * * @see java.util.Map#keySet() */ public Set keySet() { return this.delegate().keySet(); } /* * (non-Javadoc) * * @see java.util.Map#putAll(java.util.Map) */ public void putAll(Map t) { for (Map.Entry entry : t.entrySet()) { put(entry.getKey(), entry.getValue()); // Internalizing or removing keys } } /* * (non-Javadoc) * * @see java.util.Map#remove(java.lang.Object) */ public E remove(Object key) { return this.delegate().remove(key); } /* * (non-Javadoc) * * @see java.util.Map#size() */ public int size() { return this.delegate().size(); } /* * (non-Javadoc) * * @see java.util.Map#values() */ public Collection values() { return this.delegate().values(); } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals( Object obj ) { if (obj instanceof AbstractSparseArray) { AbstractSparseArray that = (AbstractSparseArray) obj; return this.delegate().equals(that.delegate()); } else { return false; } } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return this.delegate().hashCode(); } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return this.delegate().toString(); } //------------------------------------------------------------------------ // Class AsList //------------------------------------------------------------------------ /** * AsList *

* A list backed up by the sparse array: *

    *
  • its size() is the sparse array's lastKey() + 1 *
  • the sparse array's un-populated positions are represented as null values *
  • get() and set() operations are allowed for any index >= 0 *
  • clear() and add(E) are supported *
  • remove(int) and add(int,E) are not supported *
*/ private final static class AsList extends AbstractList implements Serializable { /** * Constructor * @param delegate */ AsList( final SortedMap delegate ) { this.delegate = delegate; } /** * Implements Serializable */ private static final long serialVersionUID = -7591560889642014116L; /** * */ private final SortedMap delegate; /* * (non-Javadoc) * * @see java.util.AbstractList#get(int) */ @Override public E get(int index) { return this.delegate.get(Integer.valueOf(index)); } /* * (non-Javadoc) * * @see java.util.AbstractCollection#size() */ @Override public int size() { return this.delegate.isEmpty() ? 0 : this.delegate.lastKey().intValue() + 1; } /* * (non-Javadoc) * * @see java.util.AbstractList#set(int, java.lang.Object) */ @Override public E set( int index, E element ) { return this.delegate.put( Integer.valueOf(index), element ); } /* * (non-Javadoc) * * @see java.util.AbstractList#indexOf(java.lang.Object) */ @Override public int indexOf(Object o) { if (o == null) { int i = -1; for (Map.Entry e : this.delegate.entrySet()) { if (++i != e.getKey().intValue()) return i; } } else { for (Map.Entry e : this.delegate.entrySet()) { if (o.equals(e.getValue())) return e.getKey().intValue(); } } return -1; } /* * (non-Javadoc) * * @see java.util.AbstractList#add(java.lang.Object) */ @Override public boolean add(E o) { if (o == null) { return false; } else { if (this.delegate.isEmpty()) { try { this.delegate.put(0, o); } catch (IllegalArgumentException keyRangeException) { throw (IndexOutOfBoundsException) new IndexOutOfBoundsException( "Unable to add a value to an empty SparseArray backed up by a SortedMap disallowing the index 0" ).initCause(keyRangeException); } } else { final int index = this.delegate.lastKey().intValue() + 1; try { this.delegate.put(Integer.valueOf(index), o); } catch (IllegalArgumentException keyRangeException) { throw (IndexOutOfBoundsException) new IndexOutOfBoundsException( "Unable to add a value to a full SparseArray backed up by a SortedMap disallowing the index " + index ).initCause(keyRangeException); } } return true; } } /** * Removes from this list all of the elements whose index is between * fromIndex, inclusive, and toIndex, exclusive. * Shifts any succeeding elements to the left (reduces their index). This * call shortens the ArrayList by (toIndex - fromIndex) * elements. (If toIndex==fromIndex, this operation has no * effect.) *

* * This method is called by the clear operation on this list * and its subLists. Overriding this method to take advantage of * the internals of the list implementation can substantially * improve the performance of the clear operation on this list * and its subLists. *

* * This implementation gets a list iterator positioned before * fromIndex, and repeatedly calls ListIterator.next * followed by ListIterator.remove until the entire range has * been removed. Note: if ListIterator.remove requires linear * time, this implementation requires quadratic time. * * @param fromIndex * index of first element to be removed. * @param toIndex * index after last element to be removed. */ @Override protected void removeRange( int fromIndex, int toIndex ) { this.delegate.subMap( Integer.valueOf(fromIndex), Integer.valueOf(toIndex) ).clear(); } } }