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

Last change on this file since 13254 was 13173, checked in by Don-vip, 6 years ago

see #15310 - remove most of deprecated APIs

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