source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetReader.java@ 6572

Last change on this file since 6572 was 6572, checked in by simon04, 10 years ago

see #7797 - extend presets by <preset_link preset_name="..." /> to add a link to another preset, exemplified in "Education" presets

File size: 11.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.tagging;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.BufferedReader;
7import java.io.File;
8import java.io.IOException;
9import java.io.InputStream;
10import java.io.InputStreamReader;
11import java.io.Reader;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.HashMap;
15import java.util.Iterator;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.Map;
19import java.util.Stack;
20
21import javax.swing.JOptionPane;
22
23import org.openstreetmap.josm.Main;
24import org.openstreetmap.josm.gui.preferences.SourceEntry;
25import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference;
26import org.openstreetmap.josm.io.MirroredInputStream;
27import org.openstreetmap.josm.tools.Utils;
28import org.openstreetmap.josm.tools.XmlObjectParser;
29import org.xml.sax.SAXException;
30
31/**
32 * The tagging presets reader.
33 * @since 6068
34 */
35public final class TaggingPresetReader {
36
37 private TaggingPresetReader() {
38 // Hide default constructor for utils classes
39 }
40
41 private static File zipIcons = null;
42
43 public static List<String> getPresetSources() {
44 LinkedList<String> sources = new LinkedList<String>();
45
46 for (SourceEntry e : (new TaggingPresetPreference.PresetPrefHelper()).get()) {
47 sources.add(e.url);
48 }
49
50 return sources;
51 }
52
53 /**
54 * Holds a reference to a chunk of items/objects.
55 */
56 public static class Chunk {
57 public String id;
58 }
59
60 /**
61 * Holds a reference to an earlier item/object.
62 */
63 public static class Reference {
64 public String ref;
65 }
66
67 public static List<TaggingPreset> readAll(Reader in, boolean validate) throws SAXException {
68 XmlObjectParser parser = new XmlObjectParser();
69 parser.mapOnStart("item", TaggingPreset.class);
70 parser.mapOnStart("separator", TaggingPresetSeparator.class);
71 parser.mapBoth("group", TaggingPresetMenu.class);
72 parser.map("text", TaggingPresetItems.Text.class);
73 parser.map("link", TaggingPresetItems.Link.class);
74 parser.map("preset_link", TaggingPresetItems.PresetLink.class);
75 parser.mapOnStart("optional", TaggingPresetItems.Optional.class);
76 parser.mapOnStart("roles", TaggingPresetItems.Roles.class);
77 parser.map("role", TaggingPresetItems.Role.class);
78 parser.map("checkgroup", TaggingPresetItems.CheckGroup.class);
79 parser.map("check", TaggingPresetItems.Check.class);
80 parser.map("combo", TaggingPresetItems.Combo.class);
81 parser.map("multiselect", TaggingPresetItems.MultiSelect.class);
82 parser.map("label", TaggingPresetItems.Label.class);
83 parser.map("space", TaggingPresetItems.Space.class);
84 parser.map("key", TaggingPresetItems.Key.class);
85 parser.map("list_entry", TaggingPresetItems.PresetListEntry.class);
86 parser.map("item_separator", TaggingPresetItems.ItemSeparator.class);
87 parser.mapBoth("chunk", Chunk.class);
88 parser.map("reference", Reference.class);
89
90 LinkedList<TaggingPreset> all = new LinkedList<TaggingPreset>();
91 TaggingPresetMenu lastmenu = null;
92 TaggingPresetItems.Roles lastrole = null;
93 final List<TaggingPresetItems.Check> checks = new LinkedList<TaggingPresetItems.Check>();
94 List<TaggingPresetItems.PresetListEntry> listEntries = new LinkedList<TaggingPresetItems.PresetListEntry>();
95 final Map<String, List<Object>> byId = new HashMap<String, List<Object>>();
96 final Stack<String> lastIds = new Stack<String>();
97 /** lastIdIterators contains non empty iterators of items to be handled before obtaining the next item from the XML parser */
98 final Stack<Iterator<Object>> lastIdIterators = new Stack<Iterator<Object>>();
99
100 if (validate) {
101 parser.startWithValidation(in, Main.JOSM_WEBSITE+"/tagging-preset-1.0", "resource://data/tagging-preset.xsd");
102 } else {
103 parser.start(in);
104 }
105 while (parser.hasNext() || !lastIdIterators.isEmpty()) {
106 final Object o;
107 if (!lastIdIterators.isEmpty()) {
108 // obtain elements from lastIdIterators with higher priority
109 o = lastIdIterators.peek().next();
110 if (!lastIdIterators.peek().hasNext()) {
111 // remove iterator is is empty
112 lastIdIterators.pop();
113 }
114 } else {
115 o = parser.next();
116 }
117 if (o instanceof Chunk) {
118 if (!lastIds.isEmpty() && ((Chunk) o).id.equals(lastIds.peek())) {
119 // pop last id on end of object, don't process further
120 lastIds.pop();
121 ((Chunk) o).id = null;
122 continue;
123 } else {
124 // if preset item contains an id, store a mapping for later usage
125 String lastId = ((Chunk) o).id;
126 lastIds.push(lastId);
127 byId.put(lastId, new ArrayList<Object>());
128 continue;
129 }
130 } else if (!lastIds.isEmpty()) {
131 // add object to mapping for later usage
132 byId.get(lastIds.peek()).add(o);
133 continue;
134 }
135 if (o instanceof Reference) {
136 // if o is a reference, obtain the corresponding objects from the mapping,
137 // and iterate over those before consuming the next element from parser.
138 final String ref = ((Reference) o).ref;
139 if (byId.get(ref) == null) {
140 throw new SAXException(tr("Reference {0} is being used before it was defined", ref));
141 }
142 lastIdIterators.push(byId.get(ref).iterator());
143 continue;
144 }
145 if (!(o instanceof TaggingPresetItem) && !checks.isEmpty()) {
146 all.getLast().data.addAll(checks);
147 checks.clear();
148 }
149 if (o instanceof TaggingPresetMenu) {
150 TaggingPresetMenu tp = (TaggingPresetMenu) o;
151 if (tp == lastmenu) {
152 lastmenu = tp.group;
153 } else {
154 tp.group = lastmenu;
155 tp.setDisplayName();
156 lastmenu = tp;
157 all.add(tp);
158 }
159 lastrole = null;
160 } else if (o instanceof TaggingPresetSeparator) {
161 TaggingPresetSeparator tp = (TaggingPresetSeparator) o;
162 tp.group = lastmenu;
163 all.add(tp);
164 lastrole = null;
165 } else if (o instanceof TaggingPreset) {
166 TaggingPreset tp = (TaggingPreset) o;
167 tp.group = lastmenu;
168 tp.setDisplayName();
169 all.add(tp);
170 lastrole = null;
171 } else {
172 if (!all.isEmpty()) {
173 if (o instanceof TaggingPresetItems.Roles) {
174 all.getLast().data.add((TaggingPresetItem) o);
175 if (all.getLast().roles != null) {
176 throw new SAXException(tr("Roles cannot appear more than once"));
177 }
178 all.getLast().roles = (TaggingPresetItems.Roles) o;
179 lastrole = (TaggingPresetItems.Roles) o;
180 } else if (o instanceof TaggingPresetItems.Role) {
181 if (lastrole == null)
182 throw new SAXException(tr("Preset role element without parent"));
183 lastrole.roles.add((TaggingPresetItems.Role) o);
184 } else if (o instanceof TaggingPresetItems.Check) {
185 checks.add((TaggingPresetItems.Check) o);
186 } else if (o instanceof TaggingPresetItems.PresetListEntry) {
187 listEntries.add((TaggingPresetItems.PresetListEntry) o);
188 } else if (o instanceof TaggingPresetItems.CheckGroup) {
189 all.getLast().data.add((TaggingPresetItem) o);
190 ((TaggingPresetItems.CheckGroup) o).checks.addAll(checks);
191 checks.clear();
192 } else {
193 if (!checks.isEmpty()) {
194 all.getLast().data.addAll(checks);
195 checks.clear();
196 }
197 all.getLast().data.add((TaggingPresetItem) o);
198 if (o instanceof TaggingPresetItems.ComboMultiSelect) {
199 ((TaggingPresetItems.ComboMultiSelect) o).addListEntries(listEntries);
200 } else if (o instanceof TaggingPresetItems.Key) {
201 if (((TaggingPresetItems.Key) o).value == null) {
202 ((TaggingPresetItems.Key) o).value = ""; // Fix #8530
203 }
204 }
205 listEntries = new LinkedList<TaggingPresetItems.PresetListEntry>();
206 lastrole = null;
207 }
208 } else
209 throw new SAXException(tr("Preset sub element without parent"));
210 }
211 }
212 if (!all.isEmpty() && !checks.isEmpty()) {
213 all.getLast().data.addAll(checks);
214 checks.clear();
215 }
216 return all;
217 }
218
219 public static Collection<TaggingPreset> readAll(String source, boolean validate) throws SAXException, IOException {
220 Collection<TaggingPreset> tp;
221 MirroredInputStream s = new MirroredInputStream(source);
222 try {
223 InputStream zip = s.findZipEntryInputStream("xml","preset");
224 if(zip != null) {
225 zipIcons = s.getFile();
226 }
227 InputStreamReader r = new InputStreamReader(zip == null ? s : zip, Utils.UTF_8);
228 try {
229 tp = readAll(new BufferedReader(r), validate);
230 } finally {
231 Utils.close(r);
232 }
233 } finally {
234 Utils.close(s);
235 }
236 return tp;
237 }
238
239 public static Collection<TaggingPreset> readAll(Collection<String> sources, boolean validate) {
240 LinkedList<TaggingPreset> allPresets = new LinkedList<TaggingPreset>();
241 for(String source : sources) {
242 try {
243 allPresets.addAll(readAll(source, validate));
244 } catch (IOException e) {
245 Main.error(e);
246 Main.error(source);
247 JOptionPane.showMessageDialog(
248 Main.parent,
249 tr("Could not read tagging preset source: {0}",source),
250 tr("Error"),
251 JOptionPane.ERROR_MESSAGE
252 );
253 } catch (SAXException e) {
254 Main.error(e);
255 Main.error(source);
256 JOptionPane.showMessageDialog(
257 Main.parent,
258 "<html>" + tr("Error parsing {0}: ", source) + "<br><br><table width=600>" + e.getMessage() + "</table></html>",
259 tr("Error"),
260 JOptionPane.ERROR_MESSAGE
261 );
262 }
263 }
264 return allPresets;
265 }
266
267 public static Collection<TaggingPreset> readFromPreferences(boolean validate) {
268 return readAll(getPresetSources(), validate);
269 }
270
271 public static File getZipIcons() {
272 return zipIcons;
273 }
274}
Note: See TracBrowser for help on using the repository browser.