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