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 pretendWayIsClosed For styles that require the way to be closed,
073     * we pretend it is. This is useful for generating area styles from the (segmented)
074     * outer ways of a multipolygon.
075     */
076    public abstract void apply(MultiCascade mc, OsmPrimitive osm, double scale, boolean pretendWayIsClosed);
077
078    /**
079     * Loads the style source.
080     */
081    public abstract void loadStyleSource();
082
083    /**
084     * Returns a new {@code InputStream} to the style source. When finished, {@link #closeSourceInputStream(InputStream)} must be called.
085     * @return A new {@code InputStream} to the style source that must be closed by the caller
086     * @throws IOException if any I/O error occurs.
087     * @see #closeSourceInputStream(InputStream)
088     */
089    public abstract InputStream getSourceInputStream() throws IOException;
090
091    /**
092     * Returns a new {@code CachedFile} to the local file containing style source (can be a text file or an archive).
093     * @return A new {@code CachedFile} to the local file containing style source
094     * @throws IOException if any I/O error occurs.
095     * @since 7081
096     */
097    public abstract CachedFile getCachedFile() throws IOException;
098
099    /**
100     * Closes the source input stream previously returned by {@link #getSourceInputStream()} and other linked resources, if applicable.
101     * @param is The source input stream that must be closed
102     * @since 6289
103     * @see #getSourceInputStream()
104     */
105    public void closeSourceInputStream(InputStream is) {
106        Utils.close(is);
107    }
108
109    public void logError(Throwable e) {
110        errors.add(e);
111    }
112
113    public Collection<Throwable> getErrors() {
114        return Collections.unmodifiableCollection(errors);
115    }
116
117    protected void init() {
118        errors.clear();
119        imageIcon = null;
120        icon = null;
121    }
122
123    private static ImageIcon defaultIcon;
124
125    private static ImageIcon getDefaultIcon() {
126        if (defaultIcon == null) {
127            defaultIcon = ImageProvider.get("dialogs/mappaint", "pencil");
128        }
129        return defaultIcon;
130    }
131
132    protected ImageIcon getSourceIcon() {
133        if (imageIcon == null) {
134            if (icon != null) {
135                imageIcon = MapPaintStyles.getIcon(new IconReference(icon, this), -1, -1);
136            }
137            if (imageIcon == null) {
138                imageIcon = getDefaultIcon();
139            }
140        }
141        return imageIcon;
142    }
143
144    public final ImageIcon getIcon() {
145        if (getErrors().isEmpty())
146            return getSourceIcon();
147        else
148            return ImageProvider.overlay(getSourceIcon(),
149                    ImageProvider.get("dialogs/mappaint/error_small"),
150                    ImageProvider.OverlayPosition.SOUTHEAST);
151    }
152
153    public String getToolTipText() {
154        if (errors.isEmpty())
155            return null;
156        else
157            return trn("There was an error when loading this style. Select ''Info'' from the right click menu for details.",
158                    "There were {0} errors when loading this style. Select ''Info'' from the right click menu for details.",
159                    errors.size(), errors.size());
160    }
161
162    public Color getBackgroundColorOverride() {
163        return null;
164    }
165}