source: josm/trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java@ 11385

Last change on this file since 11385 was 11385, checked in by Don-vip, 7 years ago

sonar - squid:S1066 - Collapsible "if" statements should be merged

  • Property svn:eol-style set to native
File size: 11.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.List;
14import java.util.Map;
15import java.util.Objects;
16import java.util.Set;
17import java.util.TreeSet;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
21import org.openstreetmap.josm.gui.PleaseWaitRunnable;
22import org.openstreetmap.josm.io.CachedFile;
23import org.openstreetmap.josm.io.OfflineAccessException;
24import org.openstreetmap.josm.io.OnlineResource;
25import org.openstreetmap.josm.io.imagery.ImageryReader;
26import org.openstreetmap.josm.tools.Utils;
27import org.xml.sax.SAXException;
28
29/**
30 * Manages the list of imagery entries that are shown in the imagery menu.
31 */
32public class ImageryLayerInfo {
33
34 public static final ImageryLayerInfo instance = new ImageryLayerInfo();
35 private final List<ImageryInfo> layers = new ArrayList<>();
36 private final Map<String, ImageryInfo> layerIds = new HashMap<>();
37 private static final List<ImageryInfo> defaultLayers = new ArrayList<>();
38 private static final Map<String, ImageryInfo> defaultLayerIds = new HashMap<>();
39
40 private static final String[] DEFAULT_LAYER_SITES = {
41 Main.getJOSMWebsite()+"/maps"
42 };
43
44 /**
45 * Returns the list of imagery layers sites.
46 * @return the list of imagery layers sites
47 * @since 7434
48 */
49 public static Collection<String> getImageryLayersSites() {
50 return Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES));
51 }
52
53 private ImageryLayerInfo() {
54 }
55
56 public ImageryLayerInfo(ImageryLayerInfo info) {
57 layers.addAll(info.layers);
58 }
59
60 public void clear() {
61 layers.clear();
62 layerIds.clear();
63 }
64
65 /**
66 * Loads the custom as well as default imagery entries.
67 * @param fastFail whether opening HTTP connections should fail fast, see {@link ImageryReader#setFastFail(boolean)}
68 */
69 public void load(boolean fastFail) {
70 clear();
71 List<ImageryPreferenceEntry> entries = Main.pref.getListOfStructs("imagery.entries", null, ImageryPreferenceEntry.class);
72 if (entries != null) {
73 for (ImageryPreferenceEntry prefEntry : entries) {
74 try {
75 ImageryInfo i = new ImageryInfo(prefEntry);
76 add(i);
77 } catch (IllegalArgumentException e) {
78 Main.warn("Unable to load imagery preference entry:"+e);
79 }
80 }
81 Collections.sort(layers);
82 }
83 loadDefaults(false, true, fastFail);
84 }
85
86 /**
87 * Loads the available imagery entries.
88 *
89 * The data is downloaded from the JOSM website (or loaded from cache).
90 * Entries marked as "default" are added to the user selection, if not
91 * already present.
92 *
93 * @param clearCache if true, clear the cache and start a fresh download.
94 * @param quiet whether not the loading should be performed using a {@link PleaseWaitRunnable} in the background
95 * @param fastFail whether opening HTTP connections should fail fast, see {@link ImageryReader#setFastFail(boolean)}
96 */
97 public void loadDefaults(boolean clearCache, boolean quiet, boolean fastFail) {
98 final DefaultEntryLoader loader = new DefaultEntryLoader(clearCache, fastFail);
99 if (quiet) {
100 loader.realRun();
101 loader.finish();
102 } else {
103 Main.worker.execute(new DefaultEntryLoader(clearCache, fastFail));
104 }
105 }
106
107 /**
108 * Loader/updater of the available imagery entries
109 */
110 class DefaultEntryLoader extends PleaseWaitRunnable {
111
112 private final boolean clearCache;
113 private final boolean fastFail;
114 private final List<ImageryInfo> newLayers = new ArrayList<>();
115 private ImageryReader reader;
116 private boolean canceled;
117
118 DefaultEntryLoader(boolean clearCache, boolean fastFail) {
119 super(tr("Update default entries"));
120 this.clearCache = clearCache;
121 this.fastFail = fastFail;
122 }
123
124 @Override
125 protected void cancel() {
126 canceled = true;
127 Utils.close(reader);
128 }
129
130 @Override
131 protected void realRun() {
132 for (String source : getImageryLayersSites()) {
133 if (canceled) {
134 return;
135 }
136 loadSource(source);
137 }
138 }
139
140 protected void loadSource(String source) {
141 boolean online = true;
142 try {
143 OnlineResource.JOSM_WEBSITE.checkOfflineAccess(source, Main.getJOSMWebsite());
144 } catch (OfflineAccessException e) {
145 Main.warn(e, false);
146 online = false;
147 }
148 if (clearCache && online) {
149 CachedFile.cleanup(source);
150 }
151 try {
152 reader = new ImageryReader(source);
153 reader.setFastFail(fastFail);
154 Collection<ImageryInfo> result = reader.parse();
155 newLayers.addAll(result);
156 } catch (IOException ex) {
157 Main.error(ex, false);
158 } catch (SAXException ex) {
159 Main.error(ex);
160 }
161 }
162
163 @Override
164 protected void finish() {
165 defaultLayers.clear();
166 defaultLayers.addAll(newLayers);
167 defaultLayerIds.clear();
168 Collections.sort(defaultLayers);
169 buildIdMap(defaultLayers, defaultLayerIds);
170 updateEntriesFromDefaults();
171 buildIdMap(layers, layerIds);
172 }
173 }
174
175 /**
176 * Build the mapping of unique ids to {@link ImageryInfo}s.
177 * @param lst input list
178 * @param idMap output map
179 */
180 private static void buildIdMap(List<ImageryInfo> lst, Map<String, ImageryInfo> idMap) {
181 idMap.clear();
182 Set<String> notUnique = new HashSet<>();
183 for (ImageryInfo i : lst) {
184 if (i.getId() != null) {
185 if (idMap.containsKey(i.getId())) {
186 notUnique.add(i.getId());
187 Main.error("Id ''{0}'' is not unique - used by ''{1}'' and ''{2}''!",
188 i.getId(), i.getName(), idMap.get(i.getId()).getName());
189 continue;
190 }
191 idMap.put(i.getId(), i);
192 }
193 }
194 for (String i : notUnique) {
195 idMap.remove(i);
196 }
197 }
198
199 /**
200 * Update user entries according to the list of default entries.
201 */
202 public void updateEntriesFromDefaults() {
203 // add new default entries to the user selection
204 boolean changed = false;
205 Collection<String> knownDefaults = Main.pref.getCollection("imagery.layers.default");
206 Collection<String> newKnownDefaults = new TreeSet<>(knownDefaults);
207 for (ImageryInfo def : defaultLayers) {
208 if (def.isDefaultEntry()) {
209 boolean isKnownDefault = false;
210 for (String url : knownDefaults) {
211 if (isSimilar(url, def.getUrl())) {
212 isKnownDefault = true;
213 break;
214 }
215 }
216 boolean isInUserList = false;
217 if (!isKnownDefault) {
218 newKnownDefaults.add(def.getUrl());
219 for (ImageryInfo i : layers) {
220 if (isSimilar(def, i)) {
221 isInUserList = true;
222 break;
223 }
224 }
225 }
226 if (!isKnownDefault && !isInUserList) {
227 add(new ImageryInfo(def));
228 changed = true;
229 }
230 }
231 }
232 Main.pref.putCollection("imagery.layers.default", newKnownDefaults);
233
234 // Add ids to user entries without id.
235 // Only do this the first time for each id, so the user can have
236 // custom entries that don't get updated automatically
237 Collection<String> addedIds = Main.pref.getCollection("imagery.layers.addedIds");
238 Collection<String> newAddedIds = new TreeSet<>(addedIds);
239 for (ImageryInfo info : layers) {
240 for (ImageryInfo def : defaultLayers) {
241 if (isSimilar(def, info) && def.getId() != null && !addedIds.contains(def.getId())) {
242 if (!defaultLayerIds.containsKey(def.getId())) {
243 // ignore ids used more than once (have been purged from the map)
244 continue;
245 }
246 newAddedIds.add(def.getId());
247 if (info.getId() == null) {
248 info.setId(def.getId());
249 changed = true;
250 }
251 }
252 }
253 }
254 Main.pref.putCollection("imagery.layers.addedIds", newAddedIds);
255
256 // automatically update user entries with same id as a default entry
257 for (int i = 0; i < layers.size(); i++) {
258 ImageryInfo info = layers.get(i);
259 if (info.getId() == null) {
260 continue;
261 }
262 ImageryInfo matchingDefault = defaultLayerIds.get(info.getId());
263 if (matchingDefault != null && !matchingDefault.equalsPref(info)) {
264 layers.set(i, matchingDefault);
265 changed = true;
266 }
267 }
268
269 if (changed) {
270 save();
271 }
272 }
273
274 private static boolean isSimilar(ImageryInfo iiA, ImageryInfo iiB) {
275 if (iiA == null)
276 return false;
277 if (!iiA.getImageryType().equals(iiB.getImageryType()))
278 return false;
279 if (iiA.getId() != null && iiB.getId() != null) return iiA.getId().equals(iiB.getId());
280 return isSimilar(iiA.getUrl(), iiB.getUrl());
281 }
282
283 // some additional checks to respect extended URLs in preferences (legacy workaround)
284 private static boolean isSimilar(String a, String b) {
285 return Objects.equals(a, b) || (a != null && b != null && !a.isEmpty() && !b.isEmpty() && (a.contains(b) || b.contains(a)));
286 }
287
288 public void add(ImageryInfo info) {
289 layers.add(info);
290 }
291
292 public void remove(ImageryInfo info) {
293 layers.remove(info);
294 }
295
296 public void save() {
297 List<ImageryPreferenceEntry> entries = new ArrayList<>();
298 for (ImageryInfo info : layers) {
299 entries.add(new ImageryPreferenceEntry(info));
300 }
301 Main.pref.putListOfStructs("imagery.entries", entries, ImageryPreferenceEntry.class);
302 }
303
304 public List<ImageryInfo> getLayers() {
305 return Collections.unmodifiableList(layers);
306 }
307
308 public List<ImageryInfo> getDefaultLayers() {
309 return Collections.unmodifiableList(defaultLayers);
310 }
311
312 public static void addLayer(ImageryInfo info) {
313 instance.add(info);
314 instance.save();
315 }
316
317 public static void addLayers(Collection<ImageryInfo> infos) {
318 for (ImageryInfo i : infos) {
319 instance.add(i);
320 }
321 instance.save();
322 Collections.sort(instance.layers);
323 }
324
325 /**
326 * Get unique id for ImageryInfo.
327 *
328 * This takes care, that no id is used twice (due to a user error)
329 * @param info the ImageryInfo to look up
330 * @return null, if there is no id or the id is used twice,
331 * the corresponding id otherwise
332 */
333 public String getUniqueId(ImageryInfo info) {
334 if (info.getId() != null && layerIds.get(info.getId()) == info) {
335 return info.getId();
336 }
337 return null;
338 }
339}
Note: See TracBrowser for help on using the repository browser.