diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/ArrayBackedMap.java b/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/ArrayBackedMap.java
new file mode 100644
index 0000000000..dfad10c039
--- /dev/null
+++ b/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/ArrayBackedMap.java
@@ -0,0 +1,1300 @@
+/**
+ * This software was developed and / or modified by Raytheon Company,
+ * pursuant to Contract DG133W-05-CQ-1067 with the US Government.
+ *
+ * U.S. EXPORT CONTROLLED TECHNICAL DATA
+ * This software product contains export-restricted data whose
+ * export/transfer/disclosure is restricted by U.S. law. Dissemination
+ * to non-U.S. persons whether in the United States or abroad requires
+ * an export license or other authorization.
+ *
+ * Contractor Name: Raytheon Company
+ * Contractor Address: 6825 Pine Street, Suite 340
+ * Mail Stop B8
+ * Omaha, NE 68106
+ * 402.291.0100
+ *
+ * See the AWIPS II Master Rights File ("Master Rights File.pdf") for
+ * further licensing information.
+ **/
+package com.raytheon.uf.common.dataplugin.ffmp;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+
+/**
+ * The ArrayBacked map is a navigable map implementation that is designed
+ * specifically for fast serialization/deserialization because the underlining
+ * data structure are simple sorted primitive arrays. It also has very efficient
+ * memory usage because there are no intermediary Objects associated with
+ * entries.
+ *
+ * Most operations are implemented as a binary search on the key array so they
+ * are guaranteed to complete in log(n) time. The exception to this rule is
+ * structural modifications (A structural modification is any operation that
+ * adds or deletes one or more mappings; merely changing the value associated
+ * with an existing key is not a structural modification.) Structural
+ * modification require copying both underlining arrays which will take up to n
+ * time and should not be considered a high performance operation. For any use
+ * of a NavigableMap which requires multiple structural modifications TreeMap
+ * would be a much better choice. For combining this map with other SortedMaps
+ * of similar size, putAll has been optimized and will perform much better then
+ * a naive loop which puts all elements.
+ *
+ * This map is not thread safe for modification and does not even throw
+ * ConcurrentModificationExceptions. Any concurrent modification will result in
+ * strange behavior and not explicit exceptions.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------ ---------- ----------- --------------------------
+ * Apr 18, 2013 bsteffen Initial creation
+ *
+ *
+ *
+ * @author bsteffen
+ * @version 1.0
+ */
+
+public class ArrayBackedMap extends AbstractMap implements
+ NavigableMap {
+
+ private long[] keys;
+
+ private float[] values;
+
+ public ArrayBackedMap(long[] keys, float[] values) {
+ this.keys = keys;
+ this.values = values;
+ }
+
+ public ArrayBackedMap(SortedMap map) {
+ this.keys = new long[map.size()];
+ this.values = new float[keys.length];
+ int i = 0;
+ for (Entry e : map.entrySet()) {
+ keys[i] = e.getKey().getTime();
+ values[i] = e.getValue();
+ i += 1;
+ }
+ }
+
+ public long[] getKeys() {
+ return keys;
+ }
+
+ public float[] getValues() {
+ return values;
+ }
+
+ /**
+ * Replacing existing values is fast (log(n)) but inserting new values is
+ * slow(n).
+ */
+ @Override
+ public Float put(Date key, Float value) {
+ int index = Arrays.binarySearch(keys, key.getTime());
+ if (index >= 0) {
+ float old = values[index];
+ values[index] = value;
+ return old;
+ } else {
+ long[] keys = new long[values.length + 1];
+ float[] values = new float[keys.length];
+ System.arraycopy(this.keys, 0, keys, 0, -index - 1);
+ System.arraycopy(this.values, 0, values, 0, -index - 1);
+ keys[-index - 1] = key.getTime();
+ values[-index - 1] = value;
+ System.arraycopy(this.keys, -index - 1, keys, -index,
+ this.values.length + index + 1);
+ System.arraycopy(this.values, -index - 1, values, -index,
+ this.values.length + index + 1);
+ this.keys = keys;
+ this.values = values;
+ return null;
+ }
+ }
+
+ /**
+ * Sorted maps which use the default sort order will be put in m+n time.
+ * Otherwise it will take m*log(m_n).
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public void putAll(Map extends Date, ? extends Float> map) {
+ if (map instanceof SortedMap && !map.isEmpty()) {
+ Comparator> c = ((SortedMap, ?>) map).comparator();
+ if (c == null) {
+ // much faster putAll.
+ long[] keys = new long[values.length + map.size()];
+ float[] values = new float[keys.length];
+ int oldIndex = 0;
+ int newIndex = 0;
+ Iterator> it = map.entrySet().iterator();
+ Entry next = (Entry) it.next();
+ long nextLong = next.getKey().getTime();
+ long myNextLong = this.keys[oldIndex];
+ while (nextLong != Long.MAX_VALUE
+ || myNextLong != Long.MAX_VALUE) {
+ boolean incMe = false;
+ if (nextLong <= myNextLong) {
+ incMe = nextLong == myNextLong;
+ keys[newIndex] = nextLong;
+ values[newIndex] = next.getValue();
+ if (it.hasNext()) {
+ next = (Entry) it.next();
+ nextLong = next.getKey().getTime();
+ } else {
+ nextLong = Long.MAX_VALUE;
+ }
+ } else {
+ keys[newIndex] = myNextLong;
+ values[newIndex] = this.values[oldIndex];
+ incMe = true;
+ }
+ if (incMe) {
+ oldIndex += 1;
+ if (oldIndex < this.keys.length) {
+ myNextLong = this.keys[oldIndex];
+ } else {
+ myNextLong = Long.MAX_VALUE;
+ }
+ }
+ newIndex += 1;
+ }
+ if (newIndex < keys.length) {
+ // there were some duplicates;
+ keys = Arrays.copyOf(keys, newIndex);
+ values = Arrays.copyOf(values, newIndex);
+ }
+ this.keys = keys;
+ this.values = values;
+ } else {
+ super.putAll(map);
+ }
+ } else {
+ super.putAll(map);
+ }
+ }
+
+ @Override
+ public Float get(Object key) {
+ if (key instanceof Date) {
+ int index = Arrays.binarySearch(keys, ((Date) key).getTime());
+ if (index >= 0) {
+ return values[index];
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return get(key) != null;
+ }
+
+ @Override
+ public Comparator super Date> comparator() {
+ return null;
+ }
+
+ @Override
+ public Date firstKey() {
+ if (keys.length == 0) {
+ return null;
+ } else {
+ return new Date(keys[0]);
+ }
+ }
+
+ @Override
+ public Date lastKey() {
+ if (keys.length == 0) {
+ return null;
+ } else {
+ return new Date(keys[keys.length - 1]);
+ }
+
+ }
+
+ @Override
+ public Entry firstEntry() {
+ if (keys.length == 0) {
+ return null;
+ } else {
+ return new EntryImpl(0);
+ }
+ }
+
+ @Override
+ public Entry lastEntry() {
+ if (keys.length == 0) {
+ return null;
+ } else {
+ return new EntryImpl(keys.length - 1);
+ }
+ }
+
+ @Override
+ public Set> entrySet() {
+ return new EntrySet(null, null);
+ }
+
+ public NavigableSet descendingKeySet() {
+ return descendingMap().navigableKeySet();
+ }
+
+ @Override
+ public NavigableSet navigableKeySet() {
+ return new NavigableKeySet(this);
+ }
+
+ private int lowerIndex(Date key, boolean inclusive) {
+ int index = Arrays.binarySearch(keys, key.getTime());
+ if (index < 0) {
+ return -index - 2;
+ } else if (inclusive) {
+ return index;
+ } else {
+ return index - 1;
+ }
+ }
+
+ private int higherIndex(Date key, boolean inclusive) {
+ int index = Arrays.binarySearch(keys, key.getTime());
+ if (index < 0) {
+ return -index - 1;
+ } else if (inclusive) {
+ return index;
+ } else {
+ return index + 1;
+ }
+ }
+
+ @Override
+ public Entry lowerEntry(Date key) {
+ int index = lowerIndex(key, false);
+ if (index >= 0) {
+ return new EntryImpl(index);
+ }
+ return null;
+ }
+
+ @Override
+ public Date lowerKey(Date key) {
+ int index = lowerIndex(key, false);
+ if (index >= 0) {
+ return new Date(keys[index]);
+ }
+ return null;
+ }
+
+ @Override
+ public Entry floorEntry(Date key) {
+ int index = lowerIndex(key, true);
+ if (index >= 0) {
+ return new EntryImpl(index);
+ }
+ return null;
+ }
+
+ @Override
+ public Date floorKey(Date key) {
+ int index = lowerIndex(key, true);
+ if (index >= 0) {
+ return new Date(keys[index]);
+ }
+ return null;
+ }
+
+ @Override
+ public Entry higherEntry(Date key) {
+ int index = higherIndex(key, false);
+ if (index < keys.length) {
+ return new EntryImpl(index);
+ }
+ return null;
+ }
+
+ @Override
+ public Date higherKey(Date key) {
+ int index = higherIndex(key, false);
+ if (index < keys.length) {
+ return new Date(keys[index]);
+ }
+ return null;
+ }
+
+ @Override
+ public Entry ceilingEntry(Date key) {
+ int index = higherIndex(key, true);
+ if (index < keys.length) {
+ return new EntryImpl(index);
+ }
+ return null;
+ }
+
+ public Date ceilingKey(Date key) {
+ int index = higherIndex(key, true);
+ if (index < keys.length) {
+ return new Date(keys[index]);
+ }
+ return null;
+ }
+
+ @Override
+ public NavigableMap descendingMap() {
+ return new DescendingMap(new SubMap(null, null));
+ }
+
+ @Override
+ public NavigableMap subMap(Date fromKey, Date toKey) {
+ return subMap(fromKey, true, toKey, false);
+ }
+
+ @Override
+ public NavigableMap headMap(Date toKey) {
+ return headMap(toKey, false);
+ }
+
+ @Override
+ public NavigableMap tailMap(Date fromKey) {
+ return tailMap(fromKey, true);
+ }
+
+ @Override
+ public NavigableMap subMap(Date fromKey,
+ boolean fromInclusive, Date toKey, boolean toInclusive) {
+ if (!fromInclusive) {
+ fromKey = new Date(fromKey.getTime() + 1);
+ }
+ if (!toInclusive) {
+ toKey = new Date(toKey.getTime() - 1);
+ }
+ return new SubMap(fromKey, toKey);
+ }
+
+ @Override
+ public NavigableMap headMap(Date toKey, boolean inclusive) {
+ if (!inclusive) {
+ toKey = new Date(toKey.getTime() - 1);
+ }
+ return new SubMap(null, toKey);
+ }
+
+ @Override
+ public NavigableMap tailMap(Date fromKey, boolean inclusive) {
+ if (!inclusive) {
+ fromKey = new Date(fromKey.getTime() + 1);
+ }
+ return new SubMap(fromKey, null);
+ }
+
+ @Override
+ public Entry pollFirstEntry() {
+ // This could be made faster.
+ Entry e = firstEntry();
+ remove(e.getKey());
+ return e;
+ }
+
+ @Override
+ public Entry pollLastEntry() {
+ // This could be made faster.
+ Entry e = lastEntry();
+ remove(e.getKey());
+ return e;
+ }
+
+ private void remove(int index) {
+ long[] keys = new long[values.length - 1];
+ float[] values = new float[keys.length];
+ System.arraycopy(this.keys, 0, keys, 0, index);
+ System.arraycopy(this.values, 0, values, 0, index);
+ System.arraycopy(this.keys, index + 1, keys, index, this.values.length
+ - index - 1);
+ System.arraycopy(this.values, index + 1, values, index,
+ this.values.length - index - 1);
+ this.keys = keys;
+ this.values = values;
+ }
+
+ private class EntrySet extends AbstractSet> {
+
+ private final boolean descending;
+
+ private final Date startTime;
+
+ private final Date endTime;
+
+ public EntrySet(Date startTime, Date endTime) {
+ this(startTime, endTime, false);
+ }
+
+ public EntrySet(Date startTime, Date endTime, boolean descending) {
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.descending = descending;
+ }
+
+ @Override
+ public Iterator> iterator() {
+ if (descending) {
+ return new DecsendingEntryIterator(startTime, endTime);
+ } else {
+ return new EntryIterator(startTime, endTime);
+ }
+
+ }
+
+ @Override
+ public int size() {
+ int startIndex = 0;
+ int endIndex = keys.length;
+ if (startTime != null) {
+ startIndex = higherIndex(startTime, true);
+ }
+ if (endTime != null) {
+ endIndex = lowerIndex(endTime, true) + 1;
+ }
+ return endIndex - startIndex;
+ }
+
+ }
+
+ private class EntryIterator implements Iterator> {
+
+ private final long endTime;
+
+ private int index;
+
+ public EntryIterator(Date startTime, Date endTime) {
+ if (startTime != null) {
+ index = higherIndex(startTime, true);
+ } else {
+ index = 0;
+ }
+ if (endTime != null) {
+ this.endTime = endTime.getTime();
+ } else {
+ this.endTime = Long.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return index < keys.length && keys[index] <= endTime;
+ }
+
+ @Override
+ public Entry next() {
+ if (hasNext()) {
+ EntryImpl e = new EntryImpl(index);
+ index += 1;
+ return e;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove() {
+ ArrayBackedMap.this.remove(index - 1);
+ }
+
+ }
+
+ private class DecsendingEntryIterator implements
+ Iterator> {
+
+ private final long endTime;
+
+ private int index;
+
+ public DecsendingEntryIterator(Date startTime, Date endTime) {
+ if (endTime != null) {
+ index = lowerIndex(endTime, true);
+ } else {
+ index = keys.length - 1;
+ }
+ if (startTime != null) {
+ this.endTime = startTime.getTime();
+ } else {
+ this.endTime = Long.MIN_VALUE;
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return index >= 0 && keys[index] >= endTime;
+ }
+
+ @Override
+ public Entry next() {
+ if (hasNext()) {
+ EntryImpl e = new EntryImpl(index);
+ index -= 1;
+ return e;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove() {
+ ArrayBackedMap.this.remove(index + 1);
+ }
+
+ }
+
+ private class EntryImpl implements Entry {
+
+ private final int index;
+
+ public EntryImpl(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public Date getKey() {
+ return new Date(keys[index]);
+ }
+
+ @Override
+ public Float getValue() {
+ return values[index];
+ }
+
+ @Override
+ public Float setValue(Float value) {
+ Float f = values[index];
+ values[index] = value;
+ return f;
+ }
+
+ }
+
+ private static class NavigableKeySet extends AbstractSet implements
+ NavigableSet {
+
+ private final NavigableMap map;
+
+ public NavigableKeySet(NavigableMap map) {
+ this.map = map;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return map.keySet().iterator();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public Comparator super Date> comparator() {
+ return map.comparator();
+ }
+
+ @Override
+ public Date first() {
+ return map.firstKey();
+ }
+
+ @Override
+ public Date last() {
+ return map.lastKey();
+ }
+
+ @Override
+ public Date lower(Date e) {
+ return map.lowerKey(e);
+ }
+
+ @Override
+ public Date floor(Date e) {
+ return map.floorKey(e);
+ }
+
+ @Override
+ public Date ceiling(Date e) {
+ return map.ceilingKey(e);
+ }
+
+ @Override
+ public Date higher(Date e) {
+ return map.higherKey(e);
+ }
+
+ @Override
+ public Date pollFirst() {
+ return map.pollFirstEntry().getKey();
+ }
+
+ @Override
+ public Date pollLast() {
+ return map.pollLastEntry().getKey();
+ }
+
+ @Override
+ public NavigableSet descendingSet() {
+ return map.descendingKeySet();
+ }
+
+ @Override
+ public Iterator descendingIterator() {
+ return descendingSet().iterator();
+ }
+
+ @Override
+ public NavigableSet subSet(Date fromElement,
+ boolean fromInclusive, Date toElement, boolean toInclusive) {
+ return map.subMap(fromElement, fromInclusive, toElement,
+ toInclusive).navigableKeySet();
+ }
+
+ @Override
+ public NavigableSet headSet(Date toElement, boolean inclusive) {
+ return map.headMap(toElement, inclusive).navigableKeySet();
+ }
+
+ @Override
+ public NavigableSet tailSet(Date fromElement, boolean inclusive) {
+ return map.tailMap(fromElement, inclusive).navigableKeySet();
+ }
+
+ @Override
+ public SortedSet subSet(Date fromElement, Date toElement) {
+ return subSet(fromElement, true, toElement, false);
+ }
+
+ @Override
+ public SortedSet headSet(Date toElement) {
+ return headSet(toElement, false);
+ }
+
+ @Override
+ public SortedSet tailSet(Date fromElement) {
+ return tailSet(fromElement, true);
+ }
+
+ }
+
+ private class SubMap extends AbstractMap implements
+ NavigableMap {
+
+ private final Date min;
+
+ private final Date max;
+
+ public SubMap(Date min, Date max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public Comparator super Date> comparator() {
+ return ArrayBackedMap.this.comparator();
+ }
+
+ @Override
+ public Date firstKey() {
+ if (min == null) {
+ return ArrayBackedMap.this.firstKey();
+ } else {
+ return ArrayBackedMap.this.ceilingKey(min);
+ }
+ }
+
+ @Override
+ public Date lastKey() {
+ if (max == null) {
+ return ArrayBackedMap.this.lastKey();
+ } else {
+ return ArrayBackedMap.this.floorKey(max);
+ }
+ }
+
+ @Override
+ public Entry lowerEntry(Date key) {
+ if (max != null && key.after(max)) {
+ return ArrayBackedMap.this.floorEntry(max);
+ }
+ Entry e = ArrayBackedMap.this.lowerEntry(key);
+ if (e != null && min != null && e.getKey().before(min)) {
+ return null;
+ }
+ return e;
+ }
+
+ @Override
+ public Date lowerKey(Date key) {
+ Entry e = lowerEntry(key);
+ return e == null ? null : e.getKey();
+ }
+
+ @Override
+ public Entry floorEntry(Date key) {
+ if (max != null && key.after(max)) {
+ return ArrayBackedMap.this.floorEntry(max);
+ }
+ Entry e = ArrayBackedMap.this.floorEntry(key);
+ if (e != null && min != null && e.getKey().before(min)) {
+ return null;
+ }
+ return e;
+ }
+
+ @Override
+ public Date floorKey(Date key) {
+ Entry e = floorEntry(key);
+ return e == null ? null : e.getKey();
+ }
+
+ @Override
+ public Entry ceilingEntry(Date key) {
+ if (min != null && key.before(min)) {
+ return ArrayBackedMap.this.floorEntry(min);
+ }
+ Entry e = ArrayBackedMap.this.ceilingEntry(key);
+ if (e != null && max != null && e.getKey().after(max)) {
+ return null;
+ }
+ return e;
+ }
+
+ @Override
+ public Date ceilingKey(Date key) {
+ Entry e = ceilingEntry(key);
+ return e == null ? null : e.getKey();
+ }
+
+ @Override
+ public Entry higherEntry(Date key) {
+ if (min != null && key.before(min)) {
+ return ArrayBackedMap.this.floorEntry(min);
+ }
+ Entry e = ArrayBackedMap.this.higherEntry(key);
+ if (e != null && max != null && e.getKey().after(max)) {
+ return null;
+ }
+ return e;
+ }
+
+ @Override
+ public Date higherKey(Date key) {
+ Entry e = higherEntry(key);
+ return e == null ? null : e.getKey();
+ }
+
+ @Override
+ public Entry firstEntry() {
+ if (min == null) {
+ return ArrayBackedMap.this.firstEntry();
+ } else {
+ return ArrayBackedMap.this.ceilingEntry(min);
+ }
+ }
+
+ @Override
+ public Entry lastEntry() {
+ if (max == null) {
+ return ArrayBackedMap.this.lastEntry();
+ } else {
+ return ArrayBackedMap.this.floorEntry(max);
+ }
+ }
+
+ @Override
+ public Entry pollFirstEntry() {
+ if (min == null) {
+ return ArrayBackedMap.this.pollFirstEntry();
+ } else {
+ Entry e = firstEntry();
+ ArrayBackedMap.this.remove(e.getKey());
+ return e;
+ }
+ }
+
+ @Override
+ public Entry pollLastEntry() {
+ if (min == null) {
+ return ArrayBackedMap.this.pollLastEntry();
+ } else {
+ Entry e = lastEntry();
+ ArrayBackedMap.this.remove(e.getKey());
+ return e;
+ }
+ }
+
+ @Override
+ public NavigableMap descendingMap() {
+ return new DescendingMap(this);
+ }
+
+ @Override
+ public NavigableSet navigableKeySet() {
+ return new NavigableKeySet(this);
+ }
+
+ @Override
+ public NavigableSet descendingKeySet() {
+ return descendingMap().navigableKeySet();
+ }
+
+ @Override
+ public SubMap subMap(Date fromKey,
+ boolean fromInclusive, Date toKey, boolean toInclusive) {
+ if (!fromInclusive) {
+ fromKey = new Date(fromKey.getTime() + 1);
+ }
+ if (!toInclusive) {
+ toKey = new Date(toKey.getTime() - 1);
+ }
+ if (checkKey(fromKey) && checkKey(toKey)) {
+ return new SubMap(fromKey, toKey);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public SubMap headMap(Date toKey, boolean inclusive) {
+ if (!inclusive) {
+ toKey = new Date(toKey.getTime() - 1);
+ }
+ if (checkKey(toKey)) {
+ return new SubMap(min, toKey);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public SubMap tailMap(Date fromKey, boolean inclusive) {
+ if (!inclusive) {
+ fromKey = new Date(fromKey.getTime() + 1);
+ }
+ if (checkKey(fromKey)) {
+ return new SubMap(fromKey, max);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ public SubMap subMap(Date fromKey, Date toKey) {
+ return subMap(fromKey, true, toKey, false);
+ }
+
+ @Override
+ public SubMap headMap(Date toKey) {
+ return headMap(toKey, false);
+ }
+
+ @Override
+ public SubMap tailMap(Date fromKey) {
+ return tailMap(fromKey, true);
+ }
+
+ @Override
+ public Set> entrySet() {
+ return new EntrySet(min, max);
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return get(key) != null;
+ }
+
+ @Override
+ public Float get(Object key) {
+ if (checkKey(key)) {
+ return ArrayBackedMap.this.get(key);
+ }
+ return null;
+ }
+
+ @Override
+ public Float put(Date key, Float value) {
+ if (checkKey(key)) {
+ return ArrayBackedMap.this.put(key, value);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public void putAll(Map extends Date, ? extends Float> m) {
+ if (min != null || max != null) {
+ for (Date key : m.keySet()) {
+ if (!checkKey(key)) {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+ ArrayBackedMap.this.putAll(m);
+ }
+
+ @Override
+ public Float remove(Object key) {
+ if (checkKey(key)) {
+ return ArrayBackedMap.this.remove(key);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private boolean checkKey(Object key) {
+ if (key instanceof Date) {
+ Date k = (Date) key;
+ if (min != null && min.after(k)) {
+ return false;
+ }
+ if (max != null && max.before(k)) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ }
+
+ private class DescendingMap extends AbstractMap implements
+ NavigableMap {
+
+ private final SubMap subMap;
+
+ public DescendingMap(SubMap subMap) {
+ this.subMap = subMap;
+ }
+
+ @Override
+ public Comparator super Date> comparator() {
+ return Collections.reverseOrder(subMap.comparator());
+ }
+
+ @Override
+ public Date firstKey() {
+ return subMap.lastKey();
+ }
+
+ @Override
+ public Date lastKey() {
+ return subMap.firstKey();
+ }
+
+ @Override
+ public Entry lowerEntry(Date key) {
+ return subMap.higherEntry(key);
+ }
+
+ @Override
+ public Date lowerKey(Date key) {
+ return subMap.higherKey(key);
+ }
+
+ @Override
+ public Entry floorEntry(Date key) {
+ return subMap.ceilingEntry(key);
+ }
+
+ @Override
+ public Date floorKey(Date key) {
+ return subMap.ceilingKey(key);
+ }
+
+ @Override
+ public Entry ceilingEntry(Date key) {
+ return subMap.floorEntry(key);
+ }
+
+ @Override
+ public Date ceilingKey(Date key) {
+ return subMap.floorKey(key);
+ }
+
+ @Override
+ public Entry higherEntry(Date key) {
+ return subMap.lowerEntry(key);
+ }
+
+ @Override
+ public Date higherKey(Date key) {
+ return subMap.lowerKey(key);
+ }
+
+ @Override
+ public Entry firstEntry() {
+ return subMap.lastEntry();
+ }
+
+ @Override
+ public Entry lastEntry() {
+ return subMap.firstEntry();
+ }
+
+ @Override
+ public Entry pollFirstEntry() {
+ return subMap.pollLastEntry();
+ }
+
+ @Override
+ public Entry pollLastEntry() {
+ return subMap.pollFirstEntry();
+ }
+
+ @Override
+ public NavigableMap descendingMap() {
+ return subMap;
+ }
+
+ @Override
+ public NavigableSet navigableKeySet() {
+ return new NavigableKeySet(this);
+ }
+
+ @Override
+ public NavigableSet descendingKeySet() {
+ return subMap.navigableKeySet();
+ }
+
+ @Override
+ public NavigableMap subMap(Date fromKey,
+ boolean fromInclusive, Date toKey, boolean toInclusive) {
+ return new DescendingMap(subMap.subMap(toKey, toInclusive, fromKey,
+ fromInclusive));
+ }
+
+ @Override
+ public NavigableMap headMap(Date toKey, boolean inclusive) {
+ return new DescendingMap(subMap.tailMap(toKey, inclusive));
+ }
+
+ @Override
+ public NavigableMap tailMap(Date fromKey, boolean inclusive) {
+ return new DescendingMap(subMap.headMap(fromKey, inclusive));
+ }
+
+ @Override
+ public NavigableMap subMap(Date fromKey, Date toKey) {
+ return new DescendingMap(subMap.subMap(toKey, false, fromKey, true));
+ }
+
+ @Override
+ public NavigableMap headMap(Date toKey) {
+ return new DescendingMap(subMap.tailMap(toKey, false));
+ }
+
+ @Override
+ public NavigableMap tailMap(Date fromKey) {
+ return new DescendingMap(subMap.headMap(fromKey, true));
+ }
+
+ @Override
+ public Set> entrySet() {
+ return new EntrySet(subMap.min, subMap.max, true);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return subMap.containsValue(value);
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return subMap.containsKey(key);
+ }
+
+ @Override
+ public Float get(Object key) {
+ return subMap.get(key);
+ }
+
+ @Override
+ public Float put(Date key, Float value) {
+ return subMap.put(key, value);
+ }
+
+ @Override
+ public Float remove(Object key) {
+ return subMap.remove(key);
+ }
+
+ @Override
+ public void putAll(Map extends Date, ? extends Float> m) {
+ subMap.putAll(m);
+ }
+
+ @Override
+ public void clear() {
+ subMap.clear();
+ }
+
+ }
+
+ /**
+ * compares the behavior of the provided map to a treeMap and verify all
+ * operations produce equivalent results.
+ *
+ * @param map
+ */
+ protected static void test(NavigableMap map) {
+ TreeMap treeMap = new TreeMap(map);
+ if (!map.descendingKeySet().equals(treeMap.descendingKeySet())) {
+ System.err.println("DescendingKeySet test failed");
+ }
+ if (!map.navigableKeySet().equals(treeMap.navigableKeySet())) {
+ System.err.println("NavigableKeySet test failed");
+ }
+ if (!map.firstKey().equals(treeMap.firstKey())) {
+ System.err.println("FirstKey test failed");
+ }
+ if (!map.lastKey().equals(treeMap.lastKey())) {
+ System.err.println("LastKey test failed");
+ }
+
+ List testDates = new ArrayList();
+
+ Iterator it = map.keySet().iterator();
+ Date prev = it.next();
+ Date next = it.next();
+ for (int i = 0; i < map.size() / 3; i += 1) {
+ prev = next;
+ next = it.next();
+ }
+ testDates.add(next);
+ testDates.add(prev);
+ // a date about 1/3 of the way in and not equal to any keys
+ testDates.add(new Date(prev.getTime() / 2 + next.getTime() / 2));
+ for (int i = 0; i < map.size() / 3; i += 1) {
+ prev = next;
+ next = it.next();
+ }
+ testDates.add(next);
+ testDates.add(prev);
+ // a date about 2/3 of the way in and not equal to any keys
+ testDates.add(new Date(prev.getTime() / 2 + next.getTime() / 2));
+
+ testDates.add(map.firstKey());
+ testDates.add(map.lastKey());
+ // some dates just before and after the beginning and end.
+ testDates.add(new Date(map.firstKey().getTime() - 1000));
+ testDates.add(new Date(map.firstKey().getTime() + 1000));
+ testDates.add(new Date(map.lastKey().getTime() - 1000));
+ testDates.add(new Date(map.lastKey().getTime() + 1000));
+ for (Date test : testDates) {
+ Date d1 = map.ceilingKey(test);
+ Date d2 = treeMap.ceilingKey(test);
+ if (d1 == null) {
+ if (d2 != null) {
+ System.err.println("CeilingKey test failed");
+ }
+ } else if (!d1.equals(d2)) {
+ System.err.println("CeilingKey test failed");
+ }
+ }
+ for (Date test : testDates) {
+ Date d1 = map.higherKey(test);
+ Date d2 = treeMap.higherKey(test);
+ if (d1 == null) {
+ if (d2 != null) {
+ System.err.println("HigherKey test failed");
+ }
+ } else if (!d1.equals(d2)) {
+ System.err.println("HigherKey test failed");
+ }
+ }
+ for (Date test : testDates) {
+ Date d1 = map.floorKey(test);
+ Date d2 = treeMap.floorKey(test);
+ if (d1 == null) {
+ if (d2 != null) {
+ System.err.println("FloorKey test failed");
+ }
+ } else if (!d1.equals(d2)) {
+ System.err.println("FloorKey test failed");
+ }
+ }
+ for (Date test : testDates) {
+ Date d1 = map.lowerKey(test);
+ Date d2 = treeMap.lowerKey(test);
+ if (d1 == null) {
+ if (d2 != null) {
+ System.err.println("LowerKey test failed");
+ }
+ } else if (!d1.equals(d2)) {
+ System.err.println("LowerKey test failed");
+ }
+ }
+ for (Date test : testDates) {
+ NavigableMap map1 = map.headMap(test, true);
+ NavigableMap map2 = treeMap.headMap(test, true);
+ if (!map1.equals(map2)) {
+ System.err.println("HeadMap test failed");
+ }
+ }
+ for (Date test : testDates) {
+ NavigableMap map1 = map.tailMap(test, false);
+ NavigableMap map2 = treeMap.tailMap(test, false);
+ if (!map1.equals(map2)) {
+ System.err.println("TailMap test failed");
+ }
+ }
+ for (Date test1 : testDates) {
+ for (Date test2 : testDates) {
+ try {
+ NavigableMap map1 = map.subMap(test1, false,
+ test2, true);
+ NavigableMap map2 = treeMap.subMap(test1,
+ false, test2, true);
+ if (!map1.equals(map2)) {
+ System.err.println("SubMap test failed");
+ }
+ } catch (IllegalArgumentException e) {
+ ;// dates aren't in order and tree map freaks.
+ }
+ }
+ }
+ for (Date test : testDates) {
+ map.remove(test);
+ treeMap.remove(test);
+ }
+ if (!map.equals(treeMap)) {
+ System.err.println("Remove test failed");
+ }
+ for (Date test : testDates) {
+ map.put(test, 11.0f);
+ treeMap.put(test, 11.0f);
+ }
+ if (!map.equals(treeMap)) {
+ System.err.println("Put test failed");
+ }
+ for (Date test : testDates) {
+ map.remove(test);
+ }
+ map.putAll(treeMap);
+ if (!map.equals(treeMap)) {
+ System.err.println("PutAll test failed");
+ }
+ if (map instanceof ArrayBackedMap) {
+ test(map.descendingMap());
+ }
+ }
+
+}
diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPBasin.java b/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPBasin.java
index fba5e152cc..b494cf0bb6 100644
--- a/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPBasin.java
+++ b/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPBasin.java
@@ -20,10 +20,10 @@
package com.raytheon.uf.common.dataplugin.ffmp;
import java.util.ArrayList;
-import java.util.Comparator;
+import java.util.Collections;
import java.util.Date;
-import java.util.List;
import java.util.Map.Entry;
+import java.util.NavigableMap;
import java.util.TreeMap;
import javax.persistence.Transient;
@@ -31,7 +31,6 @@ import javax.persistence.Transient;
import com.raytheon.uf.common.serialization.ISerializableObject;
import com.raytheon.uf.common.serialization.annotations.DynamicSerialize;
import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement;
-import com.raytheon.uf.common.time.util.ImmutableDate;
/**
* FFMP basin/aggregated value holder
@@ -44,6 +43,9 @@ import com.raytheon.uf.common.time.util.ImmutableDate;
* ------------ ---------- ----------- --------------------------
* 06/22/09 2152 D. Hladky Initial release
* 01/27/13 1478 D. Hladky Added support for writing aggregate record cache
+ * Apr 22, 2013 1912 bsteffen optimized the creation of NavigableMaps
+ * from aggregate records and delayed
+ * TreeMap creation to the tertiary loader.
*
*
*
@@ -65,7 +67,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
* not serialized!
**/
@Transient
- protected TreeMap values;
+ protected NavigableMap values;
/** object used for serialization **/
@DynamicSerializeElement
@@ -316,6 +318,12 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
*/
public void setValue(Date date, Float dvalue) {
synchronized (values) {
+ if (!(values instanceof TreeMap) && !values.containsKey(dvalue)) {
+ // ArrayBackedMap may have been used if this basin was
+ // deserialized. It is much faster to do inserts on a TreeMap so
+ // convert now.
+ values = new TreeMap(values);
+ }
values.put(date, dvalue);
}
}
@@ -325,7 +333,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
*
* @return
*/
- public TreeMap getValues() {
+ public NavigableMap getValues() {
return values;
}
@@ -343,13 +351,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
*/
public FFMPBasin() {
- values = new TreeMap(new Comparator() {
- @Override
- public int compare(Date o1, Date o2) {
- // Null checks?
- return (int)(o2.getTime() - o1.getTime()) ;
- }
- });
+ values = new TreeMap(Collections.reverseOrder());
}
/**
@@ -360,14 +362,7 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
public FFMPBasin(Long pfaf, boolean aggregated) {
setPfaf(pfaf);
setAggregated(aggregated);
- values = new TreeMap(new Comparator() {
- @Override
- public int compare(Date o1, Date o2) {
- // Null checks?
- return (int)(o2.getTime() - o1.getTime()) ;
- }
-
- });
+ values = new TreeMap(Collections.reverseOrder());
}
/**
@@ -375,18 +370,18 @@ public class FFMPBasin implements ISerializableObject, Cloneable {
*
* @param times
*/
- public void deserialize(List times) {
+ public void deserialize(long[] times) {
// safe to avoid Array Index Exceptions / shouldn't happen but.....
- if (serializedValues != null && (times.size() == serializedValues.length)) {
+ if (serializedValues != null
+ && (times.length == serializedValues.length)) {
+ NavigableMap fastMap = new ArrayBackedMap(times,
+ serializedValues);
+ values = fastMap.descendingMap();
- int i = 0;
- for (Long time : times) {
- values.put(new Date(time), serializedValues[i]);
- i++;
- }
- //System.out.println("populated :"+i+" pfaf : "+pfaf);
+ // values = new TreeMap(fastMap.descendingMap());
}
+ serializedValues = null;
}
/**
diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPBasinData.java b/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPBasinData.java
index 9434e4b4da..60cf06b550 100644
--- a/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPBasinData.java
+++ b/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPBasinData.java
@@ -576,8 +576,12 @@ public class FFMPBasinData implements ISerializableObject {
* @param times
*/
public void populate(List times) {
+ long[] timesArr = new long[times.size()];
+ for (int i = 0; i < timesArr.length; i += 1) {
+ timesArr[i] = times.get(i);
+ }
for (FFMPBasin basin : getBasins().values()) {
- basin.deserialize(times);
+ basin.deserialize(timesArr);
}
}