001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.preferences.sources;
003
004import java.io.File;
005import java.util.Objects;
006import java.util.regex.Matcher;
007import java.util.regex.Pattern;
008
009import org.openstreetmap.josm.tools.Logging;
010import org.openstreetmap.josm.tools.Utils;
011
012/**
013 * A source entry primarily used to save the user's selection of mappaint styles,
014 * but also for preset sources or validator rules.
015 * @since 12649 (moved from gui.preferences package)
016 * @since 3796
017 */
018public class SourceEntry {
019
020    /**
021     * The type of source entry.
022     * @since 12825
023     **/
024    public final SourceType type;
025
026    /**
027     *  A URL can be anything that CachedFile understands, i.e.
028     *  a local file, http://, or a file from the current jar
029     */
030    public String url;
031
032    /**
033     * Indicates, that {@link #url} is a zip file and the resource is
034     * inside the zip file.
035     */
036    public boolean isZip;
037
038    /**
039     * If {@link #isZip} is true, denotes the path inside the zip file.
040     */
041    public String zipEntryPath;
042
043    /**
044     *  Name is used as a namespace for color preferences and (currently) only
045     *  one file with a name can be loaded at a time. Additional styles must
046     *  either have the same name as the main style or no name at all.
047     *  If no name is provided, it will be set to the default value "standard".
048     *  The name can also be given in the xml file as attribute for the rules tag.
049     *  (This overrides the name given in the preferences, otherwise both
050     *  methods are equivalent.)
051     */
052    public String name;
053
054    /**
055     * A title that can be used as menu entry.
056     */
057    public String title;
058
059    /**
060     * active is a boolean flag that can be used to turn the source on or off at runtime.
061     */
062    public boolean active;
063
064    /**
065     * Constructs a new {@code SourceEntry}.
066     * @param type type of source entry
067     * @param url URL that {@link org.openstreetmap.josm.io.CachedFile} understands
068     * @param isZip if url is a zip file and the resource is inside the zip file
069     * @param zipEntryPath If {@code isZip} is {@code true}, denotes the path inside the zip file
070     * @param name Source name
071     * @param title title that can be used as menu entry
072     * @param active boolean flag that can be used to turn the source on or off at runtime
073     * @see #url
074     * @see #isZip
075     * @see #zipEntryPath
076     * @see #name
077     * @see #title
078     * @see #active
079     * @since 12825
080     */
081    public SourceEntry(SourceType type, String url, boolean isZip, String zipEntryPath, String name, String title, boolean active) {
082        this.type = type;
083        this.url = url;
084        this.isZip = isZip;
085        this.zipEntryPath = "".equals(zipEntryPath) ? null : zipEntryPath;
086        this.name = "".equals(name) ? null : name;
087        this.title = "".equals(title) ? null : title;
088        this.active = active;
089    }
090
091    /**
092     * Constructs a new {@code SourceEntry}.
093     * @param type type of source entry
094     * @param url URL that {@link org.openstreetmap.josm.io.CachedFile} understands
095     * @param name Source name
096     * @param title title that can be used as menu entry
097     * @param active boolean flag that can be used to turn the source on or off at runtime
098     * @see #url
099     * @see #name
100     * @see #title
101     * @see #active
102     * @since 12825
103     */
104    public SourceEntry(SourceType type, String url, String name, String title, boolean active) {
105        this(type, url, false, null, name, title, active);
106    }
107
108    /**
109     * Constructs a new {@code SourceEntry}.
110     * @param e existing source entry to copy
111     */
112    public SourceEntry(SourceEntry e) {
113        this.type = e.type;
114        this.url = e.url;
115        this.isZip = e.isZip;
116        this.zipEntryPath = e.zipEntryPath;
117        this.name = e.name;
118        this.title = e.title;
119        this.active = e.active;
120    }
121
122    @Override
123    public boolean equals(Object obj) {
124        if (this == obj) return true;
125        if (obj == null || getClass() != obj.getClass()) return false;
126        SourceEntry that = (SourceEntry) obj;
127        return isZip == that.isZip &&
128                active == that.active &&
129                Objects.equals(url, that.url) &&
130                Objects.equals(zipEntryPath, that.zipEntryPath) &&
131                Objects.equals(name, that.name) &&
132                Objects.equals(title, that.title);
133    }
134
135    @Override
136    public int hashCode() {
137        return Objects.hash(url, isZip, zipEntryPath, name, title, active);
138    }
139
140    @Override
141    public String toString() {
142        return title != null ? title : url;
143    }
144
145    /**
146     * String to show in menus and error messages.
147     * @return Usually the shortdescription, but can be the file name
148     * if no shortdescription is available.
149     */
150    public String getDisplayString() {
151        if (title != null)
152            return title;
153        else
154            return getFileNamePart();
155    }
156
157    /**
158     * Extracts file part from url, e.g.:
159     * <code>http://www.test.com/file.xml?format=text --&gt; file.xml</code>
160     * @return The filename part of the URL
161     */
162    public String getFileNamePart() {
163        Pattern p = Pattern.compile("([^/\\\\]*?)([?].*)?$");
164        Matcher m = p.matcher(url);
165        if (m.find()) {
166            return m.group(1);
167        } else {
168            Logging.warn("Unexpected URL format: "+url);
169            return url;
170        }
171    }
172
173    /**
174     * the name / identifier that should be used to save custom color values
175     * and similar stuff to the preference file
176     * @return the identifier; never null. Usually the result is "standard"
177     */
178    public String getPrefName() {
179        return name == null ? "standard" : name;
180    }
181
182    /**
183     * Determines if this source denotes a file on a local filesystem.
184     * @return {@code true} if the source is a local file
185     */
186    public boolean isLocal() {
187        return Utils.isLocalUrl(url);
188    }
189
190    /**
191     * Return the source directory, only for local files.
192     * @return The source directory, or {@code null} if this file isn't local, or does not have a parent
193     * @since 7276
194     */
195    public File getLocalSourceDir() {
196        if (!isLocal())
197            return null;
198        return new File(url).getParentFile();
199    }
200
201    /**
202     * Returns the parent directory of the resource inside the zip file.
203     *
204     * @return the parent directory of the resource inside the zip file,
205     * "." if zipEntryPath is a top level file; null, if zipEntryPath is null
206     */
207    public String getZipEntryDirName() {
208        if (zipEntryPath == null) return null;
209        File file = new File(zipEntryPath);
210        File dir = file.getParentFile();
211        if (dir == null) return ".";
212        String path = dir.getPath();
213        if (!"/".equals(File.separator)) {
214            path = path.replace(File.separator, "/");
215        }
216        return path;
217    }
218}