001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.PrintWriter;
007import java.io.StringWriter;
008import java.util.Collection;
009
010import org.openstreetmap.josm.data.osm.Changeset;
011import org.openstreetmap.josm.data.osm.IPrimitive;
012
013/**
014 * Creates an OsmChange document from JOSM edits.
015 * See http://wiki.openstreetmap.org/index.php/OsmChange for a documentation of the OsmChange format.
016 * @since 1071
017 */
018public class OsmChangeBuilder {
019    /** Default OSM API version */
020    public static final String DEFAULT_API_VERSION = "0.6";
021
022    private String currentMode;
023    private final PrintWriter writer;
024    private final StringWriter swriter;
025    private final OsmWriter osmwriter;
026    private String apiVersion = DEFAULT_API_VERSION;
027    private boolean prologWritten;
028
029    /**
030     * Constructs a new {@code OsmChangeBuilder}.
031     * @param changeset changeset
032     */
033    public OsmChangeBuilder(Changeset changeset) {
034        this(changeset, null /* default api version */);
035    }
036
037    /**
038     * Constructs a new {@code OsmChangeBuilder}.
039     * @param changeset changeset
040     * @param apiVersion OSM API version
041     */
042    public OsmChangeBuilder(Changeset changeset, String apiVersion) {
043        this.apiVersion = apiVersion == null ? DEFAULT_API_VERSION : apiVersion;
044        swriter = new StringWriter();
045        writer = new PrintWriter(swriter);
046        osmwriter = OsmWriterFactory.createOsmWriter(writer, false, apiVersion);
047        osmwriter.setChangeset(changeset);
048        osmwriter.setIsOsmChange(true);
049    }
050
051    protected void write(IPrimitive p) {
052        if (p.isDeleted()) {
053            switchMode("delete");
054            osmwriter.setWithBody(false);
055            p.accept(osmwriter);
056        } else {
057            switchMode(p.isNew() ? "create" : "modify");
058            osmwriter.setWithBody(true);
059            p.accept(osmwriter);
060        }
061    }
062
063    private void switchMode(String newMode) {
064        if ((newMode != null && !newMode.equals(currentMode)) || (newMode == null && currentMode != null)) {
065            if (currentMode != null) {
066                writer.print("</");
067                writer.print(currentMode);
068                writer.println(">");
069            }
070            if (newMode != null) {
071                writer.print("<");
072                writer.print(newMode);
073                writer.println(">");
074            }
075            currentMode = newMode;
076        }
077    }
078
079    /**
080     * Writes the prolog of the OsmChange document
081     *
082     * @throws IllegalStateException if the prologs has already been written
083     */
084    public void start() {
085        if (prologWritten)
086            throw new IllegalStateException(tr("Prolog of OsmChange document already written. Please write only once."));
087        writer.print("<osmChange version=\"");
088        writer.print(apiVersion);
089        writer.println("\" generator=\"JOSM\">");
090        prologWritten = true;
091    }
092
093    /**
094     * Appends a collection of Primitives to the OsmChange document.
095     *
096     * @param primitives the collection of primitives. Ignored if null.
097     * @throws IllegalStateException if the prologs has not been written yet
098     * @see #start()
099     * @see #append(IPrimitive)
100     */
101    public void append(Collection<? extends IPrimitive> primitives) {
102        if (primitives == null)
103            return;
104        checkProlog();
105        for (IPrimitive p : primitives) {
106            write(p);
107        }
108    }
109
110    private void checkProlog() {
111        if (!prologWritten)
112            throw new IllegalStateException(tr("Prolog of OsmChange document not written yet. Please write first."));
113    }
114
115    /**
116     * Appends an Primitive to the OsmChange document.
117     *
118     * @param p the primitive. Ignored if null.
119     * @throws IllegalStateException if the prologs has not been written yet
120     * @see #start()
121     * @see #append(Collection)
122     */
123    public void append(IPrimitive p) {
124        if (p == null)
125            return;
126        checkProlog();
127        write(p);
128    }
129
130    /**
131     * Writes the epilog of the OsmChange document
132     *
133     * @throws IllegalStateException if the prologs has not been written yet
134     */
135    public void finish() {
136        checkProlog();
137        if (currentMode != null) {
138            writer.print("</");
139            writer.print(currentMode);
140            writer.println(">");
141        }
142        writer.println("</osmChange>");
143    }
144
145    /**
146     * Returns XML document.
147     * @return XML document
148     */
149    public String getDocument() {
150        return swriter.toString();
151    }
152}