Reduce startup costs

This largely focuses on reducing memory allocations.

There are also some conversions to Java 17 standards.

Signed-off-by: Taylor Smock <tsmock@meta.com>
pull/31/head
Taylor Smock 2023-08-02 09:59:22 -06:00
rodzic 9aab77dd4f
commit 056b9db064
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 233BB2E466604E27
9 zmienionych plików z 273 dodań i 292 usunięć

Wyświetl plik

@ -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<Destroyable> destroyables;
private final PreferencesAction preferenceAction;
private final MapWithAIMenu mapwithaiMenu;
private static final Map<Class<? extends JosmAction>, 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<MapWithAIObject> 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);
}

Wyświetl plik

@ -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<OsmDataLayer> 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) {

Wyświetl plik

@ -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"),

Wyświetl plik

@ -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<MapWithAIInfo>, 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<MapWithAIInfo> 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;
}
/**

Wyświetl plik

@ -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<MapWithAIPreferenceEntry> 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.
*
* <p>
* 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<List<MapWithAIInfo>> {
@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<MapWithAIInfo> 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<MapWithAIInfo> layers) {
for (MapWithAIInfo layer : layers) {
for (var layer : layers) {
if (MapWithAIType.ESRI == layer.getSourceType()) {
for (ForkJoinTask<MapWithAIInfo> 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<FinishListener> 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<MapWithAIInfo> lst, Map<String, MapWithAIInfo> idMap) {
idMap.clear();
Set<String> notUnique = new HashSet<>();
for (MapWithAIInfo i : lst) {
final var notUnique = new HashSet<String>();
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<String> knownDefaults = new TreeSet<>(Config.getPref().getList(CONFIG_PREFIX + "layers.default"));
Collection<String> newKnownDefaults = new TreeSet<>();
var changed = false;
final var knownDefaults = new TreeSet<>(Config.getPref().getList(CONFIG_PREFIX + "layers.default"));
final var newKnownDefaults = new TreeSet<String>();
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<String> drop = new ArrayList<>();
final var drop = new ArrayList<String>();
for (Map.Entry<String, MapWithAIInfo> 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<MapWithAIPreferenceEntry> entries = new ArrayList<>();
final var entries = new ArrayList<MapWithAIPreferenceEntry>();
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.
*
* <p>
* This takes care, that no id is used twice (due to a user error)
*
* @param info the ImageryInfo to look up

Wyświetl plik

@ -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;

Wyświetl plik

@ -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<PrefEntry> list) {
final Map<String, String> current = new TreeMap<>(MapWithAIPreferenceHelper.getReplacementTags());
for (final Map.Entry<String, String> 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<PrefEntry> 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<PrefEntry> 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<String, String> convertReplacementPrefToMap(List<PrefEntry> displayData) {
final Map<String, String> 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

Wyświetl plik

@ -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<MapWithAIInfo> {
@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 <a href=\"{0}\">GitHub Repository</a>.",
"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<AreaListener> 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<Shape> shapes = bounds.getShapes();
final var shapes = bounds.getShapes();
if (shapes != null && !shapes.isEmpty()) {
if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) {
mapPolygons.computeIfAbsent(i, key -> {
List<MapPolygon> list = new ArrayList<>();
final var list = new ArrayList<MapPolygon>(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 <T> void doCleanupResidualBounds(Map<Integer, T> map, Consumer<T> removalEffect) {
List<Integer> toRemove = new ArrayList<>();
for (Integer i : map.keySet()) {
final var toRemove = new ArrayList<Integer>();
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<MapWithAIInfo> 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<MapWithAIInfo> 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<MapWithAIInfo> 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<MapWithAIInfo> 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;
/**

Wyświetl plik

@ -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<ForkJoinTask<MapWithAIInfo>> 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<ForkJoinTask<MapWithAIInfo>> information = new ArrayList<>();
final var information = new ArrayList<ForkJoinTask<MapWithAIInfo>>();
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<MapWithAIInfo> future : information) {
for (var future : information) {
try {
future.join();
future.get(1, TimeUnit.MINUTES);
@ -142,7 +142,7 @@ public class ESRISourceReader {
private ForkJoinTask<MapWithAIInfo> 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<MapWithAIInfo> 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<MapWithAICategory> 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<String, String> 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));