001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.preferences;
003
004import java.io.PrintWriter;
005import java.util.Collection;
006import java.util.List;
007import java.util.Map;
008import java.util.Optional;
009import java.util.stream.Stream;
010
011import org.openstreetmap.josm.data.Version;
012import org.openstreetmap.josm.io.XmlWriter;
013import org.openstreetmap.josm.spi.preferences.Config;
014import org.openstreetmap.josm.spi.preferences.ListListSetting;
015import org.openstreetmap.josm.spi.preferences.ListSetting;
016import org.openstreetmap.josm.spi.preferences.MapListSetting;
017import org.openstreetmap.josm.spi.preferences.Setting;
018import org.openstreetmap.josm.spi.preferences.SettingVisitor;
019import org.openstreetmap.josm.spi.preferences.StringSetting;
020
021/**
022 * Write preferences to XML.
023 * @since 9823
024 */
025public class PreferencesWriter extends XmlWriter implements SettingVisitor {
026    private final boolean noPassword;
027    private final boolean defaults;
028    private String key;
029
030    /**
031     * Construct new {@code PreferencesWriter}.
032     * @param out the {@link PrintWriter}
033     * @param noPassword if password must be excluded
034     * @param defaults true, if default values are converted to XML, false for regular preferences
035     */
036    public PreferencesWriter(PrintWriter out, boolean noPassword, boolean defaults) {
037        super(out);
038        this.noPassword = noPassword;
039        this.defaults = defaults;
040    }
041
042    /**
043     * Write preferences.
044     *
045     * @param settings preferences settings to write
046     */
047    public void write(Collection<Map.Entry<String, Setting<?>>> settings) {
048        write(settings.stream());
049    }
050
051    /**
052     * Write preferences.
053     *
054     * @param settings preferences settings to write as stream.
055     */
056    public void write(Stream<Map.Entry<String, Setting<?>>> settings) {
057        out.write(String.format("<?xml version=\"1.0\" encoding=\"UTF-8\"?>%n"));
058        String rootElement = defaults ? "preferences-defaults" : "preferences";
059        out.write(String.format("<%s xmlns='%s/preferences-1.0'", rootElement, Config.getUrls().getXMLBase()));
060        if (defaults) {
061            out.write(" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'");
062        }
063        out.write(String.format(" version='%d'>%n", Version.getInstance().getVersion()));
064        settings.forEachOrdered(e -> {
065            setKey(e.getKey());
066            e.getValue().visit(this);
067        });
068        out.write(String.format("</%s>%n", rootElement));
069    }
070
071    private void setKey(String key) {
072        this.key = key;
073    }
074
075    private void addTime(Setting<?> setting) {
076        if (defaults) {
077            out.write("' time='" + Optional.ofNullable(setting.getTime()).orElseThrow(IllegalStateException::new));
078        }
079    }
080
081    private void addDefaults() {
082        out.write(String.format("' xsi:nil='true'/>%n"));
083    }
084
085    @Override
086    public void visit(StringSetting setting) {
087        if (noPassword && "osm-server.password".equals(key))
088            return; // do not store plain password.
089        out.write("  <tag key='" + XmlWriter.encode(key));
090        addTime(setting);
091        if (setting.getValue() != null) {
092            out.write(String.format("' value='%s'/>%n", XmlWriter.encode(setting.getValue())));
093        } else if (defaults) {
094            addDefaults();
095        } else {
096            throw new IllegalArgumentException(setting.toString());
097        }
098    }
099
100    @Override
101    public void visit(ListSetting setting) {
102        out.write("  <list key='" + XmlWriter.encode(key));
103        addTime(setting);
104        if (setting.getValue() != null) {
105            out.write(String.format("'>%n"));
106            for (String s : setting.getValue()) {
107                out.write(String.format("    <entry value='%s'/>%n", XmlWriter.encode(s)));
108            }
109            out.write(String.format("  </list>%n"));
110        } else if (defaults) {
111            addDefaults();
112        } else {
113            throw new IllegalArgumentException(setting.toString());
114        }
115    }
116
117    @Override
118    public void visit(ListListSetting setting) {
119        out.write("  <lists key='" + XmlWriter.encode(key));
120        addTime(setting);
121        if (setting.getValue() != null) {
122            out.write(String.format("'>%n"));
123            for (List<String> list : setting.getValue()) {
124                out.write(String.format("    <list>%n"));
125                for (String s : list) {
126                    out.write(String.format("      <entry value='%s'/>%n", encode(s)));
127                }
128                out.write(String.format("    </list>%n"));
129            }
130            out.write(String.format("  </lists>%n"));
131        } else if (defaults) {
132            addDefaults();
133        } else {
134            throw new IllegalArgumentException(setting.toString());
135        }
136    }
137
138    @Override
139    public void visit(MapListSetting setting) {
140        out.write("  <maps key='" + encode(key));
141        addTime(setting);
142        if (setting.getValue() != null) {
143            out.write(String.format("'>%n"));
144            for (Map<String, String> struct : setting.getValue()) {
145                out.write(String.format("    <map>%n"));
146                for (Map.Entry<String, String> e : struct.entrySet()) {
147                    out.write(String.format("      <tag key='%s' value='%s'/>%n", encode(e.getKey()), encode(e.getValue())));
148                }
149                out.write(String.format("    </map>%n"));
150            }
151            out.write(String.format("  </maps>%n"));
152        } else if (defaults) {
153            addDefaults();
154        } else {
155            throw new IllegalArgumentException(setting.toString());
156        }
157    }
158}