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

Last change on this file since 71 was 71, checked in by imi, 18 years ago
  • refactored GpsPoint to be immutable and added LatLon and NorthEast
  • refactored Bounding Box calculations
  • various other renames
File size: 5.7 KB
Line 
1package org.openstreetmap.josm.io;
2
3import java.io.PrintWriter;
4import java.io.Writer;
5import java.util.HashMap;
6import java.util.Map.Entry;
7
8import org.openstreetmap.josm.data.osm.DataSet;
9import org.openstreetmap.josm.data.osm.LineSegment;
10import org.openstreetmap.josm.data.osm.Node;
11import org.openstreetmap.josm.data.osm.OsmPrimitive;
12import org.openstreetmap.josm.data.osm.Way;
13import org.openstreetmap.josm.data.osm.visitor.Visitor;
14import org.xml.sax.SAXException;
15
16/**
17 * Save the dataset into a stream as osm intern xml format. This is not using any
18 * xml library for storing.
19 * @author imi
20 */
21public class OsmWriter implements Visitor {
22
23 private class RuntimeEncodingException extends RuntimeException {
24 public RuntimeEncodingException(Throwable t) {
25 super(t);
26 }
27 public RuntimeEncodingException() {
28 }
29 }
30
31 /**
32 * The output writer to save the values to.
33 */
34 private PrintWriter out;
35
36 /**
37 * The counter for new created objects. Starting at -1 and goes down.
38 */
39 private long newIdCounter = -1;
40 /**
41 * All newly created ids and their primitive that uses it. This is a back reference
42 * map to allow references to use the correnct primitives.
43 */
44 private HashMap<OsmPrimitive, Long> usedNewIds = new HashMap<OsmPrimitive, Long>();
45
46 private final boolean osmConform;
47
48 private final static HashMap<Character, String> encoding = new HashMap<Character, String>();
49 static {
50 encoding.put('<', "&lt;");
51 encoding.put('>', "&gt;");
52 encoding.put('"', "&quot;");
53 encoding.put('\'', "&apos;");
54 encoding.put('&', "&amp;");
55 encoding.put('\n', "&#xA;");
56 encoding.put('\r', "&#xD;");
57 encoding.put('\t', "&#x9;");
58 }
59
60 /**
61 * Output the data to the stream
62 * @param osmConform <code>true</code>, if the xml should be 100% osm conform. In this
63 * case, not all information can be retrieved later (as example, modified state
64 * is lost and id's remain 0 instead of decrementing from -1)
65 */
66 public static void output(Writer out, DataSet ds, boolean osmConform) {
67 OsmWriter writer = new OsmWriter(out, osmConform);
68 writer.out.println("<?xml version='1.0' encoding='UTF-8'?>");
69 writer.out.println("<osm version='0.3' generator='JOSM'>");
70 for (Node n : ds.nodes)
71 writer.visit(n);
72 for (LineSegment ls : ds.lineSegments)
73 writer.visit(ls);
74 for (Way w : ds.ways)
75 writer.visit(w);
76 writer.out.println("</osm>");
77 }
78
79 public static void outputSingle(Writer out, OsmPrimitive osm, boolean osmConform) throws SAXException {
80 try {
81 OsmWriter writer = new OsmWriter(out, osmConform);
82 writer.out.println("<?xml version='1.0' encoding='UTF-8'?>");
83 writer.out.println("<osm version='0.3' generator='JOSM'>");
84 osm.visit(writer);
85 writer.out.println("</osm>");
86 } catch (RuntimeEncodingException e) {
87 throw new SAXException("Your Java installation does not support the required UTF-8 encoding", (Exception)e.getCause());
88 }
89 }
90
91 private OsmWriter(Writer out, boolean osmConform) {
92 if (out instanceof PrintWriter)
93 this.out = (PrintWriter)out;
94 else
95 this.out = new PrintWriter(out);
96 this.osmConform = osmConform;
97 }
98
99 public void visit(Node n) {
100 addCommon(n, "node");
101 out.print(" lat='"+n.coor.lat()+"' lon='"+n.coor.lon()+"'");
102 addTags(n, "node", true);
103 }
104
105 public void visit(LineSegment ls) {
106 if (ls.incomplete)
107 throw new IllegalArgumentException("Cannot write an incomplete LineSegment.");
108 addCommon(ls, "segment");
109 out.print(" from='"+getUsedId(ls.from)+"' to='"+getUsedId(ls.to)+"'");
110 addTags(ls, "segment", true);
111 }
112
113 public void visit(Way w) {
114 addCommon(w, "way");
115 out.println(">");
116 for (LineSegment ls : w.segments)
117 out.println(" <seg id='"+getUsedId(ls)+"' />");
118 addTags(w, "way", false);
119 }
120
121 /**
122 * Return the id for the given osm primitive (may access the usedId map)
123 */
124 private long getUsedId(OsmPrimitive osm) {
125 if (osm.id != 0)
126 return osm.id;
127 if (usedNewIds.containsKey(osm))
128 return usedNewIds.get(osm);
129 usedNewIds.put(osm, newIdCounter);
130 return osmConform ? 0 : newIdCounter--;
131 }
132
133
134 private void addTags(OsmPrimitive osm, String tagname, boolean tagOpen) {
135 if (osm.keys != null) {
136 if (tagOpen)
137 out.println(">");
138 for (Entry<String, String> e : osm.keys.entrySet())
139 out.println(" <tag k='"+ encode(e.getKey()) +
140 "' v='"+encode(e.getValue())+ "' />");
141 out.println(" </" + tagname + ">");
142 } else if (tagOpen)
143 out.println(" />");
144 else
145 out.println(" </" + tagname + ">");
146 }
147
148 /**
149 * Encode the given string in XML1.0 format.
150 * Optimized to fast pass strings that don't need encoding (normal case).
151 */
152 public String encode(String unencoded) {
153 StringBuilder buffer = null;
154 for (int i = 0; i < unencoded.length(); ++i) {
155 String encS = encoding.get(unencoded.charAt(i));
156 if (encS != null) {
157 if (buffer == null)
158 buffer = new StringBuilder(unencoded.substring(0,i));
159 buffer.append(encS);
160 } else if (buffer != null)
161 buffer.append(unencoded.charAt(i));
162 }
163 return (buffer == null) ? unencoded : buffer.toString();
164 }
165
166
167 /**
168 * Add the common part as the from of the tag as well as the id or the action tag.
169 */
170 private void addCommon(OsmPrimitive osm, String tagname) {
171 out.print(" <"+tagname+" id='"+getUsedId(osm)+"'");
172 if (!osmConform) {
173 String action = null;
174 if (osm.isDeleted())
175 action = "delete";
176 else if (osm.modified && osm.modifiedProperties)
177 action = "modify";
178 else if (osm.modified && !osm.modifiedProperties)
179 action = "modify/object";
180 else if (!osm.modified && osm.modifiedProperties)
181 action = "modify/property";
182 if (action != null)
183 out.print(" action='"+action+"'");
184 }
185 }
186}
Note: See TracBrowser for help on using the repository browser.