001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.gui.conflict.tags;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.text.MessageFormat;
007 import java.util.ArrayList;
008 import java.util.Collection;
009 import java.util.Collections;
010 import java.util.List;
011
012 import org.openstreetmap.josm.command.ChangePropertyCommand;
013 import org.openstreetmap.josm.command.Command;
014 import org.openstreetmap.josm.data.osm.OsmPrimitive;
015 import org.openstreetmap.josm.data.osm.Tag;
016 import org.openstreetmap.josm.data.osm.TagCollection;
017 import org.openstreetmap.josm.tools.CheckParameterUtil;
018 /**
019 * Represents a decision for a conflict due to multiple possible value for a tag.
020 *
021 *
022 */
023 public class MultiValueResolutionDecision {
024
025 /** the type of decision */
026 private MultiValueDecisionType type;
027 /** the collection of tags for which a decision is needed */
028 private TagCollection tags;
029 /** the selected value if {@link #type} is {@link MultiValueDecisionType#KEEP_ONE} */
030 private String value;
031
032 /**
033 * constuctor
034 */
035 public MultiValueResolutionDecision() {
036 type = MultiValueDecisionType.UNDECIDED;
037 tags = new TagCollection();
038 autoDecide();
039 }
040
041 /**
042 * Creates a new decision for the tag collection <code>tags</code>.
043 * All tags must have the same key.
044 *
045 * @param tags the tags. Must not be null.
046 * @exception IllegalArgumentException thrown if tags is null
047 * @exception IllegalArgumentException thrown if there are more than one keys
048 * @exception IllegalArgumentException thrown if tags is empty
049 */
050 public MultiValueResolutionDecision(TagCollection tags) throws IllegalArgumentException {
051 CheckParameterUtil.ensureParameterNotNull(tags, "tags");
052 if (tags.isEmpty())
053 throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' must not be empty.", "tags"));
054 if (tags.getKeys().size() != 1)
055 throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' with tags for exactly one key expected. Got {1}.", "tags", tags.getKeys().size()));
056 this.tags = tags;
057 autoDecide();
058 }
059
060 /**
061 * Tries to find the best decision based on the current values.
062 */
063 protected void autoDecide() {
064 this.type = MultiValueDecisionType.UNDECIDED;
065 // exactly one empty value ? -> delete the tag
066 if (tags.size() == 1 && tags.getValues().contains("")) {
067 this.type = MultiValueDecisionType.KEEP_NONE;
068
069 // exactly one non empty value? -> keep this value
070 } else if (tags.size() == 1) {
071 this.type = MultiValueDecisionType.KEEP_ONE;
072 this.value = tags.getValues().iterator().next();
073 }
074 }
075
076 /**
077 * Apply the decision to keep no value
078 */
079 public void keepNone() {
080 this.type = MultiValueDecisionType.KEEP_NONE;
081 }
082
083 /**
084 * Apply the decision to keep all values
085 */
086 public void keepAll() {
087 this.type = MultiValueDecisionType.KEEP_ALL;
088 }
089
090 /**
091 * Apply the decision to keep exactly one value
092 *
093 * @param value the value to keep
094 * @throws IllegalArgumentException thrown if value is null
095 * @throws IllegalStateException thrown if value is not in the list of known values for this tag
096 */
097 public void keepOne(String value) throws IllegalArgumentException, IllegalStateException {
098 CheckParameterUtil.ensureParameterNotNull(value, "value");
099 if (!tags.getValues().contains(value))
100 throw new IllegalStateException(tr("Tag collection does not include the selected value ''{0}''.", value));
101 this.value = value;
102 this.type = MultiValueDecisionType.KEEP_ONE;
103 }
104
105 /**
106 * sets a new value for this
107 *
108 * @param value the new vlaue
109 */
110 public void setNew(String value) {
111 if (value == null) {
112 value = "";
113 }
114 this.value = value;
115 this.type = MultiValueDecisionType.KEEP_ONE;
116
117 }
118
119 /**
120 * marks this as undecided
121 *
122 */
123 public void undecide() {
124 this.type = MultiValueDecisionType.UNDECIDED;
125 }
126
127 /**
128 * Replies the chosen value
129 *
130 * @return the chosen value
131 * @throws IllegalStateException thrown if this resolution is not yet decided
132 */
133 public String getChosenValue() throws IllegalStateException {
134 switch(type) {
135 case UNDECIDED: throw new IllegalStateException(tr("Not decided yet."));
136 case KEEP_ONE: return value;
137 case KEEP_NONE: return null;
138 case KEEP_ALL: return tags.getJoinedValues(getKey());
139 }
140 // should not happen
141 return null;
142 }
143
144 /**
145 * Replies the list of possible, non empty values
146 *
147 * @return the list of possible, non empty values
148 */
149 public List<String> getValues() {
150 ArrayList<String> ret = new ArrayList<String>(tags.getValues());
151 ret.remove("");
152 ret.remove(null);
153 Collections.sort(ret);
154 return ret;
155 }
156
157 /**
158 * Replies the key of the tag to be resolved by this resolution
159 *
160 * @return the key of the tag to be resolved by this resolution
161 */
162 public String getKey() {
163 return tags.getKeys().iterator().next();
164 }
165
166 /**
167 * Replies true if the empty value is a possible value in this resolution
168 *
169 * @return true if the empty value is a possible value in this resolution
170 */
171 public boolean canKeepNone() {
172 return tags.getValues().contains("");
173 }
174
175 /**
176 * Replies true, if this resolution has more than 1 possible non-empty values
177 *
178 * @return true, if this resolution has more than 1 possible non-empty values
179 */
180 public boolean canKeepAll() {
181 return getValues().size() > 1;
182 }
183
184 /**
185 * Replies true if this resolution is decided
186 *
187 * @return true if this resolution is decided
188 */
189 public boolean isDecided() {
190 return !type.equals(MultiValueDecisionType.UNDECIDED);
191 }
192
193 /**
194 * Replies the type of the resolution
195 *
196 * @return the type of the resolution
197 */
198 public MultiValueDecisionType getDecisionType() {
199 return type;
200 }
201
202 /**
203 * Applies the resolution to an {@link OsmPrimitive}
204 *
205 * @param primitive the primitive
206 * @throws IllegalStateException thrown if this resolution is not resolved yet
207 *
208 */
209 public void applyTo(OsmPrimitive primitive) throws IllegalStateException{
210 if (primitive == null) return;
211 if (!isDecided())
212 throw new IllegalStateException(tr("Not decided yet."));
213 String key = tags.getKeys().iterator().next();
214 String value = getChosenValue();
215 if (type.equals(MultiValueDecisionType.KEEP_NONE)) {
216 primitive.remove(key);
217 } else {
218 primitive.put(key, value);
219 }
220 }
221
222 /**
223 * Applies this resolution to a collection of primitives
224 *
225 * @param primitives the collection of primitives
226 * @throws IllegalStateException thrown if this resolution is not resolved yet
227 */
228 public void applyTo(Collection<? extends OsmPrimitive> primitives) throws IllegalStateException {
229 if (primitives == null) return;
230 for (OsmPrimitive primitive: primitives) {
231 if (primitive == null) {
232 continue;
233 }
234 applyTo(primitive);
235 }
236 }
237
238 /**
239 * Builds a change command for applying this resolution to a primitive
240 *
241 * @param primitive the primitive
242 * @return the change command
243 * @throws IllegalArgumentException thrown if primitive is null
244 * @throws IllegalStateException thrown if this resolution is not resolved yet
245 */
246 public Command buildChangeCommand(OsmPrimitive primitive) throws IllegalArgumentException, IllegalStateException {
247 CheckParameterUtil.ensureParameterNotNull(primitive, "primitive");
248 if (!isDecided())
249 throw new IllegalStateException(tr("Not decided yet."));
250 String key = tags.getKeys().iterator().next();
251 String value = getChosenValue();
252 ChangePropertyCommand cmd = new ChangePropertyCommand(primitive, key,value);
253 return cmd;
254 }
255
256 /**
257 * Builds a change command for applying this resolution to a collection of primitives
258 *
259 * @param primitives the collection of primitives
260 * @return the change command
261 * @throws IllegalArgumentException thrown if primitives is null
262 * @throws IllegalStateException thrown if this resolution is not resolved yet
263 */
264 public Command buildChangeCommand(Collection<? extends OsmPrimitive> primitives) {
265 CheckParameterUtil.ensureParameterNotNull(primitives, "primitives");
266 if (!isDecided())
267 throw new IllegalStateException(tr("Not decided yet."));
268 String key = tags.getKeys().iterator().next();
269 String value = getChosenValue();
270 ChangePropertyCommand cmd = new ChangePropertyCommand(primitives, key,value);
271 return cmd;
272 }
273
274 /**
275 * Replies a tag representing the current resolution. Null, if this resolution is not resolved
276 * yet.
277 *
278 * @return a tag representing the current resolution. Null, if this resolution is not resolved
279 * yet
280 */
281 public Tag getResolution() {
282 switch(type) {
283 case KEEP_ALL: return new Tag(getKey(), tags.getJoinedValues(getKey()));
284 case KEEP_ONE: return new Tag(getKey(),value);
285 case KEEP_NONE: return new Tag(getKey(), "");
286 case UNDECIDED: return null;
287 }
288 return null;
289 }
290 }