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

Last change on this file since 11918 was 11878, checked in by Don-vip, 7 years ago

findbugs - EI_EXPOSE_REP2 + javadoc

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