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

Last change on this file since 10755 was 10619, checked in by Don-vip, 8 years ago

see #11390 - Java 8: use List.sort(Comparator) instead of Collections.sort(list, Comparator)

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