001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.sources;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Image;
007import java.util.ArrayList;
008import java.util.Collection;
009import java.util.Collections;
010import java.util.HashMap;
011import java.util.List;
012import java.util.Locale;
013import java.util.Map;
014import java.util.Objects;
015import java.util.Set;
016import java.util.TreeSet;
017import java.util.stream.Collectors;
018
019import javax.swing.ImageIcon;
020
021import org.openstreetmap.gui.jmapviewer.interfaces.Attributed;
022import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
023import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource;
024import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik;
025import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;
026import org.openstreetmap.josm.data.StructUtils;
027import org.openstreetmap.josm.data.imagery.DefaultLayer;
028import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
029import org.openstreetmap.josm.io.Capabilities;
030import org.openstreetmap.josm.io.OsmApi;
031import org.openstreetmap.josm.spi.preferences.Config;
032import org.openstreetmap.josm.spi.preferences.IPreferences;
033import org.openstreetmap.josm.tools.ImageProvider;
034import org.openstreetmap.josm.tools.LanguageInfo;
035import org.openstreetmap.josm.tools.MultiMap;
036import org.openstreetmap.josm.tools.Utils;
037
038/**
039 * This class is an abstraction for source information to be used in a panel like ImageryProvidersPanel.
040 *
041 * @author Taylor Smock
042 * @param <T> The SourceCategory The categories enum for the source
043 * @param <U> The SourceType The type enum of the source
044 * @param <V> The SourceBounds The bound type for the entry
045 * @param <W> The storage for the entry
046 *
047 * @since 16545
048 */
049public class SourceInfo<T extends ISourceCategory<?>, U extends ISourceType<?>, V extends SourceBounds, W extends SourcePreferenceEntry<?>>
050    extends TileSourceInfo implements Comparable<SourceInfo<T, U, V, W>>, Attributed {
051    /** original name of the source entry in case of translation call, for multiple languages English when possible */
052    protected String origName;
053    /** (original) language of the translated name entry */
054    protected String langName;
055    /** whether this is a entry activated by default or not */
056    protected boolean defaultEntry;
057    /** Whether this service requires a explicit EULA acceptance before it can be activated */
058    protected String eulaAcceptanceRequired;
059    /** type of the services - WMS, TMS, ... */
060    protected U sourceType;
061    /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */
062    protected V bounds;
063    /** description of the imagery entry, should contain notes what type of data it is */
064    protected String description;
065    /** language of the description entry */
066    protected String langDescription;
067    /** Text of a text attribution displayed when using the imagery */
068    protected String attributionText;
069    /** Link to the privacy policy of the operator */
070    protected String privacyPolicyURL;
071    /** Link to a reference stating the permission for OSM usage */
072    protected String permissionReferenceURL;
073    /** Link behind the text attribution displayed when using the imagery */
074    protected String attributionLinkURL;
075    /** Image of a graphical attribution displayed when using the imagery */
076    protected String attributionImage;
077    /** Link behind the graphical attribution displayed when using the imagery */
078    protected String attributionImageURL;
079    /** Text with usage terms displayed when using the imagery */
080    protected String termsOfUseText;
081    /** Link behind the text with usage terms displayed when using the imagery */
082    protected String termsOfUseURL;
083    /** country code of the imagery (for country specific imagery) */
084    protected String countryCode = "";
085    /**
086      * creation date of the source (in the form YYYY-MM-DD;YYYY-MM-DD, where
087      * DD and MM as well as a second date are optional).
088      *
089      * Also used as time filter for WMS time={time} parameter (such as Sentinel-2)
090      * @since 11570
091      */
092    protected String date;
093    /**
094      * list of old IDs, only for loading, not handled anywhere else
095      * @since 13536
096      */
097    protected Collection<String> oldIds;
098    /** icon used in menu */
099    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                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 polygonal 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 polygonal 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, 16545 (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 -> !Utils.isEmpty(prefEntry.id))
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 (!Utils.isEmpty(dateStr)) {
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 (!Utils.isEmpty(desc)) {
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 (Utils.isEmpty(noTileHeaders)) {
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 (Utils.isEmpty(noTileChecksums)) {
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 (Utils.isEmpty(metadataHeaders)) {
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}