source: josm/trunk/src/org/openstreetmap/josm/io/OsmWriter.java@ 7081

Last change on this file since 7081 was 7005, checked in by Don-vip, 10 years ago

see #8465 - use diamond operator where applicable

  • Property svn:eol-style set to native
File size: 10.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.PrintWriter;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.Comparator;
11import java.util.List;
12import java.util.Map.Entry;
13
14import org.openstreetmap.josm.data.coor.CoordinateFormat;
15import org.openstreetmap.josm.data.osm.Changeset;
16import org.openstreetmap.josm.data.osm.DataSet;
17import org.openstreetmap.josm.data.osm.DataSource;
18import org.openstreetmap.josm.data.osm.INode;
19import org.openstreetmap.josm.data.osm.IPrimitive;
20import org.openstreetmap.josm.data.osm.IRelation;
21import org.openstreetmap.josm.data.osm.IWay;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.Relation;
25import org.openstreetmap.josm.data.osm.Tagged;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
28import org.openstreetmap.josm.gui.layer.OsmDataLayer;
29import org.openstreetmap.josm.tools.DateUtils;
30
31/**
32 * Save the dataset into a stream as osm intern xml format. This is not using any
33 * xml library for storing.
34 * @author imi
35 */
36public class OsmWriter extends XmlWriter implements PrimitiveVisitor {
37
38 public static final String DEFAULT_API_VERSION = "0.6";
39
40 private boolean osmConform;
41 private boolean withBody = true;
42 private boolean isOsmChange;
43 private String version;
44 private Changeset changeset;
45
46 /**
47 * Do not call this directly. Use OsmWriterFactory instead.
48 */
49 protected OsmWriter(PrintWriter out, boolean osmConform, String version) {
50 super(out);
51 this.osmConform = osmConform;
52 this.version = (version == null ? DEFAULT_API_VERSION : version);
53 }
54
55 public void setWithBody(boolean wb) {
56 this.withBody = wb;
57 }
58
59 public void setIsOsmChange(boolean isOsmChange) {
60 this.isOsmChange = isOsmChange;
61 }
62
63 public void setChangeset(Changeset cs) {
64 this.changeset = cs;
65 }
66 public void setVersion(String v) {
67 this.version = v;
68 }
69
70 public void header() {
71 header(null);
72 }
73
74 public void header(Boolean upload) {
75 out.println("<?xml version='1.0' encoding='UTF-8'?>");
76 out.print("<osm version='");
77 out.print(version);
78 if (upload != null) {
79 out.print("' upload='");
80 out.print(upload);
81 }
82 out.println("' generator='JOSM'>");
83 }
84
85 public void footer() {
86 out.println("</osm>");
87 }
88
89 protected static final Comparator<OsmPrimitive> byIdComparator = new Comparator<OsmPrimitive>() {
90 @Override public int compare(OsmPrimitive o1, OsmPrimitive o2) {
91 return (o1.getUniqueId()<o2.getUniqueId() ? -1 : (o1.getUniqueId()==o2.getUniqueId() ? 0 : 1));
92 }
93 };
94
95 protected <T extends OsmPrimitive> Collection<T> sortById(Collection<T> primitives) {
96 List<T> result = new ArrayList<>(primitives.size());
97 result.addAll(primitives);
98 Collections.sort(result, byIdComparator);
99 return result;
100 }
101
102 public void writeLayer(OsmDataLayer layer) {
103 header(!layer.isUploadDiscouraged());
104 writeDataSources(layer.data);
105 writeContent(layer.data);
106 footer();
107 }
108
109 /**
110 * Writes the contents of the given dataset (nodes, then ways, then relations)
111 * @param ds The dataset to write
112 */
113 public void writeContent(DataSet ds) {
114 writeNodes(ds.getNodes());
115 writeWays(ds.getWays());
116 writeRelations(ds.getRelations());
117 }
118
119 /**
120 * Writes the given nodes sorted by id
121 * @param nodes The nodes to write
122 * @since 5737
123 */
124 public void writeNodes(Collection<Node> nodes) {
125 for (Node n : sortById(nodes)) {
126 if (shouldWrite(n)) {
127 visit(n);
128 }
129 }
130 }
131
132 /**
133 * Writes the given ways sorted by id
134 * @param ways The ways to write
135 * @since 5737
136 */
137 public void writeWays(Collection<Way> ways) {
138 for (Way w : sortById(ways)) {
139 if (shouldWrite(w)) {
140 visit(w);
141 }
142 }
143 }
144
145 /**
146 * Writes the given relations sorted by id
147 * @param relations The relations to write
148 * @since 5737
149 */
150 public void writeRelations(Collection<Relation> relations) {
151 for (Relation r : sortById(relations)) {
152 if (shouldWrite(r)) {
153 visit(r);
154 }
155 }
156 }
157
158 protected boolean shouldWrite(OsmPrimitive osm) {
159 return !osm.isNewOrUndeleted() || !osm.isDeleted();
160 }
161
162 public void writeDataSources(DataSet ds) {
163 for (DataSource s : ds.dataSources) {
164 out.println(" <bounds minlat='"
165 + s.bounds.getMinLat()+"' minlon='"
166 + s.bounds.getMinLon()+"' maxlat='"
167 + s.bounds.getMaxLat()+"' maxlon='"
168 + s.bounds.getMaxLon()
169 +"' origin='"+XmlWriter.encode(s.origin)+"' />");
170 }
171 }
172
173 @Override
174 public void visit(INode n) {
175 if (n.isIncomplete()) return;
176 addCommon(n, "node");
177 if (!withBody) {
178 out.println("/>");
179 } else {
180 if (n.getCoor() != null) {
181 out.print(" lat='"+n.getCoor().lat()+"' lon='"+n.getCoor().lon()+"'");
182 }
183 addTags(n, "node", true);
184 }
185 }
186
187 @Override
188 public void visit(IWay w) {
189 if (w.isIncomplete()) return;
190 addCommon(w, "way");
191 if (!withBody) {
192 out.println("/>");
193 } else {
194 out.println(">");
195 for (int i=0; i<w.getNodesCount(); ++i) {
196 out.println(" <nd ref='"+w.getNodeId(i) +"' />");
197 }
198 addTags(w, "way", false);
199 }
200 }
201
202 @Override
203 public void visit(IRelation e) {
204 if (e.isIncomplete()) return;
205 addCommon(e, "relation");
206 if (!withBody) {
207 out.println("/>");
208 } else {
209 out.println(">");
210 for (int i=0; i<e.getMembersCount(); ++i) {
211 out.print(" <member type='");
212 out.print(e.getMemberType(i).getAPIName());
213 out.println("' ref='"+e.getMemberId(i)+"' role='" +
214 XmlWriter.encode(e.getRole(i)) + "' />");
215 }
216 addTags(e, "relation", false);
217 }
218 }
219
220 public void visit(Changeset cs) {
221 out.print(" <changeset ");
222 out.print(" id='"+cs.getId()+"'");
223 if (cs.getUser() != null) {
224 out.print(" user='"+cs.getUser().getName() +"'");
225 out.print(" uid='"+cs.getUser().getId() +"'");
226 }
227 if (cs.getCreatedAt() != null) {
228 out.print(" created_at='"+DateUtils.fromDate(cs.getCreatedAt()) +"'");
229 }
230 if (cs.getClosedAt() != null) {
231 out.print(" closed_at='"+DateUtils.fromDate(cs.getClosedAt()) +"'");
232 }
233 out.print(" open='"+ (cs.isOpen() ? "true" : "false") +"'");
234 if (cs.getMin() != null) {
235 out.print(" min_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
236 out.print(" min_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
237 }
238 if (cs.getMax() != null) {
239 out.print(" max_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
240 out.print(" max_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
241 }
242 out.println(">");
243 addTags(cs, "changeset", false); // also writes closing </changeset>
244 }
245
246 protected static final Comparator<Entry<String, String>> byKeyComparator = new Comparator<Entry<String,String>>() {
247 @Override public int compare(Entry<String, String> o1, Entry<String, String> o2) {
248 return o1.getKey().compareTo(o2.getKey());
249 }
250 };
251
252 protected void addTags(Tagged osm, String tagname, boolean tagOpen) {
253 if (osm.hasKeys()) {
254 if (tagOpen) {
255 out.println(">");
256 }
257 List<Entry<String, String>> entries = new ArrayList<>(osm.getKeys().entrySet());
258 Collections.sort(entries, byKeyComparator);
259 for (Entry<String, String> e : entries) {
260 out.println(" <tag k='"+ XmlWriter.encode(e.getKey()) +
261 "' v='"+XmlWriter.encode(e.getValue())+ "' />");
262 }
263 out.println(" </" + tagname + ">");
264 } else if (tagOpen) {
265 out.println(" />");
266 } else {
267 out.println(" </" + tagname + ">");
268 }
269 }
270
271 /**
272 * Add the common part as the form of the tag as well as the XML attributes
273 * id, action, user, and visible.
274 */
275 protected void addCommon(IPrimitive osm, String tagname) {
276 out.print(" <"+tagname);
277 if (osm.getUniqueId() != 0) {
278 out.print(" id='"+ osm.getUniqueId()+"'");
279 } else
280 throw new IllegalStateException(tr("Unexpected id 0 for osm primitive found"));
281 if (!isOsmChange) {
282 if (!osmConform) {
283 String action = null;
284 if (osm.isDeleted()) {
285 action = "delete";
286 } else if (osm.isModified()) {
287 action = "modify";
288 }
289 if (action != null) {
290 out.print(" action='"+action+"'");
291 }
292 }
293 if (!osm.isTimestampEmpty()) {
294 out.print(" timestamp='"+DateUtils.fromDate(osm.getTimestamp())+"'");
295 }
296 // user and visible added with 0.4 API
297 if (osm.getUser() != null) {
298 if(osm.getUser().isLocalUser()) {
299 out.print(" user='"+XmlWriter.encode(osm.getUser().getName())+"'");
300 } else if (osm.getUser().isOsmUser()) {
301 // uid added with 0.6
302 out.print(" uid='"+ osm.getUser().getId()+"'");
303 out.print(" user='"+XmlWriter.encode(osm.getUser().getName())+"'");
304 }
305 }
306 out.print(" visible='"+osm.isVisible()+"'");
307 }
308 if (osm.getVersion() != 0) {
309 out.print(" version='"+osm.getVersion()+"'");
310 }
311 if (this.changeset != null && this.changeset.getId() != 0) {
312 out.print(" changeset='"+this.changeset.getId()+"'" );
313 } else if (osm.getChangesetId() > 0 && !osm.isNew()) {
314 out.print(" changeset='"+osm.getChangesetId()+"'" );
315 }
316 }
317}
Note: See TracBrowser for help on using the repository browser.