/* * Copyright (c) 2014 Oculus Info Inc. http://www.oculusinfo.com/ * * Released under the MIT License. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.oculusinfo.binning.impl; import com.oculusinfo.binning.BinIndex; import com.oculusinfo.binning.TileData; import com.oculusinfo.binning.TileDataMetadataImpl; import com.oculusinfo.binning.TileIndex; import com.oculusinfo.factory.util.Pair; import java.util.*; /** * This class represents a tile's worth of data as a sparse array. * * It also contains the tile index describing the position of the tile. * * This object is not necessarily immutable. * * @author nkronenfeld * * @param <T> The type of data stored in the bins of this tile. */ public class SparseTileData<T> extends TileDataMetadataImpl<T> implements TileData<T> { private static final long serialVersionUID = 1L; private TileIndex _definition; // We keep data as a double-map to avoid having to construct a BinIndex each query. private Map<Integer, Map<Integer, T>> _data; private T _defaultValue; // No-argument constructor, really just for use by Kryo, but we call it from // the main constructor just to get rid of the warning. private SparseTileData () { super(); } /** * Construct an empty sparse tile data object for a particular tile with no data. * * @param definition The index of the tile whose data is to be collected by this object. */ public SparseTileData (TileIndex definition) { this(definition, (T) null); } /** * Construct an empty, sparse tile for a particular tile index. * * @param definition The index of the tile whose data is to be * collected by this object. * @param defaultValue The default value of each bin */ public SparseTileData (TileIndex definition, T defaultValue) { this(); _definition = definition; _defaultValue = defaultValue; _data = new HashMap<>(); } /** * Construct a tile for a particular tile index, with preset data. Note the passed-in preset data is used as is, * not copied. * * @param definition * The index of the tile whose data is to be represented by this * object. * @param tileData * The data for this tile. The index of the outer map specifies the X coordinate of the bin; the inner * map, the Y coordinate. */ public SparseTileData (TileIndex definition, Map<Integer, Map<Integer, T>> tileData, T defaultValue) { super(); _definition = definition; _defaultValue = defaultValue; _data = tileData; } /** {@inheritDoc} */ @Override public TileIndex getDefinition () { return _definition; } /** {@inheritDoc} */ @Override public T getDefaultValue () { return _defaultValue; } /** {@inheritDoc} */ @Override public void setBin(int x, int y, T value) { if (x < 0 || x >= _definition.getXBins()) { throw new IllegalArgumentException("Bin x index is outside of tile's valid bin range"); } if (y < 0 || y >= _definition.getYBins()) { throw new IllegalArgumentException("Bin y index is outside of tile's valid bin range"); } if (!_data.containsKey(x)) _data.put(x, new HashMap<Integer, T>()); _data.get(x).put(y, value); } /** {@inheritDoc} */ @Override public T getBin(int x, int y) { if (x < 0 || x >= _definition.getXBins()) { throw new IllegalArgumentException("Bin x index is outside of tile's valid bin range"); } if (y < 0 || y >= _definition.getYBins()) { throw new IllegalArgumentException("Bin y index is outside of tile's valid bin range"); } if (_data.containsKey(x)) { Map<Integer, T> xMap = _data.get(x); if (xMap.containsKey(y)) { return xMap.get(y); } } return _defaultValue; } private static List<Integer> getKeys (Map<Integer, ?> map) { List<Integer> vals = new ArrayList<Integer>(map.keySet()); Collections.sort(vals); return vals; } /** * Get an iterator over all our defined data, ignoring defaulted bins */ public Iterator<Pair<BinIndex, T>> getData () { return new DataIterator(); } /** * Get the default value for bins not defined in our sparse array. */ public T getDefaultBinValue () { return _defaultValue; } private class DataIterator implements Iterator<Pair<BinIndex, T>> { private Iterator<Integer> _xVals = getKeys(_data).iterator(); private int x; private Iterator<Integer> _yVals = null; private boolean hasNextY () { return (null != _yVals && _yVals.hasNext()); } private boolean incrementX () { if (!_xVals.hasNext()) return false; x = _xVals.next(); _yVals = getKeys(_data.get(x)).iterator(); return true; } @Override public boolean hasNext() { // If we can increment Y, then we're done - we have another value if (hasNextY()) return true; // If we can't increment X, then we're done - we don't have another value if (!incrementX()) return false; // We may or may not be done, we have to check if there is a Y in the next X. return hasNext(); } @Override public Pair<BinIndex, T> next() { if (hasNextY()) { int y = _yVals.next(); return new Pair<BinIndex, T>(new BinIndex(x, y), getBin(x, y)); } else if (incrementX()) { return next(); } else { return null; } } @Override public void remove() { throw new UnsupportedOperationException("Illegal to remove elements from SparseTileData.getData()"); } } @Override public String toString () { return "<sparse-tile index=\""+getDefinition()+"\", default=\""+_defaultValue+"\"/>"; } }