Ticket #19026: 19026.3.patch

File 19026.3.patch, 115.0 KB (added by taylor.smock, 4 years ago)

Update to JOSM 16539, keep new streams, fix some checkstyle issues.

  • scripts/SyncEditorLayerIndex.java

     
    13711371    }
    13721372
    13731373    static String getType(Object e) {
    1374         if (e instanceof ImageryInfo) return ((ImageryInfo) e).getImageryType().getTypeString();
     1374        if (e instanceof ImageryInfo) return ((ImageryInfo) e).getSourceType().getTypeString();
    13751375        return ((Map<String, JsonObject>) e).get("properties").getString("type");
    13761376    }
    13771377
     
    14491449
    14501450    static String getCategory(Object e) {
    14511451        if (e instanceof ImageryInfo) {
    1452             return ((ImageryInfo) e).getImageryCategoryOriginalString();
     1452            return ((ImageryInfo) e).getSourceCategoryOriginalString();
    14531453        }
    14541454        return ((Map<String, JsonObject>) e).get("properties").getString("category", null);
    14551455    }
  • 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

     
    151151        }
    152152
    153153        HashMap<String, String> hash = new LinkedHashMap<>();
    154         for (Field f : klass.getDeclaredFields()) {
     154        for (Field f : getDeclaredFieldsInClassOrSuperTypes(klass)) {
    155155            if (f.getAnnotation(StructEntry.class) == null) {
    156156                continue;
    157157            }
     
    200200        }
    201201        for (Map.Entry<String, String> keyValue : hash.entrySet()) {
    202202            Object value;
    203             Field f;
    204             try {
    205                 f = klass.getDeclaredField(keyValue.getKey().replace('-', '_'));
    206             } catch (NoSuchFieldException ex) {
    207                 Logging.trace(ex);
     203            Field f = getDeclaredFieldInClassOrSuperTypes(klass, keyValue.getKey().replace('-', '_'));
     204
     205            if (f == null || f.getAnnotation(StructEntry.class) == null) {
    208206                continue;
    209207            }
    210             if (f.getAnnotation(StructEntry.class) == null) {
    211                 continue;
    212             }
    213208            ReflectionUtils.setObjectsAccessible(f);
    214209            if (f.getType() == Boolean.class || f.getType() == boolean.class) {
    215210                value = Boolean.valueOf(keyValue.getValue());
     
    245240        return struct;
    246241    }
    247242
     243    private static <T> Field getDeclaredFieldInClassOrSuperTypes(Class<T> clazz, String fieldName) {
     244        Class<?> tClass = clazz;
     245        do {
     246            try {
     247                Field f = tClass.getDeclaredField(fieldName);
     248                return f;
     249            } catch (NoSuchFieldException ex) {
     250                Logging.trace(ex);
     251            }
     252            tClass = tClass.getSuperclass();
     253        } while (tClass != null);
     254        return null;
     255    }
     256
     257    private static <T> Field[] getDeclaredFieldsInClassOrSuperTypes(Class<T> clazz) {
     258        List<Field> fields = new ArrayList<>();
     259        Class<?> tclass = clazz;
     260        do {
     261            Collections.addAll(fields, tclass.getDeclaredFields());
     262            tclass = tclass.getSuperclass();
     263        } while (tclass != null);
     264        return fields.toArray(new Field[] {});
     265    }
     266
    248267    @SuppressWarnings("rawtypes")
    249268    private static String mapToJson(Map map) {
    250269        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.StreamUtils;
     
    5543 *
    5644 * @author Frederik Ramm
    5745 */
    58 public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed {
     46public class ImageryInfo extends
     47        SourceInfo<ImageryInfo.ImageryCategory, ImageryInfo.ImageryType, ImageryInfo.ImageryBounds, ImageryInfo.ImageryPreferenceEntry> {
    5948
    6049    /**
    6150     * Type of imagery entry.
    6251     */
    63     public enum ImageryType {
     52    public enum ImageryType implements ISourceType<ImageryType> {
    6453        /** A WMS (Web Map Service) entry. **/
    6554        WMS("wms"),
    6655        /** A TMS (Tile Map Service) entry. **/
     
    8574         * @return the unique string identifying this type
    8675         * @since 6690
    8776         */
     77        @Override
    8878        public final String getTypeString() {
    8979            return typeString;
    9080        }
     
    9484         * @param s The type string
    9585         * @return the imagery type matching the given type string
    9686         */
    97         public static ImageryType fromString(String s) {
     87        public static ImageryType getFromString(String s) {
    9888            return Arrays.stream(ImageryType.values())
    9989                    .filter(type -> type.getTypeString().equals(s))
    10090                    .findFirst().orElse(null);
    10191        }
     92
     93        @Override
     94        public ImageryType fromString(String s) {
     95            return getFromString(s);
     96        }
     97
     98        @Override
     99        public ImageryType getDefault() {
     100            return WMS;
     101        }
    102102    }
    103103
    104104    /**
     
    105105     * Category of imagery entry.
    106106     * @since 13792
    107107     */
    108     public enum ImageryCategory {
     108    public enum ImageryCategory implements ISourceCategory<ImageryCategory> {
    109109        /** A aerial or satellite photo. **/
    110110        PHOTO(/* ICON(data/imagery/) */ "photo", tr("Aerial or satellite photo")),
    111111        /** A map of digital terrain model, digital surface model or contour lines. **/
     
    137137         * Returns the unique string identifying this category.
    138138         * @return the unique string identifying this category
    139139         */
     140        @Override
    140141        public final String getCategoryString() {
    141142            return category;
    142143        }
     
    145146         * Returns the description of this category.
    146147         * @return the description of this category
    147148         */
     149        @Override
    148150        public final String getDescription() {
    149151            return description;
    150152        }
     
    155157         * @return the category icon at the given size
    156158         * @since 15049
    157159         */
     160        @Override
    158161        public final ImageIcon getIcon(ImageSizes size) {
    159162            return iconCache
    160163                    .computeIfAbsent(size, x -> Collections.synchronizedMap(new EnumMap<>(ImageryCategory.class)))
     
    166169         * @param s The category string
    167170         * @return the imagery category matching the given category string
    168171         */
    169         public static ImageryCategory fromString(String s) {
     172        public static ImageryCategory getFromString(String s) {
    170173            return Arrays.stream(ImageryCategory.values())
    171174                    .filter(category -> category.getCategoryString().equals(s))
    172175                    .findFirst().orElse(null);
    173176        }
     177
     178        @Override
     179        public ImageryCategory getDefault() {
     180            return OTHER;
     181        }
     182
     183        @Override
     184        public ImageryCategory fromString(String s) {
     185            return getFromString(s);
     186        }
    174187    }
    175188
    176189    /**
     
    177190     * Multi-polygon bounds for imagery backgrounds.
    178191     * Used to display imagery coverage in preferences and to determine relevant imagery entries based on edit location.
    179192     */
    180     public static class ImageryBounds extends Bounds {
     193    public static class ImageryBounds extends SourceBounds {
    181194
    182195        /**
    183196         * Constructs a new {@code ImageryBounds} from string.
     
    187200        public ImageryBounds(String asString, String separator) {
    188201            super(asString, separator);
    189202        }
    190 
    191         private List<Shape> shapes = new ArrayList<>();
    192 
    193         /**
    194          * Adds a new shape to this bounds.
    195          * @param shape The shape to add
    196          */
    197         public final void addShape(Shape shape) {
    198             this.shapes.add(shape);
    199         }
    200 
    201         /**
    202          * Sets the list of shapes defining this bounds.
    203          * @param shapes The list of shapes defining this bounds.
    204          */
    205         public final void setShapes(List<Shape> shapes) {
    206             this.shapes = shapes;
    207         }
    208 
    209         /**
    210          * Returns the list of shapes defining this bounds.
    211          * @return The list of shapes defining this bounds
    212          */
    213         public final List<Shape> getShapes() {
    214             return shapes;
    215         }
    216 
    217         @Override
    218         public int hashCode() {
    219             return Objects.hash(super.hashCode(), shapes);
    220         }
    221 
    222         @Override
    223         public boolean equals(Object o) {
    224             if (this == o) return true;
    225             if (o == null || getClass() != o.getClass()) return false;
    226             if (!super.equals(o)) return false;
    227             ImageryBounds that = (ImageryBounds) o;
    228             return Objects.equals(shapes, that.shapes);
    229         }
    230203    }
    231204
    232     /** original name of the imagery entry in case of translation call, for multiple languages English when possible */
    233     private String origName;
    234     /** (original) language of the translated name entry */
    235     private String langName;
    236     /** whether this is a entry activated by default or not */
    237     private boolean defaultEntry;
    238     /** Whether this service requires a explicit EULA acceptance before it can be activated */
    239     private String eulaAcceptanceRequired;
    240     /** type of the imagery servics - WMS, TMS, ... */
    241     private ImageryType imageryType = ImageryType.WMS;
    242205    private double pixelPerDegree;
    243206    /** maximum zoom level for TMS imagery */
    244207    private int defaultMaxZoom;
    245208    /** minimum zoom level for TMS imagery */
    246209    private int defaultMinZoom;
    247     /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */
    248     private ImageryBounds bounds;
    249210    /** projections supported by WMS servers */
    250211    private List<String> serverProjections = Collections.emptyList();
    251     /** description of the imagery entry, should contain notes what type of data it is */
    252     private String description;
    253     /** language of the description entry */
    254     private String langDescription;
    255     /** Text of a text attribution displayed when using the imagery */
    256     private String attributionText;
    257     /** Link to the privacy policy of the operator */
    258     private String privacyPolicyURL;
    259     /** Link to a reference stating the permission for OSM usage */
    260     private String permissionReferenceURL;
    261     /** Link behind the text attribution displayed when using the imagery */
    262     private String attributionLinkURL;
    263     /** Image of a graphical attribution displayed when using the imagery */
    264     private String attributionImage;
    265     /** Link behind the graphical attribution displayed when using the imagery */
    266     private String attributionImageURL;
    267     /** Text with usage terms displayed when using the imagery */
    268     private String termsOfUseText;
    269     /** Link behind the text with usage terms displayed when using the imagery */
    270     private String termsOfUseURL;
    271     /** country code of the imagery (for country specific imagery) */
    272     private String countryCode = "";
    273212    /**
    274       * creation date of the imagery (in the form YYYY-MM-DD;YYYY-MM-DD, where
    275       * DD and MM as well as a second date are optional).
    276       *
    277       * Also used as time filter for WMS time={time} parameter (such as Sentinel-2)
    278       * @since 11570
    279       */
    280     private String date;
    281     /**
    282213      * marked as best in other editors
    283214      * @since 11575
    284215      */
     
    288219      * @since 13536
    289220      */
    290221    private boolean overlay;
     222
     223    /** mirrors of different type for this entry */
     224    protected List<ImageryInfo> mirrors;
    291225    /**
    292       * list of old IDs, only for loading, not handled anywhere else
    293       * @since 13536
    294       */
    295     private Collection<String> oldIds;
    296     /** mirrors of different type for this entry */
    297     private List<ImageryInfo> mirrors;
    298     /** icon used in menu */
    299     private String icon;
     226     * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
     227     */
    300228    /** is the geo reference correct - don't offer offset handling */
    301229    private boolean isGeoreferenceValid;
    302     /** which layers should be activated by default on layer addition. **/
    303     private List<DefaultLayer> defaultLayers = Collections.emptyList();
    304     /** HTTP headers **/
    305     private Map<String, String> customHttpHeaders = Collections.emptyMap();
    306230    /** Should this map be transparent **/
    307231    private boolean transparent = true;
    308232    private int minimumTileExpire = (int) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get());
    309     /** category of the imagery */
    310     private ImageryCategory category;
    311     /** category of the imagery (input string, not saved, copied or used otherwise except for error checks) */
    312     private String categoryOriginalString;
    313     /** when adding a field, also adapt the:
    314      * {@link #ImageryPreferenceEntry ImageryPreferenceEntry object}
    315      * {@link #ImageryPreferenceEntry#ImageryPreferenceEntry(ImageryInfo) ImageryPreferenceEntry constructor}
    316      * {@link #ImageryInfo(ImageryPreferenceEntry) ImageryInfo constructor}
    317      * {@link #ImageryInfo(ImageryInfo) ImageryInfo constructor}
    318      * {@link #equalsPref(ImageryPreferenceEntry) equalsPref method}
    319      **/
    320233
    321234    /**
    322      * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
     235     * The ImageryPreferenceEntry class for storing data in JOSM preferences.
     236     *
     237     * @author Frederik Ramm, modified by Taylor Smock
    323238     */
    324     public static class ImageryPreferenceEntry {
    325         @StructEntry String name;
     239    public static class ImageryPreferenceEntry extends SourcePreferenceEntry<ImageryInfo> {
    326240        @StructEntry String d;
    327         @StructEntry String id;
    328         @StructEntry String type;
    329         @StructEntry String url;
    330241        @StructEntry double pixel_per_eastnorth;
    331         @StructEntry String eula;
    332         @StructEntry String attribution_text;
    333         @StructEntry String attribution_url;
    334         @StructEntry String permission_reference_url;
    335         @StructEntry String logo_image;
    336         @StructEntry String logo_url;
    337         @StructEntry String terms_of_use_text;
    338         @StructEntry String terms_of_use_url;
    339         @StructEntry String country_code = "";
    340         @StructEntry String date;
    341242        @StructEntry int max_zoom;
    342243        @StructEntry int min_zoom;
    343         @StructEntry String cookies;
    344         @StructEntry String bounds;
    345         @StructEntry String shapes;
    346244        @StructEntry String projections;
    347         @StructEntry String icon;
    348         @StructEntry String description;
    349245        @StructEntry MultiMap<String, String> noTileHeaders;
    350246        @StructEntry MultiMap<String, String> noTileChecksums;
    351247        @StructEntry int tileSize = -1;
     
    354250        @StructEntry boolean bestMarked;
    355251        @StructEntry boolean modTileFeatures;
    356252        @StructEntry boolean overlay;
    357         @StructEntry String default_layers;
    358         @StructEntry Map<String, String> customHttpHeaders;
    359253        @StructEntry boolean transparent;
    360254        @StructEntry int minimumTileExpire;
    361         @StructEntry String category;
    362255
    363256        /**
    364257         * Constructs a new empty WMS {@code ImageryPreferenceEntry}.
    365258         */
    366259        public ImageryPreferenceEntry() {
    367             // Do nothing
     260            super();
    368261        }
    369262
    370263        /**
     
    372265         * @param i The corresponding imagery info
    373266         */
    374267        public ImageryPreferenceEntry(ImageryInfo i) {
    375             name = i.name;
    376             id = i.id;
    377             type = i.imageryType.getTypeString();
    378             url = i.url;
     268            super(i);
    379269            pixel_per_eastnorth = i.pixelPerDegree;
    380             eula = i.eulaAcceptanceRequired;
    381             attribution_text = i.attributionText;
    382             attribution_url = i.attributionLinkURL;
    383             permission_reference_url = i.permissionReferenceURL;
    384             date = i.date;
    385270            bestMarked = i.bestMarked;
    386271            overlay = i.overlay;
    387             logo_image = i.attributionImage;
    388             logo_url = i.attributionImageURL;
    389             terms_of_use_text = i.termsOfUseText;
    390             terms_of_use_url = i.termsOfUseURL;
    391             country_code = i.countryCode;
    392272            max_zoom = i.defaultMaxZoom;
    393273            min_zoom = i.defaultMinZoom;
    394             cookies = i.cookies;
    395             icon = intern(i.icon);
    396             description = i.description;
    397             category = i.category != null ? i.category.getCategoryString() : null;
    398             if (i.bounds != null) {
    399                 bounds = i.bounds.encodeAsString(",");
    400                 String shapesString = Shape.encodeAsString(i.bounds.getShapes());
    401                 if (!shapesString.isEmpty()) {
    402                     shapes = shapesString;
    403                 }
    404             }
    405274            if (!i.serverProjections.isEmpty()) {
    406275                projections = String.join(",", i.serverProjections);
    407276            }
     
    421290
    422291            valid_georeference = i.isGeoreferenceValid();
    423292            modTileFeatures = i.isModTileFeatures();
    424             if (!i.defaultLayers.isEmpty()) {
    425                 default_layers = i.defaultLayers.stream().map(DefaultLayer::toJson).collect(JsonCollectors.toJsonArray()).toString();
    426             }
    427             customHttpHeaders = i.customHttpHeaders;
    428293            transparent = i.isTransparent();
    429294            minimumTileExpire = i.minimumTileExpire;
    430295        }
     
    489354    public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) {
    490355        this(name);
    491356        setExtendedUrl(url);
    492         ImageryType t = ImageryType.fromString(type);
     357        ImageryType t = ImageryType.getFromString(type);
    493358        this.cookies = cookies;
    494359        this.eulaAcceptanceRequired = eulaAcceptanceRequired;
    495360        if (t != null) {
    496             this.imageryType = t;
     361            this.sourceType = t;
    497362        } else if (type != null && !type.isEmpty()) {
    498363            throw new IllegalArgumentException("unknown type: "+type);
    499364        }
     
    525390        description = e.description;
    526391        cookies = e.cookies;
    527392        eulaAcceptanceRequired = e.eula;
    528         imageryType = ImageryType.fromString(e.type);
    529         if (imageryType == null) throw new IllegalArgumentException("unknown type");
     393        sourceType = ImageryType.getFromString(e.type);
     394        if (sourceType == null) throw new IllegalArgumentException("unknown type");
    530395        pixelPerDegree = e.pixel_per_eastnorth;
    531396        defaultMaxZoom = e.max_zoom;
    532397        defaultMinZoom = e.min_zoom;
     
    546411            // split generates null element on empty string which gives one element Array[null]
    547412            setServerProjections(Arrays.asList(e.projections.split(",")));
    548413        }
    549         attributionText = intern(e.attribution_text);
     414        attributionText = Utils.intern(e.attribution_text);
    550415        attributionLinkURL = e.attribution_url;
    551416        permissionReferenceURL = e.permission_reference_url;
    552417        attributionImage = e.logo_image;
     
    556421        overlay = e.overlay;
    557422        termsOfUseText = e.terms_of_use_text;
    558423        termsOfUseURL = e.terms_of_use_url;
    559         countryCode = intern(e.country_code);
    560         icon = intern(e.icon);
     424        countryCode = Utils.intern(e.country_code);
     425        icon = Utils.intern(e.icon);
    561426        if (e.noTileHeaders != null) {
    562427            noTileHeaders = e.noTileHeaders.toMap();
    563428        }
     
    573438                defaultLayers = jsonReader.
    574439                        readArray().
    575440                        stream().
    576                         map(x -> DefaultLayer.fromJson((JsonObject) x, imageryType)).
     441                        map(x -> DefaultLayer.fromJson((JsonObject) x, sourceType)).
    577442                        collect(Collectors.toList());
    578443            }
    579444        }
     
    580445        setCustomHttpHeaders(e.customHttpHeaders);
    581446        transparent = e.transparent;
    582447        minimumTileExpire = e.minimumTileExpire;
    583         category = ImageryCategory.fromString(e.category);
     448        category = ImageryCategory.getFromString(e.category);
    584449    }
    585450
    586451    /**
     
    602467        this.langName = i.langName;
    603468        this.defaultEntry = i.defaultEntry;
    604469        this.eulaAcceptanceRequired = null;
    605         this.imageryType = i.imageryType;
     470        this.sourceType = i.sourceType;
    606471        this.pixelPerDegree = i.pixelPerDegree;
    607472        this.defaultMaxZoom = i.defaultMaxZoom;
    608473        this.defaultMinZoom = i.defaultMinZoom;
     
    623488        this.bestMarked = i.bestMarked;
    624489        this.overlay = i.overlay;
    625490        // do not copy field {@code mirrors}
    626         this.icon = intern(i.icon);
     491        this.icon = Utils.intern(i.icon);
    627492        this.isGeoreferenceValid = i.isGeoreferenceValid;
    628493        setDefaultLayers(i.defaultLayers);
    629494        setCustomHttpHeaders(i.customHttpHeaders);
    630495        this.transparent = i.transparent;
    631496        this.minimumTileExpire = i.minimumTileExpire;
    632         this.categoryOriginalString = intern(i.categoryOriginalString);
     497        this.categoryOriginalString = Utils.intern(i.categoryOriginalString);
    633498        this.category = i.category;
    634499    }
    635500
    636     @Override
    637     public int hashCode() {
    638         return Objects.hash(url, imageryType);
     501    /**
     502     * Adds a mirror entry. Mirror entries are completed with the data from the master entry
     503     * and only describe another method to access identical data.
     504     *
     505     * @param entry the mirror to be added
     506     * @since 9658
     507     */
     508    public void addMirror(ImageryInfo entry) {
     509       if (mirrors == null) {
     510           mirrors = new ArrayList<>();
     511       }
     512       mirrors.add(entry);
    639513    }
    640514
    641515    /**
     516     * Returns the mirror entries. Entries are completed with master entry data.
     517     *
     518     * @return the list of mirrors
     519     * @since 9658
     520     */
     521    public List<ImageryInfo> getMirrors() {
     522       List<ImageryInfo> l = new ArrayList<>();
     523       if (mirrors != null) {
     524           int num = 1;
     525           for (ImageryInfo i : mirrors) {
     526               ImageryInfo n = new ImageryInfo(this);
     527               if (i.defaultMaxZoom != 0) {
     528                   n.defaultMaxZoom = i.defaultMaxZoom;
     529               }
     530               if (i.defaultMinZoom != 0) {
     531                   n.defaultMinZoom = i.defaultMinZoom;
     532               }
     533               n.setServerProjections(i.getServerProjections());
     534               n.url = i.url;
     535               n.sourceType = i.sourceType;
     536               if (i.getTileSize() != 0) {
     537                   n.setTileSize(i.getTileSize());
     538               }
     539               if (i.getPrivacyPolicyURL() != null) {
     540                   n.setPrivacyPolicyURL(i.getPrivacyPolicyURL());
     541               }
     542               if (n.id != null) {
     543                   n.id = n.id + "_mirror"+num;
     544               }
     545               if (num > 1) {
     546                   n.name = tr("{0} mirror server {1}", n.name, num);
     547                   if (n.origName != null) {
     548                       n.origName += " mirror server " + num;
     549                   }
     550               } else {
     551                   n.name = tr("{0} mirror server", n.name);
     552                   if (n.origName != null) {
     553                       n.origName += " mirror server";
     554                   }
     555               }
     556               l.add(n);
     557               ++num;
     558           }
     559       }
     560       return l;
     561    }
     562
     563    /**
    642564     * Check if this object equals another ImageryInfo with respect to the properties
    643565     * that get written to the preference file.
    644566     *
     
    647569     * @param other the ImageryInfo object to compare to
    648570     * @return true if they are equal
    649571     */
    650     public boolean equalsPref(ImageryInfo other) {
    651         if (other == null) {
     572    @Override
     573    public boolean equalsPref(SourceInfo<ImageryInfo.ImageryCategory, ImageryInfo.ImageryType,
     574            ImageryInfo.ImageryBounds, ImageryInfo.ImageryPreferenceEntry> other) {
     575        if (!(other instanceof ImageryInfo)) {
    652576            return false;
    653577        }
     578        ImageryInfo realOther = (ImageryInfo) other;
    654579
    655580        // CHECKSTYLE.OFF: BooleanExpressionComplexity
    656         return
    657                 Objects.equals(this.name, other.name) &&
    658                 Objects.equals(this.id, other.id) &&
    659                 Objects.equals(this.url, other.url) &&
    660                 Objects.equals(this.modTileFeatures, other.modTileFeatures) &&
    661                 Objects.equals(this.bestMarked, other.bestMarked) &&
    662                 Objects.equals(this.overlay, other.overlay) &&
    663                 Objects.equals(this.isGeoreferenceValid, other.isGeoreferenceValid) &&
    664                 Objects.equals(this.cookies, other.cookies) &&
    665                 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) &&
    666                 Objects.equals(this.imageryType, other.imageryType) &&
    667                 Objects.equals(this.defaultMaxZoom, other.defaultMaxZoom) &&
    668                 Objects.equals(this.defaultMinZoom, other.defaultMinZoom) &&
    669                 Objects.equals(this.bounds, other.bounds) &&
    670                 Objects.equals(this.serverProjections, other.serverProjections) &&
    671                 Objects.equals(this.attributionText, other.attributionText) &&
    672                 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) &&
    673                 Objects.equals(this.permissionReferenceURL, other.permissionReferenceURL) &&
    674                 Objects.equals(this.attributionImageURL, other.attributionImageURL) &&
    675                 Objects.equals(this.attributionImage, other.attributionImage) &&
    676                 Objects.equals(this.termsOfUseText, other.termsOfUseText) &&
    677                 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) &&
    678                 Objects.equals(this.countryCode, other.countryCode) &&
    679                 Objects.equals(this.date, other.date) &&
    680                 Objects.equals(this.icon, other.icon) &&
    681                 Objects.equals(this.description, other.description) &&
    682                 Objects.equals(this.noTileHeaders, other.noTileHeaders) &&
    683                 Objects.equals(this.noTileChecksums, other.noTileChecksums) &&
    684                 Objects.equals(this.metadataHeaders, other.metadataHeaders) &&
    685                 Objects.equals(this.defaultLayers, other.defaultLayers) &&
    686                 Objects.equals(this.customHttpHeaders, other.customHttpHeaders) &&
    687                 Objects.equals(this.transparent, other.transparent) &&
    688                 Objects.equals(this.minimumTileExpire, other.minimumTileExpire) &&
    689                 Objects.equals(this.category, other.category);
     581        return super.equalsPref(realOther) &&
     582                Objects.equals(this.bestMarked, realOther.bestMarked) &&
     583                Objects.equals(this.overlay, realOther.overlay) &&
     584                Objects.equals(this.isGeoreferenceValid, realOther.isGeoreferenceValid) &&
     585                Objects.equals(this.defaultMaxZoom, realOther.defaultMaxZoom) &&
     586                Objects.equals(this.defaultMinZoom, realOther.defaultMinZoom) &&
     587                Objects.equals(this.serverProjections, realOther.serverProjections) &&
     588                Objects.equals(this.transparent, realOther.transparent) &&
     589                Objects.equals(this.minimumTileExpire, realOther.minimumTileExpire);
    690590        // CHECKSTYLE.ON: BooleanExpressionComplexity
    691591    }
    692592
    693593    @Override
    694     public boolean equals(Object o) {
    695         if (this == o) return true;
    696         if (o == null || getClass() != o.getClass()) return false;
    697         ImageryInfo that = (ImageryInfo) o;
    698         return imageryType == that.imageryType && Objects.equals(url, that.url);
    699     }
    700 
    701     private static final Map<String, String> localizedCountriesCache = new HashMap<>();
    702     static {
    703         localizedCountriesCache.put("", tr("Worldwide"));
    704     }
    705 
    706     /**
    707      * Returns a localized name for the given country code, or "Worldwide" if empty.
    708      * This function falls back on the English name, and uses the ISO code as a last-resortvalue.
    709      *
    710      * @param countryCode An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code
    711      * @return The name of the country appropriate to the current locale.
    712      * @see Locale#getDisplayCountry
    713      * @since 15158
    714      */
    715     public static String getLocalizedCountry(String countryCode) {
    716         return localizedCountriesCache.computeIfAbsent(countryCode, code -> new Locale("en", code).getDisplayCountry());
    717     }
    718 
    719     @Override
    720     public String toString() {
    721         // Used in imagery preferences filtering, so must be efficient
    722         return new StringBuilder(name)
    723                 .append('[').append(countryCode)
    724                 // appending the localized country in toString() allows us to filter imagery preferences table with it!
    725                 .append("] ('").append(getLocalizedCountry(countryCode)).append(')')
    726                 .append(" - ").append(url)
    727                 .append(" - ").append(imageryType)
    728                 .toString();
    729     }
    730 
    731     @Override
    732     public int compareTo(ImageryInfo in) {
    733         int i = countryCode.compareTo(in.countryCode);
    734         if (i == 0) {
    735             i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH));
     594    public int compareTo(SourceInfo<ImageryInfo.ImageryCategory, ImageryInfo.ImageryType,
     595            ImageryInfo.ImageryBounds, ImageryInfo.ImageryPreferenceEntry> other) {
     596        int i = super.compareTo(other);
     597        if (other instanceof ImageryInfo) {
     598            ImageryInfo in = (ImageryInfo) other;
     599            if (i == 0) {
     600                i = Double.compare(pixelPerDegree, in.pixelPerDegree);
     601            }
    736602        }
    737         if (i == 0) {
    738             i = url.compareTo(in.url);
    739         }
    740         if (i == 0) {
    741             i = Double.compare(pixelPerDegree, in.pixelPerDegree);
    742         }
    743603        return i;
    744604    }
    745605
    746606    /**
    747      * Determines if URL is equal to given imagery info.
    748      * @param in imagery info
    749      * @return {@code true} if URL is equal to given imagery info
    750      */
    751     public boolean equalsBaseValues(ImageryInfo in) {
    752         return url.equals(in.url);
    753     }
    754 
    755     /**
    756607     * Sets the pixel per degree value.
    757608     * @param ppd The ppd value
    758609     * @see #getPixelPerDegree()
     
    778629    }
    779630
    780631    /**
    781      * Sets the imagery polygonial bounds.
    782      * @param b The imagery bounds (non-rectangular)
    783      */
    784     public void setBounds(ImageryBounds b) {
    785         this.bounds = b;
    786     }
    787 
    788     /**
    789      * Returns the imagery polygonial bounds.
    790      * @return The imagery bounds (non-rectangular)
    791      */
    792     public ImageryBounds getBounds() {
    793         return bounds;
    794     }
    795 
    796     @Override
    797     public boolean requiresAttribution() {
    798         return attributionText != null || attributionLinkURL != null || attributionImage != null
    799                 || termsOfUseText != null || termsOfUseURL != null;
    800     }
    801 
    802     @Override
    803     public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) {
    804         return attributionText;
    805     }
    806 
    807     @Override
    808     public String getAttributionLinkURL() {
    809         return attributionLinkURL;
    810     }
    811 
    812     /**
    813      * Return the permission reference URL.
    814      * @return The url
    815      * @see #setPermissionReferenceURL
    816      * @since 11975
    817      */
    818     public String getPermissionReferenceURL() {
    819         return permissionReferenceURL;
    820     }
    821 
    822     /**
    823      * Return the privacy policy URL.
    824      * @return The url
    825      * @see #setPrivacyPolicyURL
    826      * @since 16127
    827      */
    828     public String getPrivacyPolicyURL() {
    829         return privacyPolicyURL;
    830     }
    831 
    832     @Override
    833     public Image getAttributionImage() {
    834         ImageIcon i = ImageProvider.getIfAvailable(attributionImage);
    835         if (i != null) {
    836             return i.getImage();
    837         }
    838         return null;
    839     }
    840 
    841     /**
    842      * Return the raw attribution logo information (an URL to the image).
    843      * @return The url text
    844      * @since 12257
    845      */
    846     public String getAttributionImageRaw() {
    847         return attributionImage;
    848     }
    849 
    850     @Override
    851     public String getAttributionImageURL() {
    852         return attributionImageURL;
    853     }
    854 
    855     @Override
    856     public String getTermsOfUseText() {
    857         return termsOfUseText;
    858     }
    859 
    860     @Override
    861     public String getTermsOfUseURL() {
    862         return termsOfUseURL;
    863     }
    864 
    865     /**
    866      * Set the attribution text
    867      * @param text The text
    868      * @see #getAttributionText(int, ICoordinate, ICoordinate)
    869      */
    870     public void setAttributionText(String text) {
    871         attributionText = intern(text);
    872     }
    873 
    874     /**
    875      * Set the attribution image
    876      * @param url The url of the image.
    877      * @see #getAttributionImageURL()
    878      */
    879     public void setAttributionImageURL(String url) {
    880         attributionImageURL = url;
    881     }
    882 
    883     /**
    884      * Set the image for the attribution
    885      * @param res The image resource
    886      * @see #getAttributionImage()
    887      */
    888     public void setAttributionImage(String res) {
    889         attributionImage = res;
    890     }
    891 
    892     /**
    893      * Sets the URL the attribution should link to.
    894      * @param url The url.
    895      * @see #getAttributionLinkURL()
    896      */
    897     public void setAttributionLinkURL(String url) {
    898         attributionLinkURL = url;
    899     }
    900 
    901     /**
    902      * Sets the permission reference URL.
    903      * @param url The url.
    904      * @see #getPermissionReferenceURL()
    905      * @since 11975
    906      */
    907     public void setPermissionReferenceURL(String url) {
    908         permissionReferenceURL = url;
    909     }
    910 
    911     /**
    912      * Sets the privacy policy URL.
    913      * @param url The url.
    914      * @see #getPrivacyPolicyURL()
    915      * @since 16127
    916      */
    917     public void setPrivacyPolicyURL(String url) {
    918         privacyPolicyURL = url;
    919     }
    920 
    921     /**
    922      * Sets the text to display to the user as terms of use.
    923      * @param text The text
    924      * @see #getTermsOfUseText()
    925      */
    926     public void setTermsOfUseText(String text) {
    927         termsOfUseText = text;
    928     }
    929 
    930     /**
    931      * Sets a url that links to the terms of use text.
    932      * @param text The url.
    933      * @see #getTermsOfUseURL()
    934      */
    935     public void setTermsOfUseURL(String text) {
    936         termsOfUseURL = text;
    937     }
    938 
    939     /**
    940632     * Sets the extended URL of this entry.
    941633     * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info
    942634     */
     
    945637
    946638        // Default imagery type is WMS
    947639        this.url = url;
    948         this.imageryType = ImageryType.WMS;
     640        this.sourceType = ImageryType.WMS;
    949641
    950642        defaultMaxZoom = 0;
    951643        defaultMinZoom = 0;
     
    953645            Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url);
    954646            if (m.matches()) {
    955647                this.url = m.group(3);
    956                 this.imageryType = type;
     648                this.sourceType = type;
    957649                if (m.group(2) != null) {
    958650                    defaultMaxZoom = Integer.parseInt(m.group(2));
    959651                }
     
    973665    }
    974666
    975667    /**
    976      * Returns the entry name.
    977      * @return The entry name
    978      * @since 6968
    979      */
    980     public String getOriginalName() {
    981         return this.origName != null ? this.origName : this.name;
    982     }
    983 
    984     /**
    985      * Sets the entry name and handle translation.
    986      * @param language The used language
    987      * @param name The entry name
    988      * @since 8091
    989      */
    990     public void setName(String language, String name) {
    991         boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
    992         if (LanguageInfo.isBetterLanguage(langName, language)) {
    993             this.name = isdefault ? tr(name) : name;
    994             this.langName = language;
    995         }
    996         if (origName == null || isdefault) {
    997             this.origName = name;
    998         }
    999     }
    1000 
    1001     /**
    1002      * Store the id of this info to the preferences and clear it afterwards.
    1003      */
    1004     public void clearId() {
    1005         if (this.id != null) {
    1006             Collection<String> newAddedIds = new TreeSet<>(Config.getPref().getList("imagery.layers.addedIds"));
    1007             newAddedIds.add(this.id);
    1008             Config.getPref().putList("imagery.layers.addedIds", new ArrayList<>(newAddedIds));
    1009         }
    1010         setId(null);
    1011     }
    1012 
    1013     /**
    1014      * Determines if this entry is enabled by default.
    1015      * @return {@code true} if this entry is enabled by default, {@code false} otherwise
    1016      */
    1017     public boolean isDefaultEntry() {
    1018         return defaultEntry;
    1019     }
    1020 
    1021     /**
    1022      * Sets the default state of this entry.
    1023      * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise
    1024      */
    1025     public void setDefaultEntry(boolean defaultEntry) {
    1026         this.defaultEntry = defaultEntry;
    1027     }
    1028 
    1029     /**
    1030668     * Gets the pixel per degree value
    1031669     * @return The ppd value.
    1032670     */
     
    1052690        return this.defaultMinZoom;
    1053691    }
    1054692
    1055     /**
    1056      * Returns the description text when existing.
    1057      * @return The description
    1058      * @since 8065
    1059      */
    1060     public String getDescription() {
    1061         return this.description;
    1062     }
    1063693
    1064694    /**
    1065      * Sets the description text when existing.
    1066      * @param language The used language
    1067      * @param description the imagery description text
    1068      * @since 8091
    1069      */
    1070     public void setDescription(String language, String description) {
    1071         boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
    1072         if (LanguageInfo.isBetterLanguage(langDescription, language)) {
    1073             this.description = isdefault ? tr(description) : description;
    1074             this.langDescription = intern(language);
    1075         }
    1076     }
    1077 
    1078     /**
    1079      * Return the sorted list of activated Imagery IDs.
    1080      * @return sorted list of activated Imagery IDs
    1081      * @since 13536
    1082      */
    1083     public static Collection<String> getActiveIds() {
    1084         IPreferences pref = Config.getPref();
    1085         if (pref == null) {
    1086             return Collections.emptyList();
    1087         }
    1088         List<ImageryPreferenceEntry> entries = StructUtils.getListOfStructs(pref, "imagery.entries", null, ImageryPreferenceEntry.class);
    1089         if (entries == null) {
    1090             return Collections.emptyList();
    1091         }
    1092         return entries.stream()
    1093                 .filter(prefEntry -> prefEntry.id != null && !prefEntry.id.isEmpty())
    1094                 .map(prefEntry -> prefEntry.id)
    1095                 .sorted()
    1096                 .collect(Collectors.toList());
    1097     }
    1098 
    1099     /**
    1100695     * Returns a tool tip text for display.
    1101696     * @return The text
    1102697     * @since 8065
    1103698     */
     699    @Override
    1104700    public String getToolTipText() {
    1105701        StringBuilder res = new StringBuilder(getName());
    1106702        boolean html = false;
     
    1133729    }
    1134730
    1135731    /**
    1136      * Returns the EULA acceptance URL, if any.
    1137      * @return The URL to an EULA text that has to be accepted before use, or {@code null}
    1138      */
    1139     public String getEulaAcceptanceRequired() {
    1140         return eulaAcceptanceRequired;
    1141     }
    1142 
    1143     /**
    1144      * Sets the EULA acceptance URL.
    1145      * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use
    1146      */
    1147     public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {
    1148         this.eulaAcceptanceRequired = eulaAcceptanceRequired;
    1149     }
    1150 
    1151     /**
    1152      * Returns the ISO 3166-1-alpha-2 country code.
    1153      * @return The country code (2 letters)
    1154      */
    1155     public String getCountryCode() {
    1156         return countryCode;
    1157     }
    1158 
    1159     /**
    1160      * Sets the ISO 3166-1-alpha-2 country code.
    1161      * @param countryCode The country code (2 letters)
    1162      */
    1163     public void setCountryCode(String countryCode) {
    1164         this.countryCode = intern(countryCode);
    1165     }
    1166 
    1167     /**
    1168      * Returns the date information.
    1169      * @return The date (in the form YYYY-MM-DD;YYYY-MM-DD, where
    1170      * DD and MM as well as a second date are optional)
    1171      * @since 11570
    1172      */
    1173     public String getDate() {
    1174         return date;
    1175     }
    1176 
    1177     /**
    1178      * Sets the date information.
    1179      * @param date The date information
    1180      * @since 11570
    1181      */
    1182     public void setDate(String date) {
    1183         this.date = date;
    1184     }
    1185 
    1186     /**
    1187      * Returns the entry icon.
    1188      * @return The entry icon
    1189      */
    1190     public String getIcon() {
    1191         return icon;
    1192     }
    1193 
    1194     /**
    1195      * Sets the entry icon.
    1196      * @param icon The entry icon
    1197      */
    1198     public void setIcon(String icon) {
    1199         this.icon = intern(icon);
    1200     }
    1201 
    1202     /**
    1203732     * Get the projections supported by the server. Only relevant for
    1204733     * WMS-type ImageryInfo at the moment.
    1205734     * @return null, if no projections have been specified; the list
     
    1225754     * @return The extended URL
    1226755     */
    1227756    public String getExtendedUrl() {
    1228         return imageryType.getTypeString() + (defaultMaxZoom != 0
     757        return sourceType.getTypeString() + (defaultMaxZoom != 0
    1229758            ? ('['+(defaultMinZoom != 0 ? (Integer.toString(defaultMinZoom) + ',') : "")+defaultMaxZoom+']') : "") + ':' + url;
    1230759    }
    1231760
     
    1254783    }
    1255784
    1256785    /**
    1257      * Determines if this entry requires attribution.
    1258      * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise
    1259      */
    1260     public boolean hasAttribution() {
    1261         return attributionText != null;
    1262     }
    1263 
    1264     /**
    1265      * Copies attribution from another {@code ImageryInfo}.
    1266      * @param i The other imagery info to get attribution from
    1267      */
    1268     public void copyAttribution(ImageryInfo i) {
    1269         this.attributionImage = i.attributionImage;
    1270         this.attributionImageURL = i.attributionImageURL;
    1271         this.attributionText = i.attributionText;
    1272         this.attributionLinkURL = i.attributionLinkURL;
    1273         this.termsOfUseText = i.termsOfUseText;
    1274         this.termsOfUseURL = i.termsOfUseURL;
    1275     }
    1276 
    1277     /**
    1278      * Applies the attribution from this object to a tile source.
    1279      * @param s The tile source
    1280      */
    1281     public void setAttribution(AbstractTileSource s) {
    1282         if (attributionText != null) {
    1283             if ("osm".equals(attributionText)) {
    1284                 s.setAttributionText(new Mapnik().getAttributionText(0, null, null));
    1285             } else {
    1286                 s.setAttributionText(attributionText);
    1287             }
    1288         }
    1289         if (attributionLinkURL != null) {
    1290             if ("osm".equals(attributionLinkURL)) {
    1291                 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL());
    1292             } else {
    1293                 s.setAttributionLinkURL(attributionLinkURL);
    1294             }
    1295         }
    1296         if (attributionImage != null) {
    1297             ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage);
    1298             if (i != null) {
    1299                 s.setAttributionImage(i.getImage());
    1300             }
    1301         }
    1302         if (attributionImageURL != null) {
    1303             s.setAttributionImageURL(attributionImageURL);
    1304         }
    1305         if (termsOfUseText != null) {
    1306             s.setTermsOfUseText(termsOfUseText);
    1307         }
    1308         if (termsOfUseURL != null) {
    1309             if ("osm".equals(termsOfUseURL)) {
    1310                 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL());
    1311             } else {
    1312                 s.setTermsOfUseURL(termsOfUseURL);
    1313             }
    1314         }
    1315     }
    1316 
    1317     /**
    1318      * Returns the imagery type.
    1319      * @return The imagery type
    1320      */
    1321     public ImageryType getImageryType() {
    1322         return imageryType;
    1323     }
    1324 
    1325     /**
    1326      * Sets the imagery type.
    1327      * @param imageryType The imagery type
    1328      */
    1329     public void setImageryType(ImageryType imageryType) {
    1330         this.imageryType = imageryType;
    1331     }
    1332 
    1333     /**
    1334      * Returns the imagery category.
    1335      * @return The imagery category
    1336      * @since 13792
    1337      */
    1338     public ImageryCategory getImageryCategory() {
    1339         return category;
    1340     }
    1341 
    1342     /**
    1343      * Sets the imagery category.
    1344      * @param category The imagery category
    1345      * @since 13792
    1346      */
    1347     public void setImageryCategory(ImageryCategory category) {
    1348         this.category = category;
    1349     }
    1350 
    1351     /**
    1352      * Returns the imagery category original string (don't use except for error checks).
    1353      * @return The imagery category original string
    1354      * @since 13792
    1355      */
    1356     public String getImageryCategoryOriginalString() {
    1357         return categoryOriginalString;
    1358     }
    1359 
    1360     /**
    1361      * Sets the imagery category original string (don't use except for error checks).
    1362      * @param categoryOriginalString The imagery category original string
    1363      * @since 13792
    1364      */
    1365     public void setImageryCategoryOriginalString(String categoryOriginalString) {
    1366         this.categoryOriginalString = intern(categoryOriginalString);
    1367     }
    1368 
    1369     /**
    1370      * Returns true if this layer's URL is matched by one of the regular
    1371      * expressions kept by the current OsmApi instance.
    1372      * @return {@code true} is this entry is blacklisted, {@code false} otherwise
    1373      */
    1374     public boolean isBlacklisted() {
    1375         Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
    1376         return capabilities != null && capabilities.isOnImageryBlacklist(this.url);
    1377     }
    1378 
    1379     /**
    1380      * Sets the map of &lt;header name, header value&gt; that if any of this header
    1381      * will be returned, then this tile will be treated as "no tile at this zoom level"
    1382      *
    1383      * @param noTileHeaders Map of &lt;header name, header value&gt; which will be treated as "no tile at this zoom level"
    1384      * @since 9613
    1385      */
    1386     public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) {
    1387        if (noTileHeaders == null || noTileHeaders.isEmpty()) {
    1388            this.noTileHeaders = null;
    1389        } else {
    1390             this.noTileHeaders = noTileHeaders.toMap();
    1391        }
    1392     }
    1393 
    1394     @Override
    1395     public Map<String, Set<String>> getNoTileHeaders() {
    1396         return noTileHeaders;
    1397     }
    1398 
    1399     /**
    1400      * Sets the map of &lt;checksum type, checksum value&gt; that if any tile with that checksum
    1401      * will be returned, then this tile will be treated as "no tile at this zoom level"
    1402      *
    1403      * @param noTileChecksums Map of &lt;checksum type, checksum value&gt; which will be treated as "no tile at this zoom level"
    1404      * @since 9613
    1405      */
    1406     public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) {
    1407         if (noTileChecksums == null || noTileChecksums.isEmpty()) {
    1408             this.noTileChecksums = null;
    1409         } else {
    1410             this.noTileChecksums = noTileChecksums.toMap();
    1411         }
    1412     }
    1413 
    1414     @Override
    1415     public Map<String, Set<String>> getNoTileChecksums() {
    1416         return noTileChecksums;
    1417     }
    1418 
    1419     /**
    1420      * Returns the map of &lt;header name, metadata key&gt; indicating, which HTTP headers should
    1421      * be moved to metadata
    1422      *
    1423      * @param metadataHeaders map of &lt;header name, metadata key&gt; indicating, which HTTP headers should be moved to metadata
    1424      * @since 8418
    1425      */
    1426     public void setMetadataHeaders(Map<String, String> metadataHeaders) {
    1427         if (metadataHeaders == null || metadataHeaders.isEmpty()) {
    1428             this.metadataHeaders = null;
    1429         } else {
    1430             this.metadataHeaders = metadataHeaders;
    1431         }
    1432     }
    1433 
    1434     /**
    1435786     * Gets the flag if the georeference is valid.
    1436787     * @return <code>true</code> if it is valid.
    1437788     */
     
    1484835    }
    1485836
    1486837    /**
    1487      * Adds an old Id.
    1488      *
    1489      * @param id the Id to be added
    1490      * @since 13536
    1491      */
    1492     public void addOldId(String id) {
    1493        if (oldIds == null) {
    1494            oldIds = new ArrayList<>();
    1495        }
    1496        oldIds.add(id);
    1497     }
    1498 
    1499     /**
    1500      * Get old Ids.
    1501      *
    1502      * @return collection of ids
    1503      * @since 13536
    1504      */
    1505     public Collection<String> getOldIds() {
    1506         return oldIds;
    1507     }
    1508 
    1509     /**
    1510      * Adds a mirror entry. Mirror entries are completed with the data from the master entry
    1511      * and only describe another method to access identical data.
    1512      *
    1513      * @param entry the mirror to be added
    1514      * @since 9658
    1515      */
    1516     public void addMirror(ImageryInfo entry) {
    1517        if (mirrors == null) {
    1518            mirrors = new ArrayList<>();
    1519        }
    1520        mirrors.add(entry);
    1521     }
    1522 
    1523     /**
    1524      * Returns the mirror entries. Entries are completed with master entry data.
    1525      *
    1526      * @return the list of mirrors
    1527      * @since 9658
    1528      */
    1529     public List<ImageryInfo> getMirrors() {
    1530        List<ImageryInfo> l = new ArrayList<>();
    1531        if (mirrors != null) {
    1532            int num = 1;
    1533            for (ImageryInfo i : mirrors) {
    1534                ImageryInfo n = new ImageryInfo(this);
    1535                if (i.defaultMaxZoom != 0) {
    1536                    n.defaultMaxZoom = i.defaultMaxZoom;
    1537                }
    1538                if (i.defaultMinZoom != 0) {
    1539                    n.defaultMinZoom = i.defaultMinZoom;
    1540                }
    1541                n.setServerProjections(i.getServerProjections());
    1542                n.url = i.url;
    1543                n.imageryType = i.imageryType;
    1544                if (i.getTileSize() != 0) {
    1545                    n.setTileSize(i.getTileSize());
    1546                }
    1547                if (i.getPrivacyPolicyURL() != null) {
    1548                    n.setPrivacyPolicyURL(i.getPrivacyPolicyURL());
    1549                }
    1550                if (n.id != null) {
    1551                    n.id = n.id + "_mirror"+num;
    1552                }
    1553                if (num > 1) {
    1554                    n.name = tr("{0} mirror server {1}", n.name, num);
    1555                    if (n.origName != null) {
    1556                        n.origName += " mirror server " + num;
    1557                    }
    1558                } else {
    1559                    n.name = tr("{0} mirror server", n.name);
    1560                    if (n.origName != null) {
    1561                        n.origName += " mirror server";
    1562                    }
    1563                }
    1564                l.add(n);
    1565                ++num;
    1566            }
    1567        }
    1568        return l;
    1569     }
    1570 
    1571     /**
    1572      * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider)
    1573      * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer
    1574      * to work on
    1575      * @return Collection of the layer names
    1576      */
    1577     public List<DefaultLayer> getDefaultLayers() {
    1578         return defaultLayers;
    1579     }
    1580 
    1581     /**
    1582      * Sets the default layers that user will work with
    1583      * @param layers set the list of default layers
    1584      */
    1585     public void setDefaultLayers(List<DefaultLayer> layers) {
    1586         this.defaultLayers = Utils.toUnmodifiableList(layers);
    1587     }
    1588 
    1589     /**
    1590      * Returns custom HTTP headers that should be sent with request towards imagery provider
    1591      * @return headers
    1592      */
    1593     public Map<String, String> getCustomHttpHeaders() {
    1594         return customHttpHeaders;
    1595     }
    1596 
    1597     /**
    1598      * Sets custom HTTP headers that should be sent with request towards imagery provider
    1599      * @param customHttpHeaders http headers
    1600      */
    1601     public void setCustomHttpHeaders(Map<String, String> customHttpHeaders) {
    1602         this.customHttpHeaders = Utils.toUnmodifiableMap(customHttpHeaders);
    1603     }
    1604 
    1605     /**
    1606838     * Determines if this imagery should be transparent.
    1607839     * @return should this imagery be transparent
    1608840     */
     
    1640872     * @since 13890
    1641873     */
    1642874    public String getSourceName() {
    1643         if (ImageryType.BING == getImageryType()) {
     875        if (ImageryType.BING == getSourceType()) {
    1644876            return "Bing";
    1645877        } else {
    1646878            if (id != null) {
     
    1654886        }
    1655887    }
    1656888
    1657     private static String intern(String string) {
    1658         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);
    1659896    }
    1660897}
  • src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java

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

     
    115115     * @throws IllegalArgumentException if url from imagery info is null or invalid
    116116     */
    117117    public static TMSTileSource getTileSourceStatic(ImageryInfo info, Runnable attributionLoadedTask) {
    118         if (info.getImageryType() == ImageryType.TMS) {
     118        if (info.getSourceType() == ImageryType.TMS) {
    119119            JosmTemplatedTMSTileSource.checkUrl(info.getUrl());
    120120            TMSTileSource t = new JosmTemplatedTMSTileSource(info);
    121121            info.setAttribution(t);
    122122            return t;
    123         } else if (info.getImageryType() == ImageryType.BING) {
     123        } else if (info.getSourceType() == ImageryType.BING) {
    124124            return new CachedAttributionBingAerialTileSource(info, attributionLoadedTask);
    125         } else if (info.getImageryType() == ImageryType.SCANEX) {
     125        } else if (info.getSourceType() == ImageryType.SCANEX) {
    126126            return new ScanexTileSource(info);
    127127        }
    128128        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

     
    720720            ImageryInfo info = layerInfo.getAllDefaultLayers().get(row);
    721721            switch (column) {
    722722            case 0:
    723                 return Optional.ofNullable(info.getImageryCategory()).orElse(ImageryCategory.OTHER);
     723                return Optional.ofNullable(info.getSourceCategory()).orElse(ImageryCategory.OTHER);
    724724            case 1:
    725725                return info.getCountryCode();
    726726            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        return ImageryLayerInfo.instance.getDefaultLayers().stream()
    52                 .filter(i -> ImageryType.BING == i.getImageryType())
     52                .filter(i -> ImageryType.BING == i.getSourceType())
    5353                .findFirst().orElse(null);
    5454    }
    5555
  • src/org/openstreetmap/josm/tools/Utils.java

     
    6666import java.util.stream.Stream;
    6767import java.util.zip.ZipFile;
    6868
    69 import com.kitfox.svg.xml.XMLParseUtil;
    7069import org.openstreetmap.josm.spi.preferences.Config;
    7170
     71import com.kitfox.svg.xml.XMLParseUtil;
     72
    7273/**
    7374 * Basic utils, that can be useful in different parts of the program.
    7475 */
     
    19101911        // remove extra whitespaces
    19111912        return rawString.trim();
    19121913    }
     1914
     1915    /**
     1916     * Intern a string
     1917     * @param string The string to intern
     1918     * @return The interned string
     1919     * @since xxx
     1920     */
     1921    public static String intern(String string) {
     1922        return string == null ? null : string.intern();
     1923    }
    19131924}
  • 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

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