Ticket #19026: 19026.2.patch

File 19026.2.patch, 115.2 KB (added by taylor.smock, 5 years ago)
  • scripts/SyncEditorLayerIndex.java

     
    13671367    }
    13681368
    13691369    static String getType(Object e) {
    1370         if (e instanceof ImageryInfo) return ((ImageryInfo) e).getImageryType().getTypeString();
     1370        if (e instanceof ImageryInfo) return ((ImageryInfo) e).getSourceType().getTypeString();
    13711371        return ((Map<String, JsonObject>) e).get("properties").getString("type");
    13721372    }
    13731373
     
    14451445
    14461446    static String getCategory(Object e) {
    14471447        if (e instanceof ImageryInfo) {
    1448             return ((ImageryInfo) e).getImageryCategoryOriginalString();
     1448            return ((ImageryInfo) e).getSourceCategoryOriginalString();
    14491449        }
    14501450        return ((Map<String, JsonObject>) e).get("properties").getString("category", null);
    14511451    }
  • src/org/openstreetmap/josm/actions/AddImageryLayerAction.java

     
    109109                info.setDate(userDate);
    110110                // TODO persist new {time} value (via ImageryLayerInfo.save?)
    111111            }
    112             switch(info.getImageryType()) {
     112            switch(info.getSourceType()) {
    113113            case WMS_ENDPOINT:
    114114                // convert to WMS type
    115115                if (info.getDefaultLayers() == null || info.getDefaultLayers().isEmpty()) {
     
    263263     */
    264264    public static ImageryInfo getWMSLayerInfo(ImageryInfo info, Function<WMSImagery, LayerSelection> choice)
    265265            throws IOException, WMSGetCapabilitiesException {
    266         CheckParameterUtil.ensureThat(ImageryType.WMS_ENDPOINT == info.getImageryType(), "wms_endpoint imagery type expected");
     266        CheckParameterUtil.ensureThat(ImageryType.WMS_ENDPOINT == info.getSourceType(), "wms_endpoint imagery type expected");
    267267        final WMSImagery wms = new WMSImagery(info.getUrl(), info.getCustomHttpHeaders());
    268268        LayerSelection selection = choice.apply(wms);
    269269        if (selection == null) {
     
    283283        // Use full copy of original Imagery info to copy all attributes. Only overwrite what's different
    284284        ImageryInfo ret = new ImageryInfo(info);
    285285        ret.setUrl(url);
    286         ret.setImageryType(ImageryType.WMS);
     286        ret.setSourceType(ImageryType.WMS);
    287287        ret.setName(info.getName() + " - " + selectedLayers);
    288288        ret.setServerProjections(wms.getServerProjections(selection.layers));
    289289        return ret;
  • src/org/openstreetmap/josm/actions/MapRectifierWMSmenuAction.java

     
    238238     */
    239239    private static void addWMSLayer(String title, String url) {
    240240        ImageryInfo info = new ImageryInfo(title, url);
    241         if (info.getImageryType() == ImageryType.WMS_ENDPOINT) {
     241        if (info.getSourceType() == ImageryType.WMS_ENDPOINT) {
    242242            try {
    243243                info = AddImageryLayerAction.getWMSLayerInfo(info);
    244244            } catch (IOException | WMSGetCapabilitiesException e) {
  • src/org/openstreetmap/josm/data/StructUtils.java

     
    156156        }
    157157
    158158        HashMap<String, String> hash = new LinkedHashMap<>();
    159         for (Field f : klass.getDeclaredFields()) {
     159        for (Field f : getDeclaredFieldsInClassOrSuperTypes(klass)) {
    160160            if (f.getAnnotation(StructEntry.class) == null) {
    161161                continue;
    162162            }
     
    205205        }
    206206        for (Map.Entry<String, String> keyValue : hash.entrySet()) {
    207207            Object value;
    208             Field f;
    209             try {
    210                 f = klass.getDeclaredField(keyValue.getKey().replace('-', '_'));
    211             } catch (NoSuchFieldException ex) {
    212                 Logging.trace(ex);
     208            Field f = getDeclaredFieldInClassOrSuperTypes(klass, keyValue.getKey().replace('-', '_'));
     209
     210            if (f == null || f.getAnnotation(StructEntry.class) == null) {
    213211                continue;
    214212            }
    215             if (f.getAnnotation(StructEntry.class) == null) {
    216                 continue;
    217             }
    218213            ReflectionUtils.setObjectsAccessible(f);
    219214            if (f.getType() == Boolean.class || f.getType() == boolean.class) {
    220215                value = Boolean.valueOf(keyValue.getValue());
     
    250245        return struct;
    251246    }
    252247
     248    private static <T> Field getDeclaredFieldInClassOrSuperTypes(Class<T> clazz, String fieldName) {
     249        Class<?> tClass = clazz;
     250        do {
     251            try {
     252                Field f = tClass.getDeclaredField(fieldName);
     253                return f;
     254            } catch (NoSuchFieldException ex) {
     255                Logging.trace(ex);
     256            }
     257            tClass = tClass.getSuperclass();
     258        } while (tClass != null);
     259        return null;
     260    }
     261
     262    private static <T> Field[] getDeclaredFieldsInClassOrSuperTypes(Class<T> clazz) {
     263        List<Field> fields = new ArrayList<>();
     264        Class<?> tclass = clazz;
     265        do {
     266            Collections.addAll(fields, tclass.getDeclaredFields());
     267            tclass = tclass.getSuperclass();
     268        } while (tclass != null);
     269        return fields.toArray(new Field[] {});
     270    }
     271
    253272    @SuppressWarnings("rawtypes")
    254273    private static String mapToJson(Map map) {
    255274        StringWriter stringWriter = new StringWriter();
  • src/org/openstreetmap/josm/data/imagery/ImageryInfo.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.Image;
    76import java.io.StringReader;
    87import java.util.ArrayList;
    98import java.util.Arrays;
     
    109import java.util.Collection;
    1110import java.util.Collections;
    1211import java.util.EnumMap;
    13 import java.util.HashMap;
    1412import java.util.List;
    1513import java.util.Locale;
    1614import java.util.Map;
    1715import java.util.Objects;
    1816import java.util.Optional;
    19 import java.util.Set;
    20 import java.util.TreeSet;
    2117import java.util.concurrent.TimeUnit;
    2218import java.util.regex.Matcher;
    2319import java.util.regex.Pattern;
     
    2622import javax.json.Json;
    2723import javax.json.JsonObject;
    2824import javax.json.JsonReader;
    29 import javax.json.stream.JsonCollectors;
    3025import javax.swing.ImageIcon;
    3126
    32 import org.openstreetmap.gui.jmapviewer.interfaces.Attributed;
    33 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
    34 import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource;
    35 import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik;
    36 import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;
    37 import org.openstreetmap.josm.data.Bounds;
    38 import org.openstreetmap.josm.data.StructUtils;
    3927import org.openstreetmap.josm.data.StructUtils.StructEntry;
    40 import org.openstreetmap.josm.io.Capabilities;
    41 import org.openstreetmap.josm.io.OsmApi;
    42 import org.openstreetmap.josm.spi.preferences.Config;
    43 import org.openstreetmap.josm.spi.preferences.IPreferences;
     28import org.openstreetmap.josm.data.sources.ISourceCategory;
     29import org.openstreetmap.josm.data.sources.ISourceType;
     30import org.openstreetmap.josm.data.sources.SourceBounds;
     31import org.openstreetmap.josm.data.sources.SourceInfo;
     32import org.openstreetmap.josm.data.sources.SourcePreferenceEntry;
    4433import org.openstreetmap.josm.tools.CheckParameterUtil;
    4534import org.openstreetmap.josm.tools.ImageProvider;
    4635import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
    47 import org.openstreetmap.josm.tools.LanguageInfo;
    4836import org.openstreetmap.josm.tools.Logging;
    4937import org.openstreetmap.josm.tools.MultiMap;
    5038import org.openstreetmap.josm.tools.Utils;
     
    5442 *
    5543 * @author Frederik Ramm
    5644 */
    57 public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed {
     45public class ImageryInfo extends SourceInfo<ImageryInfo.ImageryCategory, ImageryInfo.ImageryType, ImageryInfo.ImageryBounds, ImageryInfo.ImageryPreferenceEntry> {
    5846
    5947    /**
    6048     * Type of imagery entry.
    6149     */
    62     public enum ImageryType {
     50    public enum ImageryType implements ISourceType<ImageryType> {
    6351        /** A WMS (Web Map Service) entry. **/
    6452        WMS("wms"),
    6553        /** A TMS (Tile Map Service) entry. **/
     
    8472         * @return the unique string identifying this type
    8573         * @since 6690
    8674         */
     75        @Override
    8776        public final String getTypeString() {
    8877            return typeString;
    8978        }
     
    9382         * @param s The type string
    9483         * @return the imagery type matching the given type string
    9584         */
    96         public static ImageryType fromString(String s) {
     85        public static ImageryType getFromString(String s) {
    9786            for (ImageryType type : ImageryType.values()) {
    9887                if (type.getTypeString().equals(s)) {
    9988                    return type;
     
    10190            }
    10291            return null;
    10392        }
     93
     94        @Override
     95        public ImageryType fromString(String s) {
     96            return getFromString(s);
     97        }
     98
     99        @Override
     100        public ImageryType getDefault() {
     101            return WMS;
     102        }
    104103    }
    105104
    106105    /**
     
    107106     * Category of imagery entry.
    108107     * @since 13792
    109108     */
    110     public enum ImageryCategory {
     109    public enum ImageryCategory implements ISourceCategory<ImageryCategory> {
    111110        /** A aerial or satellite photo. **/
    112111        PHOTO(/* ICON(data/imagery/) */ "photo", tr("Aerial or satellite photo")),
    113112        /** A map of digital terrain model, digital surface model or contour lines. **/
     
    139138         * Returns the unique string identifying this category.
    140139         * @return the unique string identifying this category
    141140         */
     141        @Override
    142142        public final String getCategoryString() {
    143143            return category;
    144144        }
     
    147147         * Returns the description of this category.
    148148         * @return the description of this category
    149149         */
     150        @Override
    150151        public final String getDescription() {
    151152            return description;
    152153        }
     
    157158         * @return the category icon at the given size
    158159         * @since 15049
    159160         */
     161        @Override
    160162        public final ImageIcon getIcon(ImageSizes size) {
    161163            return iconCache
    162164                    .computeIfAbsent(size, x -> Collections.synchronizedMap(new EnumMap<>(ImageryCategory.class)))
     
    168170         * @param s The category string
    169171         * @return the imagery category matching the given category string
    170172         */
    171         public static ImageryCategory fromString(String s) {
     173        public static ImageryCategory getFromString(String s) {
    172174            for (ImageryCategory category : ImageryCategory.values()) {
    173175                if (category.getCategoryString().equals(s)) {
    174176                    return category;
     
    176178            }
    177179            return null;
    178180        }
     181
     182        @Override
     183        public ImageryCategory getDefault() {
     184            return OTHER;
     185        }
     186
     187        @Override
     188        public ImageryCategory fromString(String s) {
     189            return getFromString(s);
     190        }
    179191    }
    180192
    181193    /**
     
    182194     * Multi-polygon bounds for imagery backgrounds.
    183195     * Used to display imagery coverage in preferences and to determine relevant imagery entries based on edit location.
    184196     */
    185     public static class ImageryBounds extends Bounds {
     197    public static class ImageryBounds extends SourceBounds {
    186198
    187199        /**
    188200         * Constructs a new {@code ImageryBounds} from string.
     
    192204        public ImageryBounds(String asString, String separator) {
    193205            super(asString, separator);
    194206        }
    195 
    196         private List<Shape> shapes = new ArrayList<>();
    197 
    198         /**
    199          * Adds a new shape to this bounds.
    200          * @param shape The shape to add
    201          */
    202         public final void addShape(Shape shape) {
    203             this.shapes.add(shape);
    204         }
    205 
    206         /**
    207          * Sets the list of shapes defining this bounds.
    208          * @param shapes The list of shapes defining this bounds.
    209          */
    210         public final void setShapes(List<Shape> shapes) {
    211             this.shapes = shapes;
    212         }
    213 
    214         /**
    215          * Returns the list of shapes defining this bounds.
    216          * @return The list of shapes defining this bounds
    217          */
    218         public final List<Shape> getShapes() {
    219             return shapes;
    220         }
    221 
    222         @Override
    223         public int hashCode() {
    224             return Objects.hash(super.hashCode(), shapes);
    225         }
    226 
    227         @Override
    228         public boolean equals(Object o) {
    229             if (this == o) return true;
    230             if (o == null || getClass() != o.getClass()) return false;
    231             if (!super.equals(o)) return false;
    232             ImageryBounds that = (ImageryBounds) o;
    233             return Objects.equals(shapes, that.shapes);
    234         }
    235207    }
    236208
    237     /** original name of the imagery entry in case of translation call, for multiple languages English when possible */
    238     private String origName;
    239     /** (original) language of the translated name entry */
    240     private String langName;
    241     /** whether this is a entry activated by default or not */
    242     private boolean defaultEntry;
    243     /** Whether this service requires a explicit EULA acceptance before it can be activated */
    244     private String eulaAcceptanceRequired;
    245     /** type of the imagery servics - WMS, TMS, ... */
    246     private ImageryType imageryType = ImageryType.WMS;
    247209    private double pixelPerDegree;
    248210    /** maximum zoom level for TMS imagery */
    249211    private int defaultMaxZoom;
    250212    /** minimum zoom level for TMS imagery */
    251213    private int defaultMinZoom;
    252     /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */
    253     private ImageryBounds bounds;
    254214    /** projections supported by WMS servers */
    255215    private List<String> serverProjections = Collections.emptyList();
    256     /** description of the imagery entry, should contain notes what type of data it is */
    257     private String description;
    258     /** language of the description entry */
    259     private String langDescription;
    260     /** Text of a text attribution displayed when using the imagery */
    261     private String attributionText;
    262     /** Link to the privacy policy of the operator */
    263     private String privacyPolicyURL;
    264     /** Link to a reference stating the permission for OSM usage */
    265     private String permissionReferenceURL;
    266     /** Link behind the text attribution displayed when using the imagery */
    267     private String attributionLinkURL;
    268     /** Image of a graphical attribution displayed when using the imagery */
    269     private String attributionImage;
    270     /** Link behind the graphical attribution displayed when using the imagery */
    271     private String attributionImageURL;
    272     /** Text with usage terms displayed when using the imagery */
    273     private String termsOfUseText;
    274     /** Link behind the text with usage terms displayed when using the imagery */
    275     private String termsOfUseURL;
    276     /** country code of the imagery (for country specific imagery) */
    277     private String countryCode = "";
    278216    /**
    279       * creation date of the imagery (in the form YYYY-MM-DD;YYYY-MM-DD, where
    280       * DD and MM as well as a second date are optional).
    281       *
    282       * Also used as time filter for WMS time={time} parameter (such as Sentinel-2)
    283       * @since 11570
    284       */
    285     private String date;
    286     /**
    287217      * marked as best in other editors
    288218      * @since 11575
    289219      */
     
    293223      * @since 13536
    294224      */
    295225    private boolean overlay;
     226
     227    /** mirrors of different type for this entry */
     228    protected List<ImageryInfo> mirrors;
    296229    /**
    297       * list of old IDs, only for loading, not handled anywhere else
    298       * @since 13536
    299       */
    300     private Collection<String> oldIds;
    301     /** mirrors of different type for this entry */
    302     private List<ImageryInfo> mirrors;
    303     /** icon used in menu */
    304     private String icon;
     230     * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
     231     */
    305232    /** is the geo reference correct - don't offer offset handling */
    306233    private boolean isGeoreferenceValid;
    307     /** which layers should be activated by default on layer addition. **/
    308     private List<DefaultLayer> defaultLayers = Collections.emptyList();
    309     /** HTTP headers **/
    310     private Map<String, String> customHttpHeaders = Collections.emptyMap();
    311234    /** Should this map be transparent **/
    312235    private boolean transparent = true;
    313236    private int minimumTileExpire = (int) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get());
    314     /** category of the imagery */
    315     private ImageryCategory category;
    316     /** category of the imagery (input string, not saved, copied or used otherwise except for error checks) */
    317     private String categoryOriginalString;
    318     /** when adding a field, also adapt the:
    319      * {@link #ImageryPreferenceEntry ImageryPreferenceEntry object}
    320      * {@link #ImageryPreferenceEntry#ImageryPreferenceEntry(ImageryInfo) ImageryPreferenceEntry constructor}
    321      * {@link #ImageryInfo(ImageryPreferenceEntry) ImageryInfo constructor}
    322      * {@link #ImageryInfo(ImageryInfo) ImageryInfo constructor}
    323      * {@link #equalsPref(ImageryPreferenceEntry) equalsPref method}
    324      **/
    325237
    326238    /**
    327      * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
     239     * The ImageryPreferenceEntry class for storing data in JOSM preferences.
     240     *
     241     * @author Frederik Ramm, modified by Taylor Smock
    328242     */
    329     public static class ImageryPreferenceEntry {
    330         @StructEntry String name;
     243    public static class ImageryPreferenceEntry extends SourcePreferenceEntry<ImageryInfo> {
    331244        @StructEntry String d;
    332         @StructEntry String id;
    333         @StructEntry String type;
    334         @StructEntry String url;
    335245        @StructEntry double pixel_per_eastnorth;
    336         @StructEntry String eula;
    337         @StructEntry String attribution_text;
    338         @StructEntry String attribution_url;
    339         @StructEntry String permission_reference_url;
    340         @StructEntry String logo_image;
    341         @StructEntry String logo_url;
    342         @StructEntry String terms_of_use_text;
    343         @StructEntry String terms_of_use_url;
    344         @StructEntry String country_code = "";
    345         @StructEntry String date;
    346246        @StructEntry int max_zoom;
    347247        @StructEntry int min_zoom;
    348         @StructEntry String cookies;
    349         @StructEntry String bounds;
    350         @StructEntry String shapes;
    351248        @StructEntry String projections;
    352         @StructEntry String icon;
    353         @StructEntry String description;
    354249        @StructEntry MultiMap<String, String> noTileHeaders;
    355250        @StructEntry MultiMap<String, String> noTileChecksums;
    356251        @StructEntry int tileSize = -1;
     
    359254        @StructEntry boolean bestMarked;
    360255        @StructEntry boolean modTileFeatures;
    361256        @StructEntry boolean overlay;
    362         @StructEntry String default_layers;
    363         @StructEntry Map<String, String> customHttpHeaders;
    364257        @StructEntry boolean transparent;
    365258        @StructEntry int minimumTileExpire;
    366         @StructEntry String category;
    367259
    368260        /**
    369261         * Constructs a new empty WMS {@code ImageryPreferenceEntry}.
    370262         */
    371263        public ImageryPreferenceEntry() {
    372             // Do nothing
     264            super();
    373265        }
    374266
    375267        /**
     
    377269         * @param i The corresponding imagery info
    378270         */
    379271        public ImageryPreferenceEntry(ImageryInfo i) {
    380             name = i.name;
    381             id = i.id;
    382             type = i.imageryType.getTypeString();
    383             url = i.url;
     272            super(i);
    384273            pixel_per_eastnorth = i.pixelPerDegree;
    385             eula = i.eulaAcceptanceRequired;
    386             attribution_text = i.attributionText;
    387             attribution_url = i.attributionLinkURL;
    388             permission_reference_url = i.permissionReferenceURL;
    389             date = i.date;
    390274            bestMarked = i.bestMarked;
    391275            overlay = i.overlay;
    392             logo_image = i.attributionImage;
    393             logo_url = i.attributionImageURL;
    394             terms_of_use_text = i.termsOfUseText;
    395             terms_of_use_url = i.termsOfUseURL;
    396             country_code = i.countryCode;
    397276            max_zoom = i.defaultMaxZoom;
    398277            min_zoom = i.defaultMinZoom;
    399             cookies = i.cookies;
    400             icon = intern(i.icon);
    401             description = i.description;
    402             category = i.category != null ? i.category.getCategoryString() : null;
    403             if (i.bounds != null) {
    404                 bounds = i.bounds.encodeAsString(",");
    405                 StringBuilder shapesString = new StringBuilder();
    406                 for (Shape s : i.bounds.getShapes()) {
    407                     if (shapesString.length() > 0) {
    408                         shapesString.append(';');
    409                     }
    410                     shapesString.append(s.encodeAsString(","));
    411                 }
    412                 if (shapesString.length() > 0) {
    413                     shapes = shapesString.toString();
    414                 }
    415             }
    416278            if (!i.serverProjections.isEmpty()) {
    417279                projections = String.join(",", i.serverProjections);
    418280            }
     
    432294
    433295            valid_georeference = i.isGeoreferenceValid();
    434296            modTileFeatures = i.isModTileFeatures();
    435             if (!i.defaultLayers.isEmpty()) {
    436                 default_layers = i.defaultLayers.stream().map(DefaultLayer::toJson).collect(JsonCollectors.toJsonArray()).toString();
    437             }
    438             customHttpHeaders = i.customHttpHeaders;
    439297            transparent = i.isTransparent();
    440298            minimumTileExpire = i.minimumTileExpire;
    441299        }
     
    500358    public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) {
    501359        this(name);
    502360        setExtendedUrl(url);
    503         ImageryType t = ImageryType.fromString(type);
     361        ImageryType t = ImageryType.getFromString(type);
    504362        this.cookies = cookies;
    505363        this.eulaAcceptanceRequired = eulaAcceptanceRequired;
    506364        if (t != null) {
    507             this.imageryType = t;
     365            this.sourceType = t;
    508366        } else if (type != null && !type.isEmpty()) {
    509367            throw new IllegalArgumentException("unknown type: "+type);
    510368        }
     
    536394        description = e.description;
    537395        cookies = e.cookies;
    538396        eulaAcceptanceRequired = e.eula;
    539         imageryType = ImageryType.fromString(e.type);
    540         if (imageryType == null) throw new IllegalArgumentException("unknown type");
     397        sourceType = ImageryType.getFromString(e.type);
     398        if (sourceType == null) throw new IllegalArgumentException("unknown type");
    541399        pixelPerDegree = e.pixel_per_eastnorth;
    542400        defaultMaxZoom = e.max_zoom;
    543401        defaultMinZoom = e.min_zoom;
     
    557415            // split generates null element on empty string which gives one element Array[null]
    558416            setServerProjections(Arrays.asList(e.projections.split(",")));
    559417        }
    560         attributionText = intern(e.attribution_text);
     418        attributionText = Utils.intern(e.attribution_text);
    561419        attributionLinkURL = e.attribution_url;
    562420        permissionReferenceURL = e.permission_reference_url;
    563421        attributionImage = e.logo_image;
     
    567425        overlay = e.overlay;
    568426        termsOfUseText = e.terms_of_use_text;
    569427        termsOfUseURL = e.terms_of_use_url;
    570         countryCode = intern(e.country_code);
    571         icon = intern(e.icon);
     428        countryCode = Utils.intern(e.country_code);
     429        icon = Utils.intern(e.icon);
    572430        if (e.noTileHeaders != null) {
    573431            noTileHeaders = e.noTileHeaders.toMap();
    574432        }
     
    584442                defaultLayers = jsonReader.
    585443                        readArray().
    586444                        stream().
    587                         map(x -> DefaultLayer.fromJson((JsonObject) x, imageryType)).
     445                        map(x -> DefaultLayer.fromJson((JsonObject) x, sourceType)).
    588446                        collect(Collectors.toList());
    589447            }
    590448        }
     
    591449        setCustomHttpHeaders(e.customHttpHeaders);
    592450        transparent = e.transparent;
    593451        minimumTileExpire = e.minimumTileExpire;
    594         category = ImageryCategory.fromString(e.category);
     452        category = ImageryCategory.getFromString(e.category);
    595453    }
    596454
    597455    /**
     
    613471        this.langName = i.langName;
    614472        this.defaultEntry = i.defaultEntry;
    615473        this.eulaAcceptanceRequired = null;
    616         this.imageryType = i.imageryType;
     474        this.sourceType = i.sourceType;
    617475        this.pixelPerDegree = i.pixelPerDegree;
    618476        this.defaultMaxZoom = i.defaultMaxZoom;
    619477        this.defaultMinZoom = i.defaultMinZoom;
     
    634492        this.bestMarked = i.bestMarked;
    635493        this.overlay = i.overlay;
    636494        // do not copy field {@code mirrors}
    637         this.icon = intern(i.icon);
     495        this.icon = Utils.intern(i.icon);
    638496        this.isGeoreferenceValid = i.isGeoreferenceValid;
    639497        setDefaultLayers(i.defaultLayers);
    640498        setCustomHttpHeaders(i.customHttpHeaders);
    641499        this.transparent = i.transparent;
    642500        this.minimumTileExpire = i.minimumTileExpire;
    643         this.categoryOriginalString = intern(i.categoryOriginalString);
     501        this.categoryOriginalString = Utils.intern(i.categoryOriginalString);
    644502        this.category = i.category;
    645503    }
    646504
    647     @Override
    648     public int hashCode() {
    649         return Objects.hash(url, imageryType);
     505    /**
     506     * Adds a mirror entry. Mirror entries are completed with the data from the master entry
     507     * and only describe another method to access identical data.
     508     *
     509     * @param entry the mirror to be added
     510     * @since 9658
     511     */
     512    public void addMirror(ImageryInfo entry) {
     513       if (mirrors == null) {
     514           mirrors = new ArrayList<>();
     515       }
     516       mirrors.add(entry);
    650517    }
    651518
    652519    /**
     520     * Returns the mirror entries. Entries are completed with master entry data.
     521     *
     522     * @return the list of mirrors
     523     * @since 9658
     524     */
     525    public List<ImageryInfo> getMirrors() {
     526       List<ImageryInfo> l = new ArrayList<>();
     527       if (mirrors != null) {
     528           int num = 1;
     529           for (ImageryInfo i : mirrors) {
     530               ImageryInfo n = new ImageryInfo(this);
     531               if (i.defaultMaxZoom != 0) {
     532                   n.defaultMaxZoom = i.defaultMaxZoom;
     533               }
     534               if (i.defaultMinZoom != 0) {
     535                   n.defaultMinZoom = i.defaultMinZoom;
     536               }
     537               n.setServerProjections(i.getServerProjections());
     538               n.url = i.url;
     539               n.sourceType = i.sourceType;
     540               if (i.getTileSize() != 0) {
     541                   n.setTileSize(i.getTileSize());
     542               }
     543               if (i.getPrivacyPolicyURL() != null) {
     544                   n.setPrivacyPolicyURL(i.getPrivacyPolicyURL());
     545               }
     546               if (n.id != null) {
     547                   n.id = n.id + "_mirror"+num;
     548               }
     549               if (num > 1) {
     550                   n.name = tr("{0} mirror server {1}", n.name, num);
     551                   if (n.origName != null) {
     552                       n.origName += " mirror server " + num;
     553                   }
     554               } else {
     555                   n.name = tr("{0} mirror server", n.name);
     556                   if (n.origName != null) {
     557                       n.origName += " mirror server";
     558                   }
     559               }
     560               l.add(n);
     561               ++num;
     562           }
     563       }
     564       return l;
     565    }
     566
     567    /**
    653568     * Check if this object equals another ImageryInfo with respect to the properties
    654569     * that get written to the preference file.
    655570     *
     
    658573     * @param other the ImageryInfo object to compare to
    659574     * @return true if they are equal
    660575     */
    661     public boolean equalsPref(ImageryInfo other) {
    662         if (other == null) {
     576    @Override
     577    public boolean equalsPref(SourceInfo<ImageryInfo.ImageryCategory,ImageryInfo.ImageryType,ImageryInfo.ImageryBounds,ImageryInfo.ImageryPreferenceEntry> other) {
     578        if (!(other instanceof ImageryInfo)) {
    663579            return false;
    664580        }
     581        ImageryInfo realOther = (ImageryInfo) other;
    665582
    666583        // CHECKSTYLE.OFF: BooleanExpressionComplexity
    667         return
    668                 Objects.equals(this.name, other.name) &&
    669                 Objects.equals(this.id, other.id) &&
    670                 Objects.equals(this.url, other.url) &&
    671                 Objects.equals(this.modTileFeatures, other.modTileFeatures) &&
    672                 Objects.equals(this.bestMarked, other.bestMarked) &&
    673                 Objects.equals(this.overlay, other.overlay) &&
    674                 Objects.equals(this.isGeoreferenceValid, other.isGeoreferenceValid) &&
    675                 Objects.equals(this.cookies, other.cookies) &&
    676                 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) &&
    677                 Objects.equals(this.imageryType, other.imageryType) &&
    678                 Objects.equals(this.defaultMaxZoom, other.defaultMaxZoom) &&
    679                 Objects.equals(this.defaultMinZoom, other.defaultMinZoom) &&
    680                 Objects.equals(this.bounds, other.bounds) &&
    681                 Objects.equals(this.serverProjections, other.serverProjections) &&
    682                 Objects.equals(this.attributionText, other.attributionText) &&
    683                 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) &&
    684                 Objects.equals(this.permissionReferenceURL, other.permissionReferenceURL) &&
    685                 Objects.equals(this.attributionImageURL, other.attributionImageURL) &&
    686                 Objects.equals(this.attributionImage, other.attributionImage) &&
    687                 Objects.equals(this.termsOfUseText, other.termsOfUseText) &&
    688                 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) &&
    689                 Objects.equals(this.countryCode, other.countryCode) &&
    690                 Objects.equals(this.date, other.date) &&
    691                 Objects.equals(this.icon, other.icon) &&
    692                 Objects.equals(this.description, other.description) &&
    693                 Objects.equals(this.noTileHeaders, other.noTileHeaders) &&
    694                 Objects.equals(this.noTileChecksums, other.noTileChecksums) &&
    695                 Objects.equals(this.metadataHeaders, other.metadataHeaders) &&
    696                 Objects.equals(this.defaultLayers, other.defaultLayers) &&
    697                 Objects.equals(this.customHttpHeaders, other.customHttpHeaders) &&
    698                 Objects.equals(this.transparent, other.transparent) &&
    699                 Objects.equals(this.minimumTileExpire, other.minimumTileExpire) &&
    700                 Objects.equals(this.category, other.category);
     584        return super.equalsPref(realOther) &&
     585                Objects.equals(this.bestMarked, realOther.bestMarked) &&
     586                Objects.equals(this.overlay, realOther.overlay) &&
     587                Objects.equals(this.isGeoreferenceValid, realOther.isGeoreferenceValid) &&
     588                Objects.equals(this.defaultMaxZoom, realOther.defaultMaxZoom) &&
     589                Objects.equals(this.defaultMinZoom, realOther.defaultMinZoom) &&
     590                Objects.equals(this.serverProjections, realOther.serverProjections) &&
     591                Objects.equals(this.transparent, realOther.transparent) &&
     592                Objects.equals(this.minimumTileExpire, realOther.minimumTileExpire);
    701593        // CHECKSTYLE.ON: BooleanExpressionComplexity
    702594    }
    703595
    704596    @Override
    705     public boolean equals(Object o) {
    706         if (this == o) return true;
    707         if (o == null || getClass() != o.getClass()) return false;
    708         ImageryInfo that = (ImageryInfo) o;
    709         return imageryType == that.imageryType && Objects.equals(url, that.url);
    710     }
    711 
    712     private static final Map<String, String> localizedCountriesCache = new HashMap<>();
    713     static {
    714         localizedCountriesCache.put("", tr("Worldwide"));
    715     }
    716 
    717     /**
    718      * Returns a localized name for the given country code, or "Worldwide" if empty.
    719      * This function falls back on the English name, and uses the ISO code as a last-resortvalue.
    720      *
    721      * @param countryCode An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code
    722      * @return The name of the country appropriate to the current locale.
    723      * @see Locale#getDisplayCountry
    724      * @since 15158
    725      */
    726     public static String getLocalizedCountry(String countryCode) {
    727         return localizedCountriesCache.computeIfAbsent(countryCode, code -> new Locale("en", code).getDisplayCountry());
    728     }
    729 
    730     @Override
    731     public String toString() {
    732         // Used in imagery preferences filtering, so must be efficient
    733         return new StringBuilder(name)
    734                 .append('[').append(countryCode)
    735                 // appending the localized country in toString() allows us to filter imagery preferences table with it!
    736                 .append("] ('").append(getLocalizedCountry(countryCode)).append(')')
    737                 .append(" - ").append(url)
    738                 .append(" - ").append(imageryType)
    739                 .toString();
    740     }
    741 
    742     @Override
    743     public int compareTo(ImageryInfo in) {
    744         int i = countryCode.compareTo(in.countryCode);
    745         if (i == 0) {
    746             i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH));
     597    public int compareTo(SourceInfo<ImageryInfo.ImageryCategory,ImageryInfo.ImageryType,ImageryInfo.ImageryBounds,ImageryInfo.ImageryPreferenceEntry> other) {
     598        int i = super.compareTo(other);
     599        if (other instanceof ImageryInfo) {
     600            ImageryInfo in = (ImageryInfo) other;
     601            if (i == 0) {
     602                i = Double.compare(pixelPerDegree, in.pixelPerDegree);
     603            }
    747604        }
    748         if (i == 0) {
    749             i = url.compareTo(in.url);
    750         }
    751         if (i == 0) {
    752             i = Double.compare(pixelPerDegree, in.pixelPerDegree);
    753         }
    754605        return i;
    755606    }
    756607
    757608    /**
    758      * Determines if URL is equal to given imagery info.
    759      * @param in imagery info
    760      * @return {@code true} if URL is equal to given imagery info
    761      */
    762     public boolean equalsBaseValues(ImageryInfo in) {
    763         return url.equals(in.url);
    764     }
    765 
    766     /**
    767609     * Sets the pixel per degree value.
    768610     * @param ppd The ppd value
    769611     * @see #getPixelPerDegree()
     
    789631    }
    790632
    791633    /**
    792      * Sets the imagery polygonial bounds.
    793      * @param b The imagery bounds (non-rectangular)
    794      */
    795     public void setBounds(ImageryBounds b) {
    796         this.bounds = b;
    797     }
    798 
    799     /**
    800      * Returns the imagery polygonial bounds.
    801      * @return The imagery bounds (non-rectangular)
    802      */
    803     public ImageryBounds getBounds() {
    804         return bounds;
    805     }
    806 
    807     @Override
    808     public boolean requiresAttribution() {
    809         return attributionText != null || attributionLinkURL != null || attributionImage != null
    810                 || termsOfUseText != null || termsOfUseURL != null;
    811     }
    812 
    813     @Override
    814     public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) {
    815         return attributionText;
    816     }
    817 
    818     @Override
    819     public String getAttributionLinkURL() {
    820         return attributionLinkURL;
    821     }
    822 
    823     /**
    824      * Return the permission reference URL.
    825      * @return The url
    826      * @see #setPermissionReferenceURL
    827      * @since 11975
    828      */
    829     public String getPermissionReferenceURL() {
    830         return permissionReferenceURL;
    831     }
    832 
    833     /**
    834      * Return the privacy policy URL.
    835      * @return The url
    836      * @see #setPrivacyPolicyURL
    837      * @since 16127
    838      */
    839     public String getPrivacyPolicyURL() {
    840         return privacyPolicyURL;
    841     }
    842 
    843     @Override
    844     public Image getAttributionImage() {
    845         ImageIcon i = ImageProvider.getIfAvailable(attributionImage);
    846         if (i != null) {
    847             return i.getImage();
    848         }
    849         return null;
    850     }
    851 
    852     /**
    853      * Return the raw attribution logo information (an URL to the image).
    854      * @return The url text
    855      * @since 12257
    856      */
    857     public String getAttributionImageRaw() {
    858         return attributionImage;
    859     }
    860 
    861     @Override
    862     public String getAttributionImageURL() {
    863         return attributionImageURL;
    864     }
    865 
    866     @Override
    867     public String getTermsOfUseText() {
    868         return termsOfUseText;
    869     }
    870 
    871     @Override
    872     public String getTermsOfUseURL() {
    873         return termsOfUseURL;
    874     }
    875 
    876     /**
    877      * Set the attribution text
    878      * @param text The text
    879      * @see #getAttributionText(int, ICoordinate, ICoordinate)
    880      */
    881     public void setAttributionText(String text) {
    882         attributionText = intern(text);
    883     }
    884 
    885     /**
    886      * Set the attribution image
    887      * @param url The url of the image.
    888      * @see #getAttributionImageURL()
    889      */
    890     public void setAttributionImageURL(String url) {
    891         attributionImageURL = url;
    892     }
    893 
    894     /**
    895      * Set the image for the attribution
    896      * @param res The image resource
    897      * @see #getAttributionImage()
    898      */
    899     public void setAttributionImage(String res) {
    900         attributionImage = res;
    901     }
    902 
    903     /**
    904      * Sets the URL the attribution should link to.
    905      * @param url The url.
    906      * @see #getAttributionLinkURL()
    907      */
    908     public void setAttributionLinkURL(String url) {
    909         attributionLinkURL = url;
    910     }
    911 
    912     /**
    913      * Sets the permission reference URL.
    914      * @param url The url.
    915      * @see #getPermissionReferenceURL()
    916      * @since 11975
    917      */
    918     public void setPermissionReferenceURL(String url) {
    919         permissionReferenceURL = url;
    920     }
    921 
    922     /**
    923      * Sets the privacy policy URL.
    924      * @param url The url.
    925      * @see #getPrivacyPolicyURL()
    926      * @since 16127
    927      */
    928     public void setPrivacyPolicyURL(String url) {
    929         privacyPolicyURL = url;
    930     }
    931 
    932     /**
    933      * Sets the text to display to the user as terms of use.
    934      * @param text The text
    935      * @see #getTermsOfUseText()
    936      */
    937     public void setTermsOfUseText(String text) {
    938         termsOfUseText = text;
    939     }
    940 
    941     /**
    942      * Sets a url that links to the terms of use text.
    943      * @param text The url.
    944      * @see #getTermsOfUseURL()
    945      */
    946     public void setTermsOfUseURL(String text) {
    947         termsOfUseURL = text;
    948     }
    949 
    950     /**
    951634     * Sets the extended URL of this entry.
    952635     * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info
    953636     */
     
    956639
    957640        // Default imagery type is WMS
    958641        this.url = url;
    959         this.imageryType = ImageryType.WMS;
     642        this.sourceType = ImageryType.WMS;
    960643
    961644        defaultMaxZoom = 0;
    962645        defaultMinZoom = 0;
     
    964647            Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url);
    965648            if (m.matches()) {
    966649                this.url = m.group(3);
    967                 this.imageryType = type;
     650                this.sourceType = type;
    968651                if (m.group(2) != null) {
    969652                    defaultMaxZoom = Integer.parseInt(m.group(2));
    970653                }
     
    982665            }
    983666        }
    984667    }
    985 
    986668    /**
    987      * Returns the entry name.
    988      * @return The entry name
    989      * @since 6968
    990      */
    991     public String getOriginalName() {
    992         return this.origName != null ? this.origName : this.name;
    993     }
    994 
    995     /**
    996      * Sets the entry name and handle translation.
    997      * @param language The used language
    998      * @param name The entry name
    999      * @since 8091
    1000      */
    1001     public void setName(String language, String name) {
    1002         boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
    1003         if (LanguageInfo.isBetterLanguage(langName, language)) {
    1004             this.name = isdefault ? tr(name) : name;
    1005             this.langName = language;
    1006         }
    1007         if (origName == null || isdefault) {
    1008             this.origName = name;
    1009         }
    1010     }
    1011 
    1012     /**
    1013      * Store the id of this info to the preferences and clear it afterwards.
    1014      */
    1015     public void clearId() {
    1016         if (this.id != null) {
    1017             Collection<String> newAddedIds = new TreeSet<>(Config.getPref().getList("imagery.layers.addedIds"));
    1018             newAddedIds.add(this.id);
    1019             Config.getPref().putList("imagery.layers.addedIds", new ArrayList<>(newAddedIds));
    1020         }
    1021         setId(null);
    1022     }
    1023 
    1024     /**
    1025      * Determines if this entry is enabled by default.
    1026      * @return {@code true} if this entry is enabled by default, {@code false} otherwise
    1027      */
    1028     public boolean isDefaultEntry() {
    1029         return defaultEntry;
    1030     }
    1031 
    1032     /**
    1033      * Sets the default state of this entry.
    1034      * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise
    1035      */
    1036     public void setDefaultEntry(boolean defaultEntry) {
    1037         this.defaultEntry = defaultEntry;
    1038     }
    1039 
    1040     /**
    1041669     * Gets the pixel per degree value
    1042670     * @return The ppd value.
    1043671     */
     
    1064692    }
    1065693
    1066694    /**
    1067      * Returns the description text when existing.
    1068      * @return The description
    1069      * @since 8065
    1070      */
    1071     public String getDescription() {
    1072         return this.description;
    1073     }
    1074 
    1075     /**
    1076      * Sets the description text when existing.
    1077      * @param language The used language
    1078      * @param description the imagery description text
    1079      * @since 8091
    1080      */
    1081     public void setDescription(String language, String description) {
    1082         boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
    1083         if (LanguageInfo.isBetterLanguage(langDescription, language)) {
    1084             this.description = isdefault ? tr(description) : description;
    1085             this.langDescription = intern(language);
    1086         }
    1087     }
    1088 
    1089     /**
    1090      * Return the sorted list of activated Imagery IDs.
    1091      * @return sorted list of activated Imagery IDs
    1092      * @since 13536
    1093      */
    1094     public static Collection<String> getActiveIds() {
    1095         ArrayList<String> ids = new ArrayList<>();
    1096         IPreferences pref = Config.getPref();
    1097         if (pref != null) {
    1098             List<ImageryPreferenceEntry> entries = StructUtils.getListOfStructs(
    1099                 pref, "imagery.entries", null, ImageryPreferenceEntry.class);
    1100             if (entries != null) {
    1101                 for (ImageryPreferenceEntry prefEntry : entries) {
    1102                     if (prefEntry.id != null && !prefEntry.id.isEmpty())
    1103                         ids.add(prefEntry.id);
    1104                 }
    1105                 Collections.sort(ids);
    1106             }
    1107         }
    1108         return ids;
    1109     }
    1110 
    1111     /**
    1112695     * Returns a tool tip text for display.
    1113696     * @return The text
    1114697     * @since 8065
    1115698     */
     699    @Override
    1116700    public String getToolTipText() {
    1117701        StringBuilder res = new StringBuilder(getName());
    1118702        boolean html = false;
     
    1143727        }
    1144728        return res.toString();
    1145729    }
    1146 
    1147730    /**
    1148      * Returns the EULA acceptance URL, if any.
    1149      * @return The URL to an EULA text that has to be accepted before use, or {@code null}
    1150      */
    1151     public String getEulaAcceptanceRequired() {
    1152         return eulaAcceptanceRequired;
    1153     }
    1154 
    1155     /**
    1156      * Sets the EULA acceptance URL.
    1157      * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use
    1158      */
    1159     public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {
    1160         this.eulaAcceptanceRequired = eulaAcceptanceRequired;
    1161     }
    1162 
    1163     /**
    1164      * Returns the ISO 3166-1-alpha-2 country code.
    1165      * @return The country code (2 letters)
    1166      */
    1167     public String getCountryCode() {
    1168         return countryCode;
    1169     }
    1170 
    1171     /**
    1172      * Sets the ISO 3166-1-alpha-2 country code.
    1173      * @param countryCode The country code (2 letters)
    1174      */
    1175     public void setCountryCode(String countryCode) {
    1176         this.countryCode = intern(countryCode);
    1177     }
    1178 
    1179     /**
    1180      * Returns the date information.
    1181      * @return The date (in the form YYYY-MM-DD;YYYY-MM-DD, where
    1182      * DD and MM as well as a second date are optional)
    1183      * @since 11570
    1184      */
    1185     public String getDate() {
    1186         return date;
    1187     }
    1188 
    1189     /**
    1190      * Sets the date information.
    1191      * @param date The date information
    1192      * @since 11570
    1193      */
    1194     public void setDate(String date) {
    1195         this.date = date;
    1196     }
    1197 
    1198     /**
    1199      * Returns the entry icon.
    1200      * @return The entry icon
    1201      */
    1202     public String getIcon() {
    1203         return icon;
    1204     }
    1205 
    1206     /**
    1207      * Sets the entry icon.
    1208      * @param icon The entry icon
    1209      */
    1210     public void setIcon(String icon) {
    1211         this.icon = intern(icon);
    1212     }
    1213 
    1214     /**
    1215731     * Get the projections supported by the server. Only relevant for
    1216732     * WMS-type ImageryInfo at the moment.
    1217733     * @return null, if no projections have been specified; the list
     
    1237753     * @return The extended URL
    1238754     */
    1239755    public String getExtendedUrl() {
    1240         return imageryType.getTypeString() + (defaultMaxZoom != 0
     756        return sourceType.getTypeString() + (defaultMaxZoom != 0
    1241757            ? ('['+(defaultMinZoom != 0 ? (Integer.toString(defaultMinZoom) + ',') : "")+defaultMaxZoom+']') : "") + ':' + url;
    1242758    }
    1243759
     
    1265781        return res;
    1266782    }
    1267783
    1268     /**
    1269      * Determines if this entry requires attribution.
    1270      * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise
    1271      */
    1272     public boolean hasAttribution() {
    1273         return attributionText != null;
    1274     }
    1275784
    1276785    /**
    1277      * Copies attribution from another {@code ImageryInfo}.
    1278      * @param i The other imagery info to get attribution from
    1279      */
    1280     public void copyAttribution(ImageryInfo i) {
    1281         this.attributionImage = i.attributionImage;
    1282         this.attributionImageURL = i.attributionImageURL;
    1283         this.attributionText = i.attributionText;
    1284         this.attributionLinkURL = i.attributionLinkURL;
    1285         this.termsOfUseText = i.termsOfUseText;
    1286         this.termsOfUseURL = i.termsOfUseURL;
    1287     }
    1288 
    1289     /**
    1290      * Applies the attribution from this object to a tile source.
    1291      * @param s The tile source
    1292      */
    1293     public void setAttribution(AbstractTileSource s) {
    1294         if (attributionText != null) {
    1295             if ("osm".equals(attributionText)) {
    1296                 s.setAttributionText(new Mapnik().getAttributionText(0, null, null));
    1297             } else {
    1298                 s.setAttributionText(attributionText);
    1299             }
    1300         }
    1301         if (attributionLinkURL != null) {
    1302             if ("osm".equals(attributionLinkURL)) {
    1303                 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL());
    1304             } else {
    1305                 s.setAttributionLinkURL(attributionLinkURL);
    1306             }
    1307         }
    1308         if (attributionImage != null) {
    1309             ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage);
    1310             if (i != null) {
    1311                 s.setAttributionImage(i.getImage());
    1312             }
    1313         }
    1314         if (attributionImageURL != null) {
    1315             s.setAttributionImageURL(attributionImageURL);
    1316         }
    1317         if (termsOfUseText != null) {
    1318             s.setTermsOfUseText(termsOfUseText);
    1319         }
    1320         if (termsOfUseURL != null) {
    1321             if ("osm".equals(termsOfUseURL)) {
    1322                 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL());
    1323             } else {
    1324                 s.setTermsOfUseURL(termsOfUseURL);
    1325             }
    1326         }
    1327     }
    1328 
    1329     /**
    1330      * Returns the imagery type.
    1331      * @return The imagery type
    1332      */
    1333     public ImageryType getImageryType() {
    1334         return imageryType;
    1335     }
    1336 
    1337     /**
    1338      * Sets the imagery type.
    1339      * @param imageryType The imagery type
    1340      */
    1341     public void setImageryType(ImageryType imageryType) {
    1342         this.imageryType = imageryType;
    1343     }
    1344 
    1345     /**
    1346      * Returns the imagery category.
    1347      * @return The imagery category
    1348      * @since 13792
    1349      */
    1350     public ImageryCategory getImageryCategory() {
    1351         return category;
    1352     }
    1353 
    1354     /**
    1355      * Sets the imagery category.
    1356      * @param category The imagery category
    1357      * @since 13792
    1358      */
    1359     public void setImageryCategory(ImageryCategory category) {
    1360         this.category = category;
    1361     }
    1362 
    1363     /**
    1364      * Returns the imagery category original string (don't use except for error checks).
    1365      * @return The imagery category original string
    1366      * @since 13792
    1367      */
    1368     public String getImageryCategoryOriginalString() {
    1369         return categoryOriginalString;
    1370     }
    1371 
    1372     /**
    1373      * Sets the imagery category original string (don't use except for error checks).
    1374      * @param categoryOriginalString The imagery category original string
    1375      * @since 13792
    1376      */
    1377     public void setImageryCategoryOriginalString(String categoryOriginalString) {
    1378         this.categoryOriginalString = intern(categoryOriginalString);
    1379     }
    1380 
    1381     /**
    1382      * Returns true if this layer's URL is matched by one of the regular
    1383      * expressions kept by the current OsmApi instance.
    1384      * @return {@code true} is this entry is blacklisted, {@code false} otherwise
    1385      */
    1386     public boolean isBlacklisted() {
    1387         Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
    1388         return capabilities != null && capabilities.isOnImageryBlacklist(this.url);
    1389     }
    1390 
    1391     /**
    1392      * Sets the map of &lt;header name, header value&gt; that if any of this header
    1393      * will be returned, then this tile will be treated as "no tile at this zoom level"
    1394      *
    1395      * @param noTileHeaders Map of &lt;header name, header value&gt; which will be treated as "no tile at this zoom level"
    1396      * @since 9613
    1397      */
    1398     public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) {
    1399        if (noTileHeaders == null || noTileHeaders.isEmpty()) {
    1400            this.noTileHeaders = null;
    1401        } else {
    1402             this.noTileHeaders = noTileHeaders.toMap();
    1403        }
    1404     }
    1405 
    1406     @Override
    1407     public Map<String, Set<String>> getNoTileHeaders() {
    1408         return noTileHeaders;
    1409     }
    1410 
    1411     /**
    1412      * Sets the map of &lt;checksum type, checksum value&gt; that if any tile with that checksum
    1413      * will be returned, then this tile will be treated as "no tile at this zoom level"
    1414      *
    1415      * @param noTileChecksums Map of &lt;checksum type, checksum value&gt; which will be treated as "no tile at this zoom level"
    1416      * @since 9613
    1417      */
    1418     public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) {
    1419         if (noTileChecksums == null || noTileChecksums.isEmpty()) {
    1420             this.noTileChecksums = null;
    1421         } else {
    1422             this.noTileChecksums = noTileChecksums.toMap();
    1423         }
    1424     }
    1425 
    1426     @Override
    1427     public Map<String, Set<String>> getNoTileChecksums() {
    1428         return noTileChecksums;
    1429     }
    1430 
    1431     /**
    1432      * Returns the map of &lt;header name, metadata key&gt; indicating, which HTTP headers should
    1433      * be moved to metadata
    1434      *
    1435      * @param metadataHeaders map of &lt;header name, metadata key&gt; indicating, which HTTP headers should be moved to metadata
    1436      * @since 8418
    1437      */
    1438     public void setMetadataHeaders(Map<String, String> metadataHeaders) {
    1439         if (metadataHeaders == null || metadataHeaders.isEmpty()) {
    1440             this.metadataHeaders = null;
    1441         } else {
    1442             this.metadataHeaders = metadataHeaders;
    1443         }
    1444     }
    1445 
    1446     /**
    1447786     * Gets the flag if the georeference is valid.
    1448787     * @return <code>true</code> if it is valid.
    1449788     */
     
    1496835    }
    1497836
    1498837    /**
    1499      * Adds an old Id.
    1500      *
    1501      * @param id the Id to be added
    1502      * @since 13536
    1503      */
    1504     public void addOldId(String id) {
    1505        if (oldIds == null) {
    1506            oldIds = new ArrayList<>();
    1507        }
    1508        oldIds.add(id);
    1509     }
    1510 
    1511     /**
    1512      * Get old Ids.
    1513      *
    1514      * @return collection of ids
    1515      * @since 13536
    1516      */
    1517     public Collection<String> getOldIds() {
    1518         return oldIds;
    1519     }
    1520 
    1521     /**
    1522      * Adds a mirror entry. Mirror entries are completed with the data from the master entry
    1523      * and only describe another method to access identical data.
    1524      *
    1525      * @param entry the mirror to be added
    1526      * @since 9658
    1527      */
    1528     public void addMirror(ImageryInfo entry) {
    1529        if (mirrors == null) {
    1530            mirrors = new ArrayList<>();
    1531        }
    1532        mirrors.add(entry);
    1533     }
    1534 
    1535     /**
    1536      * Returns the mirror entries. Entries are completed with master entry data.
    1537      *
    1538      * @return the list of mirrors
    1539      * @since 9658
    1540      */
    1541     public List<ImageryInfo> getMirrors() {
    1542        List<ImageryInfo> l = new ArrayList<>();
    1543        if (mirrors != null) {
    1544            int num = 1;
    1545            for (ImageryInfo i : mirrors) {
    1546                ImageryInfo n = new ImageryInfo(this);
    1547                if (i.defaultMaxZoom != 0) {
    1548                    n.defaultMaxZoom = i.defaultMaxZoom;
    1549                }
    1550                if (i.defaultMinZoom != 0) {
    1551                    n.defaultMinZoom = i.defaultMinZoom;
    1552                }
    1553                n.setServerProjections(i.getServerProjections());
    1554                n.url = i.url;
    1555                n.imageryType = i.imageryType;
    1556                if (i.getTileSize() != 0) {
    1557                    n.setTileSize(i.getTileSize());
    1558                }
    1559                if (i.getPrivacyPolicyURL() != null) {
    1560                    n.setPrivacyPolicyURL(i.getPrivacyPolicyURL());
    1561                }
    1562                if (n.id != null) {
    1563                    n.id = n.id + "_mirror"+num;
    1564                }
    1565                if (num > 1) {
    1566                    n.name = tr("{0} mirror server {1}", n.name, num);
    1567                    if (n.origName != null) {
    1568                        n.origName += " mirror server " + num;
    1569                    }
    1570                } else {
    1571                    n.name = tr("{0} mirror server", n.name);
    1572                    if (n.origName != null) {
    1573                        n.origName += " mirror server";
    1574                    }
    1575                }
    1576                l.add(n);
    1577                ++num;
    1578            }
    1579        }
    1580        return l;
    1581     }
    1582 
    1583     /**
    1584      * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider)
    1585      * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer
    1586      * to work on
    1587      * @return Collection of the layer names
    1588      */
    1589     public List<DefaultLayer> getDefaultLayers() {
    1590         return defaultLayers;
    1591     }
    1592 
    1593     /**
    1594      * Sets the default layers that user will work with
    1595      * @param layers set the list of default layers
    1596      */
    1597     public void setDefaultLayers(List<DefaultLayer> layers) {
    1598         this.defaultLayers = Utils.toUnmodifiableList(layers);
    1599     }
    1600 
    1601     /**
    1602      * Returns custom HTTP headers that should be sent with request towards imagery provider
    1603      * @return headers
    1604      */
    1605     public Map<String, String> getCustomHttpHeaders() {
    1606         return customHttpHeaders;
    1607     }
    1608 
    1609     /**
    1610      * Sets custom HTTP headers that should be sent with request towards imagery provider
    1611      * @param customHttpHeaders http headers
    1612      */
    1613     public void setCustomHttpHeaders(Map<String, String> customHttpHeaders) {
    1614         this.customHttpHeaders = Utils.toUnmodifiableMap(customHttpHeaders);
    1615     }
    1616 
    1617     /**
    1618838     * Determines if this imagery should be transparent.
    1619839     * @return should this imagery be transparent
    1620840     */
     
    1652872     * @since 13890
    1653873     */
    1654874    public String getSourceName() {
    1655         if (ImageryType.BING == getImageryType()) {
     875        if (ImageryType.BING == getSourceType()) {
    1656876            return "Bing";
    1657877        } else {
    1658878            if (id != null) {
     
    1666886        }
    1667887    }
    1668888
    1669     private static String intern(String string) {
    1670         return string == null ? null : string.intern();
     889    /**
     890     * Return the sorted list of activated source IDs.
     891     * @return sorted list of activated source IDs
     892     * @since 13536
     893     */
     894    public static Collection<String> getActiveIds() {
     895        return getActiveIds(ImageryInfo.class);
    1671896    }
    1672897}
  • src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java

     
    336336    }
    337337
    338338    private static boolean isSimilar(ImageryInfo iiA, ImageryInfo iiB) {
    339         if (iiA == null || iiA.getImageryType() != iiB.getImageryType())
     339        if (iiA == null || iiA.getSourceType() != iiB.getSourceType())
    340340            return false;
    341341        if (iiA.getId() != null && iiB.getId() != null)
    342342            return iiA.getId().equals(iiB.getId());
  • src/org/openstreetmap/josm/data/imagery/WMSEndpointTileSource.java

     
    4040     */
    4141    public WMSEndpointTileSource(ImageryInfo info, Projection tileProjection) {
    4242        super(info, tileProjection);
    43         CheckParameterUtil.ensureThat(info.getImageryType() == ImageryType.WMS_ENDPOINT, "imageryType");
     43        CheckParameterUtil.ensureThat(info.getSourceType() == ImageryType.WMS_ENDPOINT, "imageryType");
    4444        try {
    4545            wmsi = new WMSImagery(info.getUrl(), info.getCustomHttpHeaders());
    4646        } catch (IOException | WMSGetCapabilitiesException e) {
  • src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java

     
    392392                                        .orElse(ffirst));
    393393                    }
    394394                }
    395                 this.defaultLayer = new DefaultLayer(info.getImageryType(), first.identifier, first.style, first.tileMatrixSet.identifier);
     395                this.defaultLayer = new DefaultLayer(info.getSourceType(), first.identifier, first.style, first.tileMatrixSet.identifier);
    396396            } else {
    397397                this.defaultLayer = null;
    398398            }
  • src/org/openstreetmap/josm/data/sources/ICommonSource.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.sources;
     3
     4/**
     5 * This interface is used to ensure that a class can get a enum from a string.
     6 * For various reasons, the fromString method cannot be implemented statically.
     7 *
     8 * @author Taylor Smock
     9 * @since xxx
     10 *
     11 * @param <T> The enum type
     12 */
     13public interface ICommonSource<T extends Enum<T>> {
     14    /**
     15     * Get the default value for the Enum
     16     * @return The default value
     17     */
     18    public T getDefault();
     19
     20    /**
     21     * Returns the source category from the given category string.
     22     * @param s The category string
     23     * @return the source category matching the given category string
     24     */
     25    public T fromString(String s);
     26}
  • src/org/openstreetmap/josm/data/sources/ISourceCategory.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.sources;
     3
     4import javax.swing.ImageIcon;
     5
     6import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
     7
     8/**
     9 * @author Taylor Smock
     10 *
     11 * @param <T> The enum that is extending this interface
     12 * @since xxx
     13 */
     14public interface ISourceCategory<T extends Enum<T>> extends ICommonSource<T> {
     15
     16    /**
     17     * Returns the unique string identifying this category.
     18     * @return the unique string identifying this category
     19     */
     20    public String getCategoryString();
     21
     22    /**
     23     * Returns the description of this category.
     24     * @return the description of this category
     25     */
     26    public String getDescription();
     27
     28    /**
     29     * Returns the category icon at the given size.
     30     * @param size icon wanted size
     31     * @return the category icon at the given size
     32     */
     33    public ImageIcon getIcon(ImageSizes size);
     34}
     35 No newline at end of file
  • src/org/openstreetmap/josm/data/sources/ISourceType.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.sources;
     3
     4/**
     5 * This interface should only be used for Enums
     6 * @author Taylor Smock
     7 * @since xxx
     8 *
     9 * @param <T> The source type (e.g., Imagery or otherwise -- should be the name of the class)
     10 */
     11public interface ISourceType<T extends Enum<T>> extends ICommonSource<T> {
     12
     13    /**
     14     * Returns the unique string identifying this type.
     15     * @return the unique string identifying this type
     16     */
     17    public String getTypeString();
     18}
  • src/org/openstreetmap/josm/data/sources/SourceBounds.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.sources;
     3
     4import java.util.ArrayList;
     5import java.util.List;
     6import java.util.Objects;
     7
     8import org.openstreetmap.josm.data.Bounds;
     9import org.openstreetmap.josm.data.imagery.Shape;
     10
     11/**
     12 *
     13 * Multi-polygon bounds for source backgrounds.
     14 * Used to display source coverage in preferences and to determine relevant source entries based on edit location.
     15 *
     16 * @author Frederik Ramm, extracted by Taylor Smock
     17 * @since xxx (extracted from {@link org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds})
     18 */
     19public class SourceBounds extends Bounds {
     20
     21    /**
     22     * Constructs a new {@code SourceBounds} from string.
     23     * @param asString The string containing the list of shapes defining this bounds
     24     * @param separator The shape separator in the given string, usually a comma
     25     */
     26    public SourceBounds(String asString, String separator) {
     27        super(asString, separator);
     28    }
     29
     30    private List<Shape> shapes = new ArrayList<>();
     31
     32    /**
     33     * Adds a new shape to this bounds.
     34     * @param shape The shape to add
     35     */
     36    public final void addShape(Shape shape) {
     37        this.shapes.add(shape);
     38    }
     39
     40    /**
     41     * Sets the list of shapes defining this bounds.
     42     * @param shapes The list of shapes defining this bounds.
     43     */
     44    public final void setShapes(List<Shape> shapes) {
     45        this.shapes = shapes;
     46    }
     47
     48    /**
     49     * Returns the list of shapes defining this bounds.
     50     * @return The list of shapes defining this bounds
     51     */
     52    public final List<Shape> getShapes() {
     53        return shapes;
     54    }
     55
     56    @Override
     57    public int hashCode() {
     58        return Objects.hash(super.hashCode(), shapes);
     59    }
     60
     61    @Override
     62    public boolean equals(Object o) {
     63        if (this == o) return true;
     64        if (o == null || getClass() != o.getClass()) return false;
     65        if (!super.equals(o)) return false;
     66        SourceBounds that = (SourceBounds) o;
     67        return Objects.equals(shapes, that.shapes);
     68    }
     69}
  • src/org/openstreetmap/josm/data/sources/SourceInfo.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.sources;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.awt.Image;
     7import java.util.ArrayList;
     8import java.util.Collection;
     9import java.util.Collections;
     10import java.util.HashMap;
     11import java.util.List;
     12import java.util.Locale;
     13import java.util.Map;
     14import java.util.Objects;
     15import java.util.Set;
     16import java.util.TreeSet;
     17
     18import javax.swing.ImageIcon;
     19
     20import org.openstreetmap.gui.jmapviewer.interfaces.Attributed;
     21import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
     22import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource;
     23import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik;
     24import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;
     25import org.openstreetmap.josm.data.StructUtils;
     26import org.openstreetmap.josm.data.imagery.DefaultLayer;
     27import org.openstreetmap.josm.data.imagery.ImageryInfo;
     28import org.openstreetmap.josm.io.Capabilities;
     29import org.openstreetmap.josm.io.OsmApi;
     30import org.openstreetmap.josm.spi.preferences.Config;
     31import org.openstreetmap.josm.spi.preferences.IPreferences;
     32import org.openstreetmap.josm.tools.ImageProvider;
     33import org.openstreetmap.josm.tools.LanguageInfo;
     34import org.openstreetmap.josm.tools.MultiMap;
     35import org.openstreetmap.josm.tools.Utils;
     36
     37/**
     38 * @author Taylor Smock
     39 * @param <T> The SourceCategory The categories enum for the source
     40 * @param <U> The SourceType The type enum of the source
     41 * @param <V> The SourceBounds The bound type for the entry
     42 * @param <W> The storage for the entry
     43 *
     44 * @since xxx
     45 */
     46public class SourceInfo<T extends ISourceCategory<?>, U extends ISourceType<?>, V extends SourceBounds, W extends SourcePreferenceEntry<?>> extends TileSourceInfo implements Comparable<SourceInfo<T, U, V, W>>, Attributed {
     47    /** original name of the source entry in case of translation call, for multiple languages English when possible */
     48    protected String origName;
     49    /** (original) language of the translated name entry */
     50    protected String langName;
     51    /** whether this is a entry activated by default or not */
     52    protected boolean defaultEntry;
     53    /** Whether this service requires a explicit EULA acceptance before it can be activated */
     54    protected String eulaAcceptanceRequired;
     55    /** type of the services - WMS, TMS, ... */
     56    protected U sourceType;
     57    /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */
     58    protected V bounds;
     59    /** description of the imagery entry, should contain notes what type of data it is */
     60    protected String description;
     61    /** language of the description entry */
     62    protected String langDescription;
     63    /** Text of a text attribution displayed when using the imagery */
     64    protected String attributionText;
     65    /** Link to the privacy policy of the operator */
     66    protected String privacyPolicyURL;
     67    /** Link to a reference stating the permission for OSM usage */
     68    protected String permissionReferenceURL;
     69    /** Link behind the text attribution displayed when using the imagery */
     70    protected String attributionLinkURL;
     71    /** Image of a graphical attribution displayed when using the imagery */
     72    protected String attributionImage;
     73    /** Link behind the graphical attribution displayed when using the imagery */
     74    protected String attributionImageURL;
     75    /** Text with usage terms displayed when using the imagery */
     76    protected String termsOfUseText;
     77    /** Link behind the text with usage terms displayed when using the imagery */
     78    protected String termsOfUseURL;
     79    /** country code of the imagery (for country specific imagery) */
     80    protected String countryCode = "";
     81    /**
     82      * creation date of the source (in the form YYYY-MM-DD;YYYY-MM-DD, where
     83      * DD and MM as well as a second date are optional).
     84      *
     85      * Also used as time filter for WMS time={time} parameter (such as Sentinel-2)
     86      * @since 11570
     87      */
     88    protected String date;
     89    /**
     90      * list of old IDs, only for loading, not handled anywhere else
     91      * @since 13536
     92      */
     93    protected Collection<String> oldIds;
     94    /** icon used in menu */
     95    protected String icon;
     96    /** which layers should be activated by default on layer addition. **/
     97    protected List<DefaultLayer> defaultLayers = Collections.emptyList();
     98    /** HTTP headers **/
     99    protected Map<String, String> customHttpHeaders = Collections.emptyMap();
     100    /** category of the imagery */
     101    protected T category;
     102    /** category of the imagery (input string, not saved, copied or used otherwise except for error checks) */
     103    protected String categoryOriginalString;
     104    /** when adding a field, also adapt the:
     105     * {@link #ImageryPreferenceEntry ImageryPreferenceEntry object}
     106     * {@link #ImageryPreferenceEntry#ImageryPreferenceEntry(ImageryInfo) ImageryPreferenceEntry constructor}
     107     * {@link #ImageryInfo(ImageryPreferenceEntry) ImageryInfo constructor}
     108     * {@link #ImageryInfo(ImageryInfo) ImageryInfo constructor}
     109     * {@link #equalsPref(ImageryPreferenceEntry) equalsPref method}
     110     **/
     111
     112    /**
     113     * Creates empty SourceInfo class
     114     */
     115    public SourceInfo() {
     116        super();
     117    }
     118
     119    /**
     120     * Create a SourceInfo class
     121     *
     122     * @param name name
     123     */
     124    public SourceInfo(String name) {
     125        super(name);
     126    }
     127
     128    /**
     129     * Create a SourceInfo class
     130     *
     131     * @param name name
     132     * @param url base URL
     133     * @param id unique id
     134     */
     135    public SourceInfo(String name, String url, String id) {
     136        super(name, url, id);
     137    }
     138
     139
     140    @Override
     141    public int hashCode() {
     142        return Objects.hash(url, sourceType);
     143    }
     144
     145    /**
     146     * Check if this object equals another SourceInfo with respect to the properties
     147     * that get written to the preference file.
     148     *
     149     * This should be overridden and called in subclasses.
     150     *
     151     * @param other the SourceInfo object to compare to
     152     * @return true if they are equal
     153     */
     154    public boolean equalsPref(SourceInfo<T, U, V, W> other) {
     155        if (other == null) {
     156            return false;
     157        }
     158
     159        // CHECKSTYLE.OFF: BooleanExpressionComplexity
     160        return
     161                Objects.equals(this.name, other.name) &&
     162                Objects.equals(this.id, other.id) &&
     163                Objects.equals(this.url, other.url) &&
     164                Objects.equals(this.modTileFeatures, other.modTileFeatures) &&
     165                Objects.equals(this.cookies, other.cookies) &&
     166                Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) &&
     167                Objects.equals(this.sourceType, other.sourceType) &&
     168                Objects.equals(this.bounds, other.bounds) &&
     169                Objects.equals(this.attributionText, other.attributionText) &&
     170                Objects.equals(this.attributionLinkURL, other.attributionLinkURL) &&
     171                Objects.equals(this.permissionReferenceURL, other.permissionReferenceURL) &&
     172                Objects.equals(this.attributionImageURL, other.attributionImageURL) &&
     173                Objects.equals(this.attributionImage, other.attributionImage) &&
     174                Objects.equals(this.termsOfUseText, other.termsOfUseText) &&
     175                Objects.equals(this.termsOfUseURL, other.termsOfUseURL) &&
     176                Objects.equals(this.countryCode, other.countryCode) &&
     177                Objects.equals(this.date, other.date) &&
     178                Objects.equals(this.icon, other.icon) &&
     179                Objects.equals(this.description, other.description) &&
     180                Objects.equals(this.noTileHeaders, other.noTileHeaders) &&
     181                Objects.equals(this.noTileChecksums, other.noTileChecksums) &&
     182                Objects.equals(this.metadataHeaders, other.metadataHeaders) &&
     183                Objects.equals(this.defaultLayers, other.defaultLayers) &&
     184                Objects.equals(this.customHttpHeaders, other.customHttpHeaders) &&
     185                Objects.equals(this.category, other.category);
     186        // CHECKSTYLE.ON: BooleanExpressionComplexity
     187    }
     188
     189    @Override
     190    public boolean equals(Object o) {
     191        if (this == o) return true;
     192        if (o == null || getClass() != o.getClass()) return false;
     193        SourceInfo<?, ?, ?, ?> that = (SourceInfo<?, ?, ?, ?>) o;
     194        return sourceType == that.sourceType && Objects.equals(url, that.url);
     195    }
     196
     197    private static final Map<String, String> localizedCountriesCache = new HashMap<>();
     198    static {
     199        localizedCountriesCache.put("", tr("Worldwide"));
     200    }
     201
     202    /**
     203     * Returns a localized name for the given country code, or "Worldwide" if empty.
     204     * This function falls back on the English name, and uses the ISO code as a last-resortvalue.
     205     *
     206     * @param countryCode An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code
     207     * @return The name of the country appropriate to the current locale.
     208     * @see Locale#getDisplayCountry
     209     * @since 15158
     210     */
     211    public static String getLocalizedCountry(String countryCode) {
     212        return localizedCountriesCache.computeIfAbsent(countryCode, code -> new Locale("en", code).getDisplayCountry());
     213    }
     214
     215    @Override
     216    public String toString() {
     217        // Used in imagery preferences filtering, so must be efficient
     218        return new StringBuilder(name)
     219                .append('[').append(countryCode)
     220                // appending the localized country in toString() allows us to filter imagery preferences table with it!
     221                .append("] ('").append(getLocalizedCountry(countryCode)).append(')')
     222                .append(" - ").append(url)
     223                .append(" - ").append(sourceType)
     224                .toString();
     225    }
     226
     227    @Override
     228    public int compareTo(SourceInfo<T, U, V, W> in) {
     229        int i = countryCode.compareTo(in.countryCode);
     230        if (i == 0) {
     231            i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH));
     232        }
     233        if (i == 0) {
     234            i = url.compareTo(in.url);
     235        }
     236        return i;
     237    }
     238
     239    /**
     240     * Determines if URL is equal to given source info.
     241     * @param in source info
     242     * @return {@code true} if URL is equal to given source info
     243     */
     244    public boolean equalsBaseValues(SourceInfo<T, U, V, W> in) {
     245        return url.equals(in.url);
     246    }
     247
     248    /**
     249     * Sets the source polygonial bounds.
     250     * @param b The source bounds (non-rectangular)
     251     */
     252    public void setBounds(V b) {
     253        this.bounds = b;
     254    }
     255
     256    /**
     257     * Returns the source polygonial bounds.
     258     * @return The source bounds (non-rectangular)
     259     */
     260    public V getBounds() {
     261        return bounds;
     262    }
     263
     264    @Override
     265    public boolean requiresAttribution() {
     266        return attributionText != null || attributionLinkURL != null || attributionImage != null
     267                || termsOfUseText != null || termsOfUseURL != null;
     268    }
     269
     270    @Override
     271    public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) {
     272        return attributionText;
     273    }
     274
     275    @Override
     276    public String getAttributionLinkURL() {
     277        return attributionLinkURL;
     278    }
     279
     280    /**
     281     * Return the permission reference URL.
     282     * @return The url
     283     * @see #setPermissionReferenceURL
     284     * @since 11975
     285     */
     286    public String getPermissionReferenceURL() {
     287        return permissionReferenceURL;
     288    }
     289
     290    /**
     291     * Return the privacy policy URL.
     292     * @return The url
     293     * @see #setPrivacyPolicyURL
     294     * @since 16127
     295     */
     296    public String getPrivacyPolicyURL() {
     297        return privacyPolicyURL;
     298    }
     299
     300    @Override
     301    public Image getAttributionImage() {
     302        ImageIcon i = ImageProvider.getIfAvailable(attributionImage);
     303        if (i != null) {
     304            return i.getImage();
     305        }
     306        return null;
     307    }
     308
     309    /**
     310     * Return the raw attribution logo information (an URL to the image).
     311     * @return The url text
     312     * @since 12257
     313     */
     314    public String getAttributionImageRaw() {
     315        return attributionImage;
     316    }
     317
     318    @Override
     319    public String getAttributionImageURL() {
     320        return attributionImageURL;
     321    }
     322
     323    @Override
     324    public String getTermsOfUseText() {
     325        return termsOfUseText;
     326    }
     327
     328    @Override
     329    public String getTermsOfUseURL() {
     330        return termsOfUseURL;
     331    }
     332
     333    /**
     334     * Set the attribution text
     335     * @param text The text
     336     * @see #getAttributionText(int, ICoordinate, ICoordinate)
     337     */
     338    public void setAttributionText(String text) {
     339        attributionText = Utils.intern(text);
     340    }
     341
     342    /**
     343     * Set the attribution image
     344     * @param url The url of the image.
     345     * @see #getAttributionImageURL()
     346     */
     347    public void setAttributionImageURL(String url) {
     348        attributionImageURL = url;
     349    }
     350
     351    /**
     352     * Set the image for the attribution
     353     * @param res The image resource
     354     * @see #getAttributionImage()
     355     */
     356    public void setAttributionImage(String res) {
     357        attributionImage = res;
     358    }
     359
     360    /**
     361     * Sets the URL the attribution should link to.
     362     * @param url The url.
     363     * @see #getAttributionLinkURL()
     364     */
     365    public void setAttributionLinkURL(String url) {
     366        attributionLinkURL = url;
     367    }
     368
     369    /**
     370     * Sets the permission reference URL.
     371     * @param url The url.
     372     * @see #getPermissionReferenceURL()
     373     * @since 11975
     374     */
     375    public void setPermissionReferenceURL(String url) {
     376        permissionReferenceURL = url;
     377    }
     378
     379    /**
     380     * Sets the privacy policy URL.
     381     * @param url The url.
     382     * @see #getPrivacyPolicyURL()
     383     * @since 16127
     384     */
     385    public void setPrivacyPolicyURL(String url) {
     386        privacyPolicyURL = url;
     387    }
     388
     389    /**
     390     * Sets the text to display to the user as terms of use.
     391     * @param text The text
     392     * @see #getTermsOfUseText()
     393     */
     394    public void setTermsOfUseText(String text) {
     395        termsOfUseText = text;
     396    }
     397
     398    /**
     399     * Sets a url that links to the terms of use text.
     400     * @param text The url.
     401     * @see #getTermsOfUseURL()
     402     */
     403    public void setTermsOfUseURL(String text) {
     404        termsOfUseURL = text;
     405    }
     406
     407    /**
     408     * Returns the entry name.
     409     * @return The entry name
     410     * @since 6968
     411     */
     412    public String getOriginalName() {
     413        return this.origName != null ? this.origName : this.name;
     414    }
     415
     416    /**
     417     * Sets the entry name and handle translation.
     418     * @param language The used language
     419     * @param name The entry name
     420     * @since 8091
     421     */
     422    public void setName(String language, String name) {
     423        boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
     424        if (LanguageInfo.isBetterLanguage(langName, language)) {
     425            this.name = isdefault ? tr(name) : name;
     426            this.langName = language;
     427        }
     428        if (origName == null || isdefault) {
     429            this.origName = name;
     430        }
     431    }
     432
     433    /**
     434     * Store the id of this info to the preferences and clear it afterwards.
     435     */
     436    public void clearId() {
     437        if (this.id != null) {
     438            Collection<String> newAddedIds = new TreeSet<>(Config.getPref().getList("imagery.layers.addedIds"));
     439            newAddedIds.add(this.id);
     440            Config.getPref().putList("imagery.layers.addedIds", new ArrayList<>(newAddedIds));
     441        }
     442        setId(null);
     443    }
     444
     445    /**
     446     * Determines if this entry is enabled by default.
     447     * @return {@code true} if this entry is enabled by default, {@code false} otherwise
     448     */
     449    public boolean isDefaultEntry() {
     450        return defaultEntry;
     451    }
     452
     453    /**
     454     * Sets the default state of this entry.
     455     * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise
     456     */
     457    public void setDefaultEntry(boolean defaultEntry) {
     458        this.defaultEntry = defaultEntry;
     459    }
     460
     461    /**
     462     * Returns the description text when existing.
     463     * @return The description
     464     * @since 8065
     465     */
     466    public String getDescription() {
     467        return this.description;
     468    }
     469
     470    /**
     471     * Sets the description text when existing.
     472     * @param language The used language
     473     * @param description the imagery description text
     474     * @since 8091
     475     */
     476    public void setDescription(String language, String description) {
     477        boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
     478        if (LanguageInfo.isBetterLanguage(langDescription, language)) {
     479            this.description = isdefault ? tr(description) : description;
     480            this.langDescription = Utils.intern(language);
     481        }
     482    }
     483
     484    /**
     485     * Return the sorted list of activated source IDs.
     486     * @param <W> The type of active id to get
     487     * @param clazz The class of the type of id
     488     * @return sorted list of activated source IDs
     489     * @since 13536, xxx (extracted)
     490     */
     491    public static <W extends SourceInfo<?, ?, ?, ?>> Collection<String> getActiveIds(Class<W> clazz) {
     492        ArrayList<String> ids = new ArrayList<>();
     493        IPreferences pref = Config.getPref();
     494        if (pref != null) {
     495            List<W> entries = StructUtils.getListOfStructs(
     496                pref, (ImageryInfo.class.equals(clazz) ? "imagery" : clazz.getSimpleName()) + ".entries", null, clazz);
     497            if (entries != null) {
     498                for (W prefEntry : entries) {
     499                    if (prefEntry.id != null && !prefEntry.id.isEmpty())
     500                        ids.add(prefEntry.id);
     501                }
     502                Collections.sort(ids);
     503            }
     504        }
     505        return ids;
     506    }
     507
     508    /**
     509     * Returns a tool tip text for display.
     510     * @return The text
     511     * @since 8065
     512     */
     513    public String getToolTipText() {
     514        StringBuilder res = new StringBuilder(getName());
     515        boolean html = false;
     516        String dateStr = getDate();
     517        if (dateStr != null && !dateStr.isEmpty()) {
     518            res.append("<br>").append(tr("Date of imagery: {0}", dateStr));
     519            html = true;
     520        }
     521        if (category != null && category.getDescription() != null) {
     522            res.append("<br>").append(tr("Imagery category: {0}", category.getDescription()));
     523            html = true;
     524        }
     525        String desc = getDescription();
     526        if (desc != null && !desc.isEmpty()) {
     527            res.append("<br>").append(Utils.escapeReservedCharactersHTML(desc));
     528            html = true;
     529        }
     530        if (html) {
     531            res.insert(0, "<html>").append("</html>");
     532        }
     533        return res.toString();
     534    }
     535
     536    /**
     537     * Returns the EULA acceptance URL, if any.
     538     * @return The URL to an EULA text that has to be accepted before use, or {@code null}
     539     */
     540    public String getEulaAcceptanceRequired() {
     541        return eulaAcceptanceRequired;
     542    }
     543
     544    /**
     545     * Sets the EULA acceptance URL.
     546     * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use
     547     */
     548    public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {
     549        this.eulaAcceptanceRequired = eulaAcceptanceRequired;
     550    }
     551
     552    /**
     553     * Returns the ISO 3166-1-alpha-2 country code.
     554     * @return The country code (2 letters)
     555     */
     556    public String getCountryCode() {
     557        return countryCode;
     558    }
     559
     560    /**
     561     * Sets the ISO 3166-1-alpha-2 country code.
     562     * @param countryCode The country code (2 letters)
     563     */
     564    public void setCountryCode(String countryCode) {
     565        this.countryCode = Utils.intern(countryCode);
     566    }
     567
     568    /**
     569     * Returns the date information.
     570     * @return The date (in the form YYYY-MM-DD;YYYY-MM-DD, where
     571     * DD and MM as well as a second date are optional)
     572     * @since 11570
     573     */
     574    public String getDate() {
     575        return date;
     576    }
     577
     578    /**
     579     * Sets the date information.
     580     * @param date The date information
     581     * @since 11570
     582     */
     583    public void setDate(String date) {
     584        this.date = date;
     585    }
     586
     587    /**
     588     * Returns the entry icon.
     589     * @return The entry icon
     590     */
     591    public String getIcon() {
     592        return icon;
     593    }
     594
     595    /**
     596     * Sets the entry icon.
     597     * @param icon The entry icon
     598     */
     599    public void setIcon(String icon) {
     600        this.icon = Utils.intern(icon);
     601    }
     602
     603    /**
     604     * Determines if this entry requires attribution.
     605     * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise
     606     */
     607    public boolean hasAttribution() {
     608        return attributionText != null;
     609    }
     610
     611    /**
     612     * Copies attribution from another {@code SourceInfo}.
     613     * @param i The other source info to get attribution from
     614     */
     615    public void copyAttribution(SourceInfo<T, U, V, W> i) {
     616        this.attributionImage = i.attributionImage;
     617        this.attributionImageURL = i.attributionImageURL;
     618        this.attributionText = i.attributionText;
     619        this.attributionLinkURL = i.attributionLinkURL;
     620        this.termsOfUseText = i.termsOfUseText;
     621        this.termsOfUseURL = i.termsOfUseURL;
     622    }
     623
     624    /**
     625     * Applies the attribution from this object to a tile source.
     626     * @param s The tile source
     627     */
     628    public void setAttribution(AbstractTileSource s) {
     629        if (attributionText != null) {
     630            if ("osm".equals(attributionText)) {
     631                s.setAttributionText(new Mapnik().getAttributionText(0, null, null));
     632            } else {
     633                s.setAttributionText(attributionText);
     634            }
     635        }
     636        if (attributionLinkURL != null) {
     637            if ("osm".equals(attributionLinkURL)) {
     638                s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL());
     639            } else {
     640                s.setAttributionLinkURL(attributionLinkURL);
     641            }
     642        }
     643        if (attributionImage != null) {
     644            ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage);
     645            if (i != null) {
     646                s.setAttributionImage(i.getImage());
     647            }
     648        }
     649        if (attributionImageURL != null) {
     650            s.setAttributionImageURL(attributionImageURL);
     651        }
     652        if (termsOfUseText != null) {
     653            s.setTermsOfUseText(termsOfUseText);
     654        }
     655        if (termsOfUseURL != null) {
     656            if ("osm".equals(termsOfUseURL)) {
     657                s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL());
     658            } else {
     659                s.setTermsOfUseURL(termsOfUseURL);
     660            }
     661        }
     662    }
     663
     664    /**
     665     * Returns the source type.
     666     * @return The source type
     667     */
     668    public U getSourceType() {
     669        return sourceType;
     670    }
     671
     672    /**
     673     * Sets the source type.
     674     * @param imageryType The source type
     675     */
     676    public void setSourceType(U imageryType) {
     677        this.sourceType = imageryType;
     678    }
     679
     680    /**
     681     * Returns the source category.
     682     * @return The source category
     683     */
     684    public T getSourceCategory() {
     685        return category;
     686    }
     687
     688    /**
     689     * Sets the source category.
     690     * @param category The source category
     691     */
     692    public void setSourceCategory(T category) {
     693        this.category = category;
     694    }
     695
     696    /**
     697     * Returns the source category original string (don't use except for error checks).
     698     * @return The source category original string
     699     */
     700    public String getSourceCategoryOriginalString() {
     701        return categoryOriginalString;
     702    }
     703
     704    /**
     705     * Sets the source category original string (don't use except for error checks).
     706     * @param categoryOriginalString The source category original string
     707     */
     708    public void setSourceCategoryOriginalString(String categoryOriginalString) {
     709        this.categoryOriginalString = Utils.intern(categoryOriginalString);
     710    }
     711
     712    /**
     713     * Returns true if this layer's URL is matched by one of the regular
     714     * expressions kept by the current OsmApi instance.
     715     * @return {@code true} is this entry is blacklisted, {@code false} otherwise
     716     */
     717    public boolean isBlacklisted() {
     718        Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
     719        return capabilities != null && capabilities.isOnImageryBlacklist(this.url);
     720    }
     721
     722    /**
     723     * Sets the map of &lt;header name, header value&gt; that if any of this header
     724     * will be returned, then this tile will be treated as "no tile at this zoom level"
     725     *
     726     * @param noTileHeaders Map of &lt;header name, header value&gt; which will be treated as "no tile at this zoom level"
     727     * @since 9613
     728     */
     729    public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) {
     730       if (noTileHeaders == null || noTileHeaders.isEmpty()) {
     731           this.noTileHeaders = null;
     732       } else {
     733            this.noTileHeaders = noTileHeaders.toMap();
     734       }
     735    }
     736
     737    @Override
     738    public Map<String, Set<String>> getNoTileHeaders() {
     739        return noTileHeaders;
     740    }
     741
     742    /**
     743     * Sets the map of &lt;checksum type, checksum value&gt; that if any tile with that checksum
     744     * will be returned, then this tile will be treated as "no tile at this zoom level"
     745     *
     746     * @param noTileChecksums Map of &lt;checksum type, checksum value&gt; which will be treated as "no tile at this zoom level"
     747     * @since 9613
     748     */
     749    public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) {
     750        if (noTileChecksums == null || noTileChecksums.isEmpty()) {
     751            this.noTileChecksums = null;
     752        } else {
     753            this.noTileChecksums = noTileChecksums.toMap();
     754        }
     755    }
     756
     757    @Override
     758    public Map<String, Set<String>> getNoTileChecksums() {
     759        return noTileChecksums;
     760    }
     761
     762    /**
     763     * Returns the map of &lt;header name, metadata key&gt; indicating, which HTTP headers should
     764     * be moved to metadata
     765     *
     766     * @param metadataHeaders map of &lt;header name, metadata key&gt; indicating, which HTTP headers should be moved to metadata
     767     * @since 8418
     768     */
     769    public void setMetadataHeaders(Map<String, String> metadataHeaders) {
     770        if (metadataHeaders == null || metadataHeaders.isEmpty()) {
     771            this.metadataHeaders = null;
     772        } else {
     773            this.metadataHeaders = metadataHeaders;
     774        }
     775    }
     776
     777    /**
     778     * Adds an old Id.
     779     *
     780     * @param id the Id to be added
     781     * @since 13536
     782     */
     783    public void addOldId(String id) {
     784       if (oldIds == null) {
     785           oldIds = new ArrayList<>();
     786       }
     787       oldIds.add(id);
     788    }
     789
     790    /**
     791     * Get old Ids.
     792     *
     793     * @return collection of ids
     794     * @since 13536
     795     */
     796    public Collection<String> getOldIds() {
     797        return oldIds;
     798    }
     799
     800    /**
     801     * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider)
     802     * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer
     803     * to work on
     804     * @return Collection of the layer names
     805     */
     806    public List<DefaultLayer> getDefaultLayers() {
     807        return defaultLayers;
     808    }
     809
     810    /**
     811     * Sets the default layers that user will work with
     812     * @param layers set the list of default layers
     813     */
     814    public void setDefaultLayers(List<DefaultLayer> layers) {
     815        this.defaultLayers = Utils.toUnmodifiableList(layers);
     816    }
     817
     818    /**
     819     * Returns custom HTTP headers that should be sent with request towards imagery provider
     820     * @return headers
     821     */
     822    public Map<String, String> getCustomHttpHeaders() {
     823        return customHttpHeaders;
     824    }
     825
     826    /**
     827     * Sets custom HTTP headers that should be sent with request towards imagery provider
     828     * @param customHttpHeaders http headers
     829     */
     830    public void setCustomHttpHeaders(Map<String, String> customHttpHeaders) {
     831        this.customHttpHeaders = Utils.toUnmodifiableMap(customHttpHeaders);
     832    }
     833}
  • src/org/openstreetmap/josm/data/sources/SourcePreferenceEntry.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.sources;
     3
     4import java.util.Map;
     5
     6import javax.json.stream.JsonCollectors;
     7
     8import org.openstreetmap.josm.data.StructUtils.StructEntry;
     9import org.openstreetmap.josm.data.imagery.DefaultLayer;
     10import org.openstreetmap.josm.data.imagery.Shape;
     11import org.openstreetmap.josm.tools.Utils;
     12
     13/**
     14 * A generic SourcePreferenceEntry that is used for storing data in JOSM preferences.
     15 * This is intended to be removed, at some point. User beware.
     16 *
     17 * @author Taylor Smock
     18 *
     19 * @param <T> The type of SourceInfo
     20 */
     21public abstract class SourcePreferenceEntry<T extends SourceInfo<?, ?, ?, ?>> {
     22    /** The name of the source */
     23    @StructEntry public String name;
     24    /** A *unique* id for the source */
     25    @StructEntry public String id;
     26    /** The type of the source (e.g., WMS, WMTS, etc.) */
     27    @StructEntry public String type;
     28    /** The URL for the source (base url) */
     29    @StructEntry public String url;
     30    /** The EULA for the source */
     31    @StructEntry public String eula;
     32    /** The attribution text for the source */
     33    @StructEntry public String attribution_text;
     34    /** The attribution URL for the source */
     35    @StructEntry public String attribution_url;
     36    /** The permission reference url (i.e., how do we know we have permission?) */
     37    @StructEntry public String permission_reference_url;
     38    /** The logo to be used for the source */
     39    @StructEntry public String logo_image;
     40    /** The logo url */
     41    @StructEntry public String logo_url;
     42    /** The TOU text */
     43    @StructEntry public String terms_of_use_text;
     44    /** The URL for the TOU */
     45    @StructEntry public String terms_of_use_url;
     46    /** The country code for the source (usually ISO 3166-1 alpha-2) */
     47    @StructEntry public String country_code = "";
     48    /** The date for the source */
     49    @StructEntry public String date;
     50    /** The cookies required to get the source */
     51    @StructEntry public String cookies;
     52    /** The bounds of the source */
     53    @StructEntry public String bounds;
     54    /** The shape of the source (mostly used for visual aid purposes) */
     55    @StructEntry public String shapes;
     56    /** The icon for the source (not necessarily the same as the logo) */
     57    @StructEntry public String icon;
     58    /** The description of the source */
     59    @StructEntry public String description;
     60    /** The default layers for the source, if any (mostly useful for imagery) */
     61    @StructEntry public String default_layers;
     62    /** Any custom HTTP headers */
     63    @StructEntry public Map<String, String> customHttpHeaders;
     64    /** The category string for the source */
     65    @StructEntry public String category;
     66
     67    /**
     68     * Constructs a new empty WMS {@code ImageryPreferenceEntry}.
     69     */
     70    public SourcePreferenceEntry() {
     71        // Do nothing
     72    }
     73
     74    /**
     75     * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}.
     76     * @param i The corresponding imagery info
     77     */
     78    public SourcePreferenceEntry(T i) {
     79        name = i.getName();
     80        id = i.getId();
     81        type = i.sourceType.getTypeString();
     82        url = i.getUrl();
     83        eula = i.eulaAcceptanceRequired;
     84        attribution_text = i.attributionText;
     85        attribution_url = i.attributionLinkURL;
     86        permission_reference_url = i.permissionReferenceURL;
     87        date = i.date;
     88        logo_image = i.attributionImage;
     89        logo_url = i.attributionImageURL;
     90        terms_of_use_text = i.termsOfUseText;
     91        terms_of_use_url = i.termsOfUseURL;
     92        country_code = i.countryCode;
     93        cookies = i.getCookies();
     94        icon = Utils.intern(i.icon);
     95        description = i.description;
     96        category = i.category != null ? i.category.getCategoryString() : null;
     97        if (i.bounds != null) {
     98            bounds = i.bounds.encodeAsString(",");
     99            StringBuilder shapesString = new StringBuilder();
     100            for (Shape s : i.bounds.getShapes()) {
     101                if (shapesString.length() > 0) {
     102                    shapesString.append(';');
     103                }
     104                shapesString.append(s.encodeAsString(","));
     105            }
     106            if (shapesString.length() > 0) {
     107                shapes = shapesString.toString();
     108            }
     109        }
     110
     111        if (!i.defaultLayers.isEmpty()) {
     112            default_layers = i.defaultLayers.stream().map(DefaultLayer::toJson).collect(JsonCollectors.toJsonArray()).toString();
     113        }
     114        customHttpHeaders = i.customHttpHeaders;
     115    }
     116
     117    @Override
     118    public String toString() {
     119        StringBuilder s = new StringBuilder(getClass().getSimpleName()).append(" [name=").append(name);
     120        if (id != null) {
     121            s.append(" id=").append(id);
     122        }
     123        s.append("]");
     124        return s.toString();
     125    }
     126}
  • src/org/openstreetmap/josm/gui/ImageryMenu.java

     
    183183                    .sorted(alphabeticImageryComparator)
    184184                    .collect(Collectors.toList());
    185185            if (!inViewLayers.isEmpty()) {
    186                 if (inViewLayers.stream().anyMatch(i -> i.getImageryCategory() == ImageryCategory.PHOTO)) {
     186                if (inViewLayers.stream().anyMatch(i -> i.getSourceCategory() == ImageryCategory.PHOTO)) {
    187187                    addDynamicSeparator();
    188188                }
    189189                for (ImageryInfo i : inViewLayers) {
    190                     addDynamic(trackJosmAction(new AddImageryLayerAction(i)), i.getImageryCategory());
     190                    addDynamic(trackJosmAction(new AddImageryLayerAction(i)), i.getSourceCategory());
    191191                }
    192192            }
    193193            if (!dynamicNonPhotoItems.isEmpty()) {
  • src/org/openstreetmap/josm/gui/layer/ImageryLayer.java

     
    121121        if (info != null) {
    122122            List<List<String>> content = new ArrayList<>();
    123123            content.add(Arrays.asList(tr("Name"), info.getName()));
    124             content.add(Arrays.asList(tr("Type"), info.getImageryType().getTypeString().toUpperCase(Locale.ENGLISH)));
     124            content.add(Arrays.asList(tr("Type"), info.getSourceType().getTypeString().toUpperCase(Locale.ENGLISH)));
    125125            content.add(Arrays.asList(tr("URL"), info.getUrl()));
    126126            content.add(Arrays.asList(tr("Id"), info.getId() == null ? "-" : info.getId()));
    127127            if (info.getMinZoom() != 0) {
     
    158158     * @return The created layer
    159159     */
    160160    public static ImageryLayer create(ImageryInfo info) {
    161         switch(info.getImageryType()) {
     161        switch(info.getSourceType()) {
    162162        case WMS:
    163163        case WMS_ENDPOINT:
    164164            return new WMSLayer(info);
     
    169169        case SCANEX:
    170170            return new TMSLayer(info);
    171171        default:
    172             throw new AssertionError(tr("Unsupported imagery type: {0}", info.getImageryType()));
     172            throw new AssertionError(tr("Unsupported imagery type: {0}", info.getSourceType()));
    173173        }
    174174    }
    175175
  • src/org/openstreetmap/josm/gui/layer/TMSLayer.java

     
    114114     * @throws IllegalArgumentException if url from imagery info is null or invalid
    115115     */
    116116    public static TMSTileSource getTileSourceStatic(ImageryInfo info, Runnable attributionLoadedTask) {
    117         if (info.getImageryType() == ImageryType.TMS) {
     117        if (info.getSourceType() == ImageryType.TMS) {
    118118            JosmTemplatedTMSTileSource.checkUrl(info.getUrl());
    119119            TMSTileSource t = new JosmTemplatedTMSTileSource(info);
    120120            info.setAttribution(t);
    121121            return t;
    122         } else if (info.getImageryType() == ImageryType.BING) {
     122        } else if (info.getSourceType() == ImageryType.BING) {
    123123            return new CachedAttributionBingAerialTileSource(info, attributionLoadedTask);
    124         } else if (info.getImageryType() == ImageryType.SCANEX) {
     124        } else if (info.getSourceType() == ImageryType.SCANEX) {
    125125            return new ScanexTileSource(info);
    126126        }
    127127        return null;
  • src/org/openstreetmap/josm/gui/layer/WMSLayer.java

     
    6565    public WMSLayer(ImageryInfo info) {
    6666        super(info);
    6767        CheckParameterUtil.ensureThat(
    68                 info.getImageryType() == ImageryType.WMS || info.getImageryType() == ImageryType.WMS_ENDPOINT, "ImageryType is WMS");
     68                info.getSourceType() == ImageryType.WMS || info.getSourceType() == ImageryType.WMS_ENDPOINT, "ImageryType is WMS");
    6969        CheckParameterUtil.ensureParameterNotNull(info.getUrl(), "info.url");
    70         if (info.getImageryType() == ImageryType.WMS) {
     70        if (info.getSourceType() == ImageryType.WMS) {
    7171            TemplatedWMSTileSource.checkUrl(info.getUrl());
    7272
    7373        }
     
    9393    @Override
    9494    protected AbstractWMSTileSource getTileSource() {
    9595        AbstractWMSTileSource tileSource;
    96         if (info.getImageryType() == ImageryType.WMS) {
     96        if (info.getSourceType() == ImageryType.WMS) {
    9797            tileSource = new TemplatedWMSTileSource(info, chooseProjection(ProjectionRegistry.getProjection()));
    9898        } else {
    9999            /*
  • src/org/openstreetmap/josm/gui/layer/WMTSLayer.java

     
    5757    @Override
    5858    protected WMTSTileSource getTileSource() {
    5959        try {
    60             if (info.getImageryType() == ImageryType.WMTS && info.getUrl() != null) {
     60            if (info.getSourceType() == ImageryType.WMTS && info.getUrl() != null) {
    6161                WMTSTileSource.checkUrl(info.getUrl());
    6262                WMTSTileSource tileSource = new WMTSTileSource(info);
    6363                info.setAttribution(tileSource);
  • src/org/openstreetmap/josm/gui/preferences/imagery/AddTMSLayerPanel.java

     
    7979    @Override
    8080    public ImageryInfo getImageryInfo() {
    8181        ImageryInfo ret = new ImageryInfo(getImageryName(), getTmsUrl());
    82         ret.setImageryType(ImageryType.TMS);
     82        ret.setSourceType(ImageryType.TMS);
    8383        return ret;
    8484
    8585    }
  • src/org/openstreetmap/josm/gui/preferences/imagery/AddWMSLayerPanel.java

     
    170170        ImageryInfo info = null;
    171171        if (endpoint.isSelected()) {
    172172            info = new ImageryInfo(getImageryName(), getImageryRawUrl());
    173             info.setImageryType(ImageryInfo.ImageryType.WMS_ENDPOINT);
     173            info.setSourceType(ImageryInfo.ImageryType.WMS_ENDPOINT);
    174174            if (setDefaultLayers.isSelected()) {
    175175                info.setDefaultLayers(tree.getSelectedLayers().stream()
    176176                        .map(x -> new DefaultLayer(
     
    189189            } else {
    190190                info = new ImageryInfo(getImageryName(), getWmsUrl());
    191191            }
    192             info.setImageryType(ImageryType.WMS);
     192            info.setSourceType(ImageryType.WMS);
    193193        }
    194194        info.setGeoreferenceValid(getCommonIsValidGeoreference());
    195195        info.setCustomHttpHeaders(getCommonHeaders());
  • src/org/openstreetmap/josm/gui/preferences/imagery/AddWMTSLayerPanel.java

     
    105105        }
    106106        ret.setCustomHttpHeaders(getCommonHeaders());
    107107        ret.setGeoreferenceValid(getCommonIsValidGeoreference());
    108         ret.setImageryType(ImageryType.WMTS);
     108        ret.setSourceType(ImageryType.WMTS);
    109109        try {
    110110            new WMTSTileSource(ret); // check if constructor throws an error
    111111        } catch (IOException | WMTSGetCapabilitiesException e) {
  • src/org/openstreetmap/josm/gui/preferences/imagery/ImageryProvidersPanel.java

     
    723723            ImageryInfo info = layerInfo.getAllDefaultLayers().get(row);
    724724            switch (column) {
    725725            case 0:
    726                 return Optional.ofNullable(info.getImageryCategory()).orElse(ImageryCategory.OTHER);
     726                return Optional.ofNullable(info.getSourceCategory()).orElse(ImageryCategory.OTHER);
    727727            case 1:
    728728                return info.getCountryCode();
    729729            case 2:
  • src/org/openstreetmap/josm/io/imagery/ImageryReader.java

     
    296296                if ("layer".equals(qName)) {
    297297                    newState = State.NOOP;
    298298                    defaultLayers.add(new DefaultLayer(
    299                             entry.getImageryType(),
     299                            entry.getSourceType(),
    300300                            atts.getValue("name"),
    301301                            atts.getValue("style"),
    302302                            atts.getValue("tile-matrix-set")
     
    362362                        boolean found = false;
    363363                        for (ImageryType type : ImageryType.values()) {
    364364                            if (Objects.equals(accumulator.toString(), type.getTypeString())) {
    365                                 mirrorEntry.setImageryType(type);
     365                                mirrorEntry.setSourceType(type);
    366366                                found = true;
    367367                                break;
    368368                            }
     
    433433                    entry.addOldId(accumulator.toString());
    434434                    break;
    435435                case "type":
    436                     ImageryType type = ImageryType.fromString(accumulator.toString());
     436                    ImageryType type = ImageryType.getFromString(accumulator.toString());
    437437                    if (type != null)
    438                         entry.setImageryType(type);
     438                        entry.setSourceType(type);
    439439                    else
    440440                        skipEntry = true;
    441441                    break;
     
    532532                    break;
    533533                case "category":
    534534                    String cat = accumulator.toString();
    535                     ImageryCategory category = ImageryCategory.fromString(cat);
     535                    ImageryCategory category = ImageryCategory.getFromString(cat);
    536536                    if (category != null)
    537                         entry.setImageryCategory(category);
    538                     entry.setImageryCategoryOriginalString(cat);
     537                        entry.setSourceCategory(category);
     538                    entry.setSourceCategoryOriginalString(cat);
    539539                    break;
    540540                default: // Do nothing
    541541                }
  • src/org/openstreetmap/josm/io/remotecontrol/handler/ImageryHandler.java

     
    4949
    5050    protected static ImageryInfo findBingEntry() {
    5151        for (ImageryInfo i : ImageryLayerInfo.instance.getDefaultLayers()) {
    52             if (ImageryType.BING == i.getImageryType()) {
     52            if (ImageryType.BING == i.getSourceType()) {
    5353                return i;
    5454            }
    5555        }
  • src/org/openstreetmap/josm/tools/Utils.java

     
    6969import javax.script.ScriptEngine;
    7070import javax.script.ScriptEngineManager;
    7171
    72 import com.kitfox.svg.xml.XMLParseUtil;
    7372import org.openstreetmap.josm.spi.preferences.Config;
    7473
     74import com.kitfox.svg.xml.XMLParseUtil;
     75
    7576/**
    7677 * Basic utils, that can be useful in different parts of the program.
    7778 */
     
    20122013        // remove extra whitespaces
    20132014        return rawString.trim();
    20142015    }
     2016
     2017    /**
     2018     * Intern a string
     2019     * @param string The string to intern
     2020     * @return The interned string
     2021     * @since xxx
     2022     */
     2023    public static String intern(String string) {
     2024        return string == null ? null : string.intern();
     2025    }
    20152026}
  • test/unit/org/openstreetmap/josm/data/imagery/ImageryInfoTest.java

     
    4848    @Test
    4949    public void testConstruct13264() {
    5050        final ImageryInfo info = new ImageryInfo("test imagery", "tms[16-23]:http://localhost");
    51         assertEquals(ImageryInfo.ImageryType.TMS, info.getImageryType());
     51        assertEquals(ImageryInfo.ImageryType.TMS, info.getSourceType());
    5252        assertEquals(16, info.getMinZoom());
    5353        assertEquals(23, info.getMaxZoom());
    5454        assertEquals("http://localhost", info.getUrl());
  • test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java

     
    7474                    "test",
    7575                    new File(path).toURI().toURL().toString()
    7676                    );
    77             ret.setImageryType(ImageryType.WMTS);
     77            ret.setSourceType(ImageryType.WMTS);
    7878            return ret;
    7979        } catch (MalformedURLException e) {
    8080            e.printStackTrace();
  • test/unit/org/openstreetmap/josm/gui/layer/TMSLayerTest.java

     
    5151    private static void test(ImageryType expected, TMSLayer layer) {
    5252        try {
    5353            MainApplication.getLayerManager().addLayer(layer);
    54             assertEquals(expected, layer.getInfo().getImageryType());
     54            assertEquals(expected, layer.getInfo().getSourceType());
    5555        } finally {
    5656            // Ensure we clean the place before leaving, even if test fails.
    5757            MainApplication.getLayerManager().removeLayer(layer);
  • test/unit/org/openstreetmap/josm/gui/layer/WMSLayerTest.java

     
    3232        WMSLayer wms = new WMSLayer(new ImageryInfo("test wms", "http://localhost"));
    3333        MainApplication.getLayerManager().addLayer(wms);
    3434        try {
    35             assertEquals(ImageryType.WMS, wms.getInfo().getImageryType());
     35            assertEquals(ImageryType.WMS, wms.getInfo().getSourceType());
    3636        } finally {
    3737            // Ensure we clean the place before leaving, even if test fails.
    3838            MainApplication.getLayerManager().removeLayer(wms);
  • test/unit/org/openstreetmap/josm/gui/layer/WMTSLayerTest.java

     
    2929    @Test
    3030    public void testWMTSLayer() {
    3131        WMTSLayer wmts = new WMTSLayer(new ImageryInfo("test wmts", "http://localhost", "wmts", null, null));
    32         assertEquals(ImageryType.WMTS, wmts.getInfo().getImageryType());
     32        assertEquals(ImageryType.WMTS, wmts.getInfo().getSourceType());
    3333    }
    3434}
  • test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java

     
    334334                }
    335335            }
    336336            // checking max zoom for real is complex, see https://josm.openstreetmap.de/ticket/16073#comment:27
    337             if (info.getMaxZoom() > 0 && info.getImageryType() != ImageryType.SCANEX) {
     337            if (info.getMaxZoom() > 0 && info.getSourceType() != ImageryType.SCANEX) {
    338338                checkTileUrl(info, tileSource, center, Utils.clamp(DEFAULT_ZOOM, info.getMinZoom() + 1, info.getMaxZoom()));
    339339            }
    340340        } catch (IOException | RuntimeException | WMSGetCapabilitiesException | WMTSGetCapabilitiesException e) {
     
    366366    @SuppressWarnings("fallthrough")
    367367    private static AbstractTileSource getTileSource(ImageryInfo info)
    368368            throws IOException, WMTSGetCapabilitiesException, WMSGetCapabilitiesException {
    369         switch (info.getImageryType()) {
     369        switch (info.getSourceType()) {
    370370            case BING:
    371371                return new BingAerialTileSource(info);
    372372            case SCANEX: