- Displays a multiple choice combo box. key and values are mandatory, text, default, editable, delimiter, values_from, display_values, short_descriptions, use_last_as_default, values_searchable, length, values_no_i18n, values_sort and match is optional.
+ Displays a multiple choice combo box. key and values are mandatory, text, default, editable, delimiter, values_from, display_values, short_descriptions, use_last_as_default, values_searchable, length, values_no_i18n, values_sort, match, regions, and exclude_regions are optional.
If editable is "true" (default), combo boxes can be edited as if they were text fields (additional to the drop down menu). Non editable combo boxes can only contain one of the specified values.
@@ -413,6 +417,7 @@
+
@@ -431,6 +436,7 @@
+
@@ -489,6 +495,7 @@
+
@@ -558,7 +565,7 @@
- To specify possible roles of members in relations. The key attribute is required, text, requisite, count, type and member_expression are optional.
+ To specify possible roles of members in relations. The key attribute is required, text, requisite, count, type, member_expression, regions, and exclude_regions are optional.
@@ -574,6 +581,7 @@
+
@@ -729,4 +737,21 @@
+
+
+
+
+ Comma separated list of countries this preset group or item is applicable for. If not specified, the preset is applicable for all countries.
+
+
+
+
+
+
+ If true, invert the meaning of regions.
+
+
+
+
+
diff --git a/resources/data/uk.lang b/resources/data/uk.lang
index 9aa85ef9373..acb948b8c14 100644
Binary files a/resources/data/uk.lang and b/resources/data/uk.lang differ
diff --git a/resources/data/validator/combinations.mapcss b/resources/data/validator/combinations.mapcss
index 1776ff3beec..f8a30d975f0 100644
--- a/resources/data/validator/combinations.mapcss
+++ b/resources/data/validator/combinations.mapcss
@@ -541,8 +541,8 @@ way[highway][natural][natural!=ridge],
fixRemove: "natural";
}
-/* #9593, #11183, #12418, #12761, #17254, #19311 */
-*[sport][sport!=skiing][!building][!club][tourism != hotel][highway !~ /^(pedestrian|raceway)$/][!leisure][natural !~ /^(beach|bare_rock|cliff|peak|water)$/][amenity !~ /^(bar|dojo|pub|restaurant|swimming_pool)$/][landuse !~ /^(recreation_ground|piste|farm|farmland)$/][barrier !~ /^(wall|retaining_wall)$/][!"piste:type"][shop!=sports][attraction!=summer_toboggan] {
+/* #9593, #11183, #12418, #12761, #17254, #19311,#23604 */
+*[sport][sport!=skiing][!building][!"building:part"][!club][tourism != hotel][highway !~ /^(pedestrian|raceway)$/][!leisure][natural !~ /^(beach|bare_rock|cliff|peak|water)$/][amenity !~ /^(bar|dojo|pub|restaurant|swimming_pool)$/][landuse !~ /^(recreation_ground|piste|farm|farmland)$/][barrier !~ /^(wall|retaining_wall)$/][!"piste:type"][shop!=sports][attraction!=summer_toboggan] {
throwWarning: tr("sport without physical feature");
group: tr("missing tag");
assertMatch: "node sport=tennis";
@@ -551,6 +551,7 @@ way[highway][natural][natural!=ridge],
assertNoMatch: "node sport=skiing"; /* skiing has deprecated warning */
assertNoMatch: "node sport=swimming tourism=hotel";
assertNoMatch: "node sport=10pin amenity=restaurant";
+ assertNoMatch: "node sport=boxing building:part=yes";
}
/* {0.key} without {1.key} or {2.key} see #10140 */
@@ -666,27 +667,6 @@ way[waterway][layer][layer=~/^(-1|-2|-3|-4|-5)$/][!tunnel][culvert!=yes][covered
group: tr("suspicious tag combination");
}
-/* #13144, #15536 */
-*[unisex=yes][female=yes][male!=yes][shop=hairdresser],
-*[unisex=yes][male=yes][female!=yes][shop=hairdresser] {
- throwWarning: tr("{0} together with {1}", "{0.tag}", "{1.tag}");
- group: tr("suspicious tag combination");
-}
-*[unisex=yes][female=yes][male=yes][shop=hairdresser] {
- throwWarning: tr("{0} together with {1} and {2}. Remove {1} and {2}", "{0.tag}", "{1.tag}", "{2.tag}");
- group: tr("suspicious tag combination");
- fixRemove: "female";
- fixRemove: "male";
-}
-*[female=yes][male=yes][!unisex][shop=hairdresser] {
- throwWarning: tr("{0} together with {1}", "{0.tag}", "{1.tag}");
- suggestAlternative: "unisex=yes";
- group: tr("suspicious tag combination");
- fixRemove: "female";
- fixRemove: "male";
- fixAdd: "unisex=yes";
-}
-
/* {0.key} without {1.tag} see #13138, #14468 */
way[water][natural!~/water|bay|strait/][water!=intermittent][amenity!=lavoir]!:closed, /* water=intermittent is deprecated and has an own warning */
area[water][natural!~/water|bay|strait/][water!=intermittent][amenity!=lavoir]:closed {
diff --git a/resources/data/validator/deprecated.mapcss b/resources/data/validator/deprecated.mapcss
index 87fe15cffda..ec70e48a0e7 100644
--- a/resources/data/validator/deprecated.mapcss
+++ b/resources/data/validator/deprecated.mapcss
@@ -1617,11 +1617,6 @@ way[/^is_in:/] {
suggestAlternative: "highway=steps + conveying=*";
group: tr("deprecated tagging");
}
-*[fenced] {
- throwWarning: tr("{0} is deprecated", "{0.key}");
- suggestAlternative: "barrier=fence";
- group: tr("deprecated tagging");
-}
*[historic_name][!old_name] {
throwWarning: tr("{0} is deprecated", "{0.key}");
suggestAlternative: "old_name";
@@ -2514,4 +2509,25 @@ area[parking:orientation][orientation]["parking:orientation"!=*orientation] {
suggestAlternative: "{1.key}={1.value}";
}
+/* Tags were consolidated, see #23177 */
+*[gnis:id][!gnis:feature_id],
+*[tiger:PLACENS][!gnis:feature_id],
+*[NHD:GNIS_ID][!gnis:feature_id],
+*[nhd:gnis_id][!gnis:feature_id],
+*[ref:gnis][!gnis:feature_id] {
+ throwWarning: tr("{0} is deprecated", "{0.key}");
+ group: tr("deprecated tagging");
+ suggestAlternative: "{1.key}={0.value}";
+ fixChangeKey: "{0.key} => {1.key}";
+}
+*[gnis:id][gnis:feature_id],
+*[tiger:PLACENS][gnis:feature_id],
+*[NHD:GNIS_ID][gnis:feature_id],
+*[nhd:gnis_id][gnis:feature_id],
+*[ref:gnis][gnis:feature_id] {
+ throwWarning: tr("{0} is deprecated", "{0.key}");
+ group: tr("deprecated tagging");
+ suggestAlternative: "{1.key}";
+}
+
/* When tags are deprecated they should be added to ignoretags.cfg too. */
diff --git a/resources/data/validator/geometry.mapcss b/resources/data/validator/geometry.mapcss
index f4d583b84ad..de43241551d 100644
--- a/resources/data/validator/geometry.mapcss
+++ b/resources/data/validator/geometry.mapcss
@@ -198,7 +198,10 @@ area[building][building!~/no|entrance/] ⧉ area[building][building!~/no|entranc
}
/* Overlapping areas (spatial test) */
-area[natural =~ /^(water|wetland|coastline)$/], area[waterway=riverbank], area[landuse=reservoir] {
+area[natural =~ /^(water|wetland)$/],
+area[natural=coastline]:clockwise,
+area[waterway=riverbank],
+area[landuse=reservoir] {
set water_area;
}
@@ -210,10 +213,12 @@ area:closed[landuse=reservoir] ⧉ area:closed.water_area
}
/* Water area inside water area (spatial test) */
-area:closed[natural =~ /^(water|wetland|coastline)$/] ⊆ area:closed.water_area,
+area:closed[natural =~ /^(water|wetland)$/] ⊆ area:closed.water_area,
+area:closed[natural=coastline]:clockwise ⊆ area:closed.water_area,
area:closed[waterway=riverbank] ⊆ area:closed.water_area,
area:closed[landuse=reservoir] ⊆ area:closed.water_area,
-area:closed[natural =~ /^(water|wetland|coastline)$/] ⊇ area:closed.water_area,
+area:closed[natural =~ /^(water|wetland)$/] ⊇ area:closed.water_area,
+area:closed[natural=coastline]:clockwise ⊇ area:closed.water_area,
area:closed[waterway=riverbank] ⊇ area:closed.water_area,
area:closed[landuse=reservoir] ⊇ area:closed.water_area {
throwWarning: tr("Water area inside water area");
@@ -273,10 +278,12 @@ node:unconnected:in-downloaded-area[highway=give_way],
node:unconnected:in-downloaded-area[highway=traffic_signals],
node:unconnected:in-downloaded-area[highway=crossing],
node:unconnected:in-downloaded-area[crossing],
-node:unconnected:in-downloaded-area[railway=milestone],
+node:unconnected:in-downloaded-area[railway=buffer_stop],
node:unconnected:in-downloaded-area[railway=crossing],
node:unconnected:in-downloaded-area[railway=level_crossing],
-node:unconnected:in-downloaded-area[railway=buffer_stop],
+node:unconnected:in-downloaded-area[railway=milestone],
+node:unconnected:in-downloaded-area[railway=railway_crossing],
+node:unconnected:in-downloaded-area[railway=switch],
node:unconnected:in-downloaded-area[public_transport=stop_position],
node:unconnected:in-downloaded-area[aeroway=holding_position],
node:unconnected:in-downloaded-area[noexit],
diff --git a/resources/data/validator/ignoretags.cfg b/resources/data/validator/ignoretags.cfg
index e9536912377..3051639c51e 100644
--- a/resources/data/validator/ignoretags.cfg
+++ b/resources/data/validator/ignoretags.cfg
@@ -746,6 +746,11 @@ K:type=turnlanes:turns
K:surface=paving_stones:30
E:site_type
E:parking:orientation
+E:NHD:GNIS_ID
+E:gnis:id
+E:nhd:gnis_id
+E:ref:gnis
+E:tiger:PLACENS
;
; Tags not yet decided (to remove from this section when added or moved up when deprecated)
; see josm tickets: 17770 15309 15774 16315 16658 16793 19982 21396
diff --git a/resources/data/validator/unnecessary.mapcss b/resources/data/validator/unnecessary.mapcss
index 9c2b6dbe0e7..eac8d59d323 100644
--- a/resources/data/validator/unnecessary.mapcss
+++ b/resources/data/validator/unnecessary.mapcss
@@ -85,7 +85,7 @@ node[emergency=fire_hydrant][fire_hydrant:count=1] {
fixRemove: "{1.key}";
}
-/* #17100, #17471, #17629, #17633, #19274, #19395, #19409 */
+/* #17100, #17471, #17629, #17633, #19274, #19395, #19409, #23596 */
*[name][name=~/^(?i)(library|biblioteca|biblioteka|bibliothek|bibliotheek)$/][amenity=library],
*[name][name=~/^(?i)(parc|park)$/][leisure=park],
*[name][name=~/^(?i)(pond)$/][water=pond],
@@ -97,7 +97,7 @@ node[emergency=fire_hydrant][fire_hydrant:count=1] {
*[name][name=~/^(?i)(toilets?)$/][amenity=toilets],
*[name][name=~/^(?i)(playground|spielplatz)$/][leisure=playground],
*[name][name=~/^(?i)(shop|boutique)$/][shop][shop!=no],
-*[name][name=~/^(?i)(building|bangunan)$/][building][building!=no],
+*[name][name=~/^(?i)(building|bangunan|bâtiment|batiment)$/][building][building!=no],
*[name][name=~/^(?i)(house|maison|rumah|vivienda)$/][building=house],
*[name][name=~/^(?i)(casa)$/][building=house][outside("FR")], /* spanish for house but it is a brand name in France */
*[name][name=~/^(?i)(kiosk)$/][shop=kiosk][outside("NL")], /* it is a brand name in the Netherlands */
diff --git a/resources/data/zh_CN.lang b/resources/data/zh_CN.lang
index 32b38f2952c..69472ec2bcb 100644
Binary files a/resources/data/zh_CN.lang and b/resources/data/zh_CN.lang differ
diff --git a/resources/data/zh_TW.lang b/resources/data/zh_TW.lang
index d5a9715c9ce..e4b2b9d0020 100644
Binary files a/resources/data/zh_TW.lang and b/resources/data/zh_TW.lang differ
diff --git a/resources/images/presets/vehicle/bicycle_wash.svg b/resources/images/presets/vehicle/bicycle_wash.svg
new file mode 100644
index 00000000000..c042f7b4412
--- /dev/null
+++ b/resources/images/presets/vehicle/bicycle_wash.svg
@@ -0,0 +1,43 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_dashes.svg b/resources/images/presets/vehicle/crossing_markings_dashes.svg
new file mode 100644
index 00000000000..5624c292985
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_dashes.svg
@@ -0,0 +1,107 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_dots.svg b/resources/images/presets/vehicle/crossing_markings_dots.svg
new file mode 100644
index 00000000000..b4a2d938235
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_dots.svg
@@ -0,0 +1,107 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_ladder.svg b/resources/images/presets/vehicle/crossing_markings_ladder.svg
new file mode 100644
index 00000000000..81d3e80a1f6
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_ladder.svg
@@ -0,0 +1,100 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_ladder_paired.svg b/resources/images/presets/vehicle/crossing_markings_ladder_paired.svg
new file mode 100644
index 00000000000..9c0f4dc6617
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_ladder_paired.svg
@@ -0,0 +1,121 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_ladder_skewed.svg b/resources/images/presets/vehicle/crossing_markings_ladder_skewed.svg
new file mode 100644
index 00000000000..feb31e9dfd2
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_ladder_skewed.svg
@@ -0,0 +1,103 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_lines.svg b/resources/images/presets/vehicle/crossing_markings_lines.svg
new file mode 100644
index 00000000000..29991f86aa6
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_lines.svg
@@ -0,0 +1,79 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_lines_paired.svg b/resources/images/presets/vehicle/crossing_markings_lines_paired.svg
new file mode 100644
index 00000000000..10ba8b7aef0
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_lines_paired.svg
@@ -0,0 +1,93 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_surface.svg b/resources/images/presets/vehicle/crossing_markings_surface.svg
new file mode 100644
index 00000000000..f4c022d9804
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_surface.svg
@@ -0,0 +1,72 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_zebra_bicolour.svg b/resources/images/presets/vehicle/crossing_markings_zebra_bicolour.svg
new file mode 100644
index 00000000000..bde431e7568
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_zebra_bicolour.svg
@@ -0,0 +1,114 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_zebra_double.svg b/resources/images/presets/vehicle/crossing_markings_zebra_double.svg
new file mode 100644
index 00000000000..13d8e531909
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_zebra_double.svg
@@ -0,0 +1,106 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_markings_zebra_paired.svg b/resources/images/presets/vehicle/crossing_markings_zebra_paired.svg
new file mode 100644
index 00000000000..7f76cac9c49
--- /dev/null
+++ b/resources/images/presets/vehicle/crossing_markings_zebra_paired.svg
@@ -0,0 +1,93 @@
+
+
+
+
diff --git a/resources/images/presets/vehicle/crossing_ref_zebra.svg b/resources/images/presets/vehicle/crossing_ref_zebra.svg
index df88fc4e6d8..210d4d06617 100644
--- a/resources/images/presets/vehicle/crossing_ref_zebra.svg
+++ b/resources/images/presets/vehicle/crossing_ref_zebra.svg
@@ -2,43 +2,17 @@
diff --git a/resources/styles/standard/elemstyles.mapcss b/resources/styles/standard/elemstyles.mapcss
index 082dd37677f..9e4ba2e7e7c 100644
--- a/resources/styles/standard/elemstyles.mapcss
+++ b/resources/styles/standard/elemstyles.mapcss
@@ -833,7 +833,52 @@ node[highway=crossing][crossing=unmarked] {
icon-image: "presets/vehicle/crossing_unmarked.svg";
set icon_z17;
}
-node[highway=crossing][crossing_ref=zebra] {
+node[highway=crossing]["crossing:markings"=surface] {
+ icon-image: "presets/vehicle/crossing_markings_surface.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"=lines] {
+ icon-image: "presets/vehicle/crossing_markings_lines.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"="lines:paired"] {
+ icon-image: "presets/vehicle/crossing_markings_lines_paired.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"=dashes] {
+ icon-image: "presets/vehicle/crossing_markings_dashes.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"=dots] {
+ icon-image: "presets/vehicle/crossing_markings_dots.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"="zebra:double"] {
+ icon-image: "presets/vehicle/crossing_markings_zebra_double.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"="zebra:paired"] {
+ icon-image: "presets/vehicle/crossing_markings_zebra_paired.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"="zebra:bicolour"] {
+ icon-image: "presets/vehicle/crossing_markings_zebra_bicolour.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"=ladder] {
+ icon-image: "presets/vehicle/crossing_markings_ladder.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"="ladder:skewed"] {
+ icon-image: "presets/vehicle/crossing_markings_ladder_skewed.svg";
+ set icon_z17;
+}
+node[highway=crossing]["crossing:markings"="ladder:paired"] {
+ icon-image: "presets/vehicle/crossing_markings_ladder_paired.svg";
+ set icon_z17;
+}
+node[highway=crossing][crossing_ref=zebra],
+node[highway=crossing]["crossing:markings"=zebra] {
icon-image: "presets/vehicle/crossing_ref_zebra.svg";
set icon_z17;
}
@@ -3040,6 +3085,7 @@ area[amenity=parking],
area[amenity=motorcycle_parking],
area[amenity=bicycle_rental],
area[amenity=bicycle_repair_station],
+area[amenity=bicycle_wash],
area[amenity=car_rental],
area[amenity=car_pooling],
area[amenity=car_sharing],
@@ -3094,6 +3140,10 @@ node[amenity=bicycle_repair_station] {
icon-image: "presets/vehicle/bicycle_repair_station.svg";
set icon_z17;
}
+node[amenity=bicycle_wash] {
+ icon-image: "presets/vehicle/bicycle_wash.svg";
+ set icon_z17;
+}
node[amenity=car_rental] {
icon-image: "presets/vehicle/car_rental.svg";
set icon_z17;
diff --git a/scripts/BuildProjectionDefinitions.java b/scripts/BuildProjectionDefinitions.java
index 5463bc449e8..6a486e3c718 100644
--- a/scripts/BuildProjectionDefinitions.java
+++ b/scripts/BuildProjectionDefinitions.java
@@ -13,6 +13,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
+import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -84,6 +85,19 @@ static List initList(String baseDir, String ext) throws IOException {
.collect(Collectors.toList());
}
+ static boolean touchCustomEpsg(String baseDir) throws IOException {
+ final Path path = Paths.get(baseDir).resolve(OUTPUT_EPSG_FILE);
+ if (!Files.exists(path)) {
+ Files.createDirectories(path.getParent());
+ Files.createFile(path);
+ Logger.getLogger(BuildProjectionDefinitions.class.getCanonicalName())
+ .info("Could not generate custom-epsg; an empty custom-epsg file was not available on the classpath. " +
+ "This should now be fixed, please rerun the command.");
+ return true;
+ }
+ return false;
+ }
+
static void initMap(String baseDir, String file, Map map) throws IOException {
final Path path = Paths.get(baseDir).resolve(PROJ_DIR).resolve(file);
final List list;
@@ -106,6 +120,9 @@ static void initMap(String baseDir, String file, Map skip = new HashMap<>();
- private Map skipStart = new HashMap<>();
+ private final Map skip = new HashMap<>();
+ private final Map skipStart = new HashMap<>();
/**
* Main method.
@@ -236,7 +237,7 @@ void setupProj() {
}
void loadSkip() throws IOException {
- final Pattern pattern = Pattern.compile("^\\|\\| *(ELI|Ignore) *\\|\\| *\\{\\{\\{(.+)\\}\\}\\} *\\|\\|");
+ final Pattern pattern = Pattern.compile("^\\|\\| *(ELI|Ignore) *\\|\\| *\\{\\{\\{(.+)}}} *\\|\\|");
try (BufferedReader fr = Files.newBufferedReader(Paths.get(ignoreInputFile), UTF_8)) {
String line;
@@ -477,8 +478,8 @@ void printentries(List> entries, Writer stream) throws IOException {
}
shapes += sep + "\n";
}
- } catch (IllegalArgumentException ignored) {
- Logging.trace(ignored);
+ } catch (IllegalArgumentException illegalArgumentException) {
+ Logging.trace(illegalArgumentException);
}
if (!shapes.isEmpty()) {
stream.write(" getMirrors(Object e) {
static List getProjections(Object e) {
List r = new ArrayList<>();
List u = getProjectionsUnstripped(e);
- if (u != null) {
- for (String p : u) {
- if (!oldproj.containsKey(p) && !("CRS:84".equals(p) && !(getUrlStripped(e).matches("(?i)version=1\\.3")))) {
- r.add(p);
- }
+ for (String p : u) {
+ if (!oldproj.containsKey(p) && !("CRS:84".equals(p) && !(getUrlStripped(e).matches("(?i)version=1\\.3")))) {
+ r.add(p);
}
}
return r;
diff --git a/scripts/TagInfoExtract.java b/scripts/TagInfoExtract.java
index 3e9c4319950..b2d3a2babe4 100644
--- a/scripts/TagInfoExtract.java
+++ b/scripts/TagInfoExtract.java
@@ -1,4 +1,5 @@
// License: GPL. For details, see LICENSE file.
+
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
@@ -31,11 +32,6 @@
import java.util.stream.Stream;
import javax.imageio.ImageIO;
-import jakarta.json.Json;
-import jakarta.json.JsonArrayBuilder;
-import jakarta.json.JsonObjectBuilder;
-import jakarta.json.JsonWriter;
-import jakarta.json.stream.JsonGenerator;
import org.openstreetmap.josm.actions.DeleteAction;
import org.openstreetmap.josm.command.DeleteCommand;
@@ -84,6 +80,12 @@
import org.openstreetmap.josm.tools.Utils;
import org.xml.sax.SAXException;
+import jakarta.json.Json;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonWriter;
+import jakarta.json.stream.JsonGenerator;
+
/**
* Extracts tag information for the taginfo project.
*
@@ -100,9 +102,12 @@ public class TagInfoExtract {
/**
* Main method.
* @param args Main program arguments
- * @throws Exception if any error occurs
+ * @throws IOException if an IO exception occurs
+ * @throws OsmTransferException if something happened when communicating with the OSM server
+ * @throws ParseException if there was an issue parsing MapCSS
+ * @throws SAXException if there was an issue parsing XML
*/
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) throws IOException, OsmTransferException, ParseException, SAXException {
HttpClient.setFactory(Http1Client::new);
TagInfoExtract script = new TagInfoExtract();
script.parseCommandLineArguments(args);
@@ -162,7 +167,7 @@ private void usage() {
System.exit(0);
}
- private static class Options {
+ private static final class Options {
Mode mode;
int josmSvnRevision = Version.getInstance().getVersion();
Path baseDir = Paths.get("");
@@ -221,7 +226,7 @@ private String findImageUrl(String path) {
}
private abstract class Extractor {
- abstract void run() throws Exception;
+ abstract void run() throws IOException, OsmTransferException, ParseException, SAXException;
void writeJson(String name, String description, Iterable tags) throws IOException {
try (Writer writer = options.outputFile != null ? Files.newBufferedWriter(options.outputFile) : new StringWriter();
@@ -313,7 +318,7 @@ private Collection values(KeyedItem item) {
}
}
- private class ExternalPresets extends Presets {
+ private final class ExternalPresets extends Presets {
@Override
void run() throws IOException, OsmTransferException, SAXException {
@@ -340,7 +345,7 @@ void run() throws IOException, OsmTransferException, SAXException {
}
}
- private class StyleSheet extends Extractor {
+ private final class StyleSheet extends Extractor {
private MapCSSStyleSource styleSource;
@Override
diff --git a/scripts/TaggingPresetSchemeWikiGenerator.java b/scripts/TaggingPresetSchemeWikiGenerator.java
index af408eace97..17dd68aa155 100644
--- a/scripts/TaggingPresetSchemeWikiGenerator.java
+++ b/scripts/TaggingPresetSchemeWikiGenerator.java
@@ -34,7 +34,7 @@ private TaggingPresetSchemeWikiGenerator() {
// Hide public constructor for utility class
}
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) throws IOException, ParserConfigurationException, SAXException, XPathExpressionException {
document = parseTaggingPresetSchema();
xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new TaggingNamespaceContext());
@@ -63,7 +63,7 @@ private static void printAttributes() throws XPathExpressionException {
node.getTextContent().trim()));
}
- private static class TaggingNamespaceContext implements NamespaceContext {
+ private static final class TaggingNamespaceContext implements NamespaceContext {
@Override
public String getNamespaceURI(String prefix) {
switch (prefix) {
diff --git a/scripts/since_xxx.py b/scripts/since_xxx.py
index c6ecf25c794..be026cc7f55 100755
--- a/scripts/since_xxx.py
+++ b/scripts/since_xxx.py
@@ -1,8 +1,9 @@
-#!/usr/bin/python
+#!/usr/bin/python3
# License: CC0
"""
-Helper script to replace "@since xxx" in Javadoc by the upcoming revision number.
+Helper script to replace "@since xxx" in Javadoc by the upcoming revision
+number.
Will retrieve the current revision number from the server. It runs over all
modified and added .java files and replaces xxx in "since xxx" by the revision
@@ -12,33 +13,86 @@
import xml.etree.ElementTree as ElementTree
import subprocess
import re
+import sys
revision = None
-def main():
+
+def main() -> None:
+ """
+ Do the main work of the script.
+ :return: Nothing
+ """
+ args = sys.argv
svn_status = subprocess.check_output("svn status --xml".split(" "))
- for el in ElementTree.fromstring(svn_status).findall("./target/entry"):
- if el.find('wc-status').get("item") not in ["added", "modified"]:
- continue
- path = el.get("path")
- if not path.endswith('.java'):
- continue
- with open(path, 'r') as f:
- filedata = f.read()
- filedata2 = re.sub("since xxx", lambda _: "since {}".format(get_revision()), filedata)
- if filedata != filedata2:
- print("replacing 'since xxx' with 'since {}' in '{}'".format(get_revision(), path))
- with open(path, 'w') as f:
- f.write(filedata2)
-
-def get_revision():
+ tree = ElementTree.fromstring(svn_status)
+ rev = get_revision()
+ if len(args) == 2:
+ for change_set in tree.findall("./changelist"):
+ if (
+ "name" in change_set.attrib
+ and change_set.attrib["name"] == args[1]
+ ):
+ for el in change_set.findall("./entry"):
+ write_xxx(rev, el)
+ elif len(args) > 2:
+ raise ValueError(
+ "Too many args: only one changelist should be passed at a time, "
+ "or none at all "
+ )
+ else:
+ for el in tree.findall("./target/entry"):
+ write_xxx(rev, el)
+
+
+def write_xxx(rev: int, el: ElementTree.Element) -> None:
+ """
+ Write a revision to a changed file
+ :param rev: The revision to write
+ :param el: The element containing the path to the file to update
+ :return: Nothing
+ """
+ if el.find("wc-status").get("item") not in ["added", "modified"]:
+ return
+ path = el.get("path")
+ if not path.endswith(".java"):
+ return
+ with open(path, "r") as f:
+ old_text = f.read()
+ new_text = re.sub("since xxx", lambda _: "since {}".format(rev), old_text)
+ if old_text != new_text:
+ print(
+ "replacing 'since xxx' with 'since {}' in '{}'".format(rev, path)
+ )
+ with open(path, "w") as f:
+ f.write(new_text)
+
+
+def get_revision() -> int:
+ """
+ Get the next revision
+ :return: The current revision + 1
+ """
global revision
if revision is not None:
return revision
svn_info_local = subprocess.check_output("svn info --xml".split(" "))
- rep_url = ElementTree.fromstring(svn_info_local).findtext("./entry/repository/root")
- svn_info_server = subprocess.check_output("svn info --xml".split(" ") + [rep_url])
- revision = int(ElementTree.fromstring(svn_info_server).find("./entry").get("revision")) + 1
+ rep_url = ElementTree.fromstring(svn_info_local).findtext(
+ "./entry/repository/root"
+ )
+ svn_info_server = subprocess.check_output(
+ "svn info --xml".split(" ") + [rep_url]
+ )
+ revision = (
+ int(
+ ElementTree.fromstring(svn_info_server)
+ .find("./entry")
+ .get("revision")
+ )
+ + 1
+ )
return revision
-
-main()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/org/openstreetmap/josm/actions/AddImageryLayerAction.java b/src/org/openstreetmap/josm/actions/AddImageryLayerAction.java
index 7465565f3f0..d3f4e62ad4f 100644
--- a/src/org/openstreetmap/josm/actions/AddImageryLayerAction.java
+++ b/src/org/openstreetmap/josm/actions/AddImageryLayerAction.java
@@ -6,6 +6,7 @@
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
+import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.io.IOException;
@@ -65,7 +66,7 @@ static class SelectWmsLayersDialog extends ExtendedDialog {
scrollPane.setPreferredSize(new Dimension(400, 400));
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(scrollPane, GBC.eol().fill());
- panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
+ panel.add(formats, GBC.eol().fill(GridBagConstraints.HORIZONTAL));
setContent(panel);
}
}
@@ -114,7 +115,7 @@ private static ImageryInfo convertImagery(ImageryInfo info) {
info.setDate(userDate);
// TODO persist new {time} value (via ImageryLayerInfo.save?)
}
- switch(info.getImageryType()) {
+ switch (info.getImageryType()) {
case WMS_ENDPOINT:
// convert to WMS type
if (Utils.isEmpty(info.getDefaultLayers())) {
@@ -232,8 +233,8 @@ private static LayerSelection askToSelectLayers(WMSImagery wms) {
scrollPane.setPreferredSize(new Dimension(400, 400));
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(scrollPane, GBC.eol().fill());
- panel.add(checkBounds, GBC.eol().fill(GBC.HORIZONTAL));
- panel.add(formats, GBC.eol().fill(GBC.HORIZONTAL));
+ panel.add(checkBounds, GBC.eol().fill(GridBagConstraints.HORIZONTAL));
+ panel.add(formats, GBC.eol().fill(GridBagConstraints.HORIZONTAL));
dialog.setContent(panel);
if (dialog.showDialog().getValue() != 1) {
diff --git a/src/org/openstreetmap/josm/actions/AlignInCircleAction.java b/src/org/openstreetmap/josm/actions/AlignInCircleAction.java
index ac00573a99c..faa40c9e5ed 100644
--- a/src/org/openstreetmap/josm/actions/AlignInCircleAction.java
+++ b/src/org/openstreetmap/josm/actions/AlignInCircleAction.java
@@ -1,6 +1,7 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.actions;
+import static java.util.function.Predicate.not;
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
import static org.openstreetmap.josm.tools.I18n.tr;
@@ -134,12 +135,12 @@ public void actionPerformed(ActionEvent e) {
* All other nodes are uniformly distributed.
*
* Case 1: One unclosed way.
- * --> allow action, and align selected way nodes
+ * → allow action, and align selected way nodes
* If nodes contained by this way are selected, there are fix.
* If nodes outside from the way are selected there are ignored.
*
* Case 2: One or more ways are selected and can be joined into a polygon
- * --> allow action, and align selected ways nodes
+ * → allow action, and align selected ways nodes
* If 1 node outside of way is selected, it became center
* If 1 node outside and 1 node inside are selected there define center and radius
* If no outside node and 2 inside nodes are selected those 2 nodes define diameter
@@ -148,10 +149,10 @@ public void actionPerformed(ActionEvent e) {
* (first referrer is the selected way)
*
* Case 3: Only nodes are selected
- * --> Align these nodes, all are fix
+ * → Align these nodes, all are fix
*
* Case 4: Circularize selected ways
- * --> Circularize each way of the selection.
+ * → Circularize each way of the selection.
* @param ds data set in which the command operates
* @return the resulting command to execute to perform action, or null if nothing was changed
* @throws InvalidSelection if selection cannot be used
@@ -271,9 +272,9 @@ public static Command buildCommand(DataSet ds) throws InvalidSelection {
}
fixNodes.addAll(collectNodesWithExternReferrers(ways));
- // Check if one or more nodes are outside of download area
- if (nodes.stream().anyMatch(Node::isOutsideDownloadArea))
- throw new InvalidSelection(tr("One or more nodes involved in this action is outside of the downloaded area."));
+ // Check if one or more nodes does not have all parents available
+ if (nodes.stream().anyMatch(not(Node::isReferrersDownloaded)))
+ throw new InvalidSelection(tr("One or more nodes involved in this action may have additional referrers."));
if (center == null) {
diff --git a/src/org/openstreetmap/josm/actions/AutoScaleAction.java b/src/org/openstreetmap/josm/actions/AutoScaleAction.java
index 7e92317caa4..ccbadd22616 100644
--- a/src/org/openstreetmap/josm/actions/AutoScaleAction.java
+++ b/src/org/openstreetmap/josm/actions/AutoScaleAction.java
@@ -438,7 +438,7 @@ protected final void installAdapters() {
/**
* Adapter for zoom change events
*/
- private class ZoomChangeAdapter implements ZoomChangeListener {
+ private final class ZoomChangeAdapter implements ZoomChangeListener {
@Override
public void zoomChanged() {
updateEnabledState();
diff --git a/src/org/openstreetmap/josm/actions/ChangesetManagerToggleAction.java b/src/org/openstreetmap/josm/actions/ChangesetManagerToggleAction.java
index 6d1387d735c..aef8c62c1a3 100644
--- a/src/org/openstreetmap/josm/actions/ChangesetManagerToggleAction.java
+++ b/src/org/openstreetmap/josm/actions/ChangesetManagerToggleAction.java
@@ -48,7 +48,7 @@ public void actionPerformed(ActionEvent e) {
}
}
- private class ChangesetCacheManagerClosedHandler extends WindowAdapter {
+ private final class ChangesetCacheManagerClosedHandler extends WindowAdapter {
@Override
public void windowClosed(WindowEvent e) {
setSelected(false);
diff --git a/src/org/openstreetmap/josm/actions/CombineWayAction.java b/src/org/openstreetmap/josm/actions/CombineWayAction.java
index c0157f2bef4..05deed4274c 100644
--- a/src/org/openstreetmap/josm/actions/CombineWayAction.java
+++ b/src/org/openstreetmap/josm/actions/CombineWayAction.java
@@ -10,12 +10,10 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -279,20 +277,6 @@ public void actionPerformed(ActionEvent event) {
return;
}
- // see #18083: check if we will combine ways at nodes outside of the download area
- Set endNodesOutside = new HashSet<>();
- for (Way w : selectedWays) {
- final Node[] endnodes = {w.firstNode(), w.lastNode()};
- for (Node n : endnodes) {
- if (!n.isNew() && n.isOutsideDownloadArea() && !endNodesOutside.add(n)) {
- new Notification(tr("Combine ways refused " + "(A shared node is outside of the download area)"))
- .setIcon(JOptionPane.INFORMATION_MESSAGE).show();
- return;
-
- }
- }
- }
-
// combine and update gui
Pair combineResult;
try {
@@ -305,6 +289,10 @@ public void actionPerformed(ActionEvent event) {
if (combineResult == null)
return;
+ // see #18083: check if we will combine ways at nodes outside of the download area
+ if (!checkAndConfirmCombineOutlyingWays(selectedWays))
+ return;
+
final Way selectedWay = combineResult.a;
UndoRedoHandler.getInstance().add(combineResult.b);
Test test = new OverlappingWays();
@@ -346,4 +334,27 @@ protected void updateEnabledState(Collection extends OsmPrimitive> selection)
setEnabled(numWays >= 2);
}
+ /**
+ * Check whether user is about to combine ways with unknown parents.
+ * Request confirmation if he is.
+ * @param ways the primitives to operate on
+ * @return true, if operating on outlying primitives is OK; false, otherwise
+ */
+ private static boolean checkAndConfirmCombineOutlyingWays(Collection ways) {
+ DownloadReferrersAction action = MainApplication.getMenu().downloadReferrers;
+ final String downloadHint = tr("You should use {0}->{1}({2}) first.",
+ MainApplication.getMenu().editMenu.getText(), action.getValue(NAME), action.getShortcut().toString());
+ return Boolean.TRUE.equals(GuiHelper.runInEDTAndWaitAndReturn(() -> checkAndConfirmOutlyingOperation("combine",
+ tr("Combine confirmation"),
+ tr("You are about to combine ways which can be members of relations not yet downloaded."
+ + " "
+ + "This can lead to damaging these parent relations (that you do not see)."
+ + " "
+ + "{0}"
+ + "
"
+ + "Do you really want to combine without downloading?", downloadHint),
+ "", // not used, we never combine incomplete ways
+ ways, Collections.emptyList())));
+ }
+
}
diff --git a/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java b/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java
index de2ba7a772e..6cda25343dd 100644
--- a/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java
+++ b/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java
@@ -464,7 +464,12 @@ public static List removeTagsFromWaysIfNeeded(Relation relation) {
for (Entry entry : values.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
- List affectedWays = innerWays.stream().filter(way -> value.equals(way.get(key))).collect(Collectors.toList());
+ List affectedWays;
+ if ("area".equals(key)) {
+ affectedWays = innerWays.stream().filter(way -> value.equals(way.get(key))).collect(Collectors.toList());
+ } else {
+ affectedWays = new ArrayList<>();
+ }
if (moveTags) {
// remove duplicated tags from outer ways
diff --git a/src/org/openstreetmap/josm/actions/DeleteAction.java b/src/org/openstreetmap/josm/actions/DeleteAction.java
index 96f17c67f94..585b22f04e7 100644
--- a/src/org/openstreetmap/josm/actions/DeleteAction.java
+++ b/src/org/openstreetmap/josm/actions/DeleteAction.java
@@ -16,6 +16,9 @@
import org.openstreetmap.josm.command.DeleteCommand.DeletionCallback;
import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
+import org.openstreetmap.josm.data.osm.INode;
+import org.openstreetmap.josm.data.osm.IRelation;
+import org.openstreetmap.josm.data.osm.IWay;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.RelationToChildReference;
@@ -103,13 +106,27 @@ protected void updateEnabledState(Collection extends OsmPrimitive> selection)
*/
public static boolean checkAndConfirmOutlyingDelete(Collection extends OsmPrimitive> primitives,
Collection extends OsmPrimitive> ignore) {
+ final boolean nodes = primitives.stream().anyMatch(INode.class::isInstance);
+ final boolean ways = primitives.stream().anyMatch(IWay.class::isInstance);
+ final boolean relations = primitives.stream().anyMatch(IRelation.class::isInstance);
+ final String type;
+ if (nodes && !ways && !relations) {
+ type = tr("You are about to delete nodes which can have other referrers not yet downloaded.");
+ } else if (!nodes && ways && !relations) {
+ type = tr("You are about to delete ways which can have other referrers not yet downloaded.");
+ } else if (!nodes && !ways && relations) {
+ type = tr("You are about to delete relations which can have other referrers not yet downloaded.");
+ } else {
+ // OK. We have multiple types being deleted.
+ type = tr("You are about to delete primitives which can have other referrers not yet downloaded.");
+ }
return Boolean.TRUE.equals(GuiHelper.runInEDTAndWaitAndReturn(() -> checkAndConfirmOutlyingOperation("delete",
tr("Delete confirmation"),
- tr("You are about to delete nodes which can have other referrers not yet downloaded."
+ tr("{0}"
+ " "
+ "This can cause problems because other objects (that you do not see) might use them."
+ " "
- + "Do you really want to delete?"),
+ + "Do you really want to delete?", type),
tr("You are about to delete incomplete objects."
+ " "
+ "This will cause problems because you don''t see the real object."
diff --git a/src/org/openstreetmap/josm/actions/DownloadOsmInViewAction.java b/src/org/openstreetmap/josm/actions/DownloadOsmInViewAction.java
index baf75f358bd..477aad6a6cb 100644
--- a/src/org/openstreetmap/josm/actions/DownloadOsmInViewAction.java
+++ b/src/org/openstreetmap/josm/actions/DownloadOsmInViewAction.java
@@ -54,7 +54,7 @@ protected void updateEnabledState() {
&& !NetworkManager.isOffline(OnlineResource.OSM_API));
}
- private static class DownloadOsmInViewTask extends DownloadOsmTask {
+ private static final class DownloadOsmInViewTask extends DownloadOsmTask {
Future> download(Bounds downloadArea) {
return download(new DownloadTask(new DownloadParams(), new BoundingBoxDownloader(downloadArea), null, false), downloadArea);
}
diff --git a/src/org/openstreetmap/josm/actions/FollowLineAction.java b/src/org/openstreetmap/josm/actions/FollowLineAction.java
index b4094b4ed9f..1cbd020de97 100644
--- a/src/org/openstreetmap/josm/actions/FollowLineAction.java
+++ b/src/org/openstreetmap/josm/actions/FollowLineAction.java
@@ -83,6 +83,8 @@ public void actionPerformed(ActionEvent evt) {
if (follower.lastNode().equals(last)) {
prev = follower.getNode(follower.getNodesCount() - 2);
reversed = false;
+ } else if (!follower.firstNode().equals(last)) {
+ return; // see #23442
}
List referrers = last.getReferrers();
if (referrers.size() < 2) return; // There's nothing to follow
diff --git a/src/org/openstreetmap/josm/actions/JoinAreasAction.java b/src/org/openstreetmap/josm/actions/JoinAreasAction.java
index 3615ad59a86..300abc36cbe 100644
--- a/src/org/openstreetmap/josm/actions/JoinAreasAction.java
+++ b/src/org/openstreetmap/josm/actions/JoinAreasAction.java
@@ -102,6 +102,9 @@ public final List getPolygons() {
}
}
+ /**
+ * A record class to store how a multipolygon is constructed
+ */
public static class Multipolygon {
private final Way outerWay;
private final List innerWays;
@@ -160,7 +163,7 @@ public boolean equals(Object other) {
/**
* HelperClass - saves a way and the "inside" side.
- *
+ *
* insideToTheLeft: if true left side is "in", false -right side is "in".
* Left and right are determined along the orientation of way.
*/
@@ -234,10 +237,19 @@ public void reverse() {
}
}
+ /**
+ * A multipolygon with a list of inner ways and an assembled polygon for the outer way
+ */
public static class AssembledMultipolygon {
+ /** The outer way of the multipolygon */
public AssembledPolygon outerWay;
+ /** The inner polygons of the multipolygon */
public List innerWays;
+ /**
+ * Create a new {@link AssembledMultipolygon}
+ * @param way The outer way
+ */
public AssembledMultipolygon(AssembledPolygon way) {
outerWay = way;
innerWays = new ArrayList<>();
@@ -401,7 +413,7 @@ public WayInPolygon walk() {
}
/**
- * Search for an other way coming to the same head node at left side from last way. #9951
+ * Search for another way coming to the same head node at left side from last way. #9951
* @return left way or null if none found
*/
public WayInPolygon leftComingWay() {
@@ -651,8 +663,7 @@ private JoinAreasResult joinAreas(List areas) throws UserCancelExc
allStartingWays.addAll(outerStartingWays);
//first remove nodes in the same coordinate
- boolean removedDuplicates = false;
- removedDuplicates |= removeDuplicateNodes(allStartingWays);
+ boolean removedDuplicates = removeDuplicateNodes(allStartingWays);
if (removedDuplicates) {
hasChanges = true;
@@ -661,7 +672,7 @@ private JoinAreasResult joinAreas(List areas) throws UserCancelExc
commitCommands(marktr("Removed duplicate nodes"));
// remove now unconnected nodes without tags
List toRemove = oldNodes.stream().filter(
- n -> (n.isNew() || !n.isOutsideDownloadArea()) && !n.hasKeys() && n.getReferrers().isEmpty())
+ n -> n.isReferrersDownloaded() && !n.hasKeys() && n.getReferrers().isEmpty())
.collect(Collectors.toList());
if (!toRemove.isEmpty()) {
cmds.add(new DeleteCommand(toRemove));
@@ -886,7 +897,7 @@ private boolean removeDuplicateNodes(List ways) {
* @param description The description of what the commands do
*/
private void commitCommands(String description) {
- switch(cmds.size()) {
+ switch (cmds.size()) {
case 0:
return;
case 1:
@@ -1134,7 +1145,7 @@ private List splitWayOnNodes(Way way, Set nodes, Map oldes
if (chunks.size() > 1) {
SplitWayCommand split = SplitWayCommand.splitWay(way, chunks,
- Collections.emptyList(), SplitWayCommand.Strategy.keepFirstChunk());
+ Collections.emptyList(), SplitWayCommand.Strategy.keepFirstChunk());
if (split != null) {
//execute the command, we need the results
diff --git a/src/org/openstreetmap/josm/actions/OpenFileAction.java b/src/org/openstreetmap/josm/actions/OpenFileAction.java
index be8153cd796..80625bc5a23 100644
--- a/src/org/openstreetmap/josm/actions/OpenFileAction.java
+++ b/src/org/openstreetmap/josm/actions/OpenFileAction.java
@@ -83,7 +83,7 @@ public void actionPerformed(ActionEvent e) {
final AbstractFileChooser fc;
// If the user explicitly wants native file dialogs, let them use it.
// Rather unfortunately, this means that they will not be able to select files and directories.
- if (FileChooserManager.PROP_USE_NATIVE_FILE_DIALOG.get()
+ if (Boolean.TRUE.equals(FileChooserManager.PROP_USE_NATIVE_FILE_DIALOG.get())
// This is almost redundant, as the JDK currently doesn't support this with (all?) native file choosers.
&& !NativeFileChooser.supportsSelectionMode(FILES_AND_DIRECTORIES)) {
fc = createAndOpenFileChooser(true, true, null);
diff --git a/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java b/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
index 60d0fd1435f..b6d0cf55ffe 100644
--- a/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
+++ b/src/org/openstreetmap/josm/actions/OrthogonalizeAction.java
@@ -43,7 +43,7 @@
/**
* Tools / Orthogonalize
- *
+ *
* Align edges of a way so all angles are angles of 90 or 180 degrees.
* See USAGE String below.
*/
@@ -72,7 +72,7 @@ public OrthogonalizeAction() {
/**
* excepted deviation from an angle of 0, 90, 180, 360 degrees
* maximum value: 45 degrees
- *
+ *
* Current policy is to except just everything, no matter how strange the result would be.
*/
private static final double TOLERANCE1 = Utils.toRadians(45.); // within a way
@@ -85,10 +85,10 @@ public OrthogonalizeAction() {
/**
* Undo the previous orthogonalization for certain nodes.
- *
+ *
* This is useful, if the way shares nodes that you don't like to change, e.g. imports or
* work of another user.
- *
+ *
* This action can be triggered by shortcut only.
*/
public static class Undo extends JosmAction {
@@ -278,7 +278,8 @@ private static Command orthogonalize(List wayDataList, Node singleNode)
for (WayData wd : wayDataList) {
int n = wd.wayNodes.size();
int i = wd.wayNodes.indexOf(singleNode);
- Node n0, n2;
+ final Node n0;
+ final Node n2;
if (i == 0 && n >= 3 && singleNode.equals(wd.wayNodes.get(n-1))) {
n0 = wd.wayNodes.get(n-2);
n2 = wd.wayNodes.get(1);
@@ -427,7 +428,7 @@ private static Collection orthogonalize(List wayDataList, List
double average = 0;
for (Node n : cs) {
s.remove(n);
- average += nC.get(n).doubleValue();
+ average += nC.get(n);
}
average = average / cs.size();
@@ -546,6 +547,12 @@ public void calcDirections(Direction pInitialDirection) throws InvalidUserInputE
enum Direction {
RIGHT, UP, LEFT, DOWN;
+ /**
+ * Change a direction by the specified number of 90 degree increments counter-clockwise
+ * @param directionChange The number of increments to rotate counter-clockwise
+ * @return The new direction
+ */
+ @SuppressWarnings("EnumOrdinal") // Yes, this is very dependent on order
public Direction changeBy(int directionChange) {
int tmp = (this.ordinal() + directionChange) % 4;
if (tmp < 0) {
diff --git a/src/org/openstreetmap/josm/actions/SaveAction.java b/src/org/openstreetmap/josm/actions/SaveAction.java
index 310ea1d12e2..f0b0078b43a 100644
--- a/src/org/openstreetmap/josm/actions/SaveAction.java
+++ b/src/org/openstreetmap/josm/actions/SaveAction.java
@@ -22,8 +22,6 @@
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
-import org.openstreetmap.josm.gui.layer.SaveToFile;
-import org.openstreetmap.josm.gui.util.GuiHelper;
import org.openstreetmap.josm.spi.preferences.Config;
import org.openstreetmap.josm.tools.GBC;
import org.openstreetmap.josm.tools.Shortcut;
@@ -88,15 +86,6 @@ protected boolean listenToSelectionChange() {
return false;
}
- @Override
- protected void updateEnabledState() {
- Layer activeLayer = getLayerManager().getActiveLayer();
- boolean en = activeLayer != null
- && activeLayer.isSavable() && !(activeLayer.getAssociatedFile() != null
- && activeLayer instanceof SaveToFile && !((SaveToFile) activeLayer).requiresSaveToFile());
- GuiHelper.runInEDT(() -> setEnabled(en));
- }
-
@Override
public File getFile(Layer layer) {
File f = layer.getAssociatedFile();
diff --git a/src/org/openstreetmap/josm/actions/SaveActionBase.java b/src/org/openstreetmap/josm/actions/SaveActionBase.java
index 15f274d8ec0..f01e973b1b1 100644
--- a/src/org/openstreetmap/josm/actions/SaveActionBase.java
+++ b/src/org/openstreetmap/josm/actions/SaveActionBase.java
@@ -22,6 +22,7 @@
import org.openstreetmap.josm.gui.io.importexport.FileExporter;
import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.SaveToFile;
import org.openstreetmap.josm.gui.util.GuiHelper;
import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
import org.openstreetmap.josm.spi.preferences.Config;
@@ -182,9 +183,14 @@ protected boolean listenToSelectionChange() {
}
@Override
- protected void updateEnabledState() {
+ protected final void updateEnabledState() {
Layer activeLayer = getLayerManager().getActiveLayer();
- setEnabled(activeLayer != null && activeLayer.isSavable());
+ boolean en = activeLayer != null && activeLayer.isSavable();
+ // see #12669 and #23648
+ if (en && this instanceof SaveAction && activeLayer instanceof SaveToFile) {
+ en = activeLayer.getAssociatedFile() == null || ((SaveToFile) activeLayer).requiresSaveToFile();
+ }
+ setEnabled(en);
}
/**
diff --git a/src/org/openstreetmap/josm/actions/SelectAllAction.java b/src/org/openstreetmap/josm/actions/SelectAllAction.java
index 494a616d2e8..bd1ef14d599 100644
--- a/src/org/openstreetmap/josm/actions/SelectAllAction.java
+++ b/src/org/openstreetmap/josm/actions/SelectAllAction.java
@@ -7,6 +7,7 @@
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
+import org.openstreetmap.josm.data.osm.IPrimitive;
import org.openstreetmap.josm.data.osm.OsmData;
import org.openstreetmap.josm.tools.Shortcut;
@@ -29,12 +30,7 @@ public void actionPerformed(ActionEvent e) {
if (!isEnabled())
return;
OsmData, ?, ?, ?> ds = getLayerManager().getActiveData();
- // Do not use method reference before the Java 11 migration
- // Otherwise we face a compiler bug, see below:
- // https://bugs.openjdk.java.net/browse/JDK-8141508
- // https://bugs.openjdk.java.net/browse/JDK-8142476
- // https://bugs.openjdk.java.net/browse/JDK-8191655
- ds.setSelected(ds.getPrimitives(t -> t.isSelectable()));
+ ds.setSelected(ds.getPrimitives(IPrimitive::isSelectable));
}
@Override
diff --git a/src/org/openstreetmap/josm/actions/SelectByInternalPointAction.java b/src/org/openstreetmap/josm/actions/SelectByInternalPointAction.java
index 09b7831eae8..a76bdf51dac 100644
--- a/src/org/openstreetmap/josm/actions/SelectByInternalPointAction.java
+++ b/src/org/openstreetmap/josm/actions/SelectByInternalPointAction.java
@@ -103,18 +103,18 @@ public static OsmPrimitive getSmallestSurroundingObject(EastNorth internalPoint)
public static void performSelection(EastNorth internalPoint, boolean doAdd, boolean doRemove) {
final Collection surroundingObjects = getSurroundingObjects(internalPoint);
final DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
- if (surroundingObjects.isEmpty()) {
- return;
- } else if (doRemove) {
- final Collection newSelection = new ArrayList<>(ds.getSelected());
- newSelection.removeAll(surroundingObjects);
- ds.setSelected(newSelection);
- } else if (doAdd) {
- final Collection newSelection = new ArrayList<>(ds.getSelected());
- newSelection.add(surroundingObjects.iterator().next());
- ds.setSelected(newSelection);
- } else {
- ds.setSelected(surroundingObjects.iterator().next());
+ if (!surroundingObjects.isEmpty()) {
+ if (doRemove) {
+ final Collection newSelection = new ArrayList<>(ds.getSelected());
+ newSelection.removeAll(surroundingObjects);
+ ds.setSelected(newSelection);
+ } else if (doAdd) {
+ final Collection newSelection = new ArrayList<>(ds.getSelected());
+ newSelection.add(surroundingObjects.iterator().next());
+ ds.setSelected(newSelection);
+ } else {
+ ds.setSelected(surroundingObjects.iterator().next());
+ }
}
}
}
diff --git a/src/org/openstreetmap/josm/actions/SelectNonBranchingWaySequences.java b/src/org/openstreetmap/josm/actions/SelectNonBranchingWaySequences.java
index 13ed63d07d5..1eb153c8c98 100644
--- a/src/org/openstreetmap/josm/actions/SelectNonBranchingWaySequences.java
+++ b/src/org/openstreetmap/josm/actions/SelectNonBranchingWaySequences.java
@@ -55,10 +55,11 @@ public SelectNonBranchingWaySequences(final Collection ways) {
*/
private void addNodes(Node node) {
if (node == null) return;
- else if (!nodes.add(node))
+ if (!nodes.add(node)) {
outerNodes.remove(node);
- else
+ } else {
outerNodes.add(node);
+ }
}
/**
diff --git a/src/org/openstreetmap/josm/actions/SessionSaveAction.java b/src/org/openstreetmap/josm/actions/SessionSaveAction.java
index 9dbbcd4a649..acb1c629a26 100644
--- a/src/org/openstreetmap/josm/actions/SessionSaveAction.java
+++ b/src/org/openstreetmap/josm/actions/SessionSaveAction.java
@@ -2,11 +2,13 @@
package org.openstreetmap.josm.actions;
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.marktr;
import static org.openstreetmap.josm.tools.I18n.tr;
import static org.openstreetmap.josm.tools.I18n.trn;
import java.awt.Component;
import java.awt.Dimension;
+import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
@@ -83,6 +85,7 @@ public class SessionSaveAction extends DiskAccessAction implements MapFrameListe
private static final BooleanProperty SAVE_LOCAL_FILES_PROPERTY = new BooleanProperty("session.savelocal", true);
private static final BooleanProperty SAVE_PLUGIN_INFORMATION_PROPERTY = new BooleanProperty("session.saveplugins", false);
private static final String TOOLTIP_DEFAULT = tr("Save the current session.");
+ private static final String SAVE_SESSION = marktr("Save Session");
protected transient FileFilter joz = new ExtensionFileFilter("joz", "joz", tr("Session file (archive) (*.joz)"));
protected transient FileFilter jos = new ExtensionFileFilter("jos", "jos", tr("Session file (*.jos)"));
@@ -119,7 +122,7 @@ public SessionSaveAction() {
* @param installAdapters False, if you don't want to install layer changed and selection changed adapters
*/
protected SessionSaveAction(boolean toolbar, boolean installAdapters) {
- this(tr("Save Session"), "session", TOOLTIP_DEFAULT,
+ this(tr(SAVE_SESSION), "session", TOOLTIP_DEFAULT,
Shortcut.registerShortcut("system:savesession", tr("File: {0}", tr("Save Session...")), KeyEvent.VK_S, Shortcut.ALT_CTRL),
toolbar, "save-session", installAdapters);
setHelpId(ht("/Action/SessionSave"));
@@ -155,6 +158,14 @@ public void destroy() {
* @throws UserCancelException when the user has cancelled the save process
*/
public boolean saveSession(boolean saveAs, boolean forceSaveAll) throws UserCancelException {
+ try {
+ return saveSessionImpl(saveAs, forceSaveAll);
+ } finally {
+ cleanup();
+ }
+ }
+
+ private boolean saveSessionImpl(boolean saveAs, boolean forceSaveAll) throws UserCancelException {
if (!isEnabled()) {
return false;
}
@@ -174,7 +185,7 @@ public boolean saveSession(boolean saveAs, boolean forceSaveAll) throws UserCanc
.filter(layer -> exporters.get(layer) != null && exporters.get(layer).shallExport())
.collect(Collectors.toList());
- boolean zipRequired = layersOut.stream().map(l -> exporters.get(l))
+ boolean zipRequired = layersOut.stream().map(exporters::get)
.anyMatch(ex -> ex != null && ex.requiresZip()) || pluginsWantToSave();
saveAs = !doGetFile(saveAs, zipRequired);
@@ -324,9 +335,9 @@ protected void doGetFileChooser(boolean zipRequired) throws UserCancelException
AbstractFileChooser fc;
if (zipRequired) {
- fc = createAndOpenFileChooser(false, false, tr("Save Session"), joz, JFileChooser.FILES_ONLY, "lastDirectory");
+ fc = createAndOpenFileChooser(false, false, tr(SAVE_SESSION), joz, JFileChooser.FILES_ONLY, "lastDirectory");
} else {
- fc = createAndOpenFileChooser(false, false, tr("Save Session"), Arrays.asList(jos, joz), jos,
+ fc = createAndOpenFileChooser(false, false, tr(SAVE_SESSION), Arrays.asList(jos, joz), jos,
JFileChooser.FILES_ONLY, "lastDirectory");
}
@@ -357,7 +368,7 @@ public class SessionSaveAsDialog extends ExtendedDialog {
* Constructs a new {@code SessionSaveAsDialog}.
*/
public SessionSaveAsDialog() {
- super(MainApplication.getMainFrame(), tr("Save Session"), tr("Save As"), tr("Cancel"));
+ super(MainApplication.getMainFrame(), tr(SAVE_SESSION), tr("Save As"), tr("Cancel"));
configureContextsensitiveHelp("Action/SessionSaveAs", true /* show help button */);
initialize();
setButtonIcons("save_as", "cancel");
@@ -368,9 +379,9 @@ public SessionSaveAsDialog() {
}
/**
- * Initializes action.
+ * Initializes some action fields.
*/
- public final void initialize() {
+ private void initialize() {
layers = new ArrayList<>(getLayerManager().getLayers());
exporters = new HashMap<>();
dependencies = new MultiMap<>();
@@ -433,10 +444,10 @@ protected final Component build() {
if (exportPanel == null) continue;
JPanel wrapper = new JPanel(new GridBagLayout());
wrapper.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
- wrapper.add(exportPanel, GBC.std().fill(GBC.HORIZONTAL));
- ip.add(wrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(2, 2, 4, 2));
+ wrapper.add(exportPanel, GBC.std().fill(GridBagConstraints.HORIZONTAL));
+ ip.add(wrapper, GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(2, 2, 4, 2));
}
- ip.add(GBC.glue(0, 1), GBC.eol().fill(GBC.VERTICAL));
+ ip.add(GBC.glue(0, 1), GBC.eol().fill(GridBagConstraints.VERTICAL));
JScrollPane sp = new JScrollPane(ip);
sp.setBorder(BorderFactory.createEmptyBorder());
JPanel p = new JPanel(new GridBagLayout());
@@ -466,7 +477,7 @@ protected final Component getDisabledExportPanel(Layer layer) {
lbl.setEnabled(false);
p.add(include, GBC.std());
p.add(lbl, GBC.std());
- p.add(GBC.glue(1, 0), GBC.std().fill(GBC.HORIZONTAL));
+ p.add(GBC.glue(1, 0), GBC.std().fill(GridBagConstraints.HORIZONTAL));
return p;
}
}
@@ -529,7 +540,7 @@ private static void updateSessionFile(String fileName) throws UserCancelExceptio
* @param layers layers that are currently represented in the session file
* @deprecated since 18833, use {@link #setCurrentSession(File, List, SessionWriter.SessionWriterFlags...)} instead
*/
- @Deprecated
+ @Deprecated(since = "18833")
public static void setCurrentSession(File file, boolean zip, List layers) {
if (zip) {
setCurrentSession(file, layers, SessionWriter.SessionWriterFlags.IS_ZIP);
@@ -612,4 +623,10 @@ private static boolean pluginsWantToSave() {
return false;
}
+ protected void cleanup() {
+ layers = null;
+ exporters = null;
+ dependencies = null;
+ }
+
}
diff --git a/src/org/openstreetmap/josm/actions/SimplifyWayAction.java b/src/org/openstreetmap/josm/actions/SimplifyWayAction.java
index 5fca7d46f39..e16e92eb8dc 100644
--- a/src/org/openstreetmap/josm/actions/SimplifyWayAction.java
+++ b/src/org/openstreetmap/josm/actions/SimplifyWayAction.java
@@ -340,12 +340,21 @@ public static void simplifyWays(List ways, double threshold) {
* @since 16566 (private)
*/
private static SequenceCommand buildSimplifyWaysCommand(List ways, double threshold) {
- Collection allCommands = ways.stream()
- .map(way -> createSimplifyCommand(way, threshold))
+ List allCommands = ways.stream()
+ .map(way -> createSimplifyCommand(way, threshold, false))
.filter(Objects::nonNull)
.collect(StreamUtils.toUnmodifiableList());
if (allCommands.isEmpty())
return null;
+ final List deletedPrimitives = allCommands.stream()
+ .map(Command::getChildren)
+ .flatMap(Collection::stream)
+ .filter(DeleteCommand.class::isInstance)
+ .map(DeleteCommand.class::cast)
+ .map(DeleteCommand::getParticipatingPrimitives)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
+ allCommands.get(0).getAffectedDataSet().clearSelection(deletedPrimitives);
return new SequenceCommand(
trn("Simplify {0} way", "Simplify {0} ways", allCommands.size(), allCommands.size()),
allCommands);
@@ -371,6 +380,18 @@ public static SequenceCommand createSimplifyCommand(Way w) {
* @since 15419
*/
public static SequenceCommand createSimplifyCommand(Way w, double threshold) {
+ return createSimplifyCommand(w, threshold, true);
+ }
+
+ /**
+ * Creates the SequenceCommand to simplify a way with a given threshold.
+ *
+ * @param w the way to simplify
+ * @param threshold the max error threshold
+ * @param deselect {@code true} if we want to deselect the deleted nodes
+ * @return The sequence of commands to run
+ */
+ private static SequenceCommand createSimplifyCommand(Way w, double threshold, boolean deselect) {
int lower = 0;
int i = 0;
@@ -417,7 +438,9 @@ public static SequenceCommand createSimplifyCommand(Way w, double threshold) {
Collection cmds = new LinkedList<>();
cmds.add(new ChangeNodesCommand(w, newNodes));
cmds.add(new DeleteCommand(w.getDataSet(), delNodes));
- w.getDataSet().clearSelection(delNodes);
+ if (deselect) {
+ w.getDataSet().clearSelection(delNodes);
+ }
return new SequenceCommand(
trn("Simplify Way (remove {0} node)", "Simplify Way (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds);
}
diff --git a/src/org/openstreetmap/josm/actions/SplitWayAction.java b/src/org/openstreetmap/josm/actions/SplitWayAction.java
index a4985c23bcb..8f1331b1059 100644
--- a/src/org/openstreetmap/josm/actions/SplitWayAction.java
+++ b/src/org/openstreetmap/josm/actions/SplitWayAction.java
@@ -6,6 +6,7 @@
import static org.openstreetmap.josm.tools.I18n.trn;
import java.awt.Component;
+import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
@@ -142,6 +143,14 @@ public static void runOn(DataSet ds) {
.setIcon(JOptionPane.WARNING_MESSAGE)
.show();
return;
+ } else if (!checkAndConfirmOutlyingOperation("splitway", tr("Split way confirmation"),
+ tr("You are about to split a way that may have referrers that are not yet downloaded.")
+ + " "
+ + tr("This can lead to broken relations.") + " "
+ + tr("Do you really want to split?"),
+ tr("The selected area is incomplete. Continue?"),
+ applicableWays, null)) {
+ return;
}
// Finally, applicableWays contains only one perfect way
@@ -210,8 +219,8 @@ static class SegmentToKeepSelectionDialog extends ExtendedDialog {
setButtonIcons("ok", "cancel");
final JPanel pane = new JPanel(new GridBagLayout());
- pane.add(new JLabel(getTitle()), GBC.eol().fill(GBC.HORIZONTAL));
- pane.add(list, GBC.eop().fill(GBC.HORIZONTAL));
+ pane.add(new JLabel(getTitle()), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
+ pane.add(list, GBC.eop().fill(GridBagConstraints.HORIZONTAL));
setContent(pane);
setDefaultCloseOperation(HIDE_ON_CLOSE);
}
@@ -236,7 +245,7 @@ private void configureList() {
}
protected void setHighlightedWaySegments(Collection segments) {
- DataSet ds = selectedWay.getDataSet();
+ final DataSet ds = selectedWay.getDataSet();
if (ds != null) {
ds.setHighlightedWaySegments(segments);
MainApplication.getMap().mapView.repaint();
@@ -246,7 +255,7 @@ protected void setHighlightedWaySegments(Collection segments) {
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
- DataSet ds = selectedWay.getDataSet();
+ final DataSet ds = selectedWay.getDataSet();
if (visible) {
DISPLAY_COUNT.incrementAndGet();
list.setSelectedValue(wayToKeep, true);
@@ -275,7 +284,7 @@ protected void buttonAction(int buttonIndex, ActionEvent evt) {
}
}
- private class SplitWayDataSetListener implements DataSetListener {
+ private final class SplitWayDataSetListener implements DataSetListener {
@Override
public void primitivesAdded(PrimitivesAddedEvent event) {
@@ -358,7 +367,7 @@ static List getApplicableWays(List selectedWays, List selectedNo
// Special case - one of the selected ways touches (not cross) way that we want to split
if (selectedNodes.size() == 1) {
- Node n = selectedNodes.get(0);
+ final Node n = selectedNodes.get(0);
List referredWays = n.getParentWays();
Way inTheMiddle = null;
for (Way w: referredWays) {
diff --git a/src/org/openstreetmap/josm/actions/ToggleUploadDiscouragedLayerAction.java b/src/org/openstreetmap/josm/actions/ToggleUploadDiscouragedLayerAction.java
index a15a58e7160..f5aab4867bd 100644
--- a/src/org/openstreetmap/josm/actions/ToggleUploadDiscouragedLayerAction.java
+++ b/src/org/openstreetmap/josm/actions/ToggleUploadDiscouragedLayerAction.java
@@ -11,10 +11,12 @@
import javax.swing.AbstractAction;
import javax.swing.JCheckBoxMenuItem;
+import org.openstreetmap.josm.gui.Notification;
import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.gui.layer.Layer.LayerAction;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.util.GuiHelper;
import org.openstreetmap.josm.tools.ImageProvider;
/**
@@ -40,6 +42,8 @@ public ToggleUploadDiscouragedLayerAction(OsmDataLayer layer) {
@Override
public void actionPerformed(ActionEvent e) {
layer.setUploadDiscouraged(!layer.isUploadDiscouraged());
+ String msg = layer.isUploadDiscouraged() ? tr("Upload is discouraged") : tr("Upload is encouraged");
+ GuiHelper.runInEDT(() -> new Notification(msg).show());
LayerListDialog.getInstance().repaint();
}
diff --git a/src/org/openstreetmap/josm/actions/ValidateAction.java b/src/org/openstreetmap/josm/actions/ValidateAction.java
index 300097a5f37..ebd92e0d750 100644
--- a/src/org/openstreetmap/josm/actions/ValidateAction.java
+++ b/src/org/openstreetmap/josm/actions/ValidateAction.java
@@ -12,7 +12,6 @@
import org.openstreetmap.josm.data.validation.OsmValidator;
import org.openstreetmap.josm.data.validation.Test;
import org.openstreetmap.josm.data.validation.ValidationTask;
-import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor;
import org.openstreetmap.josm.gui.MainApplication;
import org.openstreetmap.josm.gui.MapFrame;
import org.openstreetmap.josm.tools.Shortcut;
@@ -70,8 +69,6 @@ public void doValidate(boolean getSelectedItems) {
selection = getLayerManager().getActiveDataSet().allNonDeletedPrimitives();
lastSelection = null;
} else {
- AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor();
- selection = v.visit(selection);
lastSelection = selection;
}
} else {
diff --git a/src/org/openstreetmap/josm/actions/corrector/ReverseWayNoTagCorrector.java b/src/org/openstreetmap/josm/actions/corrector/ReverseWayNoTagCorrector.java
index 15ea587f140..76b7f623630 100644
--- a/src/org/openstreetmap/josm/actions/corrector/ReverseWayNoTagCorrector.java
+++ b/src/org/openstreetmap/josm/actions/corrector/ReverseWayNoTagCorrector.java
@@ -101,7 +101,7 @@ private static boolean confirmReverseWay(Way way, TagCollection tags) {
null,
null
);
- switch(ret) {
+ switch (ret) {
case ConditionalOptionPaneUtil.DIALOG_DISABLED_OPTION:
case JOptionPane.YES_OPTION:
return true;
diff --git a/src/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTask.java b/src/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTask.java
index 521ad9c1675..39d170ffcb8 100644
--- a/src/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTask.java
+++ b/src/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTask.java
@@ -60,8 +60,7 @@ public void setFailed(boolean failed) {
}
protected static & UrlPattern> String[] patterns(Class urlPatternEnum) {
- // Do not use a method reference until we switch to Java 11, as we face JDK-8141508 with Java 8
- return Arrays.stream(urlPatternEnum.getEnumConstants()).map(/* JDK-8141508 */ t -> t.pattern()).toArray(String[]::new);
+ return Arrays.stream(urlPatternEnum.getEnumConstants()).map(UrlPattern::pattern).toArray(String[]::new);
}
protected final void rememberErrorMessage(String message) {
diff --git a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGeoJsonTask.java b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGeoJsonTask.java
index 8fd4b61267e..44b5fc89509 100644
--- a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGeoJsonTask.java
+++ b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGeoJsonTask.java
@@ -5,6 +5,7 @@
import java.util.Optional;
import java.util.concurrent.Future;
+import java.util.function.Predicate;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.osm.DataSet;
@@ -56,7 +57,7 @@ class InternalDownloadTask extends DownloadTask {
@Override
protected String generateLayerName() {
return Optional.of(url.substring(url.lastIndexOf('/')+1))
- .filter(it -> !Utils.isStripEmpty(it))
+ .filter(Predicate.not(Utils::isStripEmpty))
.orElse(super.generateLayerName());
}
diff --git a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java
index 34eeca71d8e..e140c4e4dd6 100644
--- a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java
+++ b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java
@@ -1,6 +1,7 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.actions.downloadtasks;
+import static java.util.function.Predicate.not;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.io.IOException;
@@ -298,7 +299,7 @@ protected OsmDataLayer getFirstModifiableDataLayer() {
*/
protected String generateLayerName() {
return Optional.ofNullable(settings.getLayerName())
- .filter(layerName -> !Utils.isStripEmpty(layerName))
+ .filter(not(Utils::isStripEmpty))
.orElse(OsmDataLayer.createNewName());
}
@@ -359,7 +360,7 @@ protected OsmDataLayer addNewLayerIfRequired(String newLayerName) {
if (settings.isNewLayer() || numDataLayers == 0 || (numDataLayers > 1 && getEditLayer() == null)) {
// the user explicitly wants a new layer, we don't have any layer at all
// or it is not clear which layer to merge to
- final OsmDataLayer layer = createNewLayer(Optional.ofNullable(newLayerName).filter(it -> !Utils.isStripEmpty(it)));
+ final OsmDataLayer layer = createNewLayer(Optional.ofNullable(newLayerName).filter(not(Utils::isStripEmpty)));
MainApplication.getLayerManager().addLayer(layer, zoomAfterDownload);
return layer;
}
diff --git a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java
index 221fc41d4ac..57317823c2f 100644
--- a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java
+++ b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java
@@ -123,6 +123,9 @@ protected void finish() {
DataSetMerger visitor = new DataSetMerger(targetLayer.getDataSet(), parents);
visitor.merge();
+ this.children.stream().map(p -> targetLayer.getDataSet().getPrimitiveById(p))
+ .forEach(p -> p.setReferrersDownloaded(true));
+
SwingUtilities.invokeLater(targetLayer::onPostDownloadFromServer);
if (visitor.getConflicts().isEmpty())
return;
@@ -171,7 +174,7 @@ protected void realRun() throws SAXException, IOException, OsmTransferException
return;
String msg;
String id = Long.toString(p.getUniqueId());
- switch(p.getType()) {
+ switch (p.getType()) {
case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break;
case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break;
case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break;
diff --git a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java
index 0e57109788f..f8369409b16 100644
--- a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java
+++ b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java
@@ -212,8 +212,9 @@ protected void handlePotentiallyDeletedPrimitives(Set potentiallyD
*/
public Set getDownloadedPrimitives() {
return tasks.stream()
- .filter(t -> t instanceof DownloadOsmTask)
- .map(t -> ((DownloadOsmTask) t).getDownloadedData())
+ .filter(DownloadOsmTask.class::isInstance)
+ .map(DownloadOsmTask.class::cast)
+ .map(DownloadOsmTask::getDownloadedData)
.filter(Objects::nonNull)
.flatMap(ds -> ds.allPrimitives().stream())
.collect(Collectors.toSet());
@@ -239,7 +240,11 @@ public void run() {
for (Future> future : taskFutures) {
try {
future.get();
- } catch (InterruptedException | ExecutionException | CancellationException e) {
+ } catch (InterruptedException interruptedException) {
+ Thread.currentThread().interrupt();
+ Logging.error(interruptedException);
+ return;
+ } catch (ExecutionException | CancellationException e) {
Logging.error(e);
return;
}
@@ -254,7 +259,6 @@ public void run() {
+ tr("The following errors occurred during mass download: {0}",
Utils.joinAsHtmlUnorderedList(errors)) + "