001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.tagging;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.BufferedReader;
007import java.io.File;
008import java.io.IOException;
009import java.io.InputStream;
010import java.io.InputStreamReader;
011import java.io.Reader;
012import java.io.UnsupportedEncodingException;
013import java.util.Collection;
014import java.util.LinkedList;
015import java.util.List;
016
017import javax.swing.JOptionPane;
018
019import org.openstreetmap.josm.Main;
020import org.openstreetmap.josm.gui.preferences.SourceEntry;
021import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference;
022import org.openstreetmap.josm.io.MirroredInputStream;
023import org.openstreetmap.josm.tools.Utils;
024import org.openstreetmap.josm.tools.XmlObjectParser;
025import org.xml.sax.SAXException;
026
027/**
028 * The tagging presets reader.
029 * @since 6068
030 */
031public final class TaggingPresetReader {
032
033    private TaggingPresetReader() {
034        // Hide default constructor for utils classes
035    }
036    
037    private static File zipIcons = null;
038    
039    public static List<String> getPresetSources() {
040        LinkedList<String> sources = new LinkedList<String>();
041
042        for (SourceEntry e : (new TaggingPresetPreference.PresetPrefHelper()).get()) {
043            sources.add(e.url);
044        }
045
046        return sources;
047    }
048    
049    public static List<TaggingPreset> readAll(Reader in, boolean validate) throws SAXException {
050        XmlObjectParser parser = new XmlObjectParser();
051        parser.mapOnStart("item", TaggingPreset.class);
052        parser.mapOnStart("separator", TaggingPresetSeparator.class);
053        parser.mapBoth("group", TaggingPresetMenu.class);
054        parser.map("text", TaggingPresetItems.Text.class);
055        parser.map("link", TaggingPresetItems.Link.class);
056        parser.mapOnStart("optional", TaggingPresetItems.Optional.class);
057        parser.mapOnStart("roles", TaggingPresetItems.Roles.class);
058        parser.map("role", TaggingPresetItems.Role.class);
059        parser.map("checkgroup", TaggingPresetItems.CheckGroup.class);
060        parser.map("check", TaggingPresetItems.Check.class);
061        parser.map("combo", TaggingPresetItems.Combo.class);
062        parser.map("multiselect", TaggingPresetItems.MultiSelect.class);
063        parser.map("label", TaggingPresetItems.Label.class);
064        parser.map("space", TaggingPresetItems.Space.class);
065        parser.map("key", TaggingPresetItems.Key.class);
066        parser.map("list_entry", TaggingPresetItems.PresetListEntry.class);
067        parser.map("item_separator", TaggingPresetItems.ItemSeparator.class);
068        
069        LinkedList<TaggingPreset> all = new LinkedList<TaggingPreset>();
070        TaggingPresetMenu lastmenu = null;
071        TaggingPresetItems.Roles lastrole = null;
072        final List<TaggingPresetItems.Check> checks = new LinkedList<TaggingPresetItems.Check>();
073        List<TaggingPresetItems.PresetListEntry> listEntries = new LinkedList<TaggingPresetItems.PresetListEntry>();
074
075        if (validate) {
076            parser.startWithValidation(in, Main.JOSM_WEBSITE+"/tagging-preset-1.0", "resource://data/tagging-preset.xsd");
077        } else {
078            parser.start(in);
079        }
080        while (parser.hasNext()) {
081            Object o = parser.next();
082            if (!(o instanceof TaggingPresetItem) && !checks.isEmpty()) {
083                all.getLast().data.addAll(checks);
084                checks.clear();
085            }
086            if (o instanceof TaggingPresetMenu) {
087                TaggingPresetMenu tp = (TaggingPresetMenu) o;
088                if (tp == lastmenu) {
089                    lastmenu = tp.group;
090                } else {
091                    tp.group = lastmenu;
092                    tp.setDisplayName();
093                    lastmenu = tp;
094                    all.add(tp);
095                }
096                lastrole = null;
097            } else if (o instanceof TaggingPresetSeparator) {
098                TaggingPresetSeparator tp = (TaggingPresetSeparator) o;
099                tp.group = lastmenu;
100                all.add(tp);
101                lastrole = null;
102            } else if (o instanceof TaggingPreset) {
103                TaggingPreset tp = (TaggingPreset) o;
104                tp.group = lastmenu;
105                tp.setDisplayName();
106                all.add(tp);
107                lastrole = null;
108            } else {
109                if (!all.isEmpty()) {
110                    if (o instanceof TaggingPresetItems.Roles) {
111                        all.getLast().data.add((TaggingPresetItem) o);
112                        if (all.getLast().roles != null) {
113                            throw new SAXException(tr("Roles cannot appear more than once"));
114                        }
115                        all.getLast().roles = (TaggingPresetItems.Roles) o;
116                        lastrole = (TaggingPresetItems.Roles) o;
117                    } else if (o instanceof TaggingPresetItems.Role) {
118                        if (lastrole == null)
119                            throw new SAXException(tr("Preset role element without parent"));
120                        lastrole.roles.add((TaggingPresetItems.Role) o);
121                    } else if (o instanceof TaggingPresetItems.Check) {
122                        checks.add((TaggingPresetItems.Check) o);
123                    } else if (o instanceof TaggingPresetItems.PresetListEntry) {
124                        listEntries.add((TaggingPresetItems.PresetListEntry) o);
125                    } else if (o instanceof TaggingPresetItems.CheckGroup) {
126                        all.getLast().data.add((TaggingPresetItem) o);
127                        ((TaggingPresetItems.CheckGroup) o).checks.addAll(checks);
128                        checks.clear();
129                    } else {
130                        if (!checks.isEmpty()) {
131                            all.getLast().data.addAll(checks);
132                            checks.clear();
133                        }
134                        all.getLast().data.add((TaggingPresetItem) o);
135                        if (o instanceof TaggingPresetItems.ComboMultiSelect) {
136                            ((TaggingPresetItems.ComboMultiSelect) o).addListEntries(listEntries);
137                        } else if (o instanceof TaggingPresetItems.Key) {
138                            if (((TaggingPresetItems.Key) o).value == null) {
139                                ((TaggingPresetItems.Key) o).value = ""; // Fix #8530
140                            }
141                        }
142                        listEntries = new LinkedList<TaggingPresetItems.PresetListEntry>();
143                        lastrole = null;
144                    }
145                } else
146                    throw new SAXException(tr("Preset sub element without parent"));
147            }
148        }
149        if (!all.isEmpty() && !checks.isEmpty()) {
150            all.getLast().data.addAll(checks);
151            checks.clear();
152        }
153        return all;
154    }
155    
156    public static Collection<TaggingPreset> readAll(String source, boolean validate) throws SAXException, IOException {
157        Collection<TaggingPreset> tp;
158        MirroredInputStream s = new MirroredInputStream(source);
159        try {
160            InputStream zip = s.findZipEntryInputStream("xml","preset");
161            if(zip != null) {
162                zipIcons = s.getFile();
163            }
164            InputStreamReader r;
165            try {
166                r = new InputStreamReader(zip == null ? s : zip, "UTF-8");
167            } catch (UnsupportedEncodingException e) {
168                r = new InputStreamReader(zip == null ? s: zip);
169            }
170            try {
171                tp = readAll(new BufferedReader(r), validate);
172            } finally {
173                Utils.close(r);
174            }
175        } finally {
176            Utils.close(s);
177        }
178        return tp;
179    }
180
181    public static Collection<TaggingPreset> readAll(Collection<String> sources, boolean validate) {
182        LinkedList<TaggingPreset> allPresets = new LinkedList<TaggingPreset>();
183        for(String source : sources)  {
184            try {
185                allPresets.addAll(readAll(source, validate));
186            } catch (IOException e) {
187                Main.error(e);
188                Main.error(source);
189                JOptionPane.showMessageDialog(
190                        Main.parent,
191                        tr("Could not read tagging preset source: {0}",source),
192                        tr("Error"),
193                        JOptionPane.ERROR_MESSAGE
194                        );
195            } catch (SAXException e) {
196                Main.error(e);
197                Main.error(source);
198                JOptionPane.showMessageDialog(
199                        Main.parent,
200                        "<html>" + tr("Error parsing {0}: ", source) + "<br><br><table width=600>" + e.getMessage() + "</table></html>",
201                        tr("Error"),
202                        JOptionPane.ERROR_MESSAGE
203                        );
204            }
205        }
206        return allPresets;
207    }
208    
209    public static Collection<TaggingPreset> readFromPreferences(boolean validate) {
210        return readAll(getPresetSources(), validate);
211    }
212    
213    public static File getZipIcons() {
214        return zipIcons;
215    }
216}