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}