// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.conflict.tags;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.openstreetmap.josm.command.ChangePropertyCommand;
import org.openstreetmap.josm.command.Command;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Tag;
import org.openstreetmap.josm.data.osm.TagCollection;
import org.openstreetmap.josm.tools.CheckParameterUtil;
/**
* Represents a decision for a conflict due to multiple possible value for a tag.
*
*
*/
public class MultiValueResolutionDecision {
/** the type of decision */
private MultiValueDecisionType type;
/** the collection of tags for which a decision is needed */
private TagCollection tags;
/** the selected value if {@see #type} is {@see MultiValueDecisionType#KEEP_ONE} */
private String value;
/**
* constuctor
*/
public MultiValueResolutionDecision() {
type = MultiValueDecisionType.UNDECIDED;
tags = new TagCollection();
autoDecide();
}
/**
* Creates a new decision for the tag collection <code>tags</code>.
* All tags must have the same key.
*
* @param tags the tags. Must not be null.
* @exception IllegalArgumentException thrown if tags is null
* @exception IllegalArgumentException thrown if there are more than one keys
* @exception IllegalArgumentException thrown if tags is empty
*/
public MultiValueResolutionDecision(TagCollection tags) throws IllegalArgumentException {
CheckParameterUtil.ensureParameterNotNull(tags, "tags");
if (tags.isEmpty())
throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' must not be empty.", "tags"));
if (tags.getKeys().size() != 1)
throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' with tags for exactly one key expected. Got {1}.", "tags", tags.getKeys().size()));
this.tags = tags;
autoDecide();
}
/**
* Tries to find the best decision based on the current values.
*/
protected void autoDecide() {
this.type = MultiValueDecisionType.UNDECIDED;
// exactly one empty value ? -> delete the tag
if (tags.size() == 1 && tags.getValues().contains("")) {
this.type = MultiValueDecisionType.KEEP_NONE;
// exactly one non empty value? -> keep this value
} else if (tags.size() == 1) {
this.type = MultiValueDecisionType.KEEP_ONE;
this.value = tags.getValues().iterator().next();
}
}
/**
* Apply the decision to keep no value
*/
public void keepNone() {
this.type = MultiValueDecisionType.KEEP_NONE;
}
/**
* Apply the decision to keep all values
*/
public void keepAll() {
this.type = MultiValueDecisionType.KEEP_ALL;
}
/**
* Apply the decision to keep exactly one value
*
* @param value the value to keep
* @throws IllegalArgumentException thrown if value is null
* @throws IllegalStateException thrown if value is not in the list of known values for this tag
*/
public void keepOne(String value) throws IllegalArgumentException, IllegalStateException {
CheckParameterUtil.ensureParameterNotNull(value, "value");
if (!tags.getValues().contains(value))
throw new IllegalStateException(tr("Tag collection does not include the selected value ''{0}''.", value));
this.value = value;
this.type = MultiValueDecisionType.KEEP_ONE;
}
/**
* sets a new value for this
*
* @param value the new vlaue
*/
public void setNew(String value) {
if (value == null) {
value = "";
}
this.value = value;
this.type = MultiValueDecisionType.KEEP_ONE;
}
/**
* marks this as undecided
*
*/
public void undecide() {
this.type = MultiValueDecisionType.UNDECIDED;
}
/**
* Replies the chosen value
*
* @return the chosen value
* @throws IllegalStateException thrown if this resolution is not yet decided
*/
public String getChosenValue() throws IllegalStateException {
switch(type) {
case UNDECIDED: throw new IllegalStateException(tr("Not decided yet."));
case KEEP_ONE: return value;
case KEEP_NONE: return null;
case KEEP_ALL: return tags.getJoinedValues(getKey());
}
// should not happen
return null;
}
/**
* Replies the list of possible, non empty values
*
* @return the list of possible, non empty values
*/
public List<String> getValues() {
ArrayList<String> ret = new ArrayList<String>(tags.getValues());
ret.remove("");
ret.remove(null);
Collections.sort(ret);
return ret;
}
/**
* Replies the key of the tag to be resolved by this resolution
*
* @return the key of the tag to be resolved by this resolution
*/
public String getKey() {
return tags.getKeys().iterator().next();
}
/**
* Replies true if the empty value is a possible value in this resolution
*
* @return true if the empty value is a possible value in this resolution
*/
public boolean canKeepNone() {
return tags.getValues().contains("");
}
/**
* Replies true, if this resolution has more than 1 possible non-empty values
*
* @return true, if this resolution has more than 1 possible non-empty values
*/
public boolean canKeepAll() {
return getValues().size() > 1;
}
/**
* Replies true if this resolution is decided
*
* @return true if this resolution is decided
*/
public boolean isDecided() {
return !type.equals(MultiValueDecisionType.UNDECIDED);
}
/**
* Replies the type of the resolution
*
* @return the type of the resolution
*/
public MultiValueDecisionType getDecisionType() {
return type;
}
/**
* Applies the resolution to an {@see OsmPrimitive}
*
* @param primitive the primitive
* @throws IllegalStateException thrown if this resolution is not resolved yet
*
*/
public void applyTo(OsmPrimitive primitive) throws IllegalStateException{
if (primitive == null) return;
if (!isDecided())
throw new IllegalStateException(tr("Not decided yet."));
String key = tags.getKeys().iterator().next();
String value = getChosenValue();
if (type.equals(MultiValueDecisionType.KEEP_NONE)) {
primitive.remove(key);
} else {
primitive.put(key, value);
}
}
/**
* Applies this resolution to a collection of primitives
*
* @param primtives the collection of primitives
* @throws IllegalStateException thrown if this resolution is not resolved yet
*/
public void applyTo(Collection<? extends OsmPrimitive> primtives) throws IllegalStateException {
if (primtives == null) return;
for (OsmPrimitive primitive: primtives) {
if (primitive == null) {
continue;
}
applyTo(primitive);
}
}
/**
* Builds a change command for applying this resolution to a primitive
*
* @param primitive the primitive
* @return the change command
* @throws IllegalArgumentException thrown if primitive is null
* @throws IllegalStateException thrown if this resolution is not resolved yet
*/
public Command buildChangeCommand(OsmPrimitive primitive) throws IllegalArgumentException, IllegalStateException {
CheckParameterUtil.ensureParameterNotNull(primitive, "primitive");
if (!isDecided())
throw new IllegalStateException(tr("Not decided yet."));
String key = tags.getKeys().iterator().next();
String value = getChosenValue();
ChangePropertyCommand cmd = new ChangePropertyCommand(primitive, key,value);
return cmd;
}
/**
* Builds a change command for applying this resolution to a collection of primitives
*
* @param primitives the collection of primitives
* @return the change command
* @throws IllegalArgumentException thrown if primitives is null
* @throws IllegalStateException thrown if this resolution is not resolved yet
*/
public Command buildChangeCommand(Collection<? extends OsmPrimitive> primitives) {
CheckParameterUtil.ensureParameterNotNull(primitives, "primitives");
if (!isDecided())
throw new IllegalStateException(tr("Not decided yet."));
String key = tags.getKeys().iterator().next();
String value = getChosenValue();
ChangePropertyCommand cmd = new ChangePropertyCommand(primitives, key,value);
return cmd;
}
/**
* Replies a tag representing the current resolution. Null, if this resolution is not resolved
* yet.
*
* @return a tag representing the current resolution. Null, if this resolution is not resolved
* yet
*/
public Tag getResolution() {
switch(type) {
case KEEP_ALL: return new Tag(getKey(), tags.getJoinedValues(getKey()));
case KEEP_ONE: return new Tag(getKey(),value);
case KEEP_NONE: return new Tag(getKey(), "");
case UNDECIDED: return null;
}
return null;
}
}