001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions.downloadtasks;
003
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.List;
007
008import org.openstreetmap.josm.data.ProjectionBounds;
009import org.openstreetmap.josm.io.UrlPattern;
010import org.openstreetmap.josm.io.XmlWriter;
011
012/**
013 * Common abstract implementation of other download tasks.
014 * @param <T> The downloaded data type
015 * @since 2322
016 */
017public abstract class AbstractDownloadTask<T> implements DownloadTask {
018    private final List<Object> errorMessages;
019    private boolean canceled;
020    private boolean failed;
021    protected T downloadedData;
022    protected boolean zoomAfterDownload = true;
023
024    /**
025     * Constructs a new {@code AbstractDownloadTask}.
026     */
027    public AbstractDownloadTask() {
028        errorMessages = new ArrayList<>();
029    }
030
031    /**
032     * Determines if the download task has been canceled.
033     * @return {@code true} if the download task has been canceled
034     */
035    public boolean isCanceled() {
036        return canceled;
037    }
038
039    /**
040     * Marks this download task as canceled.
041     * @param canceled {@code true} to mark this download task as canceled
042     */
043    public void setCanceled(boolean canceled) {
044        this.canceled = canceled;
045    }
046
047    /**
048     * Determines if the download task has failed.
049     * @return {@code true} if the download task has failed
050     */
051    public boolean isFailed() {
052        return failed;
053    }
054
055    /**
056     * Marks this download task as failed.
057     * @param failed {@code true} to mark this download task as failed
058     */
059    public void setFailed(boolean failed) {
060        this.failed = failed;
061    }
062
063    protected static <T extends Enum<T> & UrlPattern> String[] patterns(Class<T> urlPatternEnum) {
064        // Do not use a method reference until we switch to Java 11, as we face JDK-8141508 with Java 8
065        return Arrays.stream(urlPatternEnum.getEnumConstants()).map(/* JDK-8141508 */ t -> t.pattern()).toArray(String[]::new);
066    }
067
068    protected final void rememberErrorMessage(String message) {
069        errorMessages.add(message);
070    }
071
072    protected final void rememberException(Exception exception) {
073        errorMessages.add(exception);
074    }
075
076    protected final void rememberDownloadedData(T data) {
077        this.downloadedData = data;
078    }
079
080    /**
081     * Replies the downloaded data.
082     * @return The downloaded data.
083     */
084    public final T getDownloadedData() {
085        return downloadedData;
086    }
087
088    @Override
089    public final void setZoomAfterDownload(boolean zoomAfterDownload) {
090        this.zoomAfterDownload = zoomAfterDownload;
091    }
092
093    @Override
094    public List<Object> getErrorObjects() {
095        return errorMessages;
096    }
097
098    @Override
099    public String acceptsDocumentationSummary() {
100        StringBuilder buff = new StringBuilder(128)
101            .append("<tr><td>")
102            .append(getTitle())
103            .append(":</td><td>");
104        String[] patterns = getPatterns();
105        if (patterns.length > 0) {
106            buff.append("<ul>");
107            for (String pattern: patterns) {
108                buff.append("<li>")
109                    .append(XmlWriter.encode(pattern))
110                    .append("</li>");
111            }
112            buff.append("</ul>");
113        }
114        buff.append("</td></tr>");
115        return buff.toString();
116    }
117
118    /**
119     * Determines if the given URL is accepted by {@link #getPatterns}.
120     * Can be overridden for more complex checking logic.
121     * @param url URL to donwload
122     * @return {@code true} if this URL is accepted
123     */
124    public boolean acceptsUrl(String url) {
125        return url != null && Arrays.stream(getPatterns()).anyMatch(url::matches);
126    }
127
128    /**
129     * Check / decide if the task is safe for remotecontrol.
130     *
131     * Keep in mind that a potential attacker has full control over the content
132     * of the file that will be downloaded.
133     * If it is possible to run arbitrary code or write to the local file
134     * system, then the task is (obviously) not save for remote execution.
135     *
136     * The default value is false = unsafe. Override in a subclass to
137     * allow running the task via remotecontol.
138     *
139     * @return true if it is safe to download and open any file of the
140     * corresponding format, false otherwise
141     */
142    public boolean isSafeForRemotecontrolRequests() {
143        return false;
144    }
145
146    @Override
147    public boolean acceptsUrl(String url, boolean isRemotecontrol) {
148        if (isRemotecontrol && !isSafeForRemotecontrolRequests())
149            return false;
150        return acceptsUrl(url);
151    }
152
153    // Default name to keep old plugins compatible
154    @Override
155    public String getTitle() {
156        return getClass().getName();
157    }
158
159    @Override
160    public String toString() {
161        return this.getTitle();
162    }
163
164    // Default pattern to keep old plugins compatible
165    @Override
166    public String[] getPatterns() {
167        return new String[]{};
168    }
169
170    /**
171     * Returns the projection bounds of downloaded data.
172     * @return the projection bounds of downloaded data or {@code null}
173     * @since 11774
174     */
175    public ProjectionBounds getDownloadProjectionBounds() {
176        return null;
177    }
178}