Changeset 16545 in josm for trunk/src/org


Ignore:
Timestamp:
2020-06-07T10:12:42+02:00 (4 years ago)
Author:
simon04
Message:

fix #19026 - Make imagery preferences code more generic (patch by taylor.smock)

Location:
trunk/src/org/openstreetmap/josm
Files:
7 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/StructUtils.java

    r16436 r16545  
    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;
     
    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);
    208                 continue;
    209             }
    210             if (f.getAnnotation(StructEntry.class) == null) {
     203            Field f = getDeclaredFieldInClassOrSuperTypes(klass, keyValue.getKey().replace('-', '_'));
     204
     205            if (f == null || f.getAnnotation(StructEntry.class) == null) {
    211206                continue;
    212207            }
     
    244239        }
    245240        return struct;
     241    }
     242
     243    private static <T> Field getDeclaredFieldInClassOrSuperTypes(Class<T> clazz, String fieldName) {
     244        Class<?> tClass = clazz;
     245        do {
     246            try {
     247                return tClass.getDeclaredField(fieldName);
     248            } catch (NoSuchFieldException ex) {
     249                Logging.trace(ex);
     250            }
     251            tClass = tClass.getSuperclass();
     252        } while (tClass != null);
     253        return null;
     254    }
     255
     256    private static <T> Field[] getDeclaredFieldsInClassOrSuperTypes(Class<T> clazz) {
     257        List<Field> fields = new ArrayList<>();
     258        Class<?> tclass = clazz;
     259        do {
     260            Collections.addAll(fields, tclass.getDeclaredFields());
     261            tclass = tclass.getSuperclass();
     262        } while (tclass != null);
     263        return fields.toArray(new Field[] {});
    246264    }
    247265
  • trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java

    r16436 r16545  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.Image;
    76import java.io.StringReader;
    87import java.util.ArrayList;
     
    1110import java.util.Collections;
    1211import java.util.EnumMap;
    13 import java.util.HashMap;
    1412import java.util.List;
    1513import java.util.Locale;
     
    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;
     
    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;
     
    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"),
     
    8675         * @since 6690
    8776         */
     77        @Override
    8878        public final String getTypeString() {
    8979            return typeString;
     
    10090                    .findFirst().orElse(null);
    10191        }
     92
     93        @Override
     94        public ImageryType getFromString(String s) {
     95            return fromString(s);
     96        }
     97
     98        @Override
     99        public ImageryType getDefault() {
     100            return WMS;
     101        }
    102102    }
    103103
     
    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")),
     
    138138         * @return the unique string identifying this category
    139139         */
     140        @Override
    140141        public final String getCategoryString() {
    141142            return category;
     
    146147         * @return the description of this category
    147148         */
     149        @Override
    148150        public final String getDescription() {
    149151            return description;
     
    156158         * @since 15049
    157159         */
     160        @Override
    158161        public final ImageIcon getIcon(ImageSizes size) {
    159162            return iconCache
     
    172175                    .findFirst().orElse(null);
    173176        }
     177
     178        @Override
     179        public ImageryCategory getDefault() {
     180            return OTHER;
     181        }
     182
     183        @Override
     184        public ImageryCategory getFromString(String s) {
     185            return fromString(s);
     186        }
    174187    }
    175188
     
    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        /**
     
    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         }
    230     }
    231 
    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;
     203    }
     204
    242205    private double pixelPerDegree;
    243206    /** maximum zoom level for TMS imagery */
     
    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 = "";
    273     /**
    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;
    281212    /**
    282213      * marked as best in other editors
     
    289220      */
    290221    private boolean overlay;
    291     /**
    292       * list of old IDs, only for loading, not handled anywhere else
    293       * @since 13536
    294       */
    295     private Collection<String> oldIds;
     222
    296223    /** mirrors of different type for this entry */
    297     private List<ImageryInfo> mirrors;
    298     /** icon used in menu */
    299     private String icon;
     224    protected List<ImageryInfo> mirrors;
     225    /**
     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      **/
    320 
    321     /**
    322      * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
    323      */
    324     public static class ImageryPreferenceEntry {
    325         @StructEntry String name;
     233
     234    /**
     235     * The ImageryPreferenceEntry class for storing data in JOSM preferences.
     236     *
     237     * @author Frederik Ramm, modified by Taylor Smock
     238     */
     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;
     
    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        /**
     
    365258         */
    366259        public ImageryPreferenceEntry() {
    367             // Do nothing
     260            super();
    368261        }
    369262
     
    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);
     
    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;
     
    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);
     
    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.fromString(e.type);
     394        if (sourceType == null) throw new IllegalArgumentException("unknown type");
    530395        pixelPerDegree = e.pixel_per_eastnorth;
    531396        defaultMaxZoom = e.max_zoom;
     
    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;
     
    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();
     
    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            }
     
    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;
     
    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);
     
    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);
     513    }
     514
     515    /**
     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;
    639561    }
    640562
     
    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));
    736         }
    737         if (i == 0) {
    738             i = url.compareTo(in.url);
    739         }
    740         if (i == 0) {
    741             i = Double.compare(pixelPerDegree, in.pixelPerDegree);
     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            }
    742602        }
    743603        return i;
    744     }
    745 
    746     /**
    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);
    753604    }
    754605
     
    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
     
    946638        // Default imagery type is WMS
    947639        this.url = url;
    948         this.imageryType = ImageryType.WMS;
     640        this.sourceType = ImageryType.WMS;
    949641
    950642        defaultMaxZoom = 0;
     
    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));
     
    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.
     
    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     }
    1063 
    1064     /**
    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     }
    1098693
    1099694    /**
     
    1102697     * @since 8065
    1103698     */
     699    @Override
    1104700    public String getToolTipText() {
    1105701        StringBuilder res = new StringBuilder(getName());
     
    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.
     
    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    }
     
    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     /**
    1318786     * Returns the imagery type.
    1319787     * @return The imagery type
     788     * @see SourceInfo#getSourceType
    1320789     */
    1321790    public ImageryType getImageryType() {
    1322         return imageryType;
     791        return super.getSourceType();
    1323792    }
    1324793
     
    1326795     * Sets the imagery type.
    1327796     * @param imageryType The imagery type
     797     * @see SourceInfo#setSourceType
    1328798     */
    1329799    public void setImageryType(ImageryType imageryType) {
    1330         this.imageryType = imageryType;
     800        super.setSourceType(imageryType);
    1331801    }
    1332802
     
    1334804     * Returns the imagery category.
    1335805     * @return The imagery category
     806     * @see SourceInfo#getSourceCategory
    1336807     * @since 13792
    1337808     */
    1338809    public ImageryCategory getImageryCategory() {
    1339         return category;
     810        return super.getSourceCategory();
    1340811    }
    1341812
     
    1343814     * Sets the imagery category.
    1344815     * @param category The imagery category
     816     * @see SourceInfo#setSourceCategory
    1345817     * @since 13792
    1346818     */
    1347819    public void setImageryCategory(ImageryCategory category) {
    1348         this.category = category;
     820        super.setSourceCategory(category);
    1349821    }
    1350822
     
    1352824     * Returns the imagery category original string (don't use except for error checks).
    1353825     * @return The imagery category original string
     826     * @see SourceInfo#getSourceCategoryOriginalString
    1354827     * @since 13792
    1355828     */
    1356829    public String getImageryCategoryOriginalString() {
    1357         return categoryOriginalString;
     830        return super.getSourceCategoryOriginalString();
    1358831    }
    1359832
     
    1361834     * Sets the imagery category original string (don't use except for error checks).
    1362835     * @param categoryOriginalString The imagery category original string
     836     * @see SourceInfo#setSourceCategoryOriginalString
    1363837     * @since 13792
    1364838     */
    1365839    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         }
     840        super.setSourceCategoryOriginalString(categoryOriginalString);
    1432841    }
    1433842
     
    1482891    public void setOverlay(boolean overlay) {
    1483892        this.overlay = overlay;
    1484     }
    1485 
    1486     /**
    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);
    1603893    }
    1604894
     
    1655945    }
    1656946
    1657     private static String intern(String string) {
    1658         return string == null ? null : string.intern();
     947    /**
     948     * Return the sorted list of activated source IDs.
     949     * @return sorted list of activated source IDs
     950     * @since 13536
     951     */
     952    public static Collection<String> getActiveIds() {
     953        return getActiveIds(ImageryInfo.class);
    1659954    }
    1660955}
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r16436 r16545  
    19111911        return rawString.trim();
    19121912    }
     1913
     1914    /**
     1915     * Intern a string
     1916     * @param string The string to intern
     1917     * @return The interned string
     1918     * @since 16545
     1919     */
     1920    public static String intern(String string) {
     1921        return string == null ? null : string.intern();
     1922    }
    19131923}
Note: See TracChangeset for help on using the changeset viewer.