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

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

fix #10121 - Add a new look-and-feel preference to display ISO 8601 dates globally

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