- Timestamp:
- 2009-09-13T19:30:36+02:00 (15 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 10 added
- 16 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java
r2113 r2115 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.actions; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 3 5 4 6 import java.awt.event.ActionEvent; 5 7 import java.awt.event.KeyEvent; 6 8 import java.io.IOException; 9 import java.util.Collection; 10 import java.util.List; 7 11 8 12 import javax.swing.JOptionPane; 13 import javax.swing.SwingUtilities; 9 14 10 15 import org.openstreetmap.josm.Main; 16 import org.openstreetmap.josm.data.osm.Changeset; 17 import org.openstreetmap.josm.data.osm.UserInfo; 11 18 import org.openstreetmap.josm.gui.ExceptionDialogUtil; 12 19 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 13 import org.openstreetmap.josm.io.ChangesetProcessingType; 14 import org.openstreetmap.josm.io.OsmApi; 20 import org.openstreetmap.josm.gui.io.CloseChangesetDialog; 21 import org.openstreetmap.josm.gui.io.CloseChangesetTask; 22 import org.openstreetmap.josm.io.ChangesetQuery; 23 import org.openstreetmap.josm.io.OsmServerChangesetReader; 24 import org.openstreetmap.josm.io.OsmServerUserInfoReader; 15 25 import org.openstreetmap.josm.io.OsmTransferException; 16 26 import org.openstreetmap.josm.tools.Shortcut; 17 27 import org.xml.sax.SAXException; 18 28 19 import static org.openstreetmap.josm.tools.I18n.tr; 29 public class CloseChangesetAction extends JosmAction{ 20 30 21 public class StopChangesetAction extends JosmAction{ 22 23 public StopChangesetAction() { 24 super(tr("Close current changeset"), 31 public CloseChangesetAction() { 32 super(tr("Close open changesets"), 25 33 "closechangeset", 26 tr("Close the current changeset ..."),34 tr("Closes open changesets"), 27 35 Shortcut.registerShortcut( 28 36 "system:closechangeset", 29 tr("File: {0}", tr("Close the current changeset ...")),37 tr("File: {0}", tr("Closes open changesets")), 30 38 KeyEvent.VK_Q, 31 39 Shortcut.GROUP_HOTKEY + Shortcut.GROUPS_ALT2 … … 36 44 } 37 45 public void actionPerformed(ActionEvent e) { 38 if (OsmApi.getOsmApi().getCurrentChangeset() == null) { 46 Main.worker.submit(new DownloadOpenChangesetsTask()); 47 } 48 49 50 protected void onPostDownloadOpenChangesets(DownloadOpenChangesetsTask task) { 51 if (task.isCancelled() || task.getLastException() != null) return; 52 53 List<Changeset> openChangesets = task.getChangesets(); 54 if (openChangesets.isEmpty()) { 39 55 JOptionPane.showMessageDialog( 40 56 Main.parent, 41 tr("There is currently no changeset open."),42 tr("No open changeset "),57 tr("There are no open changesets"), 58 tr("No open changesets"), 43 59 JOptionPane.INFORMATION_MESSAGE 44 60 ); 45 61 return; 46 62 } 47 Main.worker.submit(new StopChangesetActionTask()); 63 64 CloseChangesetDialog dialog = new CloseChangesetDialog(); 65 dialog.setChangesets(openChangesets); 66 dialog.setVisible(true); 67 if (dialog.isCanceled()) 68 return; 69 70 Collection<Changeset> changesetsToClose = dialog.getSelectedChangesets(); 71 CloseChangesetTask closeChangesetTask = new CloseChangesetTask(changesetsToClose); 72 Main.worker.submit(closeChangesetTask); 48 73 } 49 74 50 @Override51 protected void updateEnabledState() {52 setEnabled(Main.map != null && OsmApi.getOsmApi().getCurrentChangeset() != null);53 }54 75 55 static class StopChangesetActionTask extends PleaseWaitRunnable { 76 private class DownloadOpenChangesetsTask extends PleaseWaitRunnable { 77 56 78 private boolean cancelled; 79 private OsmServerChangesetReader reader; 80 private List<Changeset> changesets; 57 81 private Exception lastException; 82 private UserInfo userInfo; 58 83 59 public StopChangesetActionTask() { 60 super(tr("Closing changeset"), false /* don't ignore exceptions */); 84 /** 85 * 86 * @param model provides the user id of the current user and accepts the changesets 87 * after download 88 */ 89 public DownloadOpenChangesetsTask() { 90 super(tr("Downloading open changesets ...", false /* don't ignore exceptions */)); 61 91 } 92 62 93 @Override 63 94 protected void cancel() { 64 95 this.cancelled = true; 65 OsmApi.getOsmApi().cancel(); 66 96 reader.cancel(); 67 97 } 68 98 69 99 @Override 70 100 protected void finish() { 71 if (cancelled) 72 return; 73 if (lastException != null) { 74 ExceptionDialogUtil.explainException(lastException); 75 } 101 SwingUtilities.invokeLater( 102 new Runnable() { 103 public void run() { 104 if (lastException != null) { 105 ExceptionDialogUtil.explainException(lastException); 106 } 107 onPostDownloadOpenChangesets(DownloadOpenChangesetsTask.this); 108 } 109 } 110 ); 111 } 112 113 /** 114 * Fetch the user info from the server. This is necessary if we don't know 115 * the users id yet 116 * 117 * @return the user info 118 * @throws OsmTransferException thrown in case of any communication exception 119 */ 120 protected UserInfo fetchUserInfo() throws OsmTransferException { 121 OsmServerUserInfoReader reader = new OsmServerUserInfoReader(); 122 return reader.fetchUserInfo(getProgressMonitor().createSubTaskMonitor(1, false)); 76 123 } 77 124 … … 79 126 protected void realRun() throws SAXException, IOException, OsmTransferException { 80 127 try { 81 OsmApi.getOsmApi().stopChangeset(ChangesetProcessingType.USE_EXISTING_AND_CLOSE, getProgressMonitor().createSubTaskMonitor(1, false)); 128 userInfo = fetchUserInfo(); 129 if (cancelled) 130 return; 131 reader = new OsmServerChangesetReader(); 132 ChangesetQuery query = new ChangesetQuery().forUser(userInfo.getId()).beingOpen(); 133 changesets = reader.queryChangesets( 134 query, 135 getProgressMonitor().createSubTaskMonitor(1, false /* not internal */) 136 ); 82 137 } catch(Exception e) { 83 138 if (cancelled) … … 86 141 } 87 142 } 143 144 public boolean isCancelled() { 145 return cancelled; 146 } 147 148 public List<Changeset> getChangesets() { 149 return changesets; 150 } 151 152 public Exception getLastException() { 153 return lastException; 154 } 88 155 } 89 156 } -
trunk/src/org/openstreetmap/josm/actions/UploadAction.java
r2096 r2115 28 28 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 29 29 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 30 import org.openstreetmap.josm.io.ChangesetProcessingType;31 30 import org.openstreetmap.josm.io.OsmApi; 32 31 import org.openstreetmap.josm.io.OsmApiException; … … 176 175 Main.map.mapView.getEditLayer(), 177 176 apiData.getPrimitives(), 178 Upload ConfirmationHook.getUploadDialog().getChangeset(),179 Upload ConfirmationHook.getUploadDialog().getChangesetProcessingType()177 UploadDialog.getUploadDialog().getChangeset(), 178 UploadDialog.getUploadDialog().isDoCloseAfterUpload() 180 179 ) 181 180 ); … … 244 243 ); 245 244 switch(ret) { 246 case JOptionPane.CLOSED_OPTION: return;247 case JOptionPane.CANCEL_OPTION: return;248 case 0: synchronizePrimitive(primitiveType, id); break;249 case 1: synchronizeDataSet(); break;250 default:251 // should not happen252 throw new IllegalStateException(tr("unexpected return value. Got {0}", ret));245 case JOptionPane.CLOSED_OPTION: return; 246 case JOptionPane.CANCEL_OPTION: return; 247 case 0: synchronizePrimitive(primitiveType, id); break; 248 case 1: synchronizeDataSet(); break; 249 default: 250 // should not happen 251 throw new IllegalStateException(tr("unexpected return value. Got {0}", ret)); 253 252 } 254 253 } … … 284 283 ); 285 284 switch(ret) { 286 case JOptionPane.CLOSED_OPTION: return;287 case 1: return;288 case 0: synchronizeDataSet(); break;289 default:290 // should not happen291 throw new IllegalStateException(tr("unexpected return value. Got {0}", ret));285 case JOptionPane.CLOSED_OPTION: return; 286 case 1: return; 287 case 0: synchronizeDataSet(); break; 288 default: 289 // should not happen 290 throw new IllegalStateException(tr("unexpected return value. Got {0}", ret)); 292 291 } 293 292 } … … 457 456 458 457 static public class UploadConfirmationHook implements UploadHook { 459 static private UploadDialog uploadDialog;460 461 static public UploadDialog getUploadDialog() {462 if (uploadDialog == null) {463 uploadDialog = new UploadDialog();464 }465 return uploadDialog;466 }467 458 468 459 public boolean checkUpload(APIDataSet apiData) { 469 final UploadDialog dialog = getUploadDialog();460 final UploadDialog dialog = UploadDialog.getUploadDialog(); 470 461 dialog.setUploadedPrimitives(apiData.getPrimitivesToAdd(),apiData.getPrimitivesToUpdate(), apiData.getPrimitivesToDelete()); 471 462 dialog.setVisible(true); … … 477 468 } 478 469 479 public UploadDiffTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, ChangesetProcessingType changesetProcessingType) { 480 return new UploadDiffTask(layer, toUpload, changeset, changesetProcessingType); 481 } 482 470 public UploadDiffTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) { 471 return new UploadDiffTask(layer, toUpload, changeset, closeChangesetAfterUpload); 472 } 473 474 /** 475 * The task for uploading a collection of primitives 476 * 477 */ 483 478 public class UploadDiffTask extends PleaseWaitRunnable { 484 479 private boolean uploadCancelled = false; … … 488 483 private OsmDataLayer layer; 489 484 private Changeset changeset; 490 private ChangesetProcessingType changesetProcessingType; 491 492 private UploadDiffTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, ChangesetProcessingType changesetProcessingType) { 485 private boolean closeChangesetAfterUpload; 486 487 /** 488 * 489 * @param layer the OSM data layer for which data is uploaded 490 * @param toUpload the collection of primitives to upload 491 * @param changeset the changeset to use for uploading 492 * @param closeChangesetAfterUpload true, if the changeset is to be closed after uploading 493 */ 494 private UploadDiffTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) { 493 495 super(tr("Uploading data for layer ''{0}''", layer.getName()),false /* don't ignore exceptions */); 494 496 this.toUpload = toUpload; 495 497 this.layer = layer; 496 498 this.changeset = changeset; 497 this.c hangesetProcessingType = changesetProcessingType == null ? ChangesetProcessingType.USE_NEW_AND_CLOSE : changesetProcessingType;499 this.closeChangesetAfterUpload = closeChangesetAfterUpload; 498 500 } 499 501 … … 502 504 try { 503 505 ProgressMonitor monitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 504 writer.uploadOsm(layer.data.version, toUpload, changeset,c hangesetProcessingType, monitor);506 writer.uploadOsm(layer.data.version, toUpload, changeset,closeChangesetAfterUpload, monitor); 505 507 } catch (Exception sxe) { 506 508 if (uploadCancelled) { … … 525 527 handleFailedUpload(lastException); 526 528 } else { 529 // run post upload action on the layer 530 // 527 531 layer.onPostUploadToServer(); 532 // refresh changeset dialog with the updated changeset 533 // 534 UploadDialog.getUploadDialog().setOrUpdateChangeset(changeset); 528 535 } 529 536 } -
trunk/src/org/openstreetmap/josm/data/osm/Changeset.java
r2081 r2115 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import javax.print.attribute.standard.MediaSize.Other; 7 6 import java.util.Collection; 7 import java.util.Date; 8 import java.util.HashMap; 9 import java.util.Map; 10 11 import org.openstreetmap.josm.data.coor.LatLon; 8 12 import org.openstreetmap.josm.data.osm.visitor.Visitor; 9 13 … … 13 17 * 14 18 */ 15 public final class Changeset extends OsmPrimitive { 19 public final class Changeset implements Tagged { 20 /** the changeset id */ 21 private long id; 22 /** the user who owns the changeset */ 23 private User user; 24 /** date this changeset was created at */ 25 private Date createdAt; 26 /** the date this changeset was closed at*/ 27 private Date closedAt; 28 /** indicates whether this changeset is still open or not */ 29 private boolean open; 30 /** the min. coordinates of the bounding box of this changeset */ 31 private LatLon min; 32 /** the max. coordinates of the bounding box of this changeset */ 33 private LatLon max; 34 /** the map of tags */ 35 private Map<String,String> tags; 36 /** indicates whether this changeset is incomplete. For an 37 * incomplete changeset we only know its id 38 */ 39 private boolean incomplete; 40 41 16 42 /** 17 * Time of last modification to this object. This is not set by JOSM but 18 * read from the server and delivered back to the server unmodified. 43 * Creates a new changeset with id 0. 19 44 */ 20 public String end_timestamp = null; 45 public Changeset() { 46 this.id = 0; 47 this.tags = new HashMap<String, String>(); 48 } 21 49 22 50 /** 23 * Time of first modification to this object. This is not set by JOSM but 24 * read from the server and delivered back to the server unmodified. 51 * Creates a changeset with id <code>id</code>. If id > 0, sets incomplete to true. 52 * 53 * @param id the id 25 54 */ 26 public String start_timestamp = null;27 28 public Changeset() {29 super(0);30 }31 32 55 public Changeset(long id) { 33 super(id); 34 } 35 36 public Changeset(Changeset clone){ 37 super(clone.getId()); 38 cloneFrom(clone); 39 } 40 41 @Override 56 this.id = id; 57 this.incomplete = id > 0; 58 this.tags = new HashMap<String, String>(); 59 } 60 61 /** 62 * Creates a clone of <code>other</code> 63 * 64 * @param other the other changeset. If null, creates a new changeset with id 0. 65 */ 66 public Changeset(Changeset other) { 67 if (other == null) { 68 this.id = 0; 69 this.tags = new HashMap<String, String>(); 70 } else if (other.isIncomplete()) { 71 setId(other.getId()); 72 this.incomplete = true; 73 } else { 74 cloneFrom(other); 75 this.incomplete = false; 76 } 77 } 78 79 public void cloneFrom(Changeset other) { 80 setId(other.getId()); 81 setUser(other.getUser()); 82 setCreatedAt(other.getCreatedAt()); 83 setClosedAt(other.getClosedAt()); 84 setMin(other.getMin()); 85 setMax(other.getMax()); 86 setKeys(other.getKeys()); 87 setOpen(other.isOpen()); 88 } 89 42 90 public void visit(Visitor v) { 43 91 v.visit(this); 44 92 } 45 93 46 public int compareTo(OsmPrimitive other) { 47 if (other instanceof Changeset) return Long.valueOf(getId()).compareTo(other.getId()); 48 return 1; 49 } 50 51 @Override 94 public int compareTo(Changeset other) { 95 return Long.valueOf(getId()).compareTo(other.getId()); 96 } 97 52 98 public String getName() { 53 99 // no translation … … 55 101 } 56 102 57 @Override58 103 public String getLocalName(){ 59 104 return tr("Changeset {0}",getId()); 60 105 } 61 106 62 @Override63 107 public String getDisplayName(NameFormatter formatter) { 64 108 return formatter.format(this); 65 109 } 66 110 67 68 @Override public void cloneFrom(OsmPrimitive osm) { 69 super.cloneFrom(osm); 111 public long getId() { 112 return id; 113 } 114 115 public void setId(long id) { 116 this.id = id; 117 } 118 119 public User getUser() { 120 return user; 121 } 122 123 public void setUser(User user) { 124 this.user = user; 125 } 126 127 public Date getCreatedAt() { 128 return createdAt; 129 } 130 131 public void setCreatedAt(Date createdAt) { 132 this.createdAt = createdAt; 133 } 134 135 public Date getClosedAt() { 136 return closedAt; 137 } 138 139 public void setClosedAt(Date closedAt) { 140 this.closedAt = closedAt; 141 } 142 143 public boolean isOpen() { 144 return open; 145 } 146 147 public void setOpen(boolean open) { 148 this.open = open; 149 } 150 151 public LatLon getMin() { 152 return min; 153 } 154 155 public void setMin(LatLon min) { 156 this.min = min; 157 } 158 159 public LatLon getMax() { 160 return max; 161 } 162 163 public void setMax(LatLon max) { 164 this.max = max; 165 } 166 167 public Map<String, String> getKeys() { 168 return tags; 169 } 170 171 public void setKeys(Map<String, String> keys) { 172 this.tags = keys; 173 } 174 175 public boolean isIncomplete() { 176 return incomplete; 177 } 178 179 public void setIncomplete(boolean incomplete) { 180 this.incomplete = incomplete; 181 } 182 183 public void put(String key, String value) { 184 this.tags.put(key, value); 185 } 186 187 public String get(String key) { 188 return this.tags.get(key); 189 } 190 191 public void remove(String key) { 192 this.tags.remove(key); 193 } 194 195 public boolean hasEqualSemanticAttributes(Changeset other) { 196 if (other == null) 197 return false; 198 if (closedAt == null) { 199 if (other.closedAt != null) 200 return false; 201 } else if (!closedAt.equals(other.closedAt)) 202 return false; 203 if (createdAt == null) { 204 if (other.createdAt != null) 205 return false; 206 } else if (!createdAt.equals(other.createdAt)) 207 return false; 208 if (id != other.id) 209 return false; 210 if (max == null) { 211 if (other.max != null) 212 return false; 213 } else if (!max.equals(other.max)) 214 return false; 215 if (min == null) { 216 if (other.min != null) 217 return false; 218 } else if (!min.equals(other.min)) 219 return false; 220 if (open != other.open) 221 return false; 222 if (tags == null) { 223 if (other.tags != null) 224 return false; 225 } else if (!tags.equals(other.tags)) 226 return false; 227 if (user == null) { 228 if (other.user != null) 229 return false; 230 } else if (!user.equals(other.user)) 231 return false; 232 return true; 233 } 234 235 @Override 236 public int hashCode() { 237 final int prime = 31; 238 int result = 1; 239 result = prime * result + (int) (id ^ (id >>> 32)); 240 if (id > 0) 241 return prime * result + getClass().hashCode(); 242 result = prime * result + ((closedAt == null) ? 0 : closedAt.hashCode()); 243 result = prime * result + ((createdAt == null) ? 0 : createdAt.hashCode()); 244 result = prime * result + ((max == null) ? 0 : max.hashCode()); 245 result = prime * result + ((min == null) ? 0 : min.hashCode()); 246 result = prime * result + (open ? 1231 : 1237); 247 result = prime * result + ((tags == null) ? 0 : tags.hashCode()); 248 result = prime * result + ((user == null) ? 0 : user.hashCode()); 249 return result; 250 } 251 252 @Override 253 public boolean equals(Object obj) { 254 if (this == obj) 255 return true; 256 if (obj == null) 257 return false; 258 if (getClass() != obj.getClass()) 259 return false; 260 Changeset other = (Changeset) obj; 261 if (this.id > 0 && other.id == this.id) 262 return true; 263 if (closedAt == null) { 264 if (other.closedAt != null) 265 return false; 266 } else if (!closedAt.equals(other.closedAt)) 267 return false; 268 if (createdAt == null) { 269 if (other.createdAt != null) 270 return false; 271 } else if (!createdAt.equals(other.createdAt)) 272 return false; 273 if (id != other.id) 274 return false; 275 if (max == null) { 276 if (other.max != null) 277 return false; 278 } else if (!max.equals(other.max)) 279 return false; 280 if (min == null) { 281 if (other.min != null) 282 return false; 283 } else if (!min.equals(other.min)) 284 return false; 285 if (open != other.open) 286 return false; 287 if (tags == null) { 288 if (other.tags != null) 289 return false; 290 } else if (!tags.equals(other.tags)) 291 return false; 292 if (user == null) { 293 if (other.user != null) 294 return false; 295 } else if (!user.equals(other.user)) 296 return false; 297 return true; 298 } 299 300 public boolean hasKeys() { 301 return !tags.keySet().isEmpty(); 302 } 303 304 public Collection<String> keySet() { 305 return tags.keySet(); 70 306 } 71 307 } -
trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
r2077 r2115 326 326 Collection<? extends OsmPrimitive> primitives = null; 327 327 switch(type) { 328 case NODE: primitives = nodes; break; 329 case WAY: primitives = ways; break; 330 case RELATION: primitives = relations; break; 331 case CHANGESET: throw new IllegalArgumentException(tr("unsupported value ''{0}'' or parameter ''{1}''", type, "type")); 328 case NODE: primitives = nodes; break; 329 case WAY: primitives = ways; break; 330 case RELATION: primitives = relations; break; 332 331 } 333 332 for (OsmPrimitive primitive : primitives) { -
trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
r2083 r2115 33 33 * @author imi 34 34 */ 35 abstract public class OsmPrimitive implements Comparable<OsmPrimitive> {35 abstract public class OsmPrimitive implements Comparable<OsmPrimitive>, Tagged { 36 36 37 37 static public <T extends OsmPrimitive> List<T> getFilteredList(Collection<OsmPrimitive> list, Class<T> type) { -
trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitiveType.java
r2077 r2115 7 7 NODE ("node"), 8 8 WAY ("way"), 9 RELATION ("relation"), 10 CHANGESET ("changeset"); 9 RELATION ("relation"); 11 10 12 11 private String apiTypeName; … … 35 34 if (cls.equals(Way.class)) return WAY; 36 35 if (cls.equals(Relation.class)) return RELATION; 37 if (cls.equals(Changeset.class)) return CHANGESET;38 36 throw new IllegalArgumentException(tr("parameter ''{0}'' is not an acceptable class, got ''{1}''", "cls", cls.toString())); 39 37 } -
trunk/src/org/openstreetmap/josm/gui/MainMenu.java
r2061 r2115 58 58 import org.openstreetmap.josm.actions.ShowStatusReportAction; 59 59 import org.openstreetmap.josm.actions.SplitWayAction; 60 import org.openstreetmap.josm.actions. StopChangesetAction;60 import org.openstreetmap.josm.actions.CloseChangesetAction; 61 61 import org.openstreetmap.josm.actions.ToggleGPXLinesAction; 62 62 import org.openstreetmap.josm.actions.UnGlueAction; … … 100 100 public final DownloadAction download = new DownloadAction(); 101 101 public final DownloadReferrersAction downloadReferrers = new DownloadReferrersAction(); 102 public final StopChangesetAction stopChangesetAction = new StopChangesetAction();102 public final CloseChangesetAction closeChangesetAction = new CloseChangesetAction(); 103 103 public final JosmAction update = new UpdateDataAction(); 104 104 public final JosmAction updateSelection = new UpdateSelectionAction(); … … 208 208 add(fileMenu, update); 209 209 add(fileMenu, updateSelection); 210 add(fileMenu, stopChangesetAction);210 add(fileMenu, closeChangesetAction); 211 211 fileMenu.addSeparator(); 212 212 add(fileMenu, exit); -
trunk/src/org/openstreetmap/josm/gui/conflict/tags/PasteTagsConflictResolverDialog.java
r2070 r2115 80 80 resolvers = new HashMap<OsmPrimitiveType, TagConflictResolver>(); 81 81 for (OsmPrimitiveType type: OsmPrimitiveType.values()) { 82 if (type.equals(OsmPrimitiveType.CHANGESET)) {83 continue;84 }85 82 resolvers.put(type, new TagConflictResolver()); 86 83 resolvers.get(type).getModel().addPropertyChangeListener(this); … … 148 145 String msg = ""; 149 146 switch(type) { 150 case NODE: msg= trn("{0} node", "{0} nodes", count, count); break;151 case WAY: msg= trn("{0} way", "{0} ways", count, count); break;152 case RELATION: msg= trn("{0} relation", "{0} relations", count, count); break;147 case NODE: msg= trn("{0} node", "{0} nodes", count, count); break; 148 case WAY: msg= trn("{0} way", "{0} ways", count, count); break; 149 case RELATION: msg= trn("{0} relation", "{0} relations", count, count); break; 153 150 } 154 151 return msg; … … 446 443 String msg = ""; 447 444 switch(type) { 448 case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;449 case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;450 case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;445 case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break; 446 case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break; 447 case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break; 451 448 } 452 449 text = text.equals("") ? msg : text + ", " + msg; … … 472 469 String msg = ""; 473 470 switch(type) { 474 case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;475 case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;476 case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;471 case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break; 472 case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break; 473 case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break; 477 474 } 478 475 text = text.equals("") ? msg : text + ", " + msg; … … 491 488 492 489 switch(column) { 493 case 0: renderNumTags(info); break;494 case 1: renderFrom(info); break;495 case 2: renderTo(info); break;490 case 0: renderNumTags(info); break; 491 case 1: renderFrom(info); break; 492 case 2: renderTo(info); break; 496 493 } 497 494 } -
trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java
r2096 r2115 33 33 import org.openstreetmap.josm.Main; 34 34 import org.openstreetmap.josm.actions.UploadAction; 35 import org.openstreetmap.josm.actions.UploadAction.UploadConfirmationHook;36 35 import org.openstreetmap.josm.gui.ExceptionDialogUtil; 37 36 import org.openstreetmap.josm.gui.SideButton; … … 299 298 public void cancel() { 300 299 switch(model.getMode()) { 301 case EDITING_DATA: cancelWhenInEditingModel(); break;302 case UPLOADING_AND_SAVING: cancelSafeAndUploadTask(); break;300 case EDITING_DATA: cancelWhenInEditingModel(); break; 301 case UPLOADING_AND_SAVING: cancelSafeAndUploadTask(); break; 303 302 } 304 303 } … … 334 333 Mode mode = (Mode)evt.getNewValue(); 335 334 switch(mode) { 336 case EDITING_DATA: setEnabled(true); break;337 case UPLOADING_AND_SAVING: setEnabled(false); break;335 case EDITING_DATA: setEnabled(true); break; 336 case UPLOADING_AND_SAVING: setEnabled(false); break; 338 337 } 339 338 } … … 368 367 SaveLayersModel.Mode mode = (SaveLayersModel.Mode)evt.getNewValue(); 369 368 switch(mode) { 370 case EDITING_DATA: setEnabled(true); break;371 case UPLOADING_AND_SAVING: setEnabled(false); break;369 case EDITING_DATA: setEnabled(true); break; 370 case UPLOADING_AND_SAVING: setEnabled(false); break; 372 371 } 373 372 } … … 411 410 layerInfo.getLayer(), 412 411 monitor, 413 Upload Action.UploadConfirmationHook.getUploadDialog().getChangeset(),414 Upload Action.UploadConfirmationHook.getUploadDialog().getChangesetProcessingType()412 UploadDialog.getUploadDialog().getChangeset(), 413 UploadDialog.getUploadDialog().isDoCloseAfterUpload() 415 414 ); 416 415 currentFuture = worker.submit(currentTask); -
trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java
r2081 r2115 6 6 7 7 import java.awt.BorderLayout; 8 import java.awt.Component; 8 9 import java.awt.Dimension; 9 10 import java.awt.FlowLayout; … … 12 13 import java.awt.event.ActionEvent; 13 14 import java.awt.event.ActionListener; 15 import java.awt.event.ItemEvent; 16 import java.awt.event.ItemListener; 14 17 import java.awt.event.WindowAdapter; 15 18 import java.awt.event.WindowEvent; 19 import java.text.SimpleDateFormat; 16 20 import java.util.ArrayList; 17 import java.util.HashMap; 21 import java.util.Collection; 22 import java.util.Collections; 18 23 import java.util.LinkedList; 19 24 import java.util.List; 20 import java.util.Map;21 25 22 26 import javax.swing.AbstractAction; 23 27 import javax.swing.AbstractListModel; 24 28 import javax.swing.BorderFactory; 25 import javax.swing.BoxLayout;26 29 import javax.swing.ButtonGroup; 30 import javax.swing.DefaultComboBoxModel; 31 import javax.swing.ImageIcon; 32 import javax.swing.JButton; 27 33 import javax.swing.JCheckBox; 34 import javax.swing.JComboBox; 28 35 import javax.swing.JDialog; 29 36 import javax.swing.JLabel; … … 34 41 import javax.swing.JScrollPane; 35 42 import javax.swing.JTabbedPane; 43 import javax.swing.ListCellRenderer; 44 import javax.swing.UIManager; 36 45 import javax.swing.event.ChangeEvent; 37 46 import javax.swing.event.ChangeListener; 47 import javax.swing.event.ListDataEvent; 48 import javax.swing.event.ListDataListener; 38 49 39 50 import org.openstreetmap.josm.Main; … … 46 57 import org.openstreetmap.josm.gui.tagging.TagEditorPanel; 47 58 import org.openstreetmap.josm.gui.tagging.TagModel; 48 import org.openstreetmap.josm.io.ChangesetProcessingType;49 59 import org.openstreetmap.josm.io.OsmApi; 50 60 import org.openstreetmap.josm.tools.GBC; … … 60 70 public class UploadDialog extends JDialog { 61 71 72 62 73 public static final String HISTORY_KEY = "upload.comment.history"; 74 75 /** the unique instance of the upload dialog */ 76 static private UploadDialog uploadDialog; 77 78 /** 79 * Replies the unique instance of the upload dialog 80 * 81 * @return the unique instance of the upload dialog 82 */ 83 static public UploadDialog getUploadDialog() { 84 if (uploadDialog == null) { 85 uploadDialog = new UploadDialog(); 86 } 87 return uploadDialog; 88 } 63 89 64 90 /** the list with the added primitives */ … … 77 103 private JPanel pnlLists; 78 104 /** checkbox for selecting whether an atomic upload is to be used */ 79 private JCheckBox cbUseAtomicUpload;80 /** input field for changeset comment */81 private SuggestingJHistoryComboBox cmt;82 /** ui component for editing changeset tags */83 105 private TagEditorPanel tagEditorPanel; 84 106 /** the tabbed pane used below of the list of primitives */ 85 107 private JTabbedPane southTabbedPane; 86 /** the button group with the changeset processing types */ 87 private ButtonGroup bgChangesetHandlingOptions; 88 /** radio buttons for selecting a changeset processing type */ 89 private Map<ChangesetProcessingType, JRadioButton> rbChangesetHandlingOptions; 108 109 private ChangesetSelectionPanel pnlChangesetSelection; 90 110 91 111 private boolean canceled = false; … … 104 124 } 105 125 106 /**107 * builds the panel with the ui components for controlling how the changeset108 * should be processed (opening/closing a changeset)109 *110 * @return the panel with the ui components for controlling how the changeset111 * should be processed112 */113 protected JPanel buildChangesetHandlingControlPanel() {114 JPanel pnl = new JPanel();115 pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS));116 bgChangesetHandlingOptions = new ButtonGroup();117 rbChangesetHandlingOptions = new HashMap<ChangesetProcessingType, JRadioButton>();118 ChangesetProcessingTypeChangedAction a = new ChangesetProcessingTypeChangedAction();119 for(ChangesetProcessingType type: ChangesetProcessingType.values()) {120 rbChangesetHandlingOptions.put(type, new JRadioButton());121 rbChangesetHandlingOptions.get(type).addActionListener(a);122 }123 JRadioButton rb = rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_NEW_AND_CLOSE);124 rb.setText(tr("Use a new changeset and close it"));125 rb.setToolTipText(tr("Select to upload the data using a new changeset and to close the changeset after the upload"));126 127 rb = rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_NEW_AND_LEAVE_OPEN);128 rb.setText(tr("Use a new changeset and leave it open"));129 rb.setToolTipText(tr("Select to upload the data using a new changeset and to leave the changeset open after the upload"));130 131 pnl.add(new JLabel(tr("Upload to a new or to an existing changeset?")));132 pnl.add(rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_NEW_AND_CLOSE));133 pnl.add(rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_NEW_AND_LEAVE_OPEN));134 pnl.add(rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_EXISTING_AND_CLOSE));135 pnl.add(rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_EXISTING_AND_LEAVE_OPEN));136 137 for(ChangesetProcessingType type: ChangesetProcessingType.values()) {138 rbChangesetHandlingOptions.get(type).setVisible(false);139 bgChangesetHandlingOptions.add(rbChangesetHandlingOptions.get(type));140 }141 return pnl;142 }143 144 /**145 * build the panel with the widgets for controlling how the changeset should be processed146 * (atomic upload or not, comment, opening/closing changeset)147 *148 * @return149 */150 protected JPanel buildChangesetControlPanel() {151 JPanel pnl = new JPanel();152 pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS));153 pnl.add(cbUseAtomicUpload = new JCheckBox(tr("upload all changes in one request")));154 cbUseAtomicUpload.setToolTipText(tr("Enable to upload all changes in one request, disable to use one request per changed primitive"));155 boolean useAtomicUpload = Main.pref.getBoolean("osm-server.atomic-upload", true);156 cbUseAtomicUpload.setSelected(useAtomicUpload);157 cbUseAtomicUpload.setEnabled(OsmApi.getOsmApi().hasSupportForDiffUploads());158 159 pnl.add(buildChangesetHandlingControlPanel());160 return pnl;161 }162 163 /**164 * builds the upload control panel165 *166 * @return167 */168 protected JPanel buildUploadControlPanel() {169 JPanel pnl = new JPanel();170 pnl.setLayout(new GridBagLayout());171 pnl.add(new JLabel(tr("Provide a brief comment for the changes you are uploading:")), GBC.eol().insets(0, 5, 10, 3));172 cmt = new SuggestingJHistoryComboBox();173 List<String> cmtHistory = new LinkedList<String>(Main.pref.getCollection(HISTORY_KEY, new LinkedList<String>()));174 cmt.setHistory(cmtHistory);175 cmt.getEditor().addActionListener(176 new ActionListener() {177 public void actionPerformed(ActionEvent e) {178 TagModel tm = tagEditorPanel.getModel().get("comment");179 if (tm == null) {180 tagEditorPanel.getModel().add(new TagModel("comment", cmt.getText()));181 } else {182 tm.setValue(cmt.getText());183 }184 tagEditorPanel.getModel().fireTableDataChanged();185 }186 }187 );188 pnl.add(cmt, GBC.eol().fill(GBC.HORIZONTAL));189 190 // configuration options for atomic upload191 //192 pnl.add(buildChangesetControlPanel(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));193 return pnl;194 }195 126 196 127 protected JPanel buildContentPanel() { … … 211 142 // 212 143 southTabbedPane = new JTabbedPane(); 213 southTabbedPane.add( buildUploadControlPanel());144 southTabbedPane.add(new JPanel()); 214 145 tagEditorPanel = new TagEditorPanel(); 215 146 southTabbedPane.add(tagEditorPanel); 147 southTabbedPane.setComponentAt(0, pnlChangesetSelection = new ChangesetSelectionPanel()); 216 148 southTabbedPane.setTitleAt(0, tr("Settings")); 217 149 southTabbedPane.setTitleAt(1, tr("Tags of new changeset")); … … 340 272 341 273 /** 342 * Replies true if a valid changeset comment has been entered in this dialog343 *344 * @return true if a valid changeset comment has been entered in this dialog345 */346 public boolean hasChangesetComment() {347 if (!getChangesetProcessingType().isUseNew())348 return true;349 return cmt.getText().trim().length() >= 3;350 }351 352 /**353 274 * Remembers the user input in the preference settings 354 275 */ 355 276 public void rememberUserInput() { 356 // store the history of comments 357 cmt.addCurrentItemToHistory(); 358 Main.pref.putCollection(HISTORY_KEY, cmt.getHistory()); 359 Main.pref.put("osm-server.atomic-upload", cbUseAtomicUpload.isSelected()); 277 pnlChangesetSelection.rememberUserInput(); 360 278 } 361 279 … … 365 283 public void startUserInput() { 366 284 tagEditorPanel.initAutoCompletion(Main.main.getEditLayer()); 367 initChangesetProcessingType(); 368 cmt.getEditor().selectAll(); 369 cmt.requestFocus(); 370 } 371 372 /** 373 * Replies the current changeset processing type 374 * 375 * @return the current changeset processing type 376 */ 377 public ChangesetProcessingType getChangesetProcessingType() { 378 ChangesetProcessingType changesetProcessingType = null; 379 for (ChangesetProcessingType type: ChangesetProcessingType.values()) { 380 if (rbChangesetHandlingOptions.get(type).isSelected()) { 381 changesetProcessingType = type; 382 break; 383 } 384 } 385 return changesetProcessingType == null ? 386 ChangesetProcessingType.USE_NEW_AND_CLOSE : 387 changesetProcessingType; 285 pnlChangesetSelection.startUserInput(); 388 286 } 389 287 … … 394 292 */ 395 293 public Changeset getChangeset() { 396 Changeset changeset = new Changeset(); 397 tagEditorPanel.getModel().applyToPrimitive(changeset); 398 changeset.put("comment", cmt.getText()); 399 return changeset; 400 } 401 402 /** 403 * initializes the panel depending on the possible changeset processing 404 * types 405 */ 406 protected void initChangesetProcessingType() { 407 for (ChangesetProcessingType type: ChangesetProcessingType.values()) { 408 // show options for new changeset, disable others 409 // 410 rbChangesetHandlingOptions.get(type).setVisible(type.isUseNew()); 411 } 412 if (OsmApi.getOsmApi().getCurrentChangeset() != null) { 413 Changeset cs = OsmApi.getOsmApi().getCurrentChangeset(); 414 for (ChangesetProcessingType type: ChangesetProcessingType.values()) { 415 // show options for using existing changeset 416 // 417 if (!type.isUseNew()) { 418 rbChangesetHandlingOptions.get(type).setVisible(true); 419 } 420 } 421 JRadioButton rb = rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_EXISTING_AND_CLOSE); 422 rb.setText(tr("Use the existing changeset {0} and close it after upload",cs.getId())); 423 rb.setToolTipText(tr("Select to upload to the existing changeset {0} and to close the changeset after this upload",cs.getId())); 424 425 rb = rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_EXISTING_AND_LEAVE_OPEN); 426 rb.setText(tr("Use the existing changeset {0} and leave it open",cs.getId())); 427 rb.setToolTipText(tr("Select to upload to the existing changeset {0} and to leave the changeset open for further uploads",cs.getId())); 428 429 rbChangesetHandlingOptions.get(getChangesetProcessingType()).setSelected(true); 430 431 } else { 432 ChangesetProcessingType type = getChangesetProcessingType(); 433 if (!type.isUseNew()) { 434 type = ChangesetProcessingType.USE_NEW_AND_CLOSE; 435 } 436 rbChangesetHandlingOptions.get(type).setSelected(true); 437 } 438 ChangesetProcessingType type = getChangesetProcessingType(); 439 if (type.isUseNew() || (! type.isUseNew() && OsmApi.getOsmApi().getCurrentChangeset() == null)) { 440 Changeset cs = new Changeset(); 441 cs.put("created_by", getDefaultCreatedBy()); 442 tagEditorPanel.getModel().initFromPrimitive(cs); 443 } else { 444 Changeset cs = OsmApi.getOsmApi().getCurrentChangeset(); 445 tagEditorPanel.getModel().initFromPrimitive(cs); 446 } 294 Changeset cs = pnlChangesetSelection.getChangeset(); 295 tagEditorPanel.getModel().applyToPrimitive(cs); 296 cs.put("comment", getUploadComment()); 297 return cs; 298 } 299 300 /** 301 * Sets or updates the changeset cs. 302 * If cs is null, does nothing. 303 * If cs.getId() == 0 does nothing. 304 * If cs.getId() > 0 and cs is open, adds it to the list of open 305 * changesets. If it is closed, removes it from the list of open 306 * changesets. 307 * 308 * @param cs the changeset 309 */ 310 public void setOrUpdateChangeset(Changeset cs) { 311 pnlChangesetSelection.setOrUpdateChangeset(cs); 312 } 313 314 public boolean isDoCloseAfterUpload() { 315 return pnlChangesetSelection.isCloseAfterUpload(); 447 316 } 448 317 … … 457 326 } 458 327 459 /** 460 * refreshes the panel depending on a changeset processing type 461 * 462 * @param type the changeset processing type 463 */ 464 protected void switchToProcessingType(ChangesetProcessingType type) { 465 if (type.isUseNew()) { 466 southTabbedPane.setTitleAt(1, tr("Tags of new changeset")); 467 // init a new changeset from the currently edited tags 468 // and the comment field 469 // 470 Changeset cs = new Changeset(getChangeset()); 471 if (cs.get("created_by") == null) { 472 cs.put("created_by", getDefaultCreatedBy()); 473 } 474 cs.put("comment", this.cmt.getText()); 475 tagEditorPanel.getModel().initFromPrimitive(cs); 476 } else { 477 Changeset cs = OsmApi.getOsmApi().getCurrentChangeset(); 478 if (cs != null) { 479 cs.put("comment", this.cmt.getText()); 480 cs.setKeys(getChangeset().getKeys()); 481 southTabbedPane.setTitleAt(1, tr("Tags of changeset {0}", cs.getId())); 482 tagEditorPanel.getModel().initFromPrimitive(cs); 483 } 484 } 485 } 486 487 public String getUploadComment() { 328 protected String getUploadComment() { 488 329 switch(southTabbedPane.getSelectedIndex()) { 489 case 0: return cmt.getText(); 490 case 1: 491 TagModel tm = tagEditorPanel.getModel().get("comment"); 492 return tm == null? "" : tm.getValue(); 330 case 0: 331 pnlChangesetSelection.getUploadComment(); 332 case 1: 333 TagModel tm = tagEditorPanel.getModel().get("comment"); 334 return tm == null? "" : tm.getValue(); 493 335 } 494 336 return ""; … … 509 351 getClass().getName() + ".geometry", 510 352 WindowGeometry.centerInWindow( 511 JOptionPane.getFrameForComponent(Main.parent),353 Main.parent, 512 354 new Dimension(400,600) 513 355 ) … … 520 362 } 521 363 522 class ChangesetProcessingTypeChangedAction implements ActionListener {523 public void actionPerformed(ActionEvent e) {524 ChangesetProcessingType type = getChangesetProcessingType();525 switchToProcessingType(type);526 }527 }528 529 364 /** 365 * This change listener is triggered when current tab in the tabbed pane in 366 * the lower half of the dialog is changed. 367 * 368 * It's main purpose is to keep the content in the text field for the changeset 369 * comment in sync with the changeset tag "comment". 370 * 371 */ 530 372 class TabbedPaneChangeLister implements ChangeListener { 531 373 … … 560 402 TagModel tm = getOrCreateCommentTag(); 561 403 tm.setName("comment"); 562 tm.setValue( cmt.getText().trim());563 if ( cmt.getText().trim().equals("")) {404 tm.setValue(pnlChangesetSelection.getUploadComment().trim()); 405 if (pnlChangesetSelection.getUploadComment().trim().equals("")) { 564 406 removeCommentTag(); 565 407 } 566 408 tagEditorPanel.getModel().fireTableDataChanged(); 567 409 } 568 569 410 570 411 public void stateChanged(ChangeEvent e) { 571 412 if (southTabbedPane.getSelectedIndex() ==0) { 572 413 TagModel tm = tagEditorPanel.getModel().get("comment"); 573 cmt.setText(tm == null ? "" : tm.getValue()); 574 cmt.getEditor().selectAll(); 575 cmt.requestFocus(); 414 pnlChangesetSelection.initEditingOfUploadComment(tm == null ? "" : tm.getValue()); 576 415 } else if (southTabbedPane.getSelectedIndex() == 1) { 577 416 refreshCommentTag(); … … 580 419 } 581 420 421 /** 422 * Handles an upload 423 * 424 */ 582 425 class UploadAction extends AbstractAction { 583 426 public UploadAction() { … … 599 442 if (getUploadComment().trim().length() < 3) { 600 443 warnIllegalUploadComment(); 601 cmt.getEditor().selectAll();602 cmt.requestFocus();444 southTabbedPane.setSelectedIndex(0); 445 pnlChangesetSelection.initEditingOfUploadComment(getUploadComment()); 603 446 return; 604 447 } … … 609 452 } 610 453 454 /** 455 * Action for canceling the dialog 456 * 457 */ 611 458 class CancelAction extends AbstractAction { 612 459 public CancelAction() { … … 623 470 } 624 471 472 /** 473 * A simple list of OSM primitives. 474 * 475 */ 625 476 class PrimitiveList extends JList { 626 477 public PrimitiveList() { … … 633 484 } 634 485 486 /** 487 * A list model for a list of OSM primitives. 488 * 489 */ 635 490 class PrimitiveListModel extends AbstractListModel{ 636 491 private List<OsmPrimitive> primitives; … … 664 519 } 665 520 521 /** 522 * Listens to window closing events and processes them as cancel events 523 * 524 */ 666 525 class WindowClosingAdapter extends WindowAdapter { 667 526 @Override … … 670 529 } 671 530 } 531 532 /** 533 * The panel which provides various UI widgets for controlling how to use 534 * changesets during upload. 535 * 536 */ 537 class ChangesetSelectionPanel extends JPanel implements ListDataListener{ 538 539 private ButtonGroup bgUseNewOrExisting; 540 private JRadioButton rbUseNew; 541 private JRadioButton rbExisting; 542 private JComboBox cbOpenChangesets; 543 private JButton btnRefresh; 544 private JButton btnClose; 545 private JCheckBox cbCloseAfterUpload; 546 private OpenChangesetModel model; 547 private SuggestingJHistoryComboBox cmt; 548 private JCheckBox cbUseAtomicUpload; 549 550 /** 551 * build the panel with the widgets for controlling whether an atomic upload 552 * should be used or not 553 * 554 * @return the panel 555 */ 556 protected JPanel buildAtomicUploadControlPanel() { 557 JPanel pnl = new JPanel(); 558 pnl.setLayout(new GridBagLayout()); 559 GridBagConstraints gc = new GridBagConstraints(); 560 gc.fill = GridBagConstraints.HORIZONTAL; 561 gc.weightx = 1.0; 562 gc.anchor = GridBagConstraints.FIRST_LINE_START; 563 pnl.add(cbUseAtomicUpload = new JCheckBox(tr("Upload all changes in one request")), gc); 564 cbUseAtomicUpload.setToolTipText(tr("Enable to upload all changes in one request, disable to use one request per changed primitive")); 565 boolean useAtomicUpload = Main.pref.getBoolean("osm-server.atomic-upload", true); 566 cbUseAtomicUpload.setSelected(useAtomicUpload); 567 cbUseAtomicUpload.setEnabled(OsmApi.getOsmApi().hasSupportForDiffUploads()); 568 return pnl; 569 } 570 571 protected JPanel buildUploadCommentPanel() { 572 JPanel pnl = new JPanel(); 573 pnl.setLayout(new GridBagLayout()); 574 pnl.add(new JLabel(tr("Provide a brief comment for the changes you are uploading:")), GBC.eol().insets(0, 5, 10, 3)); 575 cmt = new SuggestingJHistoryComboBox(); 576 List<String> cmtHistory = new LinkedList<String>(Main.pref.getCollection(HISTORY_KEY, new LinkedList<String>())); 577 cmt.setHistory(cmtHistory); 578 cmt.getEditor().addActionListener( 579 new ActionListener() { 580 public void actionPerformed(ActionEvent e) { 581 TagModel tm = tagEditorPanel.getModel().get("comment"); 582 if (tm == null) { 583 tagEditorPanel.getModel().add(new TagModel("comment", cmt.getText())); 584 } else { 585 tm.setValue(cmt.getText()); 586 } 587 tagEditorPanel.getModel().fireTableDataChanged(); 588 } 589 } 590 ); 591 pnl.add(cmt, GBC.eol().fill(GBC.HORIZONTAL)); 592 return pnl; 593 } 594 595 protected void build() { 596 setLayout(new GridBagLayout()); 597 GridBagConstraints gc = new GridBagConstraints(); 598 599 bgUseNewOrExisting = new ButtonGroup(); 600 601 // -- atomic upload 602 gc.gridwidth = 4; 603 gc.gridy = 0; 604 gc.fill = GridBagConstraints.HORIZONTAL; 605 gc.weightx = 1.0; 606 gc.anchor = GridBagConstraints.FIRST_LINE_START; 607 add(buildAtomicUploadControlPanel(), gc); 608 609 // -- changeset command 610 gc.gridwidth = 4; 611 gc.gridy = 1; 612 gc.fill = GridBagConstraints.HORIZONTAL; 613 gc.weightx = 1.0; 614 gc.anchor = GridBagConstraints.FIRST_LINE_START; 615 add(buildUploadCommentPanel(), gc); 616 617 gc.gridwidth = 4; 618 gc.gridy = 2; 619 gc.fill = GridBagConstraints.HORIZONTAL; 620 gc.weightx = 0.0; 621 gc.anchor = GridBagConstraints.FIRST_LINE_START; 622 rbUseNew = new JRadioButton(tr("Open a new changeset")); 623 rbUseNew.setToolTipText(tr("Open a new changeset and use it in the next upload")); 624 bgUseNewOrExisting.add(rbUseNew); 625 add(rbUseNew, gc); 626 627 gc.gridx = 0; 628 gc.gridy = 3; 629 gc.gridwidth = 1; 630 rbExisting = new JRadioButton(tr("Use an open changeset")); 631 rbExisting.setToolTipText(tr("Upload data to an already opened changeset")); 632 bgUseNewOrExisting.add(rbExisting); 633 add(rbExisting, gc); 634 635 gc.gridx = 1; 636 gc.gridy = 3; 637 gc.gridwidth = 1; 638 gc.weightx = 1.0; 639 model = new OpenChangesetModel(); 640 cbOpenChangesets = new JComboBox(model); 641 cbOpenChangesets.setToolTipText("Select an open changeset"); 642 cbOpenChangesets.setRenderer(new ChangesetCellRenderer()); 643 cbOpenChangesets.addItemListener(new ChangesetListItemStateListener()); 644 Dimension d = cbOpenChangesets.getPreferredSize(); 645 d.width = 200; 646 cbOpenChangesets.setPreferredSize(d); 647 d.width = 100; 648 cbOpenChangesets.setMinimumSize(d); 649 model.addListDataListener(this); 650 add(cbOpenChangesets, gc); 651 652 gc.gridx = 3; 653 gc.gridy = 3; 654 gc.gridwidth = 1; 655 gc.weightx = 0.0; 656 btnRefresh = new JButton(new RefreshAction()); 657 add(btnRefresh, gc); 658 659 gc.gridx = 4; 660 gc.gridy = 3; 661 gc.gridwidth = 1; 662 gc.weightx = 0.0; 663 CloseChangesetAction closeChangesetAction = new CloseChangesetAction(); 664 btnClose = new JButton(closeChangesetAction); 665 cbOpenChangesets.addItemListener(closeChangesetAction); 666 add(btnClose, gc); 667 668 gc.gridx = 0; 669 gc.gridy = 4; 670 gc.gridwidth = 4; 671 cbCloseAfterUpload = new JCheckBox(tr("Close changeset after upload")); 672 cbCloseAfterUpload.setToolTipText(tr("Select to close the changeset after the next upload")); 673 add(cbCloseAfterUpload, gc); 674 cbCloseAfterUpload.setSelected(true); 675 676 rbUseNew.getModel().addItemListener(new RadioButtonHandler()); 677 rbExisting.getModel().addItemListener(new RadioButtonHandler()); 678 679 refreshGUI(); 680 } 681 682 public ChangesetSelectionPanel() { 683 build(); 684 } 685 686 /** 687 * Remembers the user input in the preference settings 688 */ 689 public void rememberUserInput() { 690 // store the history of comments 691 cmt.addCurrentItemToHistory(); 692 Main.pref.putCollection(HISTORY_KEY, cmt.getHistory()); 693 Main.pref.put("osm-server.atomic-upload", cbUseAtomicUpload.isSelected()); 694 } 695 696 /** 697 * Initializes the panel for user input 698 */ 699 public void startUserInput() { 700 cmt.getEditor().selectAll(); 701 cmt.requestFocus(); 702 } 703 704 /** 705 * Replies the current upload comment 706 * 707 * @return 708 */ 709 public String getUploadComment() { 710 return cmt.getText(); 711 } 712 713 /** 714 * Replies the current upload comment 715 * 716 * @return 717 */ 718 public void setUploadComment(String uploadComment) { 719 cmt.setText(uploadComment); 720 } 721 722 public void initEditingOfUploadComment(String comment) { 723 setUploadComment(comment); 724 cmt.getEditor().selectAll(); 725 cmt.requestFocus(); 726 } 727 728 protected void refreshGUI() { 729 rbExisting.setEnabled(model.getSize() > 0); 730 if (model.getSize() == 0) { 731 if (!rbUseNew.isSelected()) { 732 rbUseNew.setSelected(true); 733 } 734 } 735 cbOpenChangesets.setEnabled(model.getSize() > 0 && rbExisting.isSelected()); 736 } 737 738 public void contentsChanged(ListDataEvent e) { 739 refreshGUI(); 740 } 741 742 public void intervalAdded(ListDataEvent e) { 743 refreshGUI(); 744 } 745 746 public void intervalRemoved(ListDataEvent e) { 747 refreshGUI(); 748 } 749 750 public Changeset getChangeset() { 751 if (rbUseNew.isSelected() || cbOpenChangesets.getSelectedItem() == null) 752 return new Changeset(); 753 Changeset cs = (Changeset)cbOpenChangesets.getSelectedItem(); 754 if (cs == null) 755 return new Changeset(); 756 return cs; 757 } 758 759 public void setOrUpdateChangeset(Changeset cs) { 760 if (cs == null) { 761 tagEditorPanel.getModel().clear(); 762 tagEditorPanel.getModel().add("created_by", getDefaultCreatedBy()); 763 tagEditorPanel.getModel().appendNewTag(); 764 rbUseNew.setSelected(true); 765 } else if (cs.getId() == 0) { 766 if (cs.get("created_by") == null) { 767 cs.put("created_by", getDefaultCreatedBy()); 768 } 769 tagEditorPanel.getModel().initFromPrimitive(cs); 770 tagEditorPanel.getModel().appendNewTag(); 771 rbUseNew.setSelected(true); 772 } else if (cs.getId() > 0 && cs.isOpen()){ 773 if (cs.get("created_by") == null) { 774 cs.put("created_by", getDefaultCreatedBy()); 775 } 776 tagEditorPanel.getModel().initFromPrimitive(cs); 777 model.addOrUpdate(cs); 778 cs = model.getChangesetById(cs.getId()); 779 cbOpenChangesets.setSelectedItem(cs); 780 } else if (cs.getId() > 0 && !cs.isOpen()){ 781 if (cs.get("created_by") == null) { 782 cs.put("created_by", getDefaultCreatedBy()); 783 } 784 tagEditorPanel.getModel().initFromPrimitive(cs); 785 model.removeChangeset(cs); 786 if (model.getSize() == 0) { 787 rbUseNew.setSelected(true); 788 model.setSelectedItem(null); 789 southTabbedPane.setTitleAt(1, tr("Tags of new changeset")); 790 } 791 } 792 } 793 794 public void setUseNew() { 795 rbUseNew.setSelected(true); 796 } 797 798 public void setUseExisting() { 799 rbExisting.setSelected(true); 800 if (cbOpenChangesets.getSelectedItem() == null && model.getSize() > 0) { 801 cbOpenChangesets.setSelectedItem(model.getElementAt(0)); 802 } 803 } 804 805 public boolean isCloseAfterUpload() { 806 return cbCloseAfterUpload.isSelected(); 807 } 808 809 class RadioButtonHandler implements ItemListener { 810 public void itemStateChanged(ItemEvent e) { 811 if (rbUseNew.isSelected()) { 812 southTabbedPane.setTitleAt(1, tr("Tags of new changeset")); 813 // init a new changeset from the currently edited tags 814 // and the comment field 815 // 816 Changeset cs = new Changeset(); 817 tagEditorPanel.getModel().applyToPrimitive(cs); 818 if (cs.get("created_by") == null) { 819 cs.put("created_by", getDefaultCreatedBy()); 820 } 821 cs.put("comment", cmt.getText()); 822 tagEditorPanel.getModel().initFromPrimitive(cs); 823 } else { 824 if (cbOpenChangesets.getSelectedItem() == null) { 825 model.selectFirstChangeset(); 826 } 827 Changeset cs = (Changeset)cbOpenChangesets.getSelectedItem(); 828 if (cs != null) { 829 cs.put("comment", cmt.getText()); 830 southTabbedPane.setTitleAt(1, tr("Tags of changeset {0}", cs.getId())); 831 tagEditorPanel.getModel().initFromPrimitive(cs); 832 } 833 } 834 refreshGUI(); 835 } 836 } 837 838 class ChangesetListItemStateListener implements ItemListener { 839 public void itemStateChanged(ItemEvent e) { 840 841 Changeset cs = (Changeset)cbOpenChangesets.getSelectedItem(); 842 if (cs == null) { 843 southTabbedPane.setTitleAt(1, tr("Tags of new changeset")); 844 // init a new changeset from the currently edited tags 845 // and the comment field 846 // 847 cs = new Changeset(); 848 tagEditorPanel.getModel().applyToPrimitive(cs); 849 if (cs.get("created_by") == null) { 850 cs.put("created_by", getDefaultCreatedBy()); 851 } 852 cs.put("comment", cmt.getText()); 853 tagEditorPanel.getModel().initFromPrimitive(cs); 854 } else { 855 southTabbedPane.setTitleAt(1, tr("Tags of changeset {0}", cs.getId())); 856 cs.put("comment", cmt.getText()); 857 southTabbedPane.setTitleAt(1, tr("Tags of changeset {0}", cs.getId())); 858 if (cs.get("created_by") == null) { 859 cs.put("created_by", getDefaultCreatedBy()); 860 } 861 tagEditorPanel.getModel().initFromPrimitive(cs); 862 } 863 } 864 } 865 866 class RefreshAction extends AbstractAction { 867 public RefreshAction() { 868 //putValue(NAME, tr("Reload")); 869 putValue(SHORT_DESCRIPTION, tr("Load the list of your open changesets from the server")); 870 putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh")); 871 } 872 873 public void actionPerformed(ActionEvent e) { 874 DownloadOpenChangesetsTask task = new DownloadOpenChangesetsTask(model); 875 Main.worker.submit(task); 876 } 877 } 878 879 class CloseChangesetAction extends AbstractAction implements ItemListener{ 880 public CloseChangesetAction() { 881 putValue(NAME, tr("Close")); 882 putValue(SHORT_DESCRIPTION, tr("Close the currently selected open changeset")); 883 refreshEnabledState(); 884 } 885 886 public void actionPerformed(ActionEvent e) { 887 Changeset cs = (Changeset)cbOpenChangesets.getSelectedItem(); 888 if (cs == null) return; 889 CloseChangesetTask task = new CloseChangesetTask(Collections.singletonList(cs)); 890 Main.worker.submit(task); 891 } 892 893 protected void refreshEnabledState() { 894 setEnabled(cbOpenChangesets.getModel().getSize() > 0 && cbOpenChangesets.getSelectedItem() != null); 895 } 896 897 public void itemStateChanged(ItemEvent e) { 898 refreshEnabledState(); 899 } 900 } 901 } 902 903 public class OpenChangesetModel extends DefaultComboBoxModel { 904 private List<Changeset> changesets; 905 private long uid; 906 private Changeset selectedChangeset = null; 907 908 protected Changeset getChangesetById(long id) { 909 for (Changeset cs : changesets) { 910 if (cs.getId() == id) return cs; 911 } 912 return null; 913 } 914 915 public OpenChangesetModel() { 916 this.changesets = new ArrayList<Changeset>(); 917 } 918 919 protected void internalAddOrUpdate(Changeset cs) { 920 Changeset other = getChangesetById(cs.getId()); 921 if (other != null) { 922 cs.cloneFrom(other); 923 } else { 924 changesets.add(cs); 925 } 926 } 927 928 public void addOrUpdate(Changeset cs) { 929 if (cs.getId() <= 0 ) 930 throw new IllegalArgumentException(tr("changeset id > 0 expected. Got {1}", "id", cs.getId())); 931 internalAddOrUpdate(cs); 932 fireContentsChanged(this, 0, getSize()); 933 } 934 935 public void remove(long id) { 936 Changeset cs = getChangesetById(id); 937 if (cs != null) { 938 changesets.remove(cs); 939 } 940 fireContentsChanged(this, 0, getSize()); 941 } 942 943 public void addOrUpdate(Collection<Changeset> changesets) { 944 for (Changeset cs: changesets) { 945 internalAddOrUpdate(cs); 946 } 947 fireContentsChanged(this, 0, getSize()); 948 if (getSelectedItem() == null && !this.changesets.isEmpty()) { 949 setSelectedItem(this.changesets.get(0)); 950 } else { 951 setSelectedItem(null); 952 } 953 } 954 955 public void setUserId(long uid) { 956 this.uid = uid; 957 } 958 959 public long getUserId() { 960 return uid; 961 } 962 963 public void selectFirstChangeset() { 964 if (changesets == null || changesets.isEmpty()) return; 965 setSelectedItem(changesets.get(0)); 966 } 967 968 public void removeChangeset(Changeset cs) { 969 if (cs == null) return; 970 changesets.remove(cs); 971 if (selectedChangeset == cs) { 972 selectFirstChangeset(); 973 } 974 fireContentsChanged(this, 0, getSize()); 975 } 976 /* ------------------------------------------------------------------------------------ */ 977 /* ComboBoxModel */ 978 /* ------------------------------------------------------------------------------------ */ 979 @Override 980 public Object getElementAt(int index) { 981 return changesets.get(index); 982 } 983 984 @Override 985 public int getIndexOf(Object anObject) { 986 return changesets.indexOf(anObject); 987 } 988 989 @Override 990 public int getSize() { 991 return changesets.size(); 992 } 993 994 @Override 995 public Object getSelectedItem() { 996 return selectedChangeset; 997 } 998 999 @Override 1000 public void setSelectedItem(Object anObject) { 1001 if (anObject == null) { 1002 this.selectedChangeset = null; 1003 super.setSelectedItem(null); 1004 return; 1005 } 1006 if (! (anObject instanceof Changeset)) return; 1007 Changeset cs = (Changeset)anObject; 1008 if (cs.getId() == 0 || ! cs.isOpen()) return; 1009 Changeset candidate = getChangesetById(cs.getId()); 1010 if (candidate == null) return; 1011 this.selectedChangeset = candidate; 1012 super.setSelectedItem(selectedChangeset); 1013 } 1014 } 672 1015 } -
trunk/src/org/openstreetmap/josm/gui/io/UploadLayerTask.java
r2040 r2115 36 36 private ProgressMonitor monitor; 37 37 private Changeset changeset; 38 private ChangesetProcessingType changesetProcessingType;38 private boolean closeChangesetAfterUpload; 39 39 40 40 /** … … 44 44 * @param changeset the changeset to be used if <code>changesetProcessingType</code> indicates that a new 45 45 * changeset is to be used 46 * @param c hangesetProcessingType how we handle changesets46 * @param closeChangesetAfterUpload true, if the changeset should be closed after the upload 47 47 * @throws IllegalArgumentException thrown, if layer is null 48 48 */ 49 public UploadLayerTask(OsmDataLayer layer, ProgressMonitor monitor, Changeset changeset, ChangesetProcessingType changesetProcessingType) {49 public UploadLayerTask(OsmDataLayer layer, ProgressMonitor monitor, Changeset changeset, boolean closeChangesetAfterUpload) { 50 50 if (layer == null) 51 51 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", layer)); … … 56 56 this.monitor = monitor; 57 57 this.changeset = changeset; 58 this.c hangesetProcessingType = changesetProcessingType == null ? ChangesetProcessingType.USE_NEW_AND_CLOSE : changesetProcessingType;58 this.closeChangesetAfterUpload = closeChangesetAfterUpload; 59 59 } 60 60 … … 69 69 ProgressMonitor m = monitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 70 70 if (isCancelled()) return; 71 writer.uploadOsm(layer.data.version, toUpload, changeset, c hangesetProcessingType, m);71 writer.uploadOsm(layer.data.version, toUpload, changeset, closeChangesetAfterUpload, m); 72 72 } catch (Exception sxe) { 73 73 if (isCancelled()) { -
trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java
r2081 r2115 9 9 import java.util.Collection; 10 10 import java.util.Comparator; 11 import java.util.HashMap; 11 12 import java.util.Iterator; 12 13 import java.util.List; 14 import java.util.Map; 13 15 import java.util.logging.Logger; 14 16 … … 19 21 import org.openstreetmap.josm.command.SequenceCommand; 20 22 import org.openstreetmap.josm.data.osm.OsmPrimitive; 23 import org.openstreetmap.josm.data.osm.Tagged; 21 24 22 25 … … 83 86 TagModel tag = tags.get(rowIndex); 84 87 switch(columnIndex) { 85 case 0:86 case 1: return tag;87 88 default:89 throw new IndexOutOfBoundsException("unexpected columnIndex: columnIndex=" + columnIndex);88 case 0: 89 case 1: return tag; 90 91 default: 92 throw new IndexOutOfBoundsException("unexpected columnIndex: columnIndex=" + columnIndex); 90 93 } 91 94 } … … 280 283 * @param primitive the OSM primitive 281 284 */ 282 public void initFromPrimitive( OsmPrimitiveprimitive) {285 public void initFromPrimitive(Tagged primitive) { 283 286 clear(); 284 287 for (String key : primitive.keySet()) { … … 293 296 294 297 /** 298 * initializes the model with the tags of an OSM primitive 299 * 300 * @param primitive the OSM primitive 301 */ 302 public void initFromTags(Map<String,String> tags) { 303 clear(); 304 for (String key : tags.keySet()) { 305 String value = tags.get(key); 306 add(key,value); 307 } 308 TagModel tag = new TagModel(); 309 sort(); 310 this.tags.add(tag); 311 setDirty(false); 312 } 313 314 /** 295 315 * applies the current state of the tag editor model to a primitive 296 316 * … … 298 318 * 299 319 */ 300 public void applyToPrimitive(OsmPrimitive primitive) { 301 primitive.removeAll(); 302 for (TagModel tag: tags) { 320 public void applyToPrimitive(Tagged primitive) { 321 Map<String,String> tags = primitive.getKeys(); 322 applyToTags(tags); 323 primitive.setKeys(tags); 324 } 325 326 /** 327 * applies the current state of the tag editor model to a map of tags 328 * 329 * @param tags the map of key/value pairs 330 * 331 */ 332 public void applyToTags(Map<String, String> tags) { 333 tags.clear(); 334 for (TagModel tag: this.tags) { 303 335 // tag still holds an unchanged list of different values for the same key. 304 336 // no property change command required … … 312 344 continue; 313 345 } 314 primitive.put(tag.getName(), tag.getValue()); 315 } 346 tags.put(tag.getName(), tag.getValue()); 347 } 348 } 349 350 public Map<String,String> getTags() { 351 Map<String,String> tags = new HashMap<String, String>(); 352 applyToTags(tags); 353 return tags; 316 354 } 317 355 -
trunk/src/org/openstreetmap/josm/io/OsmApi.java
r2074 r2115 23 23 import java.util.Collections; 24 24 import java.util.HashMap; 25 import java.util.Properties;26 25 27 26 import javax.xml.parsers.SAXParserFactory; … … 32 31 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 33 32 import org.openstreetmap.josm.data.osm.visitor.CreateOsmChangeVisitor; 33 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 34 34 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 35 35 import org.xml.sax.Attributes; … … 205 205 206 206 /** 207 * Makes an XML string from an OSM primitive. Uses the OsmWriter class. 208 * @param o the OSM primitive 209 * @param addBody true to generate the full XML, false to only generate the encapsulating tag 210 * @return XML string 211 */ 212 private String toXml(Changeset s) { 213 swriter.getBuffer().setLength(0); 214 osmWriter.header(); 215 s.visit(osmWriter); 216 osmWriter.footer(); 217 osmWriter.out.flush(); 218 return swriter.toString(); 219 } 220 221 /** 207 222 * Returns the base URL for API requests, including the negotiated version number. 208 223 * @return base URL string … … 229 244 */ 230 245 public void createPrimitive(OsmPrimitive osm, ProgressMonitor monitor) throws OsmTransferException { 231 initialize(monitor);232 246 String ret = ""; 233 247 try { 248 ensureValidChangeset(); 249 initialize(monitor); 234 250 ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/create", toXml(osm, true),monitor); 235 251 osm.setOsmId(Long.parseLong(ret.trim()), 1); … … 244 260 * version. 245 261 * 246 * @param osm the primitive 262 * @param osm the primitive. Must not be null 247 263 * @throws OsmTransferException if something goes wrong 248 264 */ 249 265 public void modifyPrimitive(OsmPrimitive osm, ProgressMonitor monitor) throws OsmTransferException { 250 initialize(monitor); 251 if (version.equals("0.5")) { 252 // legacy mode does not return the new object version. 253 sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true),monitor); 254 } else { 255 String ret = null; 256 // normal mode (0.6 and up) returns new object version. 257 try { 266 String ret = null; 267 try { 268 ensureValidChangeset(); 269 initialize(monitor); 270 if (version.equals("0.5")) { 271 // legacy mode does not return the new object version. 272 sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true),monitor); 273 } else { 274 // normal mode (0.6 and up) returns new object version. 258 275 ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true), monitor); 259 276 osm.setOsmId(osm.getId(), Integer.parseInt(ret.trim())); 260 } catch(NumberFormatException e) {261 throw new OsmTransferException(tr("unexpected format of new version of modified primitive ''{0}'', got ''{1}''", osm.getId(), ret));262 277 } 278 } catch(NumberFormatException e) { 279 throw new OsmTransferException(tr("unexpected format of new version of modified primitive ''{0}'', got ''{1}''", osm.getId(), ret)); 263 280 } 264 281 } … … 270 287 */ 271 288 public void deletePrimitive(OsmPrimitive osm, ProgressMonitor monitor) throws OsmTransferException { 289 ensureValidChangeset(); 272 290 initialize(monitor); 273 291 // can't use a the individual DELETE method in the 0.6 API. Java doesn't allow … … 280 298 281 299 /** 282 * Creates a new changeset based on the keys in <code>changeset</code> 283 * 284 * @param changeset the changeset to be used for uploading 300 * Creates a new changeset based on the keys in <code>changeset</code>. If this 301 * method succeeds, changeset.getId() replies the id the server assigned to the new 302 * changeset 303 * 304 * The changeset must not be null, but its key/value-pairs may be empty. 305 * 306 * @param changeset the changeset toe be created. Must not be null. 285 307 * @param progressMonitor the progress monitor 286 308 * @throws OsmTransferException signifying a non-200 return code, or connection errors 287 */ 288 public void createChangeset(Changeset changeset, ProgressMonitor progressMonitor) throws OsmTransferException { 309 * @throws IllegalArgumentException thrown if changeset is null 310 */ 311 public void openChangeset(Changeset changeset, ProgressMonitor progressMonitor) throws OsmTransferException { 312 if (changeset == null) 313 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "changeset")); 289 314 try { 290 315 progressMonitor.beginTask((tr("Creating changeset..."))); 291 createPrimitive(changeset, progressMonitor); 292 this.changeset = changeset; 293 progressMonitor.setCustomText((tr("Successfully opened changeset {0}",this.changeset.getId()))); 316 initialize(progressMonitor); 317 String ret = ""; 318 try { 319 ret = sendRequest("PUT", "changeset/create", toXml(changeset),progressMonitor); 320 changeset.setId(Long.parseLong(ret.trim())); 321 changeset.setOpen(true); 322 } catch(NumberFormatException e){ 323 throw new OsmTransferException(tr("unexpected format of id replied by the server, got ''{0}''", ret)); 324 } 325 progressMonitor.setCustomText((tr("Successfully opened changeset {0}",changeset.getId()))); 294 326 } finally { 295 327 progressMonitor.finishTask(); … … 298 330 299 331 /** 300 * Updates the current changeset with the keys in <code>changesetUpdate</code>. 301 * 302 * @param changesetUpdate the changeset to update 303 * @param progressMonitor the progress monitor 332 * Updates a changeset with the keys in <code>changesetUpdate</code>. The changeset must not 333 * be null and id > 0 must be true. 334 * 335 * @param changeset the changeset to update. Must not be null. 336 * @param monitor the progress monitor. If null, uses the {@see NullProgressMonitor#INSTANCE}. 304 337 * 305 338 * @throws OsmTransferException if something goes wrong. 306 */ 307 public void updateChangeset(Changeset changesetUpdate, ProgressMonitor progressMonitor) throws OsmTransferException { 339 * @throws IllegalArgumentException if changeset is null 340 * @throws IllegalArgumentException if changeset.getId() == 0 341 * 342 */ 343 public void updateChangeset(Changeset changeset, ProgressMonitor monitor) throws OsmTransferException { 344 if (changeset == null) 345 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "changeset")); 346 if (monitor == null) { 347 monitor = NullProgressMonitor.INSTANCE; 348 } 349 if (changeset.getId() <= 0) 350 throw new IllegalArgumentException(tr("id of changeset > 0 required. Got {0}", changeset.getId())); 308 351 try { 309 progressMonitor.beginTask(tr("Updating changeset...")); 310 initialize(progressMonitor); 311 if (this.changeset != null && this.changeset.getId() > 0) { 312 if (this.changeset.hasEqualSemanticAttributes(changesetUpdate)) { 313 progressMonitor.setCustomText(tr("Changeset {0} is unchanged. Skipping update.", changesetUpdate.getId())); 314 return; 315 } 316 this.changeset.setKeys(changesetUpdate.getKeys()); 317 progressMonitor.setCustomText(tr("Updating changeset {0}...", this.changeset.getId())); 318 sendRequest( 319 "PUT", 320 OsmPrimitiveType.from(changesetUpdate).getAPIName() + "/" + this.changeset.getId(), 321 toXml(this.changeset, true), 322 progressMonitor 323 ); 324 } else 325 throw new OsmTransferException(tr("Failed to update changeset. Either there is no current changeset or the id of the current changeset is 0")); 352 monitor.beginTask(tr("Updating changeset...")); 353 initialize(monitor); 354 monitor.setCustomText(tr("Updating changeset {0}...", changeset.getId())); 355 sendRequest( 356 "PUT", 357 "changeset/" + this.changeset.getId(), 358 toXml(this.changeset), 359 monitor 360 ); 326 361 } finally { 327 progressMonitor.finishTask(); 328 } 329 } 330 331 /** 332 * Closes a changeset on the server. 333 * 334 * @param changesetProcessingType how changesets are currently handled 335 * @param progressMonitor the progress monitor 362 monitor.finishTask(); 363 } 364 } 365 366 367 /** 368 * Closes a changeset on the server. Sets changeset.setOpen(false) if this operation 369 * succeeds. 370 * 371 * @param changeset the changeset to be closed. Must not be null. changeset.getId() > 0 required. 372 * @param monitor the progress monitor. If null, uses {@see NullProgressMonitor#INSTANCE} 336 373 * 337 374 * @throws OsmTransferException if something goes wrong. 338 */ 339 public void stopChangeset(ChangesetProcessingType changesetProcessingType, ProgressMonitor progressMonitor) throws OsmTransferException { 340 if (changesetProcessingType == null) { 341 changesetProcessingType = ChangesetProcessingType.USE_NEW_AND_CLOSE; 342 } 375 * @throws IllegalArgumentException thrown if changeset is null 376 * @throws IllegalArgumentException thrown if changeset.getId() <= 0 377 */ 378 public void closeChangeset(Changeset changeset, ProgressMonitor monitor) throws OsmTransferException { 379 if (changeset == null) 380 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "changeset")); 381 if (monitor == null) { 382 monitor = NullProgressMonitor.INSTANCE; 383 } 384 if (changeset.getId() <= 0) 385 throw new IllegalArgumentException(tr("id of changeset > 0 required. Got {0}", changeset.getId())); 343 386 try { 344 progressMonitor.beginTask(tr("Closing changeset...")); 345 initialize(progressMonitor); 346 if (changesetProcessingType.isCloseAfterUpload()) { 347 progressMonitor.setCustomText(tr("Closing changeset {0}...", changeset.getId())); 348 if (this.changeset != null && this.changeset.getId() > 0) { 349 sendRequest("PUT", "changeset" + "/" + changeset.getId() + "/close", null, progressMonitor); 350 changeset = null; 351 } 352 } else { 353 progressMonitor.setCustomText(tr("Leaving changeset {0} open...", changeset.getId())); 354 } 387 monitor.beginTask(tr("Closing changeset...")); 388 initialize(monitor); 389 sendRequest("PUT", "changeset" + "/" + changeset.getId() + "/close", null, monitor); 390 changeset.setOpen(false); 355 391 } finally { 356 progressMonitor.finishTask();392 monitor.finishTask(); 357 393 } 358 394 } … … 365 401 * @throws OsmTransferException if something is wrong 366 402 */ 367 public Collection<OsmPrimitive> uploadDiff(final Collection<OsmPrimitive> list, ProgressMonitor progressMonitor) throws OsmTransferException { 368 369 progressMonitor.beginTask("", list.size() * 2); 403 public Collection<OsmPrimitive> uploadDiff(Collection<OsmPrimitive> list, ProgressMonitor progressMonitor) throws OsmTransferException { 370 404 try { 405 progressMonitor.beginTask("", list.size() * 2); 371 406 if (changeset == null) 372 407 throw new OsmTransferException(tr("No changeset present for diff upload")); … … 385 420 386 421 String diff = duv.getDocument(); 387 try { 388 String diffresult = sendRequest("POST", "changeset/" + changeset.getId() + "/upload", diff,progressMonitor); 389 DiffResultReader.parseDiffResult(diffresult, list, processed, duv.getNewIdMap(), 390 progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 391 } catch(OsmTransferException e) { 392 throw e; 393 } catch(Exception e) { 394 throw new OsmTransferException(e); 395 } 396 422 String diffresult = sendRequest("POST", "changeset/" + changeset.getId() + "/upload", diff,progressMonitor); 423 DiffResultReader.parseDiffResult(diffresult, list, processed, duv.getNewIdMap(), 424 progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 397 425 return processed; 426 } catch(OsmTransferException e) { 427 throw e; 428 } catch(Exception e) { 429 throw new OsmTransferException(e); 398 430 } finally { 399 431 progressMonitor.finishTask(); … … 536 568 } 537 569 throw new OsmTransferException(e); 570 } catch(OsmTransferException e) { 571 throw e; 538 572 } catch (Exception e) { 539 if (e instanceof OsmTransferException) throw (OsmTransferException) e;540 573 throw new OsmTransferException(e); 541 574 } … … 552 585 } 553 586 554 /** 555 * Replies the current changeset 556 * 557 * @return the current changeset 558 */ 559 public Changeset getCurrentChangeset() { 587 588 /** 589 * Ensures that the current changeset can be used for uploading data 590 * 591 * @throws OsmTransferException thrown if the current changeset can't be used for 592 * uploading data 593 */ 594 protected void ensureValidChangeset() throws OsmTransferException { 595 if (changeset == null) 596 throw new OsmTransferException(tr("current changeset is null. Can't upload data.")); 597 if (changeset.getId() <= 0) 598 throw new OsmTransferException(tr("id of current changeset > required. Current id is {0}", changeset.getId())); 599 } 600 /** 601 * Replies the changeset data uploads are currently directed to 602 * 603 * @return the changeset data uploads are currently directed to 604 */ 605 public Changeset getChangeset() { 560 606 return changeset; 561 607 } 608 609 /** 610 * Sets the changesets to which further data uploads are directed. The changeset 611 * can be null. If it isn't null it must have been created, i.e. id > 0 is required. Furthermore, 612 * it must be open. 613 * 614 * @param changeset the changeset 615 * @throws IllegalArgumentException thrown if changeset.getId() <= 0 616 * @throws IllegalArgumentException thrown if !changeset.isOpen() 617 */ 618 public void setChangeset(Changeset changeset) { 619 if (changeset == null) { 620 this.changeset = null; 621 return; 622 } 623 if (changeset.getId() <= 0) 624 throw new IllegalArgumentException(tr("Changeset id > 0 expected. Got {0}", changeset.getId())); 625 if (!changeset.isOpen()) 626 throw new IllegalArgumentException(tr("Open changeset expected. Got closed changeset with id {0}", changeset.getId())); 627 this.changeset = changeset; 628 } 629 562 630 } -
trunk/src/org/openstreetmap/josm/io/OsmDataParsingException.java
r2094 r2115 7 7 import org.xml.sax.SAXException; 8 8 9 /**10 * Represents a parsing error in an OSM data file.11 *12 * Use {@see #getColumnNumber()} and {@see #getLineNumber()} to locate13 * the position in the file where the parsing error occured.14 *15 */16 9 public class OsmDataParsingException extends SAXException { 17 10 private int columnNumber; -
trunk/src/org/openstreetmap/josm/io/OsmReader.java
r2094 r2115 36 36 37 37 /** 38 * Parser for the Osm Api. Read from an input stream and construct sa dataset out of it.38 * Parser for the Osm Api. Read from an input stream and construct a dataset out of it. 39 39 * 40 40 */ … … 80 80 } 81 81 82 private static class OsmPrimitiveData { 83 public long id = 0; 84 public boolean modified = false; 85 public boolean deleted = false; 86 public Date timestamp = new Date(); 87 public User user = null; 88 public boolean visible = true; 89 public int version = 0; 90 public LatLon latlon = new LatLon(0,0); 91 private OsmPrimitive primitive; 92 93 public void copyTo(OsmPrimitive osm) { 94 osm.setModified(modified); 95 osm.setDeleted(deleted); 96 // id < 0 possible if read from a file 97 if (id <= 0) { 98 osm.clearOsmId(); 99 } else { 100 osm.setOsmId(id, version); 101 } 102 osm.setTimestamp(timestamp); 103 osm.user = user; 104 osm.setVisible(visible); 105 osm.mappaintStyle = null; 106 } 107 108 public Node createNode() { 109 Node node = new Node(); 110 node.setCoor(latlon); 111 copyTo(node); 112 primitive = node; 113 return node; 114 } 115 116 public Way createWay() { 117 Way way = new Way(); 118 copyTo(way); 119 primitive = way; 120 return way; 121 } 122 public Relation createRelation() { 123 Relation relation= new Relation(); 124 copyTo(relation); 125 primitive = relation; 126 return relation; 127 } 128 129 public void rememberTag(String key, String value) { 130 primitive.put(key, value); 131 } 132 } 82 133 83 134 /** … … 100 151 */ 101 152 private Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>(); 102 103 153 104 154 private class Parser extends DefaultHandler { … … 241 291 current.rememberTag(key, value); 242 292 } else { 243 System.out.println(tr("Warning: Undefined element ''{0}'' found in input stream. Skipping.", qName));293 throwException(tr("Undefined element ''{0}'' found in input stream. Aborting.", qName)); 244 294 } 245 295 } … … 503 553 } 504 554 } 505 506 /**507 * Temporarily holds data for a parsed {@see OsmPrimitive} and provides508 * methods for creating an {@see OsmPrimitive} based on this data.509 */510 private static class OsmPrimitiveData {511 public long id = 0;512 public boolean modified = false;513 public boolean deleted = false;514 public Date timestamp = new Date();515 public User user = null;516 public boolean visible = true;517 public int version = 0;518 public LatLon latlon = new LatLon(0,0);519 private OsmPrimitive primitive;520 521 public void copyTo(OsmPrimitive osm) {522 // id < 0 possible if read from a file523 if (id <= 0) {524 osm.clearOsmId();525 } else {526 osm.setOsmId(id, version);527 }528 osm.setDeleted(deleted);529 osm.setModified(modified);530 osm.setTimestamp(timestamp);531 osm.user = user;532 osm.setVisible(visible);533 osm.mappaintStyle = null;534 }535 536 public Node createNode() {537 Node node = new Node();538 node.setCoor(latlon);539 copyTo(node);540 primitive = node;541 return node;542 }543 544 public Way createWay() {545 Way way = new Way();546 copyTo(way);547 primitive = way;548 return way;549 }550 public Relation createRelation() {551 Relation relation= new Relation();552 copyTo(relation);553 primitive = relation;554 return relation;555 }556 557 public void rememberTag(String key, String value) {558 primitive.put(key, value);559 }560 }561 555 } -
trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java
r2081 r2115 65 65 * 66 66 * @param primitives the collection of primitives to upload 67 * @param changeset the changeset to be used if <code>changesetProcessingType</code> indicates that68 * a new changeset should be opened69 * @param changesetProcessingType how we handle changesets70 67 * @param progressMonitor the progress monitor 71 68 * @throws OsmTransferException thrown if an exception occurs 72 69 */ 73 protected void uploadChangesIndividually(Collection<OsmPrimitive> primitives, Changeset changeset, ChangesetProcessingType changesetProcessingType,ProgressMonitor progressMonitor) throws OsmTransferException {70 protected void uploadChangesIndividually(Collection<OsmPrimitive> primitives, ProgressMonitor progressMonitor) throws OsmTransferException { 74 71 try { 75 72 progressMonitor.beginTask(tr("Starting to upload with one request per primitive ...")); 76 73 progressMonitor.setTicksCount(primitives.size()); 77 if (changesetProcessingType.isUseNew()) {78 api.createChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));79 } else {80 api.updateChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));81 }82 74 uploadStartTime = System.currentTimeMillis(); 83 75 for (OsmPrimitive osm : primitives) { … … 86 78 String msg = ""; 87 79 switch(OsmPrimitiveType.from(osm)) { 88 case NODE: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading node ''{4}'' (id: {5})"); break;89 case WAY: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading way ''{4}'' (id: {5})"); break;90 case RELATION: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading relation ''{4}'' (id: {5})"); break;80 case NODE: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading node ''{4}'' (id: {5})"); break; 81 case WAY: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading way ''{4}'' (id: {5})"); break; 82 case RELATION: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading relation ''{4}'' (id: {5})"); break; 91 83 } 92 84 progressMonitor.subTask( … … 107 99 throw new OsmTransferException(e); 108 100 } finally { 109 try { 110 // starting the changeset may have failed, for instance because the user 111 // cancelled the upload task. Only close the changeset if we currently have 112 // an open changeset 113 114 if (api.getCurrentChangeset() != null && api.getCurrentChangeset().getId() > 0) { 115 api.stopChangeset(changesetProcessingType, progressMonitor.createSubTaskMonitor(0, false)); 116 } 117 } catch(Exception e) { 118 OsmChangesetCloseException closeException = new OsmChangesetCloseException(e); 119 closeException.setChangeset(api.getCurrentChangeset()); 120 throw closeException; 121 } finally { 122 progressMonitor.finishTask(); 123 } 101 progressMonitor.finishTask(); 124 102 } 125 103 } … … 132 110 * @throws OsmTransferException thrown if an exception occurs 133 111 */ 134 protected void uploadChangesAsDiffUpload(Collection<OsmPrimitive> primitives, Changeset changeset, ChangesetProcessingType changesetProcessingType,ProgressMonitor progressMonitor) throws OsmTransferException {112 protected void uploadChangesAsDiffUpload(Collection<OsmPrimitive> primitives, ProgressMonitor progressMonitor) throws OsmTransferException { 135 113 // upload everything in one changeset 136 114 // 137 115 try { 138 116 progressMonitor.beginTask(tr("Starting to upload in one request ...")); 139 if (changesetProcessingType.isUseNew()) {140 api.createChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));141 } else {142 api.updateChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));143 }144 117 processed.addAll(api.uploadDiff(primitives, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false))); 145 118 } catch(OsmTransferException e) { … … 148 121 throw new OsmTransferException(e); 149 122 } finally { 150 try { 151 api.stopChangeset(changesetProcessingType, progressMonitor.createSubTaskMonitor(0, false)); 152 } catch (Exception ee) { 153 OsmChangesetCloseException closeException = new OsmChangesetCloseException(ee); 154 closeException.setChangeset(api.getCurrentChangeset()); 155 throw closeException; 156 } finally { 157 progressMonitor.finishTask(); 158 } 159 123 progressMonitor.finishTask(); 160 124 } 161 125 } … … 167 131 * @param primitives list of objects to send 168 132 */ 169 public void uploadOsm(String apiVersion, Collection<OsmPrimitive> primitives, Changeset changeset, ChangesetProcessingType changesetProcessingType, ProgressMonitor progressMonitor) throws OsmTransferException {133 public void uploadOsm(String apiVersion, Collection<OsmPrimitive> primitives, Changeset changeset, boolean closeChangesetAfterUpload, ProgressMonitor progressMonitor) throws OsmTransferException { 170 134 processed = new LinkedList<OsmPrimitive>(); 171 135 progressMonitor.beginTask(tr("Uploading data ...")); … … 184 148 useDiffUpload = false; 185 149 } 186 150 if (changeset == null) { 151 changeset = new Changeset(); 152 } 153 if (changeset.getId() == 0) { 154 api.openChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false)); 155 } else { 156 api.updateChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false)); 157 } 158 api.setChangeset(changeset); 187 159 if (useDiffUpload) { 188 uploadChangesAsDiffUpload(primitives, changeset, changesetProcessingType,progressMonitor.createSubTaskMonitor(0,false));160 uploadChangesAsDiffUpload(primitives,progressMonitor.createSubTaskMonitor(0,false)); 189 161 } else { 190 uploadChangesIndividually(primitives,changeset,changesetProcessingType, progressMonitor.createSubTaskMonitor(0,false)); 191 } 162 uploadChangesIndividually(primitives,progressMonitor.createSubTaskMonitor(0,false)); 163 } 164 } catch(OsmTransferException e) { 165 throw e; 166 } catch(Exception e) { 167 throw new OsmTransferException(e); 192 168 } finally { 193 progressMonitor.finishTask(); 169 try { 170 if (closeChangesetAfterUpload && api.getChangeset() != null && api.getChangeset().getId() > 0) { 171 api.closeChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false)); 172 api.setChangeset(null); 173 } 174 } catch (Exception ee) { 175 OsmChangesetCloseException closeException = new OsmChangesetCloseException(ee); 176 closeException.setChangeset(api.getChangeset()); 177 throw closeException; 178 } finally { 179 progressMonitor.finishTask(); 180 } 194 181 } 195 182 } -
trunk/src/org/openstreetmap/josm/io/OsmWriter.java
r2070 r2115 6 6 import java.util.Map.Entry; 7 7 8 import org.openstreetmap.josm.data.coor.CoordinateFormat; 8 9 import org.openstreetmap.josm.data.osm.Changeset; 9 10 import org.openstreetmap.josm.data.osm.DataSet; … … 14 15 import org.openstreetmap.josm.data.osm.Relation; 15 16 import org.openstreetmap.josm.data.osm.RelationMember; 16 import org.openstreetmap.josm.data.osm. User;17 import org.openstreetmap.josm.data.osm.Tagged; 17 18 import org.openstreetmap.josm.data.osm.Way; 18 19 import org.openstreetmap.josm.data.osm.visitor.Visitor; … … 143 144 144 145 public void visit(Changeset cs) { 145 addCommon(cs, "changeset"); 146 out.println(">\n"); 147 addTags(cs, "changeset", false); 148 } 149 150 public final void footer(PrintWriter out) { 151 out.println("</osm>"); 146 out.print(" <changeset "); 147 out.print(" id='"+cs.getId()+"'"); 148 if (cs.getUser() != null) { 149 out.print(" user='"+cs.getUser().getName() +"'"); 150 out.print(" uid='"+cs.getUser().getId() +"'"); 151 } 152 if (cs.getCreatedAt() != null) { 153 out.print(" created_at='"+DateUtils.fromDate(cs.getCreatedAt()) +"'"); 154 } 155 if (cs.getClosedAt() != null) { 156 out.print(" closed_at='"+DateUtils.fromDate(cs.getClosedAt()) +"'"); 157 } 158 out.print(" open='"+ (cs.isOpen() ? "true" : "false") +"'"); 159 if (cs.getMin() != null) { 160 out.print(" min_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'"); 161 out.print(" min_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'"); 162 } 163 if (cs.getMax() != null) { 164 out.print(" max_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'"); 165 out.print(" max_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'"); 166 } 167 out.println(">"); 168 addTags(cs, "changeset", false); // also writes closing </changeset> 152 169 } 153 170 … … 164 181 } 165 182 166 private void addTags( OsmPrimitiveosm, String tagname, boolean tagOpen) {183 private void addTags(Tagged osm, String tagname, boolean tagOpen) { 167 184 if (osm.hasKeys()) { 168 185 if (tagOpen) { 169 186 out.println(">"); 170 187 } 171 for (Entry<String, String> e : osm. entrySet()) {188 for (Entry<String, String> e : osm.getKeys().entrySet()) { 172 189 if ((osm instanceof Changeset) || !("created_by".equals(e.getKey()))) { 173 190 out.println(" <tag k='"+ XmlWriter.encode(e.getKey()) +
Note:
See TracChangeset
for help on using the changeset viewer.