001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.vector;
003
004import java.util.Collection;
005import java.util.Collections;
006import java.util.HashSet;
007import java.util.LinkedList;
008import java.util.Map;
009import java.util.Set;
010import java.util.concurrent.locks.ReentrantReadWriteLock;
011
012import org.openstreetmap.gui.jmapviewer.Tile;
013import org.openstreetmap.josm.data.DataSource;
014import org.openstreetmap.josm.data.osm.DataSet;
015import org.openstreetmap.josm.data.osm.INode;
016import org.openstreetmap.josm.data.osm.IPrimitive;
017import org.openstreetmap.josm.data.osm.IRelation;
018import org.openstreetmap.josm.data.osm.IWay;
019import org.openstreetmap.josm.data.osm.PrimitiveId;
020import org.openstreetmap.josm.data.osm.QuadBucketPrimitiveStore;
021import org.openstreetmap.josm.data.osm.Storage;
022import org.openstreetmap.josm.tools.Logging;
023
024/**
025 * A class that stores data (essentially a simple {@link DataSet})
026 * @author Taylor Smock
027 * @param <O> Type of OSM primitive
028 * @param <N> Type of node
029 * @param <W> Type of way
030 * @param <R> Type of relation
031 * @since 17862
032 */
033class DataStore<O extends IPrimitive, N extends INode, W extends IWay<N>, R extends IRelation<?>> {
034    /**
035     * This literally only exists to make {@link QuadBucketPrimitiveStore#removePrimitive} public
036     *
037     * @param <N> The node type
038     * @param <W> The way type
039     * @param <R> The relation type
040     */
041    static class LocalQuadBucketPrimitiveStore<N extends INode, W extends IWay<N>, R extends IRelation<?>>
042      extends QuadBucketPrimitiveStore<N, W, R> {
043        // Allow us to remove primitives (protected in {@link QuadBucketPrimitiveStore})
044        @Override
045        public void removePrimitive(IPrimitive primitive) {
046            super.removePrimitive(primitive);
047        }
048    }
049
050    protected final LocalQuadBucketPrimitiveStore<N, W, R> store = new LocalQuadBucketPrimitiveStore<>();
051    protected final Storage<O> allPrimitives = new Storage<>(new Storage.PrimitiveIdHash(), true);
052    // TODO what happens when I use hashCode?
053    protected final Set<Tile> addedTiles = Collections.synchronizedSet(new HashSet<>());
054    protected final Map<PrimitiveId, O> primitivesMap = Collections.synchronizedMap(allPrimitives
055      .foreignKey(new Storage.PrimitiveIdHash()));
056    protected final Collection<DataSource> dataSources = new LinkedList<>();
057    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
058
059    public QuadBucketPrimitiveStore<N, W, R> getStore() {
060        return this.store;
061    }
062
063    public Storage<O> getAllPrimitives() {
064        return this.allPrimitives;
065    }
066
067    /**
068     * Get the primitives map.
069     * The returned map is a {@link Collections#synchronizedMap}. Please synchronize on it.
070     * @return The Primitives map.
071     */
072    public Map<PrimitiveId, O> getPrimitivesMap() {
073        return this.primitivesMap;
074    }
075
076    public Collection<DataSource> getDataSources() {
077        return Collections.unmodifiableCollection(dataSources);
078    }
079
080    /**
081     * Add a datasource to this data set
082     * @param dataSource The data soure to add
083     */
084    public void addDataSource(DataSource dataSource) {
085        this.dataSources.add(dataSource);
086    }
087
088    /**
089     * Add a primitive to this dataset
090     * @param primitive The primitive to remove
091     */
092    protected void removePrimitive(O primitive) {
093        if (primitive == null) {
094            return;
095        }
096        try {
097            this.readWriteLock.writeLock().lockInterruptibly();
098            if (this.allPrimitives.contains(primitive)) {
099                this.store.removePrimitive(primitive);
100                this.allPrimitives.remove(primitive);
101                this.primitivesMap.remove(primitive.getPrimitiveId());
102            }
103        } catch (InterruptedException e) {
104            Logging.error(e);
105            Thread.currentThread().interrupt();
106        } finally {
107            if (this.readWriteLock.isWriteLockedByCurrentThread()) {
108                this.readWriteLock.writeLock().unlock();
109            }
110        }
111    }
112
113    /**
114     * Add a primitive to this dataset
115     * @param primitive The primitive to add
116     */
117    protected void addPrimitive(O primitive) {
118        this.store.addPrimitive(primitive);
119        this.allPrimitives.add(primitive);
120        this.primitivesMap.put(primitive.getPrimitiveId(), primitive);
121    }
122
123    /**
124     * Get the read/write lock for this dataset
125     * @return The read/write lock
126     */
127    protected ReentrantReadWriteLock getReadWriteLock() {
128        return this.readWriteLock;
129    }
130}