diff --git a/.github/workflows/ant.yml b/.github/workflows/ant.yml index 153d9d2a..417a4782 100644 --- a/.github/workflows/ant.yml +++ b/.github/workflows/ant.yml @@ -2,71 +2,48 @@ name: Java CI on: push: - branches: [ main, fixup-tests ] + branches: + - main + - $default-branch + - $protected-branches pull_request: - branches: [ main ] - release: - types: [created] + branches: + - main + - $default-branch + workflow_dispatch: jobs: - build: + check-release-needed: runs-on: ubuntu-latest + outputs: + release_needed: ${{ steps.create_release_needed.outputs.release_needed }} steps: - - name: Set up JDK 8 - uses: actions/setup-java@v3 - with: - java-version: '8' - distribution: 'temurin' - - name: Cache - uses: actions/cache@v2.0.0 - with: - path: | - ~/.ivy2/cache/ - $GITHUB_WORKSPACE/josm/core/tools/ - key: ${{ runner.os }}-ivy2-${{ hashFiles('josm/plugins/${{ github.event.repository.name }}/build.xml', 'josm/plugins/00_core_tools/ivy.xml', 'josm/core/ivy.xml', 'josm/core/tools/ivy.xml') }} - - name: Clone JOSM - run: | - svn co --depth=immediates https://josm.openstreetmap.de/osmsvn/applications/editors/josm $GITHUB_WORKSPACE/josm - cd $GITHUB_WORKSPACE/josm - svn up --set-depth=immediates plugins - svn up --set-depth=infinity --accept=theirs-full core i18n plugins/{00_core_test_config,00_core_test_lib,00_core_tools,00_tools} - svn propget svn:externals | grep core | xargs -L1 svn co - cd core - ant dist - cd ../plugins - svn propget svn:externals | grep 00_core | xargs -L1 svn co - - uses: actions/checkout@v3 - with: - path: josm/plugins/${{ github.event.repository.name }} - - name: Build with Ant - run: | - cd $GITHUB_WORKSPACE/josm/plugins/${{ github.event.repository.name }} - if [ $GITHUB_REF_TYPE == "tag" ]; then - version=$GITHUB_REF_NAME - else - version="$GITHUB_REF_NAME-$GITHUB_SHA" - fi - ant -noinput -buildfile build.xml -Dplugin.version=$version - - name: Test with Ant - run: | - cd $GITHUB_WORKSPACE/josm/plugins/${{ github.event.repository.name }} - ant -noinput -buildfile build.xml -Dplugin.version=$version -Dtest.headless test - - name: Dump errors if failed - if: ${{ failure() }} - run: "grep -L ', Failures: 0, Skipped: ' test/report/*.txt | xargs cat" - - name: Upload Ant reports - if: ${{ always() }} - uses: actions/upload-artifact@v2 - with: - name: Ant reports for JOSM plugin ${{ github.event.repository.name }} - path: | - $GITHUB_WORKSPACE/josm/plugins/${{ github.event.repository.name }}/test/report/*.txt - $GITHUB_WORKSPACE/josm/plugins/${{ github.event.repository.name }}/test/report/TEST*.xml - - name: Release - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') - with: - files: | - josm/dist/${{ github.event.repository.name }}.jar - josm/dist/${{ github.event.repository.name }}-javadoc.jar - josm/dist/${{ github.event.repository.name }}-sources.jar + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: ${{ (github.repository == 'JOSM/wikipedia' && github.ref_type == 'branch' && github.ref_name == 'main' && github.event_name != 'schedule' && github.event_name != 'pull_request') && '0' || '1' }} + + - name: Set release needed + id: create_release_needed + run: | + last_tag=$(git describe --tags --abbrev=0 --always) + release_needed="false" + for file in $(git diff ${last_tag}..HEAD --name-only); do + if [[ $file = "src/main"* ]] || [[ $file = "" ]]; then + release_needed="true" + break + fi + done + echo "release_needed=$release_needed" >> $GITHUB_OUTPUT + + + call-workflow: + needs: check-release-needed + strategy: + matrix: + josm-revision: ["", "r18877"] + uses: JOSM/JOSMPluginAction/.github/workflows/ant.yml@v2 + with: + josm-revision: ${{ matrix.josm-revision }} + perform-revision-tagging: ${{ matrix.josm-revision == 'r18877' && needs.check-release-needed.outputs.release_needed == 'true' }} + secrets: inherit diff --git a/src/main/java/org/wikipedia/WikipediaApp.java b/src/main/java/org/wikipedia/WikipediaApp.java index aa93ab29..c7512a83 100644 --- a/src/main/java/org/wikipedia/WikipediaApp.java +++ b/src/main/java/org/wikipedia/WikipediaApp.java @@ -4,6 +4,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.AbstractList; @@ -29,9 +30,11 @@ import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.osm.OsmPrimitive; +import org.openstreetmap.josm.data.osm.Tagged; import org.openstreetmap.josm.gui.Notification; import org.openstreetmap.josm.tools.HttpClient; import org.openstreetmap.josm.tools.I18n; +import org.openstreetmap.josm.tools.JosmRuntimeException; import org.openstreetmap.josm.tools.LanguageInfo; import org.openstreetmap.josm.tools.Logging; import org.openstreetmap.josm.tools.Pair; @@ -55,21 +58,28 @@ public final class WikipediaApp { private static final String STRING_URI_PIPE = Utils.encodeUrl("|"); + private static final String WIKIDATA = "wikidata"; + private static final String WIKIPEDIA = "wikipedia"; + + private final String[] wikipediaKeys; private final String wikipediaLang; private final SitematrixResult.Sitematrix.Site site; private WikipediaApp(final String wikipediaLang) throws IOException { this.wikipediaLang = wikipediaLang; + this.wikipediaKeys = new String[] {WIKIDATA, WIKIPEDIA, WIKIPEDIA + ':' + wikipediaLang}; final SitematrixResult.Sitematrix sitematrix = ApiQueryClient.query(WikidataActionApiQuery.sitematrix()); - final SitematrixResult.Sitematrix.Language language = sitematrix.getLanguages().stream().filter(it -> wikipediaLang.equalsIgnoreCase(it.getCode())).findFirst().orElse(null); - final SitematrixResult.Sitematrix.Site site; + final SitematrixResult.Sitematrix.Language language = sitematrix.getLanguages().stream() + .filter(it -> wikipediaLang.equalsIgnoreCase(it.getCode())).findFirst().orElse(null); if (language != null) { - site = language.getSites().stream().filter(it -> "wiki".equals(it.getCode())).findFirst().orElseThrow(() -> new IllegalArgumentException("No Wikipedia for language " + language.getName() + " (" + language.getCode() + ") found!")); + this.site = language.getSites().stream().filter(it -> "wiki".equals(it.getCode())).findFirst() + .orElseThrow(() -> new IllegalArgumentException("No Wikipedia for language " + language.getName() + + " (" + language.getCode() + ") found!")); } else { - site = sitematrix.getSpecialSites().stream().filter(it -> wikipediaLang.equals(it.getCode())).findFirst().orElseThrow(() -> new IllegalArgumentException("No wiki site for code '" + wikipediaLang + "' found!")); + this.site = sitematrix.getSpecialSites().stream().filter(it -> wikipediaLang.equals(it.getCode())).findFirst() + .orElseThrow(() -> new IllegalArgumentException("No wiki site for code '" + wikipediaLang + "' found!")); } - this.site = site; } public static WikipediaApp forLanguage(final String wikipediaLang) { @@ -108,20 +118,19 @@ private static HttpClient.Response connect(String url) throws IOException { public List getEntriesFromCoordinates(LatLon min, LatLon max) { try { // construct url - final String url = new StringBuilder(getSiteUrl()).append("/w/api.php") - .append("?action=query") - .append("&list=geosearch") - .append("&format=xml") - .append("&gslimit=500") - .append("&gsbbox=") - .append(max.lat()).append(STRING_URI_PIPE).append(min.lon()) - .append(STRING_URI_PIPE).append(min.lat()).append(STRING_URI_PIPE).append(max.lon()) - .toString(); + final String url = getSiteUrl() + "/w/api.php" + + "?action=query" + + "&list=geosearch" + + "&format=xml" + + "&gslimit=500" + + "&gsbbox=" + + max.lat() + STRING_URI_PIPE + min.lon() + + STRING_URI_PIPE + min.lat() + STRING_URI_PIPE + max.lon(); // parse XML document try (InputStream in = connect(url).getContent()) { final Document doc = newDocumentBuilder().parse(in); final String errorInfo = X_PATH.evaluateString("//error/@info", doc); - if (errorInfo != null && errorInfo.length() >= 1) { + if (errorInfo != null && !errorInfo.isEmpty()) { // I18n: {0} is the error message returned by the API new Notification(I18n.tr("Downloading entries with geo coordinates failed: {0}", errorInfo)) .setIcon(WikipediaPlugin.NOTIFICATION_ICON) @@ -133,24 +142,25 @@ public List getEntriesFromCoordinates(LatLon min, LatLon max) { final LatLon latLon = new LatLon( X_PATH.evaluateDouble("@lat", node), X_PATH.evaluateDouble("@lon", node)); - if ("wikidata".equals(wikipediaLang)) { + if (WIKIDATA.equals(wikipediaLang)) { return new WikidataEntry(name, null, latLon, null); } else { return new WikipediaEntry(wikipediaLang, name, latLon); } }).collect(Collectors.toList()); - if ("wikidata".equals(wikipediaLang)) { + if (WIKIDATA.equals(wikipediaLang)) { return new ArrayList<>(getLabelForWikidata(entries, Locale.getDefault())); } else { return entries; } } } catch (Exception ex) { - throw new RuntimeException(ex); + throw new JosmRuntimeException(ex); } } - public static List getWikidataEntriesForQuery(final String languageForQuery, final String query, final Locale localeForLabels) { + public static List getWikidataEntriesForQuery(final String languageForQuery, final String query, + final Locale localeForLabels) { try { final String url = "https://www.wikidata.org/w/api.php" + "?action=wbsearchentities" + @@ -167,7 +177,7 @@ public static List getWikidataEntriesForQuery(final String langua return getLabelForWikidata(r, localeForLabels); } } catch (Exception ex) { - throw new RuntimeException(ex); + throw new JosmRuntimeException(ex); } } @@ -184,12 +194,12 @@ public List getEntriesFromCategory(String category, int depth) { .collect(Collectors.toList()); } } catch (IOException ex) { - throw new RuntimeException(ex); + throw new UncheckedIOException(ex); } } public static List getEntriesFromClipboard(final String wikipediaLang, String clipboardStringContent) { - if ("wikidata".equals(wikipediaLang)) { + if (WIKIDATA.equals(wikipediaLang)) { List entries = new ArrayList<>(); Matcher matcher = RegexUtil.Q_ID_PATTERN.matcher(clipboardStringContent); while (matcher.find()) { @@ -231,7 +241,7 @@ public void updateWIWOSMStatus(List entries) { }); } } catch (Exception ex) { - throw new RuntimeException(ex); + throw new JosmRuntimeException(ex); } } for (WikipediaEntry i : entries) { @@ -240,15 +250,33 @@ public void updateWIWOSMStatus(List entries) { } public boolean hasWikipediaTag(final OsmPrimitive p) { - return p.hasKey("wikidata", "wikipedia", "wikipedia:" + wikipediaLang); + return p.hasKey(wikipediaKeys); + } + + /** + * Check to see if a tagged object has had its wikipedia tag change + * @param primitive The tagged object to check + * @param originalKeys The original keys + * @return {@code true} if the tagged object has had a change in wikipedia keys + */ + public boolean tagChangeWikipedia(Tagged primitive, Map originalKeys) { + for (String key : wikipediaKeys) { + // If the key has been added or removed, it has been changed. + if (primitive.hasKey(key) != originalKeys.containsKey(key) || + // If the original key doesn't equal the new key, then it has been changed + (primitive.hasKey(key) && originalKeys.containsKey(key) && !originalKeys.get(key).equals(primitive.get(key)))) { + return true; + } + } + return false; } public Stream getWikipediaArticles(final OsmPrimitive p) { - if ("wikidata".equals(wikipediaLang)) { - return Stream.of(p.get("wikidata")).filter(Objects::nonNull); + if (WIKIDATA.equals(wikipediaLang)) { + return Stream.of(p.get(WIKIDATA)).filter(Objects::nonNull); } return Stream - .of("wikipedia", "wikipedia:" + wikipediaLang) + .of(WIKIPEDIA, WIKIPEDIA + ':' + wikipediaLang) .map(key -> WikipediaEntry.parseTag(key, p.get(key))) .filter(Objects::nonNull) .filter(wp -> wikipediaLang.equals(wp.lang)) @@ -263,9 +291,7 @@ public Stream getWikipediaArticles(final OsmPrimitive p) { public Map getWikidataForArticles(Collection articles) { final Map result = new HashMap<>(); // maximum of 50 titles - ListUtil.processInBatches(new ArrayList<>(articles), 50, batch -> { - result.putAll(resolveWikidataItems(batch)); - }); + ListUtil.processInBatches(new ArrayList<>(articles), 50, batch -> result.putAll(resolveWikidataItems(batch))); return result; } @@ -321,10 +347,10 @@ private Map getWikidataForArticles0(Collection articles) return ApiQueryClient.query(WikidataActionApiQuery.wbgetentities(site, articles)) .getEntities().values() .stream() - .filter(it -> RegexUtil.isValidQId(it.getId()) && it.getSitelinks().size() >= 1) + .filter(it -> RegexUtil.isValidQId(it.getId()) && !it.getSitelinks().isEmpty()) .collect(Collectors.toMap(it -> it.getSitelinks().iterator().next().getTitle(), WbgetentitiesResult.Entity::getId)); } catch (IOException ex) { - throw new RuntimeException(ex); + throw new UncheckedIOException(ex); } } @@ -336,9 +362,10 @@ private Map getWikidataForArticles0(Collection articles) */ Map resolveRedirectsForArticles(Collection articles) { try { - return articles.stream().collect(Collectors.toMap(it -> it, ApiQueryClient.query(WikipediaActionApiQuery.query(site, articles)).getQuery()::resolveRedirect)); + return articles.stream().collect(Collectors.toMap(it -> it, + ApiQueryClient.query(WikipediaActionApiQuery.query(site, articles)).getQuery()::resolveRedirect)); } catch (Exception ex) { - throw new RuntimeException(ex); + throw new JosmRuntimeException(ex); } } @@ -359,7 +386,7 @@ public List getCategoriesForPrefix(final String prefix) { .collect(Collectors.toList()) ).orElse(new ArrayList<>()); } catch (IOException ex) { - throw new RuntimeException(ex); + throw new UncheckedIOException(ex); } } @@ -367,13 +394,16 @@ public static String getLabelForWikidata(String wikidataId, Locale locale, Strin try { final List entry = Collections.singletonList(new WikidataEntry(wikidataId)); return getLabelForWikidata(entry, locale, preferredLanguage).get(0).label; - } catch (IndexOutOfBoundsException ignore) { + } catch (IndexOutOfBoundsException indexOutOfBoundsException) { + Logging.trace(indexOutOfBoundsException); return null; } } - static List getLabelForWikidata(final List entries, final Locale locale, final String... preferredLanguage) { - final List wdEntries = entries.stream().map(it -> it instanceof WikidataEntry ? (WikidataEntry) it : null).filter(Objects::nonNull).collect(Collectors.toList()); + static List getLabelForWikidata(final List entries, final Locale locale, + final String... preferredLanguage) { + final List wdEntries = entries.stream() + .map(it -> it instanceof WikidataEntry ? (WikidataEntry) it : null).filter(Objects::nonNull).collect(Collectors.toList()); if (wdEntries.size() != entries.size()) { throw new IllegalArgumentException("The entries given to method `getLabelForWikidata` must all be of type WikidataEntry!"); } @@ -389,7 +419,9 @@ static List getLabelForWikidata(final List result = new ArrayList<>(wdEntries.size()); ListUtil.processInBatches(wdEntries, 50, batch -> { try { - final Map> entities = ApiQueryClient.query(WikidataActionApiQuery.wbgetentitiesLabels(batch.stream().map(it -> it.article).collect(Collectors.toList()))); + final Map> entities = + ApiQueryClient.query(WikidataActionApiQuery.wbgetentitiesLabels(batch.stream().map(it -> it.article) + .collect(Collectors.toList()))); if (entities != null) { for (final WikidataEntry batchEntry : batch) { Optional.ofNullable(entities.get(batchEntry.article)).flatMap(it -> it).ifPresent(entity -> { @@ -403,7 +435,7 @@ static List getLabelForWikidata(final List getInterwikiArticles(String article) { }).collect(Collectors.toList()); } } catch (Exception ex) { - throw new RuntimeException(ex); + throw new JosmRuntimeException(ex); } } @@ -456,7 +488,7 @@ public LatLon getCoordinateForArticle(String article) { } } } catch (Exception ex) { - throw new RuntimeException(ex); + throw new JosmRuntimeException(ex); } } @@ -482,7 +514,7 @@ private static DocumentBuilder newDocumentBuilder() { } catch (ParserConfigurationException e) { Logging.warn("Cannot create DocumentBuilder"); Logging.warn(e); - throw new RuntimeException(e); + throw new JosmRuntimeException(e); } } } diff --git a/src/main/java/org/wikipedia/gui/WikipediaToggleDialog.java b/src/main/java/org/wikipedia/gui/WikipediaToggleDialog.java index 8f78f2b2..03391a3c 100644 --- a/src/main/java/org/wikipedia/gui/WikipediaToggleDialog.java +++ b/src/main/java/org/wikipedia/gui/WikipediaToggleDialog.java @@ -11,11 +11,16 @@ import java.net.URL; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.DefaultListCellRenderer; @@ -33,9 +38,11 @@ import org.openstreetmap.josm.data.osm.Tag; import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent; import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent.DatasetEventType; +import org.openstreetmap.josm.data.osm.event.DataChangedEvent; import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter; import org.openstreetmap.josm.data.osm.event.DatasetEventManager; import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode; +import org.openstreetmap.josm.data.osm.event.TagsChangedEvent; import org.openstreetmap.josm.data.osm.search.SearchMode; import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; import org.openstreetmap.josm.gui.MainApplication; @@ -47,8 +54,10 @@ import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; import org.openstreetmap.josm.tools.I18n; import org.openstreetmap.josm.tools.ImageProvider; +import org.openstreetmap.josm.tools.JosmRuntimeException; import org.openstreetmap.josm.tools.Logging; import org.openstreetmap.josm.tools.OpenBrowser; +import org.openstreetmap.josm.tools.Utils; import org.wikipedia.WikipediaApp; import org.wikipedia.actions.FetchWikidataAction; import org.wikipedia.actions.MultiAction; @@ -59,6 +68,12 @@ public class WikipediaToggleDialog extends ToggleDialog implements ActiveLayerChangeListener, DataSetListenerAdapter.Listener { + /** A string describing the context (use-case) for determining the dialog title */ + String titleContext; + final Set articles = new HashSet<>(); + final DefaultListModel model = new DefaultListModel<>(); + final JList list = new JList<>(model); + public WikipediaToggleDialog() { super( tr("Wikipedia"), @@ -75,6 +90,7 @@ public WikipediaToggleDialog() { new WikipediaLoadCoordinatesAction(true), new WikipediaLoadCategoryAction() }; + listSetup(list); createLayout(list, true, Arrays.asList( new SideButton(new ToggleWikiLayerAction(this)), MultiAction.createButton( @@ -89,57 +105,50 @@ public WikipediaToggleDialog() { updateTitle(); } - /** A string describing the context (use-case) for determining the dialog title */ - String titleContext = null; - final Set articles = new HashSet<>(); - final DefaultListModel model = new DefaultListModel<>(); - final JList list = new JList(model) { - - { - setToolTipText(tr("Double click on item to search for object with article name (and center coordinate)")); - addMouseListener(new MouseAdapter() { - - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2 && getSelectedValue() != null && MainApplication.getLayerManager().getEditDataSet() != null) { - final WikipediaEntry entry = getSelectedValue(); - if (entry.coordinate != null) { - BoundingXYVisitor bbox = new BoundingXYVisitor(); - bbox.visit(entry.coordinate); - MainApplication.getMap().mapView.zoomTo(bbox); - } - final String search = entry.getSearchText().replaceAll("\\(.*\\)", ""); - SearchAction.search(search, SearchMode.replace); + private void listSetup(JList list) { + list.setToolTipText(tr("Double click on item to search for object with article name (and center coordinate)")); + list.addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2 && list.getSelectedValue() != null && MainApplication.getLayerManager().getEditDataSet() != null) { + final WikipediaEntry entry = list.getSelectedValue(); + if (entry.coordinate != null) { + BoundingXYVisitor bbox = new BoundingXYVisitor(); + bbox.visit(entry.coordinate); + MainApplication.getMap().mapView.zoomTo(bbox); } + final String search = entry.getSearchText().replaceAll("\\(.*\\)", ""); + SearchAction.search(search, SearchMode.replace); } - }); - - setCellRenderer(new DefaultListCellRenderer() { - - @Override - public JLabel getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - final WikipediaEntry entry = (WikipediaEntry) value; - final String labelText = "" + entry.getLabelText(); - final JLabel label = (JLabel) super.getListCellRendererComponent(list, labelText, index, isSelected, cellHasFocus); - if (entry.getWiwosmStatus() != null && entry.getWiwosmStatus()) { - label.setIcon(ImageProvider.getIfAvailable("misc", "grey_check")); - label.setToolTipText(/* I18n: WIWOSM server already links Wikipedia article to object/s */ tr("Available via WIWOSM server")); - } else if (articles.contains(entry.article)) { - label.setIcon(ImageProvider.getIfAvailable("misc", "green_check")); - label.setToolTipText(/* I18n: object/s from dataset contain link to Wikipedia article */ tr("Available in local dataset")); - } else { - label.setToolTipText(tr("Not linked yet")); - } - return label; + } + }); + + list.setCellRenderer(new DefaultListCellRenderer() { + + @Override + public JLabel getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + final WikipediaEntry entry = (WikipediaEntry) value; + final String labelText = "" + entry.getLabelText(); + final JLabel label = (JLabel) super.getListCellRendererComponent(list, labelText, index, isSelected, cellHasFocus); + if (entry.getWiwosmStatus() != null && entry.getWiwosmStatus()) { + label.setIcon(ImageProvider.getIfAvailable("misc", "grey_check")); + label.setToolTipText(/* I18n: WIWOSM server already links Wikipedia article to object/s */ tr("Available via WIWOSM server")); + } else if (articles.contains(entry.article)) { + label.setIcon(ImageProvider.getIfAvailable("misc", "green_check")); + label.setToolTipText(/* I18n: object/s from dataset contain link to Wikipedia article */ tr("Available in local dataset")); + } else { + label.setToolTipText(tr("Not linked yet")); } - }); + return label; + } + }); - final JPopupMenu popupMenu = new JPopupMenu(); - popupMenu.add(new OpenWikipediaArticleAction()); - popupMenu.add(new ZoomToWikipediaArticleAction()); - setComponentPopupMenu(popupMenu); - } - }; + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.add(new OpenWikipediaArticleAction()); + popupMenu.add(new ZoomToWikipediaArticleAction()); + list.setComponentPopupMenu(popupMenu); + } private void updateTitle() { final WikipediaApp app = newWikipediaApp(); @@ -159,7 +168,8 @@ private void updateTitle() { private WikipediaApp newWikipediaApp() { try { return WikipediaApp.forLanguage(list.getModel().getElementAt(0).lang); - } catch (ArrayIndexOutOfBoundsException ignore) { + } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) { + Logging.trace(arrayIndexOutOfBoundsException); return WikipediaApp.forLanguage(WikiProperties.WIKIPEDIA_LANGUAGE.get()); } } @@ -200,7 +210,7 @@ List getEntries() { } }.execute(); } catch (Exception ex) { - throw new RuntimeException(ex); + throw new JosmRuntimeException(ex); } } } @@ -213,7 +223,7 @@ abstract class UpdateWikipediaArticlesSwingWorker extends SwingWorker entries = getEntries(); entries.sort(null); - publish(entries.toArray(new WikipediaEntry[entries.size()])); + publish(entries.toArray(new WikipediaEntry[0])); ListUtil.processInBatches(entries, 20, batch -> { WikipediaApp.forLanguage(batch.get(0).lang).updateWIWOSMStatus(batch); list.repaint(); @@ -410,18 +420,53 @@ public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) { @Override public void processDatasetEvent(AbstractDatasetChangedEvent event) { final Set typesToProcess = EnumSet.of( - DatasetEventType.DATA_CHANGED, DatasetEventType.PRIMITIVES_ADDED, DatasetEventType.PRIMITIVES_REMOVED, + DatasetEventType.PRIMITIVE_FLAGS_CHANGED, // Most "delete" commands actually hide the primitive in the dataset. DatasetEventType.TAGS_CHANGED); - if (!typesToProcess.contains(event.getType())) { + final Map> events; + if (event.getType() == DatasetEventType.DATA_CHANGED && event instanceof DataChangedEvent) { + final Map> temporaryEvents = + getRootEvents((DataChangedEvent) event).collect(Collectors.groupingBy(AbstractDatasetChangedEvent::getType)); + if (temporaryEvents.isEmpty()) { + events = Collections.singletonMap(event.getType(), Collections.singletonList(event)); + } else { + events = temporaryEvents; + } + } else if (typesToProcess.contains(event.getType())) { + events = Collections.singletonMap(event.getType(), Collections.singletonList(event)); + } else { + events = Collections.emptyMap(); + } + if (events.isEmpty()) { return; } final WikipediaApp app = newWikipediaApp(); - if (event.getPrimitives().stream().noneMatch(app::hasWikipediaTag)) { + final boolean tagChange = events.getOrDefault(DatasetEventType.TAGS_CHANGED, Collections.emptyList()).stream() + .filter(TagsChangedEvent.class::isInstance).map(TagsChangedEvent.class::cast) + .anyMatch(e -> app.tagChangeWikipedia(e.getPrimitive(), e.getOriginalKeys())); + final boolean primitiveAdded = events.getOrDefault(DatasetEventType.PRIMITIVES_ADDED, Collections.emptyList()).stream() + .map(AbstractDatasetChangedEvent::getPrimitives).flatMap(Collection::stream).anyMatch(app::hasWikipediaTag); + final boolean primitiveRemoved = events.getOrDefault(DatasetEventType.PRIMITIVES_REMOVED, Collections.emptyList()).stream() + .map(AbstractDatasetChangedEvent::getPrimitives).flatMap(Collection::stream).anyMatch(app::hasWikipediaTag); + final boolean primitiveMaybeRemoved = events.getOrDefault(DatasetEventType.PRIMITIVE_FLAGS_CHANGED, Collections.emptyList()).stream() + .map(AbstractDatasetChangedEvent::getPrimitives).flatMap(Collection::stream).anyMatch(app::hasWikipediaTag); + if (!tagChange && !primitiveAdded && !primitiveRemoved && !primitiveMaybeRemoved && !events.containsKey(DatasetEventType.DATA_CHANGED)) { return; } updateWikipediaArticles(); list.repaint(); } + + private static Stream getRootEvents(DataChangedEvent event) { + if (Utils.isEmpty(event.getEvents())) { + return Stream.empty(); + } + return event.getEvents().stream().flatMap(e -> { + if (e instanceof DataChangedEvent) { + return getRootEvents(event); + } + return Stream.of(e); + }); + } }