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.ImageOverlay;
024import org.openstreetmap.josm.tools.ImageProvider;
025import org.openstreetmap.josm.tools.Utils;
026
027/**
028 * A mappaint style (abstract class).
029 *
030 * Handles everything from parsing the style definition to application
031 * of the style to an osm primitive.
032 */
033public abstract class StyleSource extends SourceEntry {
034
035    private List<Throwable> errors = new ArrayList<>();
036    public File zipIcons;
037
038    /** image provider returning the icon for this style */
039    private ImageProvider imageIconProvider;
040
041    /** image provider returning the default icon */
042    private static ImageProvider defaultIconProvider;
043
044    /******
045     * The following fields is additional information found in the header
046     * of the source file.
047     */
048    public String icon;
049
050    /**
051     * List of settings for user customization.
052     */
053    public final List<StyleSetting> settings = new ArrayList<>();
054    /**
055     * Values of the settings for efficient lookup.
056     */
057    public Map<String, Object> settingValues = new HashMap<>();
058
059    /**
060     * Constructs a new, active {@link StyleSource}.
061     * @param url URL that {@link org.openstreetmap.josm.io.CachedFile} understands
062     * @param name The name for this StyleSource
063     * @param title The title that can be used as menu entry
064     */
065    public StyleSource(String url, String name, String title) {
066        super(url, name, title, true);
067    }
068
069    /**
070     * Constructs a new {@link StyleSource}
071     * @param entry The entry to copy the data (url, name, ...) from.
072     */
073    public StyleSource(SourceEntry entry) {
074        super(entry);
075    }
076
077    /**
078     * Apply style to osm primitive.
079     *
080     * Adds properties to a MultiCascade. All active {@link StyleSource}s add
081     * their properties on after the other. At a later stage, concrete painting
082     * primitives (lines, icons, text, ...) are derived from the MultiCascade.
083     * @param mc the current MultiCascade, empty for the first StyleSource
084     * @param osm the primitive
085     * @param scale the map scale
086     * @param pretendWayIsClosed For styles that require the way to be closed,
087     * we pretend it is. This is useful for generating area styles from the (segmented)
088     * outer ways of a multipolygon.
089     */
090    public abstract void apply(MultiCascade mc, OsmPrimitive osm, double scale, boolean pretendWayIsClosed);
091
092    /**
093     * Loads the style source.
094     */
095    public abstract void loadStyleSource();
096
097    /**
098     * Returns a new {@code InputStream} to the style source. When finished, {@link #closeSourceInputStream(InputStream)} must be called.
099     * @return A new {@code InputStream} to the style source that must be closed by the caller
100     * @throws IOException if any I/O error occurs.
101     * @see #closeSourceInputStream(InputStream)
102     */
103    public abstract InputStream getSourceInputStream() throws IOException;
104
105    /**
106     * Returns a new {@code CachedFile} to the local file containing style source (can be a text file or an archive).
107     * @return A new {@code CachedFile} to the local file containing style source
108     * @throws IOException if any I/O error occurs.
109     * @since 7081
110     */
111    public abstract CachedFile getCachedFile() throws IOException;
112
113    /**
114     * Closes the source input stream previously returned by {@link #getSourceInputStream()} and other linked resources, if applicable.
115     * @param is The source input stream that must be closed
116     * @see #getSourceInputStream()
117     * @since 6289
118     */
119    public void closeSourceInputStream(InputStream is) {
120        Utils.close(is);
121    }
122
123    public void logError(Throwable e) {
124        errors.add(e);
125    }
126
127    public Collection<Throwable> getErrors() {
128        return Collections.unmodifiableCollection(errors);
129    }
130
131    /**
132     * Initialize the class.
133     */
134    protected void init() {
135        errors.clear();
136        imageIconProvider = null;
137        icon = null;
138    }
139
140    /**
141     * Image provider for default icon.
142     *
143     * @return image provider for default styles icon
144     * @see #getIconProvider()
145     * @since 8097
146     */
147    private static synchronized ImageProvider getDefaultIconProvider() {
148        if (defaultIconProvider == null) {
149            defaultIconProvider = new ImageProvider("dialogs/mappaint", "pencil");
150        }
151        return defaultIconProvider;
152    }
153
154    /**
155     * Image provider for source icon. Uses default icon, when not else available.
156     *
157     * @return image provider for styles icon
158     * @see #getIconProvider()
159     * @since 8097
160     */
161    protected ImageProvider getSourceIconProvider() {
162        if (imageIconProvider == null) {
163            if (icon != null) {
164                imageIconProvider = MapPaintStyles.getIconProvider(new IconReference(icon, this), true);
165            }
166            if (imageIconProvider == null) {
167                imageIconProvider = getDefaultIconProvider();
168            }
169        }
170        return imageIconProvider;
171    }
172
173    /**
174     * Image provider for source icon.
175     *
176     * @return image provider for styles icon
177     * @since 8097
178     */
179    public final ImageProvider getIconProvider() {
180        ImageProvider i = getSourceIconProvider();
181        if (!getErrors().isEmpty()) {
182            i = new ImageProvider(i).addOverlay(new ImageOverlay(new ImageProvider("dialogs/mappaint/error_small")));
183        }
184        return i;
185    }
186
187    /**
188     * Image for source icon.
189     *
190     * @return styles icon for display
191     */
192    public final ImageIcon getIcon() {
193        return getIconProvider().setMaxSize(ImageProvider.ImageSizes.MENU).get();
194    }
195
196    /**
197     * Return text to display as ToolTip.
198     *
199     * @return tooltip text containing error status
200     */
201    public String getToolTipText() {
202        if (errors.isEmpty())
203            return null;
204        else
205            return trn("There was an error when loading this style. Select ''Info'' from the right click menu for details.",
206                    "There were {0} errors when loading this style. Select ''Info'' from the right click menu for details.",
207                    errors.size(), errors.size());
208    }
209
210    public Color getBackgroundColorOverride() {
211        return null;
212    }
213}