001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.properties; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.event.ActionEvent; 007import java.util.Map; 008import java.util.Objects; 009import java.util.function.IntFunction; 010import java.util.function.Supplier; 011 012import javax.swing.AbstractAction; 013import javax.swing.JTable; 014 015import org.openstreetmap.josm.data.osm.IRelation; 016import org.openstreetmap.josm.data.osm.Tag; 017import org.openstreetmap.josm.data.preferences.StringProperty; 018import org.openstreetmap.josm.tools.ImageProvider; 019import org.openstreetmap.josm.tools.OpenBrowser; 020import org.openstreetmap.josm.tools.Utils; 021 022/** 023 * Launch browser with Taginfo statistics for selected object. 024 * @since 13521 025 */ 026public class TaginfoAction extends AbstractAction { 027 028 private static final StringProperty TAGINFO_URL_PROP = new StringProperty("taginfo.url", "https://taginfo.openstreetmap.org/"); 029 private static final StringProperty TAG_HISTORY_URL_PROP = new StringProperty("taghistory.url", "https://taghistory.raifer.tech/#***"); 030 031 private final Supplier<Tag> tagSupplier; 032 private final Supplier<String> relationTypeSupplier; 033 protected final String taginfoUrl; 034 035 private TaginfoAction(String name, Supplier<Tag> tagSupplier, Supplier<String> relationTypeSupplier, String taginfoUrl) { 036 super(name); 037 this.tagSupplier = Objects.requireNonNull(tagSupplier); 038 this.relationTypeSupplier = Objects.requireNonNull(relationTypeSupplier); 039 this.taginfoUrl = withoutTrailingSlash(Objects.requireNonNull(taginfoUrl)); 040 } 041 042 /** 043 * Constructs a new {@code TaginfoAction}. 044 * @param tagSupplier Supplies the tag for which Taginfo should be opened 045 * @param relationTypeSupplier Supplies a relation type for which Taginfo should be opened 046 * @since 16275 047 */ 048 public TaginfoAction(Supplier<Tag> tagSupplier, Supplier<String> relationTypeSupplier) { 049 this(tr("Go to Taginfo"), tagSupplier, relationTypeSupplier, TAGINFO_URL_PROP.get()); 050 new ImageProvider("dialogs/taginfo").getResource().attachImageIcon(this, true); 051 } 052 053 /** 054 * Constructs a new {@code TaginfoAction} with a given URL and optional name suffix. 055 * @param tagTable The tag table. Cannot be null 056 * @param tagKeySupplier Finds the key from given row of tag table. Cannot be null 057 * @param tagValuesSupplier Finds the values from given row of tag table (map of values and number of occurrences). Cannot be null 058 * @param membershipTable The membership table. Can be null 059 * @param memberValueSupplier Finds the parent relation from given row of membership table. Can be null 060 * @since 16597 061 */ 062 public TaginfoAction(JTable tagTable, IntFunction<String> tagKeySupplier, IntFunction<Map<String, Integer>> tagValuesSupplier, 063 JTable membershipTable, IntFunction<IRelation<?>> memberValueSupplier) { 064 this(getTagSupplier(tagTable, tagKeySupplier, tagValuesSupplier), 065 getRelationTypeSupplier(membershipTable, memberValueSupplier)); 066 } 067 068 private static Supplier<Tag> getTagSupplier(JTable tagTable, IntFunction<String> tagKeySupplier, 069 IntFunction<Map<String, Integer>> tagValuesSupplier) { 070 Objects.requireNonNull(tagTable); 071 Objects.requireNonNull(tagKeySupplier); 072 Objects.requireNonNull(tagValuesSupplier); 073 return () -> { 074 if (tagTable.getSelectedRowCount() == 1) { 075 final int row = tagTable.getSelectedRow(); 076 final String key = tagKeySupplier.apply(row); 077 Map<String, Integer> values = tagValuesSupplier.apply(row); 078 String value = values.size() == 1 ? values.keySet().iterator().next() : null; 079 return new Tag(key, value); 080 } 081 return null; 082 }; 083 } 084 085 private static Supplier<String> getRelationTypeSupplier(JTable membershipTable, IntFunction<IRelation<?>> memberValueSupplier) { 086 return () -> membershipTable != null && membershipTable.getSelectedRowCount() == 1 087 ? memberValueSupplier.apply(membershipTable.getSelectedRow()).get("type") : null; 088 } 089 090 @Override 091 public void actionPerformed(ActionEvent e) { 092 Tag tag = tagSupplier.get(); 093 if (tag != null) { 094 OpenBrowser.displayUrl(getTaginfoUrlForTag(tag)); 095 return; 096 } 097 String type = relationTypeSupplier.get(); 098 if (type != null) { 099 OpenBrowser.displayUrl(getTaginfoUrlForRelationType(type)); 100 } 101 } 102 103 private static String withoutTrailingSlash(String url) { 104 return Utils.strip(url, "/"); 105 } 106 107 /** 108 * Returns the Taginfo URL for the given tag or key (if the tag value is null) 109 * @param tag the tag 110 * @return the Taginfo URL for the given tag or key 111 * @since 16596 112 */ 113 public String getTaginfoUrlForTag(Tag tag) { 114 if (tag.getValue().isEmpty()) { 115 return taginfoUrl + "/keys/" + encodeKeyValue(tag.getKey()); 116 } else { 117 return taginfoUrl + "/tags/" + encodeKeyValue(tag.getKey()) + '=' + encodeKeyValue(tag.getValue()); 118 } 119 } 120 121 private static String encodeKeyValue(String string) { 122 return Utils.encodeUrl(string).replaceAll("\\+", "%20"); 123 } 124 125 /** 126 * Returns the Taginfo URL for the given relation type 127 * @param type the relation type 128 * @return the Taginfo URL for the given relation type 129 * @since 16596 130 */ 131 public String getTaginfoUrlForRelationType(String type) { 132 return taginfoUrl + "/relations/" + type; 133 } 134 135 /** 136 * Returns a new action which launches the Taginfo instance from the given URL 137 * @param name the action's text as displayed on the menu (if it is added to a menu) 138 * @param taginfoUrl Taginfo URL 139 * @return a new action which launches the Taginfo instance from the given URL 140 * @since 16597 141 */ 142 public TaginfoAction withTaginfoUrl(String name, String taginfoUrl) { 143 TaginfoAction action = new TaginfoAction(name, tagSupplier, relationTypeSupplier, taginfoUrl); 144 new ImageProvider("dialogs/taginfo").getResource().attachImageIcon(action, true); 145 return action; 146 } 147 148 /** 149 * Returns a new action which launches https://taghistory.raifer.tech/ for the given tag 150 * @return a new action 151 * @since 16596 152 */ 153 public TaginfoAction toTagHistoryAction() { 154 String url = TAG_HISTORY_URL_PROP.get(); 155 TaginfoAction action = new TaginfoAction(tr("Go to OSM Tag History"), tagSupplier, relationTypeSupplier, url) { 156 @Override 157 public String getTaginfoUrlForTag(Tag tag) { 158 return String.join("/", taginfoUrl, tag.getKey(), tag.getValue()); 159 } 160 161 @Override 162 public String getTaginfoUrlForRelationType(String type) { 163 return null; 164 } 165 }; 166 new ImageProvider("dialogs/taghistory").getResource().attachImageIcon(action, true); 167 return action; 168 } 169}