001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint;
003
004import static org.openstreetmap.josm.tools.I18n.trn;
005
006import java.awt.Color;
007import java.io.File;
008import java.io.IOException;
009import java.io.InputStream;
010import java.util.ArrayList;
011import java.util.Collection;
012import java.util.Collections;
013import java.util.HashMap;
014import java.util.List;
015import java.util.Map;
016
017import javax.swing.ImageIcon;
018
019import org.openstreetmap.josm.data.osm.OsmPrimitive;
020import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
021import org.openstreetmap.josm.gui.preferences.SourceEntry;
022import org.openstreetmap.josm.io.CachedFile;
023import org.openstreetmap.josm.tools.ImageProvider;
024import org.openstreetmap.josm.tools.Utils;
025
026/**
027 * A mappaint style (abstract class).
028 *
029 * Handles everything from parsing the style definition to application
030 * of the style to an osm primitive.
031 */
032public abstract class StyleSource extends SourceEntry {
033
034    private List<Throwable> errors = new ArrayList<>();
035    public File zipIcons;
036
037    private ImageIcon imageIcon;
038
039    /******
040     * The following fields is additional information found in the header
041     * of the source file.
042     */
043
044    public String icon;
045
046    /**
047     * List of settings for user customization.
048     */
049    public final List<StyleSetting> settings = new ArrayList<>();
050    /**
051     * Values of the settings for efficient lookup.
052     */
053    public Map<String, Object> settingValues = new HashMap<>();
054
055    public StyleSource(String url, String name, String title) {
056        super(url, name, title, true);
057    }
058
059    public StyleSource(SourceEntry entry) {
060        super(entry);
061    }
062
063    /**
064     * Apply style to osm primitive.
065     *
066     * Adds properties to a MultiCascade. All active {@link StyleSource}s add
067     * their properties on after the other. At a later stage, concrete painting
068     * primitives (lines, icons, text, ...) are derived from the MultiCascade.
069     * @param mc the current MultiCascade, empty for the first StyleSource
070     * @param osm the primitive
071     * @param scale the map scale
072     * @param multipolyOuterWay support for a very old multipolygon tagging style
073     * where you add the tags both to the outer and the inner way.
074     * However, independent inner way style is also possible.
075     * @param pretendWayIsClosed For styles that require the way to be closed,
076     * we pretend it is. This is useful for generating area styles from the (segmented)
077     * outer ways of a multipolygon.
078     */
079    public abstract void apply(MultiCascade mc, OsmPrimitive osm, double scale, OsmPrimitive multipolyOuterWay, boolean pretendWayIsClosed);
080
081    /**
082     * Loads the style source.
083     */
084    public abstract void loadStyleSource();
085
086    /**
087     * Returns a new {@code InputStream} to the style source. When finished, {@link #closeSourceInputStream(InputStream)} must be called.
088     * @return A new {@code InputStream} to the style source that must be closed by the caller
089     * @throws IOException if any I/O error occurs.
090     * @see #closeSourceInputStream(InputStream)
091     */
092    public abstract InputStream getSourceInputStream() throws IOException;
093
094    /**
095     * Returns a new {@code CachedFile} to the local file containing style source (can be a text file or an archive).
096     * @return A new {@code CachedFile} to the local file containing style source
097     * @throws IOException if any I/O error occurs.
098     * @since 7081
099     */
100    public abstract CachedFile getCachedFile() throws IOException;
101
102    /**
103     * Closes the source input stream previously returned by {@link #getSourceInputStream()} and other linked resources, if applicable.
104     * @param is The source input stream that must be closed
105     * @since 6289
106     * @see #getSourceInputStream()
107     */
108    public void closeSourceInputStream(InputStream is) {
109        Utils.close(is);
110    }
111
112    public void logError(Throwable e) {
113        errors.add(e);
114    }
115
116    public Collection<Throwable> getErrors() {
117        return Collections.unmodifiableCollection(errors);
118    }
119
120    protected void init() {
121        errors.clear();
122        imageIcon = null;
123        icon = null;
124    }
125
126    private static ImageIcon defaultIcon;
127
128    private static ImageIcon getDefaultIcon() {
129        if (defaultIcon == null) {
130            defaultIcon = ImageProvider.get("dialogs/mappaint", "pencil");
131        }
132        return defaultIcon;
133    }
134
135    protected ImageIcon getSourceIcon() {
136        if (imageIcon == null) {
137            if (icon != null) {
138                imageIcon = MapPaintStyles.getIcon(new IconReference(icon, this), -1, -1);
139            }
140            if (imageIcon == null) {
141                imageIcon = getDefaultIcon();
142            }
143        }
144        return imageIcon;
145    }
146
147    public final ImageIcon getIcon() {
148        if (getErrors().isEmpty())
149            return getSourceIcon();
150        else
151            return ImageProvider.overlay(getSourceIcon(),
152                    ImageProvider.get("dialogs/mappaint/error_small"),
153                    ImageProvider.OverlayPosition.SOUTHEAST);
154    }
155
156    public String getToolTipText() {
157        if (errors.isEmpty())
158            return null;
159        else
160            return trn("There was an error when loading this style. Select ''Info'' from the right click menu for details.",
161                    "There were {0} errors when loading this style. Select ''Info'' from the right click menu for details.",
162                    errors.size(), errors.size());
163    }
164
165    public Color getBackgroundColorOverride() {
166        return null;
167    }
168}