diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPlugin.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPlugin.java index 70d2f2d..3d00eb3 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPlugin.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPlugin.java @@ -3,7 +3,6 @@ package org.openstreetmap.josm.plugins.mapwithai; import static org.openstreetmap.josm.tools.I18n.tr; -import javax.swing.JMenu; import javax.swing.JMenuItem; import java.lang.reflect.InvocationTargetException; @@ -12,7 +11,6 @@ import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import org.openstreetmap.josm.actions.JosmAction; import org.openstreetmap.josm.actions.PreferencesAction; @@ -70,8 +68,6 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable { private final List destroyables; - private final PreferencesAction preferenceAction; - private final MapWithAIMenu mapwithaiMenu; private static final Map, Boolean> MENU_ENTRIES = new LinkedHashMap<>(); @@ -92,7 +88,7 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable { preferenceSetting = new MapWithAIPreferences(); // Add MapWithAI specific menu - JMenu dataMenu = MainApplication.getMenu().dataMenu; + final var dataMenu = MainApplication.getMenu().dataMenu; mapwithaiMenu = new MapWithAIMenu(); dataMenu.add(mapwithaiMenu); @@ -111,8 +107,8 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable { } // Add the preferences last to data - preferenceAction = PreferencesAction.forPreferenceTab(tr("MapWithAI Preferences"), tr("MapWithAI Preferences"), - MapWithAIPreferences.class); + final var preferenceAction = PreferencesAction.forPreferenceTab(tr("MapWithAI Preferences"), + tr("MapWithAI Preferences"), MapWithAIPreferences.class); MainMenu.add(mapwithaiMenu, preferenceAction); VALIDATORS.forEach(clazz -> { @@ -137,6 +133,9 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable { mapFrameInitialized(null, MainApplication.getMap()); OSMDownloadSource.addDownloadType(new MapWithAIDownloadSourceType()); MainApplication.worker.execute(() -> UpdateProd.doProd(info.mainversion)); + // Preload the MapWithAILayerInfo for the JOSM download window + // This reduces the amount of time taken for first button click by 100ms. + MainApplication.worker.execute(MapWithAILayerInfo::getInstance); destroyables.add(new MapWithAICopyProhibit()); } @@ -146,7 +145,7 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable { // Run in EDT to avoid blocking (has to be run before MapWithAIDownloadOptions // so its already initialized) GuiHelper.runInEDT(MapWithAILayerInfo::getInstance); - MapWithAIDownloadOptions mapWithAIDownloadOptions = new MapWithAIDownloadOptions(); + final var mapWithAIDownloadOptions = new MapWithAIDownloadOptions(); MainApplication.worker .execute(() -> GuiHelper.runInEDT(() -> mapWithAIDownloadOptions.addGui(DownloadDialog.getInstance()))); destroyables.add(mapWithAIDownloadOptions); @@ -154,9 +153,8 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable { @Override public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { - final Optional possibleMapWithAIObject = destroyables.stream() - .filter(MapWithAIObject.class::isInstance).map(MapWithAIObject.class::cast).findFirst(); - final MapWithAIObject mapWithAIObject = possibleMapWithAIObject.orElse(new MapWithAIObject()); + final var mapWithAIObject = destroyables.stream().filter(MapWithAIObject.class::isInstance) + .map(MapWithAIObject.class::cast).findFirst().orElseGet(MapWithAIObject::new); if ((oldFrame != null) && (oldFrame.statusLine != null)) { mapWithAIObject.removeMapStatus(oldFrame.statusLine); } diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/BoundingBoxMapWithAIDownloader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/BoundingBoxMapWithAIDownloader.java index f8a2b36..5bd0034 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/BoundingBoxMapWithAIDownloader.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/BoundingBoxMapWithAIDownloader.java @@ -4,14 +4,12 @@ package org.openstreetmap.josm.plugins.mapwithai.backend; import static org.openstreetmap.josm.tools.I18n.tr; import javax.json.Json; -import javax.json.JsonObject; import javax.json.JsonReader; import javax.json.JsonStructure; import javax.json.JsonValue; import javax.json.stream.JsonParser; import javax.swing.JOptionPane; -import java.awt.geom.Area; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -24,13 +22,10 @@ import java.time.Instant; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.List; import java.util.Map; -import java.util.OptionalInt; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.DataSource; @@ -54,6 +49,7 @@ import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo; import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; import org.openstreetmap.josm.plugins.mapwithai.tools.MapPaintUtils; +import org.openstreetmap.josm.spi.preferences.Config; import org.openstreetmap.josm.tools.HttpClient; import org.openstreetmap.josm.tools.JosmRuntimeException; import org.openstreetmap.josm.tools.Logging; @@ -64,11 +60,6 @@ import org.openstreetmap.josm.tools.Logging; * @author Taylor Smock */ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { - /** - * The time that the data URL's were last updated. See #22683 for why this is - * necessary. - */ - private static Instant DATA_LAST_UPDATED = Instant.EPOCH; private final String url; private final boolean crop; private final int start; @@ -157,23 +148,22 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { } catch (OsmTransferException e) { if (e.getCause() instanceof SocketTimeoutException && (System.nanoTime() - startTime) > 30_000_000_000L) { updateLastErrorTime(System.nanoTime()); - Notification note = new Notification(); + final var note = new Notification(); GuiHelper.runInEDT(() -> note.setContent(tr( "Attempting to download data in the background. This may fail or succeed in a few minutes."))); GuiHelper.runInEDT(note::show); } else if (e.getCause() instanceof IllegalDataException) { final Instant lastUpdated; - final Instant now; + final var now = Instant.now(); synchronized (BoundingBoxMapWithAIDownloader.class) { - lastUpdated = DATA_LAST_UPDATED; - now = Instant.now(); - DATA_LAST_UPDATED = now; + lastUpdated = Instant.ofEpochSecond(Config.getPref().getLong("mapwithai.layerinfo.lastupdated", 0)); + Config.getPref().putLong("mapwithai.layerinfo.lastupdated", now.getEpochSecond()); } // Only force an update if the last update time is sufficiently old. if (now.toEpochMilli() - lastUpdated.toEpochMilli() > TimeUnit.MINUTES.toMillis(10)) { MapWithAILayerInfo.getInstance().loadDefaults(true, MapWithAIDataUtils.getForkJoinPool(), false, () -> GuiHelper.runInEDT(() -> { - Notification notification = new Notification(tr( + final var notification = new Notification(tr( "MapWithAI layers reloaded. Removing and re-adding the MapWithAI layer may be necessary.")); notification.setIcon(JOptionPane.INFORMATION_MESSAGE); notification.setDuration(Notification.TIME_LONG); @@ -185,8 +175,8 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { } } // Just in case something happens, try again... - DataSet ds = new DataSet(); - GetDataRunnable runnable = new GetDataRunnable(downloadArea, ds, NullProgressMonitor.INSTANCE); + final var ds = new DataSet(); + final var runnable = new GetDataRunnable(downloadArea, ds, NullProgressMonitor.INSTANCE); runnable.setMapWithAIInfo(info); MainApplication.worker.execute(() -> { try { @@ -210,12 +200,11 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { * @return The dataset to send to the server */ private static DataSet getConflationData(Bounds bound) { - Area area = DataSource.getDataSourceArea(Collections.singleton(new DataSource(bound, ""))); + final var area = DataSource.getDataSourceArea(Collections.singleton(new DataSource(bound, ""))); if (area != null) { - List layers = MainApplication - .getLayerManager().getLayersOfType(OsmDataLayer.class).stream().filter(l -> l.getDataSet() - .getDataSourceBounds().stream().anyMatch(b -> area.contains(bound.asRect()))) - .collect(Collectors.toList()); + final var layers = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream().filter( + l -> l.getDataSet().getDataSourceBounds().stream().anyMatch(b -> area.contains(bound.asRect()))) + .toList(); return layers.stream().max(Comparator.comparingInt(l -> l.getDataSet().allPrimitives().size())) .map(OsmDataLayer::getDataSet).orElse(null); } @@ -229,7 +218,7 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { @Override protected DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException { DataSet ds; - String contentType = this.activeConnection.getResponse().getContentType(); + final var contentType = this.activeConnection.getResponse().getContentType(); if (Arrays.asList("text/json", "application/json", "application/geo+json").contains(contentType) // Fall back to Esri Feature Server check. They don't always indicate a json // return type. :( @@ -238,15 +227,14 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { // if we need to make additional calls try (JsonReader reader = Json.createReader(source)) { JsonStructure structure = reader.read(); - try (ByteArrayInputStream bais = new ByteArrayInputStream( - structure.toString().getBytes(StandardCharsets.UTF_8))) { + try (var bais = new ByteArrayInputStream(structure.toString().getBytes(StandardCharsets.UTF_8))) { ds = GeoJSONReader.parseDataSet(bais, progressMonitor); } catch (IOException e) { throw new UncheckedIOException(e); } /* We should only call this from the "root" call */ if (this.start == 0 && structure.getValueType() == JsonValue.ValueType.OBJECT) { - final JsonObject serverObj = structure.asJsonObject(); + final var serverObj = structure.asJsonObject(); final boolean exceededTransferLimit = serverObj.entrySet().stream() .filter(entry -> "properties".equals(entry.getKey()) && entry.getValue().getValueType() == JsonValue.ValueType.OBJECT) @@ -254,7 +242,7 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { .map(obj -> obj.getBoolean("exceededTransferLimit", false)).findFirst().orElse(false); if (exceededTransferLimit && this.info.getSourceType() == MapWithAIType.ESRI_FEATURE_SERVER) { final int size = serverObj.getJsonArray("features").size(); - final DataSet other = this.getAdditionalEsriData(progressMonitor, + final var other = this.getAdditionalEsriData(progressMonitor, this.getRequestForBbox(this.lon1, this.lat1, this.lon2, this.lat2), size); ds.mergeFrom(other, progressMonitor.createSubTaskMonitor(0, false)); } @@ -280,15 +268,15 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { } private DataSet getAdditionalEsriData(ProgressMonitor progressMonitor, String baseUrl, int size) { - DataSet returnDs = new DataSet(); + final var returnDs = new DataSet(); try { - HttpClient client = HttpClient.create(new URL(baseUrl + "&returnCountOnly=true")); + final var client = HttpClient.create(new URL(baseUrl + "&returnCountOnly=true")); int objects = Integer.MIN_VALUE; try (InputStream is = client.connect().getContent(); JsonParser parser = Json.createParser(is)) { while (parser.hasNext()) { - JsonParser.Event event = parser.next(); + final var event = parser.next(); if (event == JsonParser.Event.START_OBJECT) { - OptionalInt objCount = parser.getObjectStream() + final var objCount = parser.getObjectStream() .filter(entry -> "properties".equals(entry.getKey())) .map(entry -> entry.getValue().asJsonObject()) .mapToInt(properties -> properties.getInt("count")).findFirst(); @@ -310,7 +298,7 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { // We have already downloaded some of the objects. Set the ticks. progressMonitor.worked(size); while (progressMonitor.getTicks() < progressMonitor.getTicksCount() - 1) { - DataSet next = new BoundingBoxMapWithAIDownloader(this.downloadArea, this.info, this.crop, + final var next = new BoundingBoxMapWithAIDownloader(this.downloadArea, this.info, this.crop, this.start + size).parseOsm(progressMonitor.createSubTaskMonitor(0, false)); progressMonitor.worked((int) next.allPrimitives().stream().filter(IPrimitive::isTagged).count()); returnDs.mergeFrom(next); @@ -348,7 +336,7 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader { @Override protected void adaptRequest(HttpClient request) { - final StringBuilder defaultUserAgent = new StringBuilder(); + final var defaultUserAgent = new StringBuilder(); request.setReadTimeout(DEFAULT_TIMEOUT); defaultUserAgent.append(request.getHeaders().get("User-Agent")); if (defaultUserAgent.toString().trim().length() == 0) { diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java index f6efc54..15214b8 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java @@ -59,6 +59,11 @@ public final class MapWithAIDataUtils { public static final int MAXIMUM_SIDE_DIMENSIONS = 10_000; // RapiD is about 1 km, max is 10 km, but 10 km causes // timeouts private static final int TOO_MANY_BBOXES = 4; + /** + * {@code true} if we need a fork join pool that is not the + * {@link ForkJoinPool#commonPool()} + */ + private static Boolean requiresForkJoinPool; private static ForkJoinPool forkJoinPool; static final Object LAYER_LOCK = new Object(); @@ -248,7 +253,10 @@ public final class MapWithAIDataUtils { * @return The {@link ForkJoinPool} for MapWithAI use. */ public static ForkJoinPool getForkJoinPool() { - if (Utils.isRunningWebStart() || System.getSecurityManager() != null) { + if (requiresForkJoinPool == null) { + requiresForkJoinPool = Utils.isRunningWebStart() || System.getSecurityManager() != null; + } + if (requiresForkJoinPool) { synchronized (MapWithAIDataUtils.class) { if (Objects.isNull(forkJoinPool) || forkJoinPool.isShutdown()) { forkJoinPool = Utils.newForkJoinPool(MapWithAIPlugin.NAME.concat(".forkjoinpoolthreads"), diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java index 39f7407..a44c199 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java @@ -9,6 +9,7 @@ import javax.json.JsonObject; import javax.json.JsonValue; import javax.json.stream.JsonParser; +import java.io.Serial; import java.io.Serializable; import java.io.StringReader; import java.util.ArrayList; @@ -140,7 +141,7 @@ public class MapWithAIInfo extends @Override public String toString() { - StringBuilder s = new StringBuilder("MapWithAIPreferenceEntry [name=").append(name); + final var s = new StringBuilder("MapWithAIPreferenceEntry [name=").append(name); if (id != null) { s.append(" id=").append(id); } @@ -154,6 +155,7 @@ public class MapWithAIInfo extends */ public static class MapWithAIInfoCategoryComparator implements Comparator, Serializable { + @Serial private static final long serialVersionUID = -7992892476979310835L; @Override @@ -219,15 +221,8 @@ public class MapWithAIInfo extends */ public MapWithAIInfo(String name, String url, String type, String eulaAcceptanceRequired, String id) { this(name, url, id); - MapWithAIType t = MapWithAIType.fromString(type); this.setEulaAcceptanceRequired(eulaAcceptanceRequired); - if (t != null) { - super.setSourceType(t); - } else if (type != null && !type.isEmpty()) { - throw new IllegalArgumentException("unknown type: " + type); - } else { - super.setSourceType(MapWithAIType.THIRD_PARTY.getDefault()); - } + super.setSourceType(MapWithAIType.fromString(type)); } /** @@ -243,7 +238,7 @@ public class MapWithAIInfo extends setCookies(e.cookies); setEulaAcceptanceRequired(e.eula); if (e.parameters != null) { - try (JsonParser parser = Json.createParser(new StringReader(e.parameters))) { + try (var parser = Json.createParser(new StringReader(e.parameters))) { if (parser.hasNext() && JsonParser.Event.START_ARRAY == parser.next()) { setParameters(parser.getArray()); } @@ -379,7 +374,7 @@ public class MapWithAIInfo extends return true; } if (first != null && second != null && first.size() == second.size()) { - for (JsonObject value : Utils.filteredCollection(first, JsonObject.class)) { + for (var value : Utils.filteredCollection(first, JsonObject.class)) { if (value.containsKey(PARAMETER_STRING) && value.get(PARAMETER_STRING).getValueType() == JsonValue.ValueType.STRING && Utils.filteredCollection(second, JsonObject.class).stream() @@ -483,35 +478,42 @@ public class MapWithAIInfo extends return getParametersString(this.conflationParameters); } + /** + * Check if this source will have a valid URL when {@link #getUrlExpanded()} is + * called + * + * @return {@code true} if this source will have a valid url + */ + public boolean hasValidUrl() { + return this.url != null || (this.isConflated() && this.conflationUrl != null); + } + public String getUrlExpanded() { - StringBuilder sb; if (this.isConflated()) { - sb = getConflationUrl(); + return getConflationUrl().toString(); } else { - sb = getNonConflatedUrl(); + return getNonConflatedUrl().toString(); } - return sb.toString(); } private StringBuilder getConflationUrl() { if (conflationUrl == null) { return getNonConflatedUrl(); } - StringBuilder sb = new StringBuilder(); + final var sb = new StringBuilder(); if (this.conflationUrl.contains("{id}") && this.id != null) { sb.append(conflationUrl.replace("{id}", this.id)); } else if (this.conflationUrl.contains("{id}")) { // We need to trigger synchronization. This means that the current download // may won't be conflated. // But this should automatically correct the behavior for the next attempt. - final MapWithAILayerInfo mwli = MapWithAILayerInfo.getInstance(); + final var mwli = MapWithAILayerInfo.getInstance(); mwli.load(false, () -> { - Optional defaultLayer = mwli.getAllDefaultLayers().stream().filter(this::equals) - .findFirst(); + final var defaultLayer = mwli.getAllDefaultLayers().stream().filter(this::equals).findFirst(); if (defaultLayer.isPresent()) { this.id = defaultLayer.get().id; } else { - MapWithAIInfo newInfo = mwli.getAllDefaultLayers().stream() + final var newInfo = mwli.getAllDefaultLayers().stream() .filter(layer -> Objects.equals(this.url, layer.url) && Objects.nonNull(layer.id)) .findFirst().orElse(this); this.id = newInfo.id; @@ -530,7 +532,7 @@ public class MapWithAIInfo extends } private StringBuilder getNonConflatedUrl() { - StringBuilder sb = new StringBuilder(); + final var sb = new StringBuilder(); if (url != null && !url.trim().isEmpty()) { sb.append(url); if (MapWithAIType.ESRI_FEATURE_SERVER == sourceType) { @@ -624,7 +626,7 @@ public class MapWithAIInfo extends * @param parameters Set the conflation parameters */ public void setConflationParameters(JsonArray parameters) { - this.conflationParameters = parameters != null ? Json.createArrayBuilder(parameters).build() : null; + this.conflationParameters = parameters; } /** diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java index 18858cd..80779c0 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java @@ -7,6 +7,8 @@ import javax.annotation.Nonnull; import javax.swing.SwingUtilities; import java.io.IOException; +import java.io.Serial; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -16,7 +18,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; @@ -80,7 +81,7 @@ public class MapWithAILayerInfo { if (instance != null) { return instance; } - final AtomicBoolean finished = new AtomicBoolean(); + final var finished = new AtomicBoolean(); synchronized (MapWithAILayerInfo.class) { if (instance == null) { instance = new MapWithAILayerInfo(() -> { @@ -156,16 +157,6 @@ public class MapWithAILayerInfo { layerIds.clear(); } - /** - * Loads the custom as well as default imagery entries. - * - * @param fastFail whether opening HTTP connections should fail fast, see - * {@link ImageryReader#setFastFail(boolean)} - */ - public void load(boolean fastFail) { - load(fastFail, null); - } - /** * Loads the custom as well as default imagery entries. * @@ -175,12 +166,12 @@ public class MapWithAILayerInfo { */ public void load(boolean fastFail, FinishListener listener) { clear(); - List entries = StructUtils.getListOfStructs(Config.getPref(), - CONFIG_PREFIX + "entries", null, MapWithAIPreferenceEntry.class); + final var entries = StructUtils.getListOfStructs(Config.getPref(), CONFIG_PREFIX + "entries", null, + MapWithAIPreferenceEntry.class); if (entries != null) { for (MapWithAIPreferenceEntry prefEntry : entries) { try { - MapWithAIInfo i = new MapWithAIInfo(prefEntry); + final var i = new MapWithAIInfo(prefEntry); add(i); } catch (IllegalArgumentException e) { Logging.warn("Unable to load imagery preference entry:" + e); @@ -200,7 +191,7 @@ public class MapWithAILayerInfo { /** * Loads the available imagery entries. - * + *

* The data is downloaded from the JOSM website (or loaded from cache). Entries * marked as "default" are added to the user selection, if not already present. * @@ -214,7 +205,7 @@ public class MapWithAILayerInfo { * @since 12634 */ public void loadDefaults(boolean clearCache, ForkJoinPool worker, boolean fastFail, FinishListener listener) { - final DefaultEntryLoader loader = new DefaultEntryLoader(clearCache, fastFail); + final var loader = new DefaultEntryLoader(clearCache, fastFail); if (this.finishListenerListenerList == null) { this.finishListenerListenerList = ListenerList.create(); } @@ -222,7 +213,7 @@ public class MapWithAILayerInfo { this.finishListenerListenerList.addListener(listener); } if (worker == null) { - PleaseWaitRunnable pleaseWaitRunnable = new PleaseWaitRunnable(tr("Update default entries")) { + final var pleaseWaitRunnable = new PleaseWaitRunnable(tr("Update default entries")) { @Override protected void cancel() { loader.canceled = true; @@ -262,6 +253,7 @@ public class MapWithAILayerInfo { */ class DefaultEntryLoader extends RecursiveTask> { + @Serial private static final long serialVersionUID = 12550342142551680L; private final boolean clearCache; private final boolean fastFail; @@ -329,7 +321,7 @@ public class MapWithAILayerInfo { reader = new MapWithAISourceReader(source); this.reader.setClearCache(this.clearCache); reader.setFastFail(fastFail); - Collection result = reader.parse().orElse(Collections.emptyList()); + final var result = reader.parse().orElse(Collections.emptyList()); // This is called here to "pre-cache" the layer information, to avoid blocking // the EDT this.updateEsriLayers(result); @@ -346,9 +338,9 @@ public class MapWithAILayerInfo { * @param layers The layers to update */ private void updateEsriLayers(@Nonnull final Collection layers) { - for (MapWithAIInfo layer : layers) { + for (var layer : layers) { if (MapWithAIType.ESRI == layer.getSourceType()) { - for (ForkJoinTask future : parseEsri(layer)) { + for (var future : parseEsri(layer)) { try { allDefaultLayers.add(future.get()); } catch (InterruptedException e) { @@ -385,8 +377,9 @@ public class MapWithAILayerInfo { if (!loadError && !defaultLayerIds.isEmpty()) { dropOldEntries(); } - final ListenerList listenerList = MapWithAILayerInfo.this.finishListenerListenerList; + final var listenerList = MapWithAILayerInfo.this.finishListenerListenerList; MapWithAILayerInfo.this.finishListenerListenerList = null; + Config.getPref().putLong("mapwithai.layerinfo.lastupdated", Instant.now().getEpochSecond()); if (listenerList != null) { listenerList.fireEvent(FinishListener::onFinish); } @@ -415,8 +408,8 @@ public class MapWithAILayerInfo { */ private void buildIdMap(List lst, Map idMap) { idMap.clear(); - Set notUnique = new HashSet<>(); - for (MapWithAIInfo i : lst) { + final var notUnique = new HashSet(); + for (var i : lst) { if (i.getId() != null) { if (idMap.containsKey(i.getId())) { notUnique.add(i.getId()); @@ -427,7 +420,7 @@ public class MapWithAILayerInfo { idMap.put(i.getId(), i); } } - for (String i : notUnique) { + for (var i : notUnique) { idMap.remove(i); } } @@ -441,14 +434,14 @@ public class MapWithAILayerInfo { */ public void updateEntriesFromDefaults(boolean dropold) { // add new default entries to the user selection - boolean changed = false; - Collection knownDefaults = new TreeSet<>(Config.getPref().getList(CONFIG_PREFIX + "layers.default")); - Collection newKnownDefaults = new TreeSet<>(); + var changed = false; + final var knownDefaults = new TreeSet<>(Config.getPref().getList(CONFIG_PREFIX + "layers.default")); + final var newKnownDefaults = new TreeSet(); synchronized (defaultLayers) { - for (MapWithAIInfo def : defaultLayers) { + for (var def : defaultLayers) { if (def.isDefaultEntry()) { - boolean isKnownDefault = false; - for (String entry : knownDefaults) { + var isKnownDefault = false; + for (var entry : knownDefaults) { if (entry.equals(def.getId())) { isKnownDefault = true; newKnownDefaults.add(entry); @@ -463,11 +456,11 @@ public class MapWithAILayerInfo { break; } } - boolean isInUserList = false; + var isInUserList = false; if (!isKnownDefault) { if (def.getId() != null) { newKnownDefaults.add(def.getId()); - for (MapWithAIInfo i : layers) { + for (var i : layers) { if (isSimilar(def, i)) { isInUserList = true; break; @@ -490,12 +483,12 @@ public class MapWithAILayerInfo { Config.getPref().putList(CONFIG_PREFIX + "layers.default", new ArrayList<>(newKnownDefaults)); // automatically update user entries with same id as a default entry - for (int i = 0; i < layers.size(); i++) { - MapWithAIInfo info = layers.get(i); + for (var i = 0; i < layers.size(); i++) { + final var info = layers.get(i); if (info.getId() == null) { continue; } - MapWithAIInfo matchingDefault = defaultLayerIds.get(info.getId()); + final var matchingDefault = defaultLayerIds.get(info.getId()); if (matchingDefault != null && !matchingDefault.equalsPref(info)) { layers.set(i, matchingDefault); Logging.info(tr("Update imagery ''{0}''", info.getName())); @@ -514,9 +507,9 @@ public class MapWithAILayerInfo { * @since 11527 */ public void dropOldEntries() { - List drop = new ArrayList<>(); + final var drop = new ArrayList(); - for (Map.Entry info : layerIds.entrySet()) { + for (var info : layerIds.entrySet()) { if (!defaultLayerIds.containsKey(info.getKey())) { remove(info.getValue()); drop.add(info.getKey()); @@ -525,7 +518,7 @@ public class MapWithAILayerInfo { } if (!drop.isEmpty()) { - for (String id : drop) { + for (var id : drop) { layerIds.remove(id); } save(); @@ -573,9 +566,9 @@ public class MapWithAILayerInfo { * Save the list of imagery entries to preferences. */ public synchronized void save() { - List entries = new ArrayList<>(); + final var entries = new ArrayList(); synchronized (layers) { - for (MapWithAIInfo info : layers) { + for (var info : layers) { entries.add(new MapWithAIPreferenceEntry(info)); } } @@ -625,8 +618,7 @@ public class MapWithAILayerInfo { return layers.stream() .filter(i -> i.getCategory() != MapWithAICategory.PREVIEW && !i.getAdditionalCategories().contains(MapWithAICategory.PREVIEW)) - .filter(info -> !Utils.isBlank(info.getUrlExpanded()) && !Utils.isBlank(info.getUrl())) - .collect(Collectors.toList()); + .filter(MapWithAIInfo::hasValidUrl).collect(Collectors.toList()); } /** @@ -652,7 +644,7 @@ public class MapWithAILayerInfo { /** * Get unique id for ImageryInfo. - * + *

* This takes care, that no id is used twice (due to a user error) * * @param info the ImageryInfo to look up diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadOptions.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadOptions.java index 248a0f9..505f451 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadOptions.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/download/MapWithAIDownloadOptions.java @@ -37,7 +37,7 @@ public class MapWithAIDownloadOptions extends JPanel implements DownloadSelectio */ public MapWithAIDownloadOptions() { optionPanel = new JPanel(new GridBagLayout()); - JPanel infoHeader = new JPanel(); + final var infoHeader = new JPanel(); infoHeader.add(new JLabel("Browse and activate extra data sets to facilitate your mapping needs.")); optionPanel.add(infoHeader, GBC.eol().fill(GridBagConstraints.HORIZONTAL).anchor(GridBagConstraints.NORTH)); mapwithaiProvidersPanel = new MapWithAIProvidersPanel(this); @@ -63,7 +63,7 @@ public class MapWithAIDownloadOptions extends JPanel implements DownloadSelectio @Override public void destroy() { if (this.iGui != null) { - for (JComponent component : getJComponents(this.iGui.getComponents())) { + for (var component : getJComponents(this.iGui.getComponents())) { removeFromComponent(component); } this.iGui.removeDownloadAreaListener(this); @@ -77,7 +77,7 @@ public class MapWithAIDownloadOptions extends JPanel implements DownloadSelectio } private boolean removeFromComponent(JComponent component) { - for (JComponent newComponent : getJComponents(component.getComponents())) { + for (var newComponent : getJComponents(component.getComponents())) { if (optionPanel.equals(newComponent)) { component.remove(optionPanel); return true; diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/MapWithAIPreferences.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/MapWithAIPreferences.java index 3bd24fd..022cf86 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/MapWithAIPreferences.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/MapWithAIPreferences.java @@ -25,10 +25,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.TreeMap; +import java.util.stream.Collectors; import org.openstreetmap.josm.actions.ExpertToggleAction; -import org.openstreetmap.josm.data.preferences.BooleanProperty; import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting; import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; import org.openstreetmap.josm.gui.preferences.advanced.PrefEntry; @@ -68,17 +67,14 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { } private static void fillReplacementTagDisplayData(List list) { - final Map current = new TreeMap<>(MapWithAIPreferenceHelper.getReplacementTags()); - for (final Map.Entry entry : current.entrySet()) { - list.add( - new PrefEntry(entry.getKey(), new StringSetting(entry.getValue()), new StringSetting(null), false)); - } + MapWithAIPreferenceHelper.getReplacementTags().forEach( + (key, value) -> list.add(new PrefEntry(key, new StringSetting(value), new StringSetting(null), false))); } @Override public void addGui(PreferenceTabbedPane gui) { - final JPanel p = gui.createPreferenceTab(this); - final JTabbedPane panel = getTabPane(); + final var p = gui.createPreferenceTab(this); + final var panel = getTabPane(); if (panel.getTabCount() == 0) { panel.addTab(tr("Servers"), getServerList(gui)); panel.addTab(tr("Settings"), getSettingsPanel(gui)); @@ -91,12 +87,12 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { } private Component getSettingsPanel(PreferenceTabbedPane gui) { - final JPanel pane = new JPanel(new GridBagLayout()); - final int width = 200; - final int height = 200; - final JLabel switchLayer = new JLabel(tr("Automatically switch layers")); - final JLabel maximumAddition = new JLabel(tr("Maximum features (add)")); - final JLabel mergeBuildingWithAddress = new JLabel(tr("Merge address nodes and buildings")); + final var pane = new JPanel(new GridBagLayout()); + final var width = 200; + final var height = 200; + final var switchLayer = new JLabel(tr("Automatically switch layers")); + final var maximumAddition = new JLabel(tr("Maximum features (add)")); + final var mergeBuildingWithAddress = new JLabel(tr("Merge address nodes and buildings")); switchLayer.setToolTipText( tr("If checked, automatically switch from the {0} layer to the OSM layer when objects are added", @@ -116,9 +112,9 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { pane.setAlignmentY(Component.TOP_ALIGNMENT); pane.setAlignmentX(Component.LEFT_ALIGNMENT); - final GBC first = GBC.std().weight(0, 1).anchor(GridBagConstraints.WEST); - final GBC second = GBC.eol().fill(GridBagConstraints.HORIZONTAL); - final GBC buttonInsets = GBC.std().insets(5, 5, 0, 0); + final var first = GBC.std().weight(0, 1).anchor(GridBagConstraints.WEST); + final var second = GBC.eol().fill(GridBagConstraints.HORIZONTAL); + final var buttonInsets = GBC.std().insets(5, 5, 0, 0); pane.add(switchLayer, first); @@ -132,11 +128,11 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { pane.add(mergeBuildingWithAddress, first); pane.add(mergeBuildingAddressCheckBox, second); - final Component expertHorizontalGlue = Box.createHorizontalGlue(); + final var expertHorizontalGlue = Box.createHorizontalGlue(); pane.add(expertHorizontalGlue, GBC.eol().fill(GridBagConstraints.HORIZONTAL)); - final JLabel previewFeatureSets = new JLabel(tr("Show Preview DataSets")); - final JCheckBox previewFeatureSetCheckbox = new JCheckBox(); - BooleanProperty previewFeatureSetProperty = MapWithAILayerInfo.SHOW_PREVIEW; + final var previewFeatureSets = new JLabel(tr("Show Preview DataSets")); + final var previewFeatureSetCheckbox = new JCheckBox(); + final var previewFeatureSetProperty = MapWithAILayerInfo.SHOW_PREVIEW; previewFeatureSetCheckbox.setToolTipText(tr("If selected, show datasets which may have various issues")); previewFeatureSetCheckbox.setSelected(Boolean.TRUE.equals(previewFeatureSetProperty.get())); previewFeatureSetCheckbox @@ -144,19 +140,19 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { pane.add(previewFeatureSets, first); pane.add(previewFeatureSetCheckbox, second); - final JLabel replacementTags = new JLabel(tr("Replacement Tags (to be replaced on download)")); + final var replacementTags = new JLabel(tr("Replacement Tags (to be replaced on download)")); pane.add(replacementTags, first); - final JScrollPane scroll2 = new JScrollPane(replacementPreferenceTable); + final var scroll2 = new JScrollPane(replacementPreferenceTable); pane.add(scroll2, GBC.eol().fill(GridBagConstraints.BOTH)); scroll2.setPreferredSize(new Dimension(width, height)); pane.add(new JLabel(), first); - final JPanel replaceAddEditDeleteScroll2 = new JPanel(new GridBagLayout()); + final var replaceAddEditDeleteScroll2 = new JPanel(new GridBagLayout()); pane.add(replaceAddEditDeleteScroll2, second); - final JButton addScroll2 = new JButton(tr("Add")); + final var addScroll2 = new JButton(tr("Add")); replaceAddEditDeleteScroll2.add(addScroll2, buttonInsets); addScroll2.addActionListener(e -> { - final PrefEntry pe = replacementPreferenceTable.addPreference(gui); + final var pe = replacementPreferenceTable.addPreference(gui); if ((pe != null) && (pe.getValue() instanceof StringSetting)) { replacementTableDisplayData.add(pe); Collections.sort(replacementTableDisplayData); @@ -164,19 +160,19 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { } }); - final JButton editScroll2 = new JButton(tr("Edit")); + final var editScroll2 = new JButton(tr("Edit")); replaceAddEditDeleteScroll2.add(editScroll2, buttonInsets); editScroll2.addActionListener(e -> { - final List toEdit = replacementPreferenceTable.getSelectedItems(); + final var toEdit = replacementPreferenceTable.getSelectedItems(); if (toEdit.size() == MAX_SELECTED_TO_EDIT) { replacementPreferenceTable.editPreference(gui); } }); - final JButton deleteScroll2 = new JButton(tr("Delete")); + final var deleteScroll2 = new JButton(tr("Delete")); replaceAddEditDeleteScroll2.add(deleteScroll2, buttonInsets); deleteScroll2.addActionListener(e -> { - final List toRemove = replacementPreferenceTable.getSelectedItems(); + final var toRemove = replacementPreferenceTable.getSelectedItems(); if (!toRemove.isEmpty()) { replacementTableDisplayData.removeAll(toRemove); } @@ -185,7 +181,7 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { pane.add(Box.createHorizontalGlue(), second); - JButton kaartLogo = new JButton(ImageProvider.getIfAvailable("kaart") == null ? null + final var kaartLogo = new JButton(ImageProvider.getIfAvailable("kaart") == null ? null : new ImageProvider("kaart").setHeight(ImageProvider.ImageSizes.SETTINGS_TAB.getAdjustedHeight()) .get()); kaartLogo.setToolTipText(tr("Link to source code repository")); @@ -198,7 +194,7 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { kaartLogo.setCursor(new Cursor(Cursor.HAND_CURSOR)); pane.add(kaartLogo, GBC.std().anchor(GridBagConstraints.WEST)); - JButton mapWithAILogo = new JButton(ImageProvider.getIfAvailable("mapwithai_text") == null ? null + final var mapWithAILogo = new JButton(ImageProvider.getIfAvailable("mapwithai_text") == null ? null : new ImageProvider("mapwithai_text") .setHeight(ImageProvider.ImageSizes.SETTINGS_TAB.getAdjustedHeight()).get()); mapWithAILogo.setCursor(new Cursor(Cursor.HAND_CURSOR)); @@ -218,22 +214,21 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting { @Override public boolean ok() { MapWithAIPreferenceHelper.setSwitchLayers(switchLayerCheckBox.isSelected(), true); - final Object value = maximumAdditionSpinner.getValue(); + final var value = maximumAdditionSpinner.getValue(); MapWithAIPreferenceHelper.setMergeBuildingAddress(this.mergeBuildingAddressCheckBox.isSelected(), true); - if (value instanceof Number) { - MapWithAIPreferenceHelper.setMaximumAddition(((Number) value).intValue(), true); + if (value instanceof Number number) { + MapWithAIPreferenceHelper.setMaximumAddition(number.intValue(), true); } MapWithAILayerInfo.getInstance().save(); MapWithAILayerInfo.getInstance().clear(); - MapWithAILayerInfo.getInstance().load(false); + MapWithAILayerInfo.getInstance().load(false, null); MapWithAIPreferenceHelper.setReplacementTags(convertReplacementPrefToMap(replacementTableDisplayData)); return false; } private static Map convertReplacementPrefToMap(List displayData) { - final Map returnMap = displayData.isEmpty() ? Collections.emptyMap() : new TreeMap<>(); - displayData.forEach(entry -> returnMap.put(entry.getKey(), entry.getValue().getValue().toString())); - return returnMap; + return displayData.stream() + .collect(Collectors.toMap(PrefEntry::getKey, entry -> entry.getValue().getValue().toString())); } @Override diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java index 032a8db..59607c2 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/gui/preferences/mapwithai/MapWithAIProvidersPanel.java @@ -4,6 +4,7 @@ package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai; import static org.openstreetmap.josm.tools.I18n.marktr; import static org.openstreetmap.josm.tools.I18n.tr; +import javax.annotation.Nonnull; import javax.swing.AbstractAction; import javax.swing.Box; import javax.swing.JButton; @@ -18,7 +19,6 @@ import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableColumnModel; import java.awt.Color; import java.awt.Component; @@ -28,8 +28,8 @@ import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.geom.Rectangle2D; import java.io.IOException; +import java.io.Serial; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -37,7 +37,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; @@ -47,13 +46,10 @@ import java.util.stream.Stream; import org.openstreetmap.gui.jmapviewer.Coordinate; import org.openstreetmap.gui.jmapviewer.MapPolygonImpl; import org.openstreetmap.gui.jmapviewer.MapRectangleImpl; -import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon; import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; -import org.openstreetmap.josm.data.imagery.Shape; import org.openstreetmap.josm.data.preferences.NamedColorProperty; import org.openstreetmap.josm.gui.MainApplication; import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser; @@ -92,6 +88,7 @@ public class MapWithAIProvidersPanel extends JPanel { SHOW_ACTIVE } + @Serial private static final long serialVersionUID = -5876039771496409422L; // Public JTables and JosmMapViewer /** The table of active providers **/ @@ -112,9 +109,9 @@ public class MapWithAIProvidersPanel extends JPanel { // Public models /** The model of active providers **/ - public static final MapWithAILayerTableModel ACTIVE_MODEL = new MapWithAILayerTableModel(); + static final MapWithAILayerTableModel ACTIVE_MODEL = new MapWithAILayerTableModel(); /** The model of default providers **/ - public static final MapWithAIDefaultLayerTableModel DEFAULT_MODEL = new MapWithAIDefaultLayerTableModel(); + static final MapWithAIDefaultLayerTableModel DEFAULT_MODEL = new MapWithAIDefaultLayerTableModel(); // Public JToolbars /** The toolbar on the right of active providers **/ @@ -188,24 +185,24 @@ public class MapWithAIProvidersPanel extends JPanel { public final Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { T obj = (T) value; - JLabel label = (JLabel) super.getTableCellRendererComponent(table, mapper.apply(obj), isSelected, hasFocus, - row, column); - Color defaultColor = UIManager.getColor("Table.background"); - Color selectedColor = UIManager.getColor("Table.selectionBackground"); + final var label = (JLabel) super.getTableCellRendererComponent(table, mapper.apply(obj), isSelected, + hasFocus, row, column); + final var defaultColor = UIManager.getColor("Table.background"); + final var selectedColor = UIManager.getColor("Table.selectionBackground"); GuiHelper.setBackgroundReadable(label, defaultColor); GuiHelper.setBackgroundReadable(label, isSelected ? selectedColor : defaultColor); if (this.highlightIfActive && obj != null) { - MapWithAIInfo info = obj instanceof MapWithAIInfo ? (MapWithAIInfo) obj : reverseMapper.apply(obj); + final var info = obj instanceof MapWithAIInfo ? (MapWithAIInfo) obj : reverseMapper.apply(obj); if (info == null) { GuiHelper.setBackgroundReadable(label, defaultColor); } else { if (MapWithAILayerTableModel.contains(info)) { - Color t = IMAGERY_BACKGROUND_COLOR.get(); + final var t = IMAGERY_BACKGROUND_COLOR.get(); GuiHelper.setBackgroundReadable(label, isSelected ? t.darker() : t); } else if (this.area != null && info.getBounds() != null && (this.area.intersects(info.getBounds()) || info.getBounds().intersects(this.area))) { - Color t = MAPWITHAI_AREA_BACKGROUND_COLOR.get(); + final var t = MAPWITHAI_AREA_BACKGROUND_COLOR.get(); GuiHelper.setBackgroundReadable(label, isSelected ? t.darker() : t); } else { GuiHelper.setBackgroundReadable(label, isSelected ? selectedColor : defaultColor); @@ -300,6 +297,7 @@ public class MapWithAIProvidersPanel extends JPanel { private static class MapWithAINameTableCellRenderer extends MapWithAIProvidersPanel.MapWithAITableCellRenderer { + @Serial private static final long serialVersionUID = 6669934435517244629L; MapWithAINameTableCellRenderer(boolean showActive) { @@ -308,6 +306,26 @@ public class MapWithAIProvidersPanel extends JPanel { } } + private static class ProvidersTable extends JTable { + @Serial + private static final long serialVersionUID = -6136421378119093719L; + + ProvidersTable() { + super(ACTIVE_MODEL); + } + + @Override + public String getToolTipText(@Nonnull MouseEvent e) { + final var p = e.getPoint(); + try { + return ACTIVE_MODEL.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); + } catch (ArrayIndexOutOfBoundsException ex) { + Logging.debug(ex); + return null; + } + } + } + /** * Constructs a new {@code MapWithAIProvidersPanel}. * @@ -320,21 +338,7 @@ public class MapWithAIProvidersPanel extends JPanel { this.options = options; boolean showActive = Arrays.asList(options).contains(Options.SHOW_ACTIVE); - activeTable = new JTable(ACTIVE_MODEL) { - - private static final long serialVersionUID = -6136421378119093719L; - - @Override - public String getToolTipText(MouseEvent e) { - java.awt.Point p = e.getPoint(); - try { - return ACTIVE_MODEL.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString(); - } catch (ArrayIndexOutOfBoundsException ex) { - Logging.debug(ex); - return null; - } - } - }; + activeTable = new ProvidersTable(); activeTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); defaultTable = new JTable(DEFAULT_MODEL); @@ -346,25 +350,25 @@ public class MapWithAIProvidersPanel extends JPanel { setupDefaultTable(defaultTable, options, areaListeners); - TableColumnModel mod = activeTable.getColumnModel(); + final var mod = activeTable.getColumnModel(); mod.getColumn(1).setPreferredWidth(800); - MapWithAIURLTableCellRenderer activeTableCellRenderer = new MapWithAIURLTableCellRenderer(); + final var activeTableCellRenderer = new MapWithAIURLTableCellRenderer(); areaListeners.addListener(activeTableCellRenderer); mod.getColumn(1).setCellRenderer(activeTableCellRenderer); mod.getColumn(0).setMaxWidth(200); - RemoveEntryAction remove = new RemoveEntryAction(); + final var remove = new RemoveEntryAction(); activeTable.getSelectionModel().addListSelectionListener(remove); - EditEntryAction edit = new EditEntryAction(); + final var edit = new EditEntryAction(); activeTable.getSelectionModel().addListSelectionListener(edit); add(new JLabel(tr("Available default entries:")), GBC.std().insets(5, 5, 0, 0)); add(new JLabel(tr("Boundaries of selected MapWithAI entries:")), GBC.eol().insets(5, 5, 0, 0)); // Add default item list - JPanel defaultPane = new JPanel(new GridBagLayout()); - JScrollPane scrolldef = new JScrollPane(defaultTable); + final var defaultPane = new JPanel(new GridBagLayout()); + final var scrolldef = new JScrollPane(defaultTable); scrolldef.setPreferredSize(new Dimension(200, 200)); defaultPane.add(defaultFilter, GBC.eol().insets(0, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL)); defaultPane.add(scrolldef, GBC.eol().insets(0, 0, 0, 0).fill(GridBagConstraints.BOTH)); @@ -384,10 +388,10 @@ public class MapWithAIProvidersPanel extends JPanel { defaultMap.setZoomControlsVisible(false); defaultMap.setMinimumSize(new Dimension(100, 200)); defaultMap.addJMVListener(e -> { - Rectangle2D visibleRect = defaultMap.getVisibleRect(); - ICoordinate max = defaultMap.getPosition((int) visibleRect.getMaxX(), (int) visibleRect.getMaxY()); - ICoordinate min = defaultMap.getPosition((int) visibleRect.getMinX(), (int) visibleRect.getMinY()); - Bounds b = new Bounds( + final var visibleRect = defaultMap.getVisibleRect(); + final var max = defaultMap.getPosition((int) visibleRect.getMaxX(), (int) visibleRect.getMaxY()); + final var min = defaultMap.getPosition((int) visibleRect.getMinX(), (int) visibleRect.getMinY()); + final var b = new Bounds( new LatLon(Math.min(max.getLat(), min.getLat()), LatLon.toIntervalLon(Math.min(max.getLon(), min.getLon()))), new LatLon(Math.max(max.getLat(), min.getLat()), @@ -411,15 +415,15 @@ public class MapWithAIProvidersPanel extends JPanel { defaultToolbar.add(new ReloadAction()); add(defaultToolbar, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 5, 0)); - HtmlPanel help = new HtmlPanel( + final var help = new HtmlPanel( tr("New default entries can be added in the GitHub Repository.", "https://github.com/JOSM/MapWithAI/blob/pages/json/sources.json")); help.enableClickableHyperlinks(); add(help, GBC.eol().insets(10, 0, 0, 0).fill(GBC.HORIZONTAL)); - ActivateAction activate = new ActivateAction(); + final var activate = new ActivateAction(); defaultTable.getSelectionModel().addListSelectionListener(activate); - JButton btnActivate = new JButton(activate); + final var btnActivate = new JButton(activate); middleToolbar = new JToolBar(JToolBar.HORIZONTAL); middleToolbar.setFloatable(false); @@ -430,7 +434,7 @@ public class MapWithAIProvidersPanel extends JPanel { add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); - JScrollPane scroll = new JScrollPane(activeTable); + final var scroll = new JScrollPane(activeTable); scroll.setPreferredSize(new Dimension(200, 200)); activeToolbar = new JToolBar(JToolBar.VERTICAL); @@ -452,7 +456,7 @@ public class MapWithAIProvidersPanel extends JPanel { ListenerList areaListeners) { boolean showActive = Arrays.asList(options).contains(Options.SHOW_ACTIVE); int tenXWidth = defaultTable.getFontMetrics(defaultTable.getFont()).stringWidth("XXXXXXXXXX"); - TableColumnModel mod = defaultTable.getColumnModel(); + final var mod = defaultTable.getColumnModel(); int urlWidth = (showActive ? 3 : 0) * tenXWidth; mod.getColumn(6).setCellRenderer(defaultTable.getDefaultRenderer(Boolean.class)); mod.getColumn(6).setMaxWidth(20); @@ -461,13 +465,13 @@ public class MapWithAIProvidersPanel extends JPanel { mod.getColumn(4).setPreferredWidth((showActive ? 2 : 0) * tenXWidth); mod.getColumn(4).setCellRenderer(new MapWithAIProviderTableCellRenderer()); mod.getColumn(3).setPreferredWidth(urlWidth); - MapWithAIURLTableCellRenderer defaultUrlTableCellRenderer = new MapWithAIURLTableCellRenderer(); + final var defaultUrlTableCellRenderer = new MapWithAIURLTableCellRenderer(); mod.getColumn(3).setCellRenderer(defaultUrlTableCellRenderer); mod.getColumn(2).setPreferredWidth((int) ((showActive ? 0 : 0.3) * tenXWidth)); mod.getColumn(2).setCellRenderer(new MapWithAITypeTableCellRenderer()); - MapWithAINameTableCellRenderer defaultNameTableCellRenderer = new MapWithAINameTableCellRenderer(!showActive); + final var defaultNameTableCellRenderer = new MapWithAINameTableCellRenderer(!showActive); mod.getColumn(1).setCellRenderer(defaultNameTableCellRenderer); mod.getColumn(1).setPreferredWidth((showActive ? 3 : 2) * tenXWidth); mod.getColumn(0).setCellRenderer(new MapWithAICategoryTableCellRenderer()); @@ -496,25 +500,22 @@ public class MapWithAIProvidersPanel extends JPanel { * @param e The MouseEvent (used to get the appropriate JTable) */ private static void clickListener(MouseEvent e) { - if (e.getSource() instanceof JTable) { - JTable table = (JTable) e.getSource(); - if (table.getSelectedRow() >= 0 && table.getSelectedColumn() >= 0) { - int realCol = table.convertColumnIndexToModel(table.getSelectedColumn()); - int realRow = table.convertRowIndexToModel(table.getSelectedRow()); - String tableName = table.getModel().getColumnName(realCol); - if (tr("License").equals(tableName)) { - MapWithAIInfo info = MapWithAIDefaultLayerTableModel.getRow(realRow); - if (info.getTermsOfUseURL() != null) { - OpenBrowser.displayUrl(info.getTermsOfUseURL()); - } - } else if (tr("Enabled").equals(tableName)) { - MapWithAIInfo info = MapWithAIDefaultLayerTableModel.getRow(realRow); - MapWithAILayerInfo instance = MapWithAILayerInfo.getInstance(); - if (instance.getLayers().contains(info)) { - instance.remove(info); - } else { - instance.add(info); - } + if (e.getSource() instanceof JTable table && table.getSelectedRow() >= 0 && table.getSelectedColumn() >= 0) { + final int realCol = table.convertColumnIndexToModel(table.getSelectedColumn()); + final int realRow = table.convertRowIndexToModel(table.getSelectedRow()); + final var tableName = table.getModel().getColumnName(realCol); + if (tr("License").equals(tableName)) { + final var info = MapWithAIDefaultLayerTableModel.getRow(realRow); + if (info.getTermsOfUseURL() != null) { + OpenBrowser.displayUrl(info.getTermsOfUseURL()); + } + } else if (tr("Enabled").equals(tableName)) { + final var info = MapWithAIDefaultLayerTableModel.getRow(realRow); + final var instance = MapWithAILayerInfo.getInstance(); + if (instance.getLayers().contains(info)) { + instance.remove(info); + } else { + instance.add(info); } } } @@ -585,17 +586,17 @@ public class MapWithAIProvidersPanel extends JPanel { * @param i model index */ private void updateBoundsAndShapes(int i) { - ImageryBounds bounds = MapWithAIDefaultLayerTableModel.getRow(i).getBounds(); + final var bounds = MapWithAIDefaultLayerTableModel.getRow(i).getBounds(); if (bounds != null) { int viewIndex = defaultTable.convertRowIndexToView(i); - List shapes = bounds.getShapes(); + final var shapes = bounds.getShapes(); if (shapes != null && !shapes.isEmpty()) { if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) { mapPolygons.computeIfAbsent(i, key -> { - List list = new ArrayList<>(); + final var list = new ArrayList(shapes.size()); // Add new map polygons - for (Shape shape : shapes) { - MapPolygon polygon = new MapPolygonImpl(shape.getPoints()); + for (var shape : shapes) { + final var polygon = new MapPolygonImpl(shape.getPoints()); list.add(polygon); defaultMap.addMapPolygon(polygon); } @@ -603,7 +604,7 @@ public class MapWithAIProvidersPanel extends JPanel { }); } else if (mapPolygons.containsKey(i)) { // Remove previously drawn map polygons - for (MapPolygon polygon : mapPolygons.get(i)) { + for (var polygon : mapPolygons.get(i)) { defaultMap.removeMapPolygon(polygon); } mapPolygons.remove(i); @@ -613,9 +614,9 @@ public class MapWithAIProvidersPanel extends JPanel { if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) { mapRectangles.computeIfAbsent(i, key -> { // Add new map rectangle - Coordinate topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon()); - Coordinate bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon()); - MapRectangle rectangle = new MapRectangleImpl(topLeft, bottomRight); + final var topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon()); + final var bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon()); + final var rectangle = new MapRectangleImpl(topLeft, bottomRight); defaultMap.addMapRectangle(rectangle); return rectangle; }); @@ -629,8 +630,8 @@ public class MapWithAIProvidersPanel extends JPanel { } private void doCleanupResidualBounds(Map map, Consumer removalEffect) { - List toRemove = new ArrayList<>(); - for (Integer i : map.keySet()) { + final var toRemove = new ArrayList(); + for (var i : map.keySet()) { int viewIndex = defaultTable.convertRowIndexToView(i); if (!defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) { toRemove.add(i); @@ -647,32 +648,32 @@ public class MapWithAIProvidersPanel extends JPanel { private class NewEntryAction extends AbstractAction { + @Serial private static final long serialVersionUID = 7451336680150337942L; NewEntryAction(MapWithAIType type) { putValue(NAME, type.toString()); putValue(SHORT_DESCRIPTION, tr("Add a new {0} entry by entering the URL", type.toString())); - String icon = /* ICON(dialogs/) */ "add"; - new ImageProvider(DIALOG_IMAGES_DIR, icon).getResource().attachImageIcon(this, true); + new ImageProvider(DIALOG_IMAGES_DIR, "add").getResource().attachImageIcon(this, true); } @Override public void actionPerformed(ActionEvent evt) { - final AddMapWithAIPanel p = new AddMapWithAIPanel(); - final AddMapWithAIDialog addDialog = new AddMapWithAIDialog(gui, p); + final var p = new AddMapWithAIPanel(); + final var addDialog = new AddMapWithAIDialog(gui, p); addDialog.showDialog(); if (addDialog.getValue() == 1) { try { - MapWithAIInfo info = p.getSourceInfo(); + final var info = p.getSourceInfo(); // Fix a possible NPE if (info.getSourceType() == null) { info.setSourceType(MapWithAIType.THIRD_PARTY); } if (MapWithAIType.ESRI == info.getSourceType()) { - final ESRISourceReader reader = new ESRISourceReader(info); + final var reader = new ESRISourceReader(info); try { - for (Future i : reader.parse()) { + for (var i : reader.parse()) { try { ACTIVE_MODEL.addRow(i.get()); } catch (InterruptedException e) { @@ -701,6 +702,7 @@ public class MapWithAIProvidersPanel extends JPanel { private class EditEntryAction extends AbstractAction implements ListSelectionListener { + @Serial private static final long serialVersionUID = -1682304557691078801L; /** @@ -725,9 +727,8 @@ public class MapWithAIProvidersPanel extends JPanel { @Override public void actionPerformed(ActionEvent e) { if (activeTable.getSelectedRow() != -1) { - final AddMapWithAIPanel p = new AddMapWithAIPanel( - MapWithAILayerTableModel.getRow(activeTable.getSelectedRow())); - final AddMapWithAIDialog addDialog = new AddMapWithAIDialog(gui, p); + final var p = new AddMapWithAIPanel(MapWithAILayerTableModel.getRow(activeTable.getSelectedRow())); + final var addDialog = new AddMapWithAIDialog(gui, p); addDialog.showDialog(); if (addDialog.getValue() == 1) { p.getSourceInfo(); @@ -738,6 +739,7 @@ public class MapWithAIProvidersPanel extends JPanel { private class RemoveEntryAction extends AbstractAction implements ListSelectionListener { + @Serial private static final long serialVersionUID = 2666450386256004180L; /** @@ -770,6 +772,7 @@ public class MapWithAIProvidersPanel extends JPanel { private class ActivateAction extends AbstractAction implements ListSelectionListener, LayerChangeListener { + @Serial private static final long serialVersionUID = -452335751201424801L; private final transient ImageResource activate; private final transient ImageResource deactivate; @@ -788,9 +791,8 @@ public class MapWithAIProvidersPanel extends JPanel { protected void updateEnabledState() { setEnabled(defaultTable.getSelectedRowCount() > 0); - List selected = Arrays.stream(defaultTable.getSelectedRows()) - .map(defaultTable::convertRowIndexToModel).mapToObj(MapWithAIDefaultLayerTableModel::getRow) - .collect(Collectors.toList()); + final var selected = Arrays.stream(defaultTable.getSelectedRows()).map(defaultTable::convertRowIndexToModel) + .mapToObj(MapWithAIDefaultLayerTableModel::getRow).toList(); if (selected.stream().anyMatch(MapWithAILayerTableModel::doesNotContain)) { activate.attachImageIcon(this, true); putValue(NAME, tr("Activate")); @@ -817,14 +819,12 @@ public class MapWithAIProvidersPanel extends JPanel { JOptionPane.INFORMATION_MESSAGE); return; } - List selected = Arrays.stream(defaultTable.getSelectedRows()) - .map(defaultTable::convertRowIndexToModel).mapToObj(MapWithAIDefaultLayerTableModel::getRow) - .collect(Collectors.toList()); + final var selected = Arrays.stream(defaultTable.getSelectedRows()).map(defaultTable::convertRowIndexToModel) + .mapToObj(MapWithAIDefaultLayerTableModel::getRow).collect(Collectors.toCollection(ArrayList::new)); if (selected.stream().anyMatch(MapWithAILayerTableModel::doesNotContain)) { - List toAdd = selected.stream().filter(MapWithAILayerTableModel::doesNotContain) - .collect(Collectors.toList()); + final var toAdd = selected.stream().filter(MapWithAILayerTableModel::doesNotContain).toList(); activeTable.getSelectionModel().clearSelection(); - for (MapWithAIInfo info : toAdd) { + for (var info : toAdd) { ACTIVE_MODEL.addRow(new MapWithAIInfo(info)); int lastLine = ACTIVE_MODEL.getRowCount() - 1; activeTable.getSelectionModel().setSelectionInterval(lastLine, lastLine); @@ -853,6 +853,7 @@ public class MapWithAIProvidersPanel extends JPanel { private class ReloadAction extends AbstractAction { + @Serial private static final long serialVersionUID = 7801339998423585685L; /** diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ESRISourceReader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ESRISourceReader.java index f90cc98..3a7a76d 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ESRISourceReader.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/ESRISourceReader.java @@ -7,10 +7,9 @@ import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonNumber; import javax.json.JsonObject; -import javax.json.JsonReader; import javax.json.JsonString; -import javax.json.JsonStructure; import javax.json.JsonValue; +import javax.json.spi.JsonProvider; import javax.json.stream.JsonParsingException; import java.io.ByteArrayInputStream; @@ -31,7 +30,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.jcs3.access.CacheAccess; -import org.apache.commons.jcs3.engine.behavior.IElementAttributes; import org.openstreetmap.josm.data.cache.JCSCacheManager; import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; import org.openstreetmap.josm.data.preferences.LongProperty; @@ -59,6 +57,8 @@ public class ESRISourceReader { private static final String JSON_QUERY_PARAM = "?f=json"; private static final LongProperty MIRROR_MAXTIME = new LongProperty("mirror.maxtime", TimeUnit.DAYS.toSeconds(7)); + private final JsonProvider jsonProvider = JsonProvider.provider(); + /** * Constructs a {@code ImageryReader} from a given filename, URL or internal * resource. @@ -88,36 +88,36 @@ public class ESRISourceReader { * @throws IOException if any I/O error occurs */ public List> parse() throws IOException { - Pattern startReplace = Pattern.compile("\\{start}"); - String search = "/search" + JSON_QUERY_PARAM + "&sortField=added&sortOrder=desc&num=" + INITIAL_SEARCH + final var startReplace = Pattern.compile("\\{start}"); + final var search = "/search" + JSON_QUERY_PARAM + "&sortField=added&sortOrder=desc&num=" + INITIAL_SEARCH + "&start={start}"; - String url = source.getUrl(); - String group = source.getId(); + final var group = source.getId(); + var url = source.getUrl(); if (!url.endsWith("/")) { url = url.concat("/"); } - final List> information = new ArrayList<>(); + final var information = new ArrayList>(); - int next = 1; - String searchUrl = startReplace.matcher(search).replaceAll(Integer.toString(next)); + var next = 1; + var searchUrl = startReplace.matcher(search).replaceAll(Integer.toString(next)); while (next != -1) { - final String finalUrl = url + "content/groups/" + group + searchUrl; - final String jsonString = getJsonString(finalUrl, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()) / 7, + final var finalUrl = url + "content/groups/" + group + searchUrl; + final var jsonString = getJsonString(finalUrl, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()) / 7, this.fastFail); if (jsonString == null) { continue; } - try (JsonReader reader = Json + try (var reader = jsonProvider .createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) { - JsonStructure parser = reader.read(); + final var parser = reader.read(); if (parser.getValueType() == JsonValue.ValueType.OBJECT) { - JsonObject obj = parser.asJsonObject(); + final var obj = parser.asJsonObject(); next = obj.getInt("nextStart", -1); searchUrl = startReplace.matcher(search).replaceAll(Integer.toString(next)); - JsonArray features = obj.getJsonArray("results"); - for (JsonObject feature : features.getValuesAs(JsonObject.class)) { + final var features = obj.getJsonArray("results"); + for (var feature : features.getValuesAs(JsonObject.class)) { information.add(parse(feature)); } } @@ -126,7 +126,7 @@ public class ESRISourceReader { next = -1; } } - for (ForkJoinTask future : information) { + for (var future : information) { try { future.join(); future.get(1, TimeUnit.MINUTES); @@ -142,7 +142,7 @@ public class ESRISourceReader { private ForkJoinTask parse(JsonObject feature) { // Use the initial esri server information to keep conflation info - MapWithAIInfo newInfo = new MapWithAIInfo(source); + final var newInfo = new MapWithAIInfo(source); newInfo.setId(feature.getString("id")); ForkJoinTask future; if ("Feature Service".equals(feature.getString("type", ""))) { @@ -154,11 +154,10 @@ public class ESRISourceReader { } MapWithAIDataUtils.getForkJoinPool().execute(future); newInfo.setName(feature.getString("title", feature.getString("name"))); - String[] extent = feature.getJsonArray("extent").getValuesAs(JsonArray.class).stream() + final var extent = feature.getJsonArray("extent").getValuesAs(JsonArray.class).stream() .flatMap(array -> array.getValuesAs(JsonNumber.class).stream()).map(JsonNumber::doubleValue) .map(Object::toString).toArray(String[]::new); - ImageryBounds imageryBounds = new ImageryBounds(String.join(",", extent[1], extent[0], extent[3], extent[2]), - ","); + final var imageryBounds = new ImageryBounds(String.join(",", extent[1], extent[0], extent[3], extent[2]), ","); newInfo.setBounds(imageryBounds); newInfo.setSourceType(MapWithAIType.ESRI_FEATURE_SERVER); newInfo.setTermsOfUseText(feature.getString("licenseInfo", null)); @@ -167,11 +166,10 @@ public class ESRISourceReader { source.getUrl() + "content/items/" + newInfo.getId() + "/info/" + feature.getString("thumbnail")); } if (feature.containsKey("groupCategories")) { - List categories = feature.getJsonArray("groupCategories").getValuesAs(JsonString.class) - .stream().map(JsonString::getString).map(s -> s.replace("/Categories/", "")) - .map(MapWithAICategory::fromString).filter(Objects::nonNull) - .collect(Collectors.toCollection(ArrayList::new)); - MapWithAICategory category = categories.stream().filter(c -> MapWithAICategory.FEATURED != c).findFirst() + final var categories = feature.getJsonArray("groupCategories").getValuesAs(JsonString.class).stream() + .map(JsonString::getString).map(s -> s.replace("/Categories/", "")) + .map(MapWithAICategory::fromString).collect(Collectors.toCollection(ArrayList::new)); + final var category = categories.stream().filter(c -> MapWithAICategory.FEATURED != c).findFirst() .orElse(MapWithAICategory.OTHER); newInfo.setCategory(category); categories.remove(category); @@ -187,7 +185,7 @@ public class ESRISourceReader { } newInfo.setDescription(feature.getString("snippet")); if (newInfo.getSource() != null) { - StringBuilder sourceTag = new StringBuilder(newInfo.getSource()); + final var sourceTag = new StringBuilder(newInfo.getSource()); if (!sourceTag.toString().endsWith("/")) { sourceTag.append('/'); } @@ -208,7 +206,7 @@ public class ESRISourceReader { */ @Nullable private static String getJsonString(@Nonnull final String url, final long defaultMaxAge, final boolean fastFail) { - String jsonString = SOURCE_CACHE.get(url); + var jsonString = SOURCE_CACHE.get(url); if (jsonString == null) { HttpClient client = null; try { @@ -216,12 +214,12 @@ public class ESRISourceReader { if (fastFail) { client.setReadTimeout(1000); } - final HttpClient.Response response = client.connect(); + final var response = client.connect(); jsonString = response.fetchContent(); if (jsonString != null && response.getResponseCode() < 400 && response.getResponseCode() >= 200) { // getExpiration returns milliseconds final long expirationTime = response.getExpiration(); - final IElementAttributes elementAttributes = SOURCE_CACHE.getDefaultElementAttributes(); + final var elementAttributes = SOURCE_CACHE.getDefaultElementAttributes(); if (expirationTime > 0) { elementAttributes.setMaxLife(response.getExpiration()); } else { @@ -249,22 +247,21 @@ public class ESRISourceReader { */ @Nullable private String featureService(@Nonnull MapWithAIInfo mapwithaiInfo, @Nonnull String url) { - final String toGet = url.endsWith(JSON_QUERY_PARAM) ? url : url.concat(JSON_QUERY_PARAM); - final String jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail); + final var toGet = url.endsWith(JSON_QUERY_PARAM) ? url : url + JSON_QUERY_PARAM; + final var jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail); if (jsonString == null) { return null; } - try (JsonReader reader = Json - .createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) { - JsonObject info = reader.readObject(); - JsonArray layers = info.getJsonArray("layers"); + try (var reader = Json.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) { + final var info = reader.readObject(); + final var layers = info.getJsonArray("layers"); // This fixes #20551 if (layers == null || layers.stream().noneMatch(Objects::nonNull)) { return null; } // TODO use all the layers? - JsonObject layer = layers.stream().filter(Objects::nonNull).findFirst().orElse(JsonValue.EMPTY_JSON_OBJECT) + final var layer = layers.stream().filter(Objects::nonNull).findFirst().orElse(JsonValue.EMPTY_JSON_OBJECT) .asJsonObject(); if (layer.containsKey("id")) { String partialUrl = (url.endsWith("/") ? url : url + "/") + layer.getInt("id"); @@ -286,10 +283,10 @@ public class ESRISourceReader { */ @Nonnull private Map getReplacementTags(@Nonnull String layerUrl) { - String toGet = layerUrl.endsWith(JSON_QUERY_PARAM) ? layerUrl : layerUrl.concat(JSON_QUERY_PARAM); - final String jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail); + final var toGet = layerUrl.endsWith(JSON_QUERY_PARAM) ? layerUrl : layerUrl.concat(JSON_QUERY_PARAM); + final var jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail); if (jsonString != null) { - try (JsonReader reader = Json + try (var reader = Json .createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) { return reader.readObject().getJsonArray("fields").getValuesAs(JsonObject.class).stream() .collect(Collectors.toMap(o -> o.getString("name"), ESRISourceReader::getReplacementTag));