001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm.command; 003 004 import static org.openstreetmap.josm.tools.I18n.marktr; 005 import static org.openstreetmap.josm.tools.I18n.tr; 006 007 import java.util.AbstractMap; 008 import java.util.ArrayList; 009 import java.util.Arrays; 010 import java.util.Collection; 011 import java.util.Collections; 012 import java.util.HashMap; 013 import java.util.LinkedList; 014 import java.util.List; 015 import java.util.Map; 016 017 import javax.swing.Icon; 018 019 import org.openstreetmap.josm.Main; 020 import org.openstreetmap.josm.data.osm.OsmPrimitive; 021 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 022 import org.openstreetmap.josm.gui.DefaultNameFormatter; 023 import org.openstreetmap.josm.tools.ImageProvider; 024 025 /** 026 * Command that manipulate the key/value structure of several objects. Manages deletion, 027 * adding and modify of values and keys. 028 * 029 * @author imi 030 */ 031 public class ChangePropertyCommand extends Command { 032 /** 033 * All primitives that are affected with this command. 034 */ 035 private final List<OsmPrimitive> objects; 036 /** 037 * Key and value pairs. If value is <code>null</code>, delete all key references with the given 038 * key. Otherwise, change the properties of all objects to the given value or create keys of 039 * those objects that do not have the key yet. 040 */ 041 private final AbstractMap<String, String> tags; 042 043 /** 044 * Creates a command to change multiple properties of multiple objects 045 * 046 * @param objects the objects to modify 047 * @param tags the properties to set 048 */ 049 public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, AbstractMap<String, String> tags) { 050 super(); 051 this.objects = new LinkedList<OsmPrimitive>(); 052 this.tags = tags; 053 init(objects); 054 } 055 056 /** 057 * Creates a command to change one property of multiple objects 058 * 059 * @param objects the objects to modify 060 * @param key the key of the property to set 061 * @param value the value of the key to set 062 */ 063 public ChangePropertyCommand(Collection<? extends OsmPrimitive> objects, String key, String value) { 064 this.objects = new LinkedList<OsmPrimitive>(); 065 this.tags = new HashMap<String, String>(1); 066 this.tags.put(key, value); 067 init(objects); 068 } 069 070 /** 071 * Creates a command to change on property of one object 072 * 073 * @param object the object to modify 074 * @param key the key of the property to set 075 * @param value the value of the key to set 076 */ 077 public ChangePropertyCommand(OsmPrimitive object, String key, String value) { 078 this(Arrays.asList(object), key, value); 079 } 080 081 /** 082 * Initialize the instance by finding what objects will be modified 083 * 084 * @param objects the objects to (possibly) modify 085 */ 086 private void init(Collection<? extends OsmPrimitive> objects) { 087 // determine what objects will be modified 088 for (OsmPrimitive osm : objects) { 089 boolean modified = false; 090 091 // loop over all tags 092 for (Map.Entry<String, String> tag : this.tags.entrySet()) { 093 String oldVal = osm.get(tag.getKey()); 094 String newVal = tag.getValue(); 095 096 if (newVal == null || newVal.isEmpty()) { 097 if (oldVal != null) 098 // new value is null and tag exists (will delete tag) 099 modified = true; 100 } 101 else if (oldVal == null || !newVal.equals(oldVal)) 102 // new value is not null and is different from current value 103 modified = true; 104 } 105 if (modified) 106 this.objects.add(osm); 107 } 108 } 109 110 @Override public boolean executeCommand() { 111 Main.main.getCurrentDataSet().beginUpdate(); 112 try { 113 super.executeCommand(); // save old 114 115 for (OsmPrimitive osm : objects) { 116 // loop over all tags 117 for (Map.Entry<String, String> tag : this.tags.entrySet()) { 118 String oldVal = osm.get(tag.getKey()); 119 String newVal = tag.getValue(); 120 121 if (newVal == null || newVal.isEmpty()) { 122 if (oldVal != null) 123 osm.remove(tag.getKey()); 124 } 125 else if (oldVal == null || !newVal.equals(oldVal)) 126 osm.put(tag.getKey(), newVal); 127 } 128 // init() only keeps modified primitives. Therefore the modified 129 // bit can be set without further checks. 130 osm.setModified(true); 131 } 132 return true; 133 } 134 finally { 135 Main.main.getCurrentDataSet().endUpdate(); 136 } 137 } 138 139 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 140 modified.addAll(objects); 141 } 142 143 @Override 144 public String getDescriptionText() { 145 String text; 146 if (objects.size() == 1 && tags.size() == 1) { 147 OsmPrimitive primitive = objects.iterator().next(); 148 String msg = ""; 149 Map.Entry<String, String> entry = tags.entrySet().iterator().next(); 150 if (entry.getValue() == null) { 151 switch(OsmPrimitiveType.from(primitive)) { 152 case NODE: msg = marktr("Remove \"{0}\" for node ''{1}''"); break; 153 case WAY: msg = marktr("Remove \"{0}\" for way ''{1}''"); break; 154 case RELATION: msg = marktr("Remove \"{0}\" for relation ''{1}''"); break; 155 } 156 text = tr(msg, entry.getKey(), primitive.getDisplayName(DefaultNameFormatter.getInstance())); 157 } else { 158 switch(OsmPrimitiveType.from(primitive)) { 159 case NODE: msg = marktr("Set {0}={1} for node ''{2}''"); break; 160 case WAY: msg = marktr("Set {0}={1} for way ''{2}''"); break; 161 case RELATION: msg = marktr("Set {0}={1} for relation ''{2}''"); break; 162 } 163 text = tr(msg, entry.getKey(), entry.getValue(), primitive.getDisplayName(DefaultNameFormatter.getInstance())); 164 } 165 } else if (objects.size() > 1 && tags.size() == 1) { 166 Map.Entry<String, String> entry = tags.entrySet().iterator().next(); 167 if (entry.getValue() == null) 168 text = tr("Remove \"{0}\" for {1} objects", entry.getKey(), objects.size()); 169 else 170 text = tr("Set {0}={1} for {2} objects", entry.getKey(), entry.getValue(), objects.size()); 171 } 172 else { 173 boolean allnull = true; 174 for (Map.Entry<String, String> tag : this.tags.entrySet()) { 175 if (tag.getValue() != null) { 176 allnull = false; 177 break; 178 } 179 } 180 181 if (allnull) { 182 text = tr("Deleted {0} properties for {1} objects", tags.size(), objects.size()); 183 } else 184 text = tr("Set {0} properties for {1} objects", tags.size(), objects.size()); 185 } 186 return text; 187 } 188 189 @Override 190 public Icon getDescriptionIcon() { 191 return ImageProvider.get("data", "key"); 192 } 193 194 @Override public Collection<PseudoCommand> getChildren() { 195 if (objects.size() == 1) 196 return null; 197 List<PseudoCommand> children = new ArrayList<PseudoCommand>(); 198 for (final OsmPrimitive osm : objects) { 199 children.add(new PseudoCommand() { 200 @Override public String getDescriptionText() { 201 return osm.getDisplayName(DefaultNameFormatter.getInstance()); 202 } 203 204 @Override public Icon getDescriptionIcon() { 205 return ImageProvider.get(osm.getDisplayType()); 206 } 207 208 @Override public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 209 return Collections.singleton(osm); 210 } 211 212 }); 213 } 214 return children; 215 } 216 }