001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.imagery;
003
004import java.io.IOException;
005import java.util.ArrayList;
006import java.util.Arrays;
007import java.util.Collection;
008import java.util.Collections;
009import java.util.List;
010
011import org.openstreetmap.josm.Main;
012import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
013import org.openstreetmap.josm.io.MirroredInputStream;
014import org.openstreetmap.josm.io.imagery.ImageryReader;
015import org.openstreetmap.josm.tools.Utils;
016import org.xml.sax.SAXException;
017
018/**
019 * Manages the list of imagery entries that are shown in the imagery menu.
020 */
021public class ImageryLayerInfo {
022
023    public static final ImageryLayerInfo instance = new ImageryLayerInfo();
024    List<ImageryInfo> layers = new ArrayList<ImageryInfo>();
025    static List<ImageryInfo> defaultLayers = new ArrayList<ImageryInfo>();
026
027    private final static String[] DEFAULT_LAYER_SITES = {
028        Main.JOSM_WEBSITE+"/maps"
029    };
030
031    private ImageryLayerInfo() {
032    }
033
034    public ImageryLayerInfo(ImageryLayerInfo info) {
035        layers.addAll(info.layers);
036    }
037
038    public void clear() {
039        layers.clear();
040    }
041
042    public void load() {
043        boolean addedDefault = !layers.isEmpty();
044        List<ImageryPreferenceEntry> entries = Main.pref.getListOfStructs("imagery.entries", null, ImageryPreferenceEntry.class);
045        if (entries != null) {
046            for (ImageryPreferenceEntry prefEntry : entries) {
047                try {
048                    ImageryInfo i = new ImageryInfo(prefEntry);
049                    add(i);
050                } catch (IllegalArgumentException e) {
051                    Main.warn("Unable to load imagery preference entry:"+e);
052                }
053            }
054            Collections.sort(layers);
055        }
056        if (addedDefault) {
057            save();
058        }
059    }
060
061    /**
062     * Loads the available imagery entries.
063     *
064     * The data is downloaded from the JOSM website (or loaded from cache).
065     * Entries marked as "default" are added to the user selection, if not
066     * already present.
067     *
068     * @param clearCache if true, clear the cache and start a fresh download.
069     */
070    public void loadDefaults(boolean clearCache) {
071        defaultLayers.clear();
072        for (String source : Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES))) {
073            if (clearCache) {
074                MirroredInputStream.cleanup(source);
075            }
076            MirroredInputStream stream = null;
077            try {
078                ImageryReader reader = new ImageryReader(source);
079                Collection<ImageryInfo> result = reader.parse();
080                defaultLayers.addAll(result);
081            } catch (IOException ex) {
082                Utils.close(stream);
083                ex.printStackTrace();
084                continue;
085            } catch (SAXException sex) {
086                Utils.close(stream);
087                sex.printStackTrace();
088                continue;
089            }
090        }
091        while (defaultLayers.remove(null));
092
093        Collection<String> defaults = Main.pref.getCollection("imagery.layers.default");
094        List<String> defaultsSave = new ArrayList<String>();
095        for (ImageryInfo def : defaultLayers) {
096            if (def.isDefaultEntry()) {
097                defaultsSave.add(def.getUrl());
098
099                boolean isKnownDefault = false;
100                for (String url : defaults) {
101                    if (isSimilar(url, def.getUrl())) {
102                        isKnownDefault = true;
103                        break;
104                    }
105                }
106                boolean isInUserList = false;
107                if (!isKnownDefault) {
108                    for (ImageryInfo i : layers) {
109                        if (isSimilar(def.getUrl(), i.getUrl())) {
110                            isInUserList = true;
111                            break;
112                        }
113                    }
114                }
115                if (!isKnownDefault && !isInUserList) {
116                    add(new ImageryInfo(def));
117                }
118            }
119        }
120
121        Collections.sort(defaultLayers);
122        Main.pref.putCollection("imagery.layers.default", defaultsSave.isEmpty() ? defaults : defaultsSave);
123    }
124
125    // some additional checks to respect extended URLs in preferences (legacy workaround)
126    private boolean isSimilar(String a, String b) {
127        return Utils.equal(a, b) || (a != null && b != null && !a.isEmpty() && !b.isEmpty() && (a.contains(b) || b.contains(a)));
128    }
129
130    public void add(ImageryInfo info) {
131        layers.add(info);
132    }
133
134    public void remove(ImageryInfo info) {
135        layers.remove(info);
136    }
137
138    public void save() {
139        List<ImageryPreferenceEntry> entries = new ArrayList<ImageryPreferenceEntry>();
140        for (ImageryInfo info : layers) {
141            entries.add(new ImageryPreferenceEntry(info));
142        }
143        Main.pref.putListOfStructs("imagery.entries", entries, ImageryPreferenceEntry.class);
144    }
145
146    public List<ImageryInfo> getLayers() {
147        return Collections.unmodifiableList(layers);
148    }
149
150    public List<ImageryInfo> getDefaultLayers() {
151        return Collections.unmodifiableList(defaultLayers);
152    }
153
154    public static void addLayer(ImageryInfo info) {
155        instance.add(info);
156        instance.save();
157    }
158
159    public static void addLayers(Collection<ImageryInfo> infos) {
160        for (ImageryInfo i : infos) {
161            instance.add(i);
162        }
163        instance.save();
164        Collections.sort(instance.layers);
165    }
166}