001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003
004import java.io.Closeable;
005import java.io.IOException;
006import java.io.PrintWriter;
007import java.util.HashMap;
008import java.util.Map;
009
010/**
011 * Helper class to use for xml outputting classes.
012 *
013 * @author imi
014 */
015public class XmlWriter implements Closeable {
016
017    protected final PrintWriter out;
018
019    public XmlWriter(PrintWriter out) {
020        this.out = out;
021    }
022
023    /**
024     * Flushes the stream.
025     */
026    public void flush() {
027        if (out != null) {
028            out.flush();
029        }
030    }
031
032    public static String encode(String unencoded) {
033        return encode(unencoded, false);
034    }
035
036    /**
037     * Encode the given string in XML1.0 format.
038     * Optimized to fast pass strings that don't need encoding (normal case).
039     *
040     * @param unencoded the unencoded input string
041     * @param keepApos true if apostrophe sign should stay as it is (in order to work around
042     * a Java bug that renders
043     *     new JLabel("<html>&apos;</html>")
044     * literally as 6 character string, see #7558)
045     */
046    public static String encode(String unencoded, boolean keepApos) {
047        StringBuilder buffer = null;
048        for (int i = 0; i < unencoded.length(); ++i) {
049            String encS = null;
050            if (!keepApos || unencoded.charAt(i) != '\'') {
051                encS = XmlWriter.encoding.get(unencoded.charAt(i));
052            }
053            if (encS != null) {
054                if (buffer == null) {
055                    buffer = new StringBuilder(unencoded.substring(0,i));
056                }
057                buffer.append(encS);
058            } else if (buffer != null) {
059                buffer.append(unencoded.charAt(i));
060            }
061        }
062        return (buffer == null) ? unencoded : buffer.toString();
063    }
064
065    /**
066     * The output writer to save the values to.
067     */
068    private static final Map<Character, String> encoding = new HashMap<Character, String>();
069    static {
070        encoding.put('<', "&lt;");
071        encoding.put('>', "&gt;");
072        encoding.put('"', "&quot;");
073        encoding.put('\'', "&apos;");
074        encoding.put('&', "&amp;");
075        encoding.put('\n', "&#xA;");
076        encoding.put('\r', "&#xD;");
077        encoding.put('\t', "&#x9;");
078    }
079
080    @Override
081    public void close() throws IOException {
082        if (out != null) {
083            out.close();
084        }
085    }
086}