// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.conflict.tags;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.table.DefaultTableModel;
import org.openstreetmap.josm.data.osm.TagCollection;
import org.openstreetmap.josm.tools.CheckParameterUtil;
public class TagConflictResolverModel extends DefaultTableModel {
static public final String NUM_CONFLICTS_PROP = TagConflictResolverModel.class.getName() + ".numConflicts";
private TagCollection tags;
private List<String> displayedKeys;
private Set<String> keysWithConflicts;
private HashMap<String, MultiValueResolutionDecision> decisions;
private int numConflicts;
private PropertyChangeSupport support;
private boolean showTagsWithConflictsOnly = false;
private boolean showTagsWithMultiValuesOnly = false;
public TagConflictResolverModel() {
numConflicts = 0;
support = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
protected void setNumConflicts(int numConflicts) {
int oldValue = this.numConflicts;
this.numConflicts = numConflicts;
if (oldValue != this.numConflicts) {
support.firePropertyChange(NUM_CONFLICTS_PROP, oldValue, this.numConflicts);
}
}
protected void refreshNumConflicts() {
int count = 0;
for (MultiValueResolutionDecision d : decisions.values()) {
if (!d.isDecided()) {
count++;
}
}
setNumConflicts(count);
}
protected void sort() {
Collections.sort(
displayedKeys,
new Comparator<String>() {
public int compare(String key1, String key2) {
if (decisions.get(key1).isDecided() && ! decisions.get(key2).isDecided())
return 1;
else if (!decisions.get(key1).isDecided() && decisions.get(key2).isDecided())
return -1;
return key1.compareTo(key2);
}
}
);
}
/**
* initializes the model from the current tags
*
*/
protected void rebuild() {
if (tags == null) return;
for(String key: tags.getKeys()) {
MultiValueResolutionDecision decision = new MultiValueResolutionDecision(tags.getTagsFor(key));
if (decisions.get(key) == null) {
decisions.put(key,decision);
}
}
displayedKeys.clear();
Set<String> keys = tags.getKeys();
if (showTagsWithConflictsOnly) {
keys.retainAll(keysWithConflicts);
if (showTagsWithMultiValuesOnly) {
Set<String> keysWithMultiValues = new HashSet<String>();
for (String key: keys) {
if (decisions.get(key).canKeepAll()) {
keysWithMultiValues.add(key);
}
}
keys.retainAll(keysWithMultiValues);
}
for (String key: tags.getKeys()) {
if (!decisions.get(key).isDecided() && !keys.contains(key)) {
keys.add(key);
}
}
}
displayedKeys.addAll(keys);
refreshNumConflicts();
sort();
fireTableDataChanged();
}
/**
* Populates the model with the tags for which conflicts are to be resolved.
*
* @param tags the tag collection with the tags. Must not be null.
* @param keysWithConflicts the set of tag keys with conflicts
* @throws IllegalArgumentException thrown if tags is null
*/
public void populate(TagCollection tags, Set<String> keysWithConflicts) {
CheckParameterUtil.ensureParameterNotNull(tags, "tags");
this.tags = tags;
displayedKeys = new ArrayList<String>();
this.keysWithConflicts = keysWithConflicts == null ? new HashSet<String>() : keysWithConflicts;
decisions = new HashMap<String, MultiValueResolutionDecision>();
rebuild();
}
@Override
public int getRowCount() {
if (displayedKeys == null) return 0;
return displayedKeys.size();
}
@Override
public Object getValueAt(int row, int column) {
return decisions.get(displayedKeys.get(row));
}
@Override
public boolean isCellEditable(int row, int column) {
return column == 2;
}
@Override
public void setValueAt(Object value, int row, int column) {
MultiValueResolutionDecision decision = decisions.get(displayedKeys.get(row));
if (value instanceof String) {
decision.keepOne((String)value);
} else if (value instanceof MultiValueDecisionType) {
MultiValueDecisionType type = (MultiValueDecisionType)value;
switch(type) {
case KEEP_NONE:
decision.keepNone();
break;
case KEEP_ALL:
decision.keepAll();
break;
}
}
fireTableDataChanged();
refreshNumConflicts();
}
/**
* Replies true if each {@see MultiValueResolutionDecision} is decided.
*
* @return true if each {@see MultiValueResolutionDecision} is decided; false
* otherwise
*/
public boolean isResolvedCompletely() {
return numConflicts == 0;
}
public int getNumConflicts() {
return numConflicts;
}
public int getNumDecisions() {
return getRowCount();
}
//TODO Should this method work with all decisions or only with displayed decisions? For MergeNodes it should be
//all decisions, but this method is also used on other places, so I've made new method just for MergeNodes
public TagCollection getResolution() {
TagCollection tc = new TagCollection();
for (String key: displayedKeys) {
tc.add(decisions.get(key).getResolution());
}
return tc;
}
public TagCollection getAllResolutions() {
TagCollection tc = new TagCollection();
for (MultiValueResolutionDecision value: decisions.values()) {
tc.add(value.getResolution());
}
return tc;
}
public MultiValueResolutionDecision getDecision(int row) {
return decisions.get(displayedKeys.get(row));
}
/**
* Sets whether all tags or only tags with conflicts are displayed
*
* @param showTagsWithConflictsOnly if true, only tags with conflicts are displayed
*/
public void setShowTagsWithConflictsOnly(boolean showTagsWithConflictsOnly) {
this.showTagsWithConflictsOnly = showTagsWithConflictsOnly;
rebuild();
}
/**
* Sets whether all conflicts or only conflicts with multiple values are displayed
*
* @param showTagsWithMultiValuesOnly if true, only tags with multiple values are displayed
*/
public void setShowTagsWithMultiValuesOnly(boolean showTagsWithMultiValuesOnly) {
this.showTagsWithMultiValuesOnly = showTagsWithMultiValuesOnly;
rebuild();
}
/**
* Prepare the default decisions for the current model
*
*/
public void prepareDefaultTagDecisions() {
for (MultiValueResolutionDecision decision: decisions.values()) {
List<String> values = decision.getValues();
values.remove("");
if (values.size() == 1) {
decision.keepOne(values.get(0));
} else {
decision.keepAll();
}
}
rebuild();
}
}