* ds.getReadLock().lock();
* try {
* // .. do something with dataset
* } finally {
* ds.getReadLock().unlock();
* }
*
*
* Write lock should be used in case of bulk operations. In addition to ensuring that other threads can't
* use dataset in the middle of modifications it also stops sending of dataset events. That's good for performance
* reasons - GUI can be updated after all changes are done.
* Sample usage:
*
* ds.beginUpdate()
* try {
* // .. do modifications
* } finally {
* ds.endUpdate();
* }
*
*
* Note that it is not necessary to call beginUpdate/endUpdate for every dataset modification - dataset will get locked
* automatically.
*
* Note that locks cannot be upgraded - if one threads use read lock and and then write lock, dead lock will occur - see #5814 for
* sample ticket
*
* @author imi
*/
public final class DataSet extends QuadBucketPrimitiveStore implements Data, ProjectionChangeListener {
/**
* Upload policy.
*
* Determines if upload to the OSM server is intended, discouraged, or
* disabled / blocked.
*/
public enum UploadPolicy {
/**
* Normal dataset, upload intended.
*/
NORMAL("true"),
/**
* Upload discouraged, for example when using or distributing a private dataset.
*/
DISCOURAGED("false"),
/**
* Upload blocked.
* Upload options completely disabled. Intended for special cases
* where a warning dialog is not enough, see #12731.
*
* For the user, it shouldn't be too easy to disable this flag.
*/
BLOCKED("never");
final String xmlFlag;
UploadPolicy(String xmlFlag) {
this.xmlFlag = xmlFlag;
}
/**
* Get the corresponding value of the upload='...'
XML-attribute
* in the .osm file.
* @return value of the upload
attribute
*/
public String getXmlFlag() {
return xmlFlag;
}
}
/**
* Maximum number of events that can be fired between beginUpdate/endUpdate to be send as single events (ie without DatasetChangedEvent)
*/
private static final int MAX_SINGLE_EVENTS = 30;
/**
* Maximum number of events to kept between beginUpdate/endUpdate. When more events are created, that simple DatasetChangedEvent is sent)
*/
private static final int MAX_EVENTS = 1000;
private final Storageselection
.
* Notifies all {@link SelectionChangedListener} if fireSelectionChangeEvent
is true.
*
* @param selection the selection
* @param fireSelectionChangeEvent true, if the selection change listeners are to be notified; false, otherwise
* @deprecated Use {@link #setSelected(Collection)} instead. To be removed end of 2017. Does not seem to be used by plugins.
*/
@Deprecated
public void setSelected(Collection extends PrimitiveId> selection, boolean fireSelectionChangeEvent) {
setSelected(selection);
}
/**
* Sets the current selection to the primitives in selection
* and notifies all {@link SelectionChangedListener}.
*
* @param selection the selection
*/
public void setSelected(Collection extends PrimitiveId> selection) {
setSelected(selection.stream());
}
/**
* Sets the current selection to the primitives in osm
* and notifies all {@link SelectionChangedListener}.
*
* @param osm the primitives to set. null
values are ignored for now, but this may be removed in the future.
*/
public void setSelected(PrimitiveId... osm) {
setSelected(Stream.of(osm).filter(Objects::nonNull));
}
private void setSelected(Stream extends PrimitiveId> stream) {
doSelectionChange(old -> new SelectionReplaceEvent(this, old,
stream.map(this::getPrimitiveByIdChecked).filter(Objects::nonNull)));
}
/**
* Adds the primitives in selection
to the current selection
* and notifies all {@link SelectionChangedListener}.
*
* @param selection the selection
*/
public void addSelected(Collection extends PrimitiveId> selection) {
addSelected(selection.stream());
}
/**
* Adds the primitives in osm
to the current selection
* and notifies all {@link SelectionChangedListener}.
*
* @param osm the primitives to add
*/
public void addSelected(PrimitiveId... osm) {
addSelected(Stream.of(osm));
}
private void addSelected(Stream extends PrimitiveId> stream) {
doSelectionChange(old -> new SelectionAddEvent(this, old,
stream.map(this::getPrimitiveByIdChecked).filter(Objects::nonNull)));
}
/**
* Removes the selection from every value in the collection.
* @param osm The collection of ids to remove the selection from.
*/
public void clearSelection(PrimitiveId... osm) {
clearSelection(Stream.of(osm));
}
/**
* Removes the selection from every value in the collection.
* @param list The collection of ids to remove the selection from.
*/
public void clearSelection(Collection extends PrimitiveId> list) {
clearSelection(list.stream());
}
/**
* Clears the current selection.
*/
public void clearSelection() {
setSelected(Stream.empty());
}
private void clearSelection(Stream extends PrimitiveId> stream) {
doSelectionChange(old -> new SelectionRemoveEvent(this, old,
stream.map(this::getPrimitiveByIdChecked).filter(Objects::nonNull)));
}
/**
* Toggles the selected state of the given collection of primitives.
* @param osm The primitives to toggle
*/
public void toggleSelected(Collection extends PrimitiveId> osm) {
toggleSelected(osm.stream());
}
/**
* Toggles the selected state of the given collection of primitives.
* @param osm The primitives to toggle
*/
public void toggleSelected(PrimitiveId... osm) {
toggleSelected(Stream.of(osm));
}
private void toggleSelected(Stream extends PrimitiveId> stream) {
doSelectionChange(old -> new SelectionToggleEvent(this, old,
stream.map(this::getPrimitiveByIdChecked).filter(Objects::nonNull)));
}
/**
* Do a selection change.
*
* This is the only method that changes the current selection state.
* @param command A generator that generates the {@link SelectionChangeEvent} for the given base set of currently selected primitives.
* @return true iff the command did change the selection.
* @since 12048
*/
private boolean doSelectionChange(Functiontrue
.
*
* @return true if there is at least one primitive in this dataset with
* {@link OsmPrimitive#isModified()} == true
.
*/
public boolean isModified() {
for (OsmPrimitive p: allPrimitives) {
if (p.isModified())
return true;
}
return false;
}
/**
* Adds a new data set listener.
* @param dsl The data set listener to add
*/
public void addDataSetListener(DataSetListener dsl) {
listeners.addIfAbsent(dsl);
}
/**
* Removes a data set listener.
* @param dsl The data set listener to remove
*/
public void removeDataSetListener(DataSetListener dsl) {
listeners.remove(dsl);
}
/**
* Can be called before bigger changes on dataset. Events are disabled until {@link #endUpdate()}.
* {@link DataSetListener#dataChanged(DataChangedEvent event)} event is triggered after end of changes
*
* Typical usecase should look like this:
*
* ds.beginUpdate();
* try {
* ...
* } finally {
* ds.endUpdate();
* }
*
*/
public void beginUpdate() {
lock.writeLock().lock();
updateCount++;
}
/**
* @see DataSet#beginUpdate()
*/
public void endUpdate() {
if (updateCount > 0) {
updateCount--;
List