1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.gui.dialogs.properties;
|
---|
3 |
|
---|
4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
5 |
|
---|
6 | import java.awt.event.ActionEvent;
|
---|
7 | import java.awt.event.KeyEvent;
|
---|
8 | import java.io.IOException;
|
---|
9 | import java.net.URI;
|
---|
10 | import java.net.URISyntaxException;
|
---|
11 | import java.util.ArrayList;
|
---|
12 | import java.util.Arrays;
|
---|
13 | import java.util.List;
|
---|
14 | import java.util.Map;
|
---|
15 | import java.util.Objects;
|
---|
16 | import java.util.function.IntFunction;
|
---|
17 |
|
---|
18 | import javax.swing.AbstractAction;
|
---|
19 | import javax.swing.JTable;
|
---|
20 | import javax.swing.KeyStroke;
|
---|
21 |
|
---|
22 | import org.openstreetmap.josm.data.osm.IRelation;
|
---|
23 | import org.openstreetmap.josm.gui.MainApplication;
|
---|
24 | import org.openstreetmap.josm.spi.preferences.Config;
|
---|
25 | import org.openstreetmap.josm.tools.HttpClient;
|
---|
26 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
27 | import org.openstreetmap.josm.tools.LanguageInfo;
|
---|
28 | import org.openstreetmap.josm.tools.Logging;
|
---|
29 | import org.openstreetmap.josm.tools.OpenBrowser;
|
---|
30 | import org.openstreetmap.josm.tools.Utils;
|
---|
31 |
|
---|
32 | /**
|
---|
33 | * Launch browser with wiki help for selected object.
|
---|
34 | * @since 13521
|
---|
35 | */
|
---|
36 | public class HelpAction extends AbstractAction {
|
---|
37 | private final JTable tagTable;
|
---|
38 | private final IntFunction<String> tagKeySupplier;
|
---|
39 | private final IntFunction<Map<String, Integer>> tagValuesSupplier;
|
---|
40 |
|
---|
41 | private final JTable membershipTable;
|
---|
42 | private final IntFunction<IRelation<?>> memberValueSupplier;
|
---|
43 |
|
---|
44 | /**
|
---|
45 | * Constructs a new {@code HelpAction}.
|
---|
46 | * @param tagTable The tag table. Cannot be null
|
---|
47 | * @param tagKeySupplier Finds the key from given row of tag table. Cannot be null
|
---|
48 | * @param tagValuesSupplier Finds the values from given row of tag table (map of values and number of occurrences). Cannot be null
|
---|
49 | * @param membershipTable The membership table. Can be null
|
---|
50 | * @param memberValueSupplier Finds the parent relation from given row of membership table. Can be null
|
---|
51 | * @since 13959 (signature)
|
---|
52 | */
|
---|
53 | public HelpAction(JTable tagTable, IntFunction<String> tagKeySupplier, IntFunction<Map<String, Integer>> tagValuesSupplier,
|
---|
54 | JTable membershipTable, IntFunction<IRelation<?>> memberValueSupplier) {
|
---|
55 | this.tagTable = Objects.requireNonNull(tagTable);
|
---|
56 | this.tagKeySupplier = Objects.requireNonNull(tagKeySupplier);
|
---|
57 | this.tagValuesSupplier = Objects.requireNonNull(tagValuesSupplier);
|
---|
58 | this.membershipTable = membershipTable;
|
---|
59 | this.memberValueSupplier = memberValueSupplier;
|
---|
60 | putValue(NAME, tr("Go to OSM wiki for tag help"));
|
---|
61 | putValue(SHORT_DESCRIPTION, tr("Launch browser with wiki help for selected object"));
|
---|
62 | new ImageProvider("dialogs", "search").getResource().attachImageIcon(this, true);
|
---|
63 | putValue(ACCELERATOR_KEY, getKeyStroke());
|
---|
64 | }
|
---|
65 |
|
---|
66 | /**
|
---|
67 | * Returns the keystroke launching this action (F1).
|
---|
68 | * @return the keystroke launching this action
|
---|
69 | */
|
---|
70 | public KeyStroke getKeyStroke() {
|
---|
71 | return KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0);
|
---|
72 | }
|
---|
73 |
|
---|
74 | @Override
|
---|
75 | public void actionPerformed(ActionEvent e) {
|
---|
76 | try {
|
---|
77 | String base = Config.getPref().get("url.openstreetmap-wiki", "https://wiki.openstreetmap.org/wiki/");
|
---|
78 | String lang = LanguageInfo.getWikiLanguagePrefix();
|
---|
79 | final List<URI> uris = new ArrayList<>();
|
---|
80 | if (tagTable.getSelectedRowCount() == 1) {
|
---|
81 | int row = tagTable.getSelectedRow();
|
---|
82 | String key = Utils.encodeUrl(tagKeySupplier.apply(row));
|
---|
83 | Map<String, Integer> m = tagValuesSupplier.apply(row);
|
---|
84 | if (!m.isEmpty()) {
|
---|
85 | String val = Utils.encodeUrl(m.entrySet().iterator().next().getKey());
|
---|
86 | uris.addAll(getTagURIs(base, lang, key, val));
|
---|
87 | }
|
---|
88 | } else if (membershipTable != null && membershipTable.getSelectedRowCount() == 1) {
|
---|
89 | int row = membershipTable.getSelectedRow();
|
---|
90 | uris.addAll(getRelationURIs(base, lang, memberValueSupplier.apply(row)));
|
---|
91 | } else {
|
---|
92 | // give the generic help page, if more than one element is selected
|
---|
93 | uris.addAll(getGenericURIs(base, lang));
|
---|
94 | }
|
---|
95 |
|
---|
96 | MainApplication.worker.execute(() -> displayHelp(uris));
|
---|
97 | } catch (URISyntaxException e1) {
|
---|
98 | Logging.error(e1);
|
---|
99 | }
|
---|
100 | }
|
---|
101 |
|
---|
102 | /**
|
---|
103 | * Returns a list of URIs for the given key/value.
|
---|
104 | * @param base OSM wiki base URL
|
---|
105 | * @param lang Language prefix
|
---|
106 | * @param key Key
|
---|
107 | * @param val Value
|
---|
108 | * @return a list of URIs for the given key/value by order of relevance
|
---|
109 | * @throws URISyntaxException in case of internal error
|
---|
110 | * @since 13522
|
---|
111 | */
|
---|
112 | public static List<URI> getTagURIs(String base, String lang, String key, String val) throws URISyntaxException {
|
---|
113 | return Arrays.asList(
|
---|
114 | new URI(String.format("%s%sTag:%s=%s", base, lang, key, val)),
|
---|
115 | new URI(String.format("%sTag:%s=%s", base, key, val)),
|
---|
116 | new URI(String.format("%s%sKey:%s", base, lang, key)),
|
---|
117 | new URI(String.format("%sKey:%s", base, key)),
|
---|
118 | new URI(String.format("%s%sMap_Features", base, lang)),
|
---|
119 | new URI(String.format("%sMap_Features", base))
|
---|
120 | );
|
---|
121 | }
|
---|
122 |
|
---|
123 | /**
|
---|
124 | * Returns a list of URIs for the given relation.
|
---|
125 | * @param base OSM wiki base URL
|
---|
126 | * @param lang Language prefix
|
---|
127 | * @param rel Relation
|
---|
128 | * @return a list of URIs for the given relation by order of relevance
|
---|
129 | * @throws URISyntaxException in case of internal error
|
---|
130 | * @since 13522
|
---|
131 | * @since 13959 (signature)
|
---|
132 | */
|
---|
133 | public static List<URI> getRelationURIs(String base, String lang, IRelation<?> rel) throws URISyntaxException {
|
---|
134 | List<URI> uris = new ArrayList<>();
|
---|
135 | String type = rel.get("type");
|
---|
136 | if (type != null) {
|
---|
137 | type = Utils.encodeUrl(type);
|
---|
138 | }
|
---|
139 |
|
---|
140 | if (type != null && !type.isEmpty()) {
|
---|
141 | uris.add(new URI(String.format("%s%sRelation:%s", base, lang, type)));
|
---|
142 | uris.add(new URI(String.format("%sRelation:%s", base, type)));
|
---|
143 | }
|
---|
144 |
|
---|
145 | uris.add(new URI(String.format("%s%sRelations", base, lang)));
|
---|
146 | uris.add(new URI(String.format("%sRelations", base)));
|
---|
147 | return uris;
|
---|
148 | }
|
---|
149 |
|
---|
150 | /**
|
---|
151 | * Returns a list of generic URIs (Map Features).
|
---|
152 | * @param base OSM wiki base URL
|
---|
153 | * @param lang Language prefix
|
---|
154 | * @return a list of generic URIs (Map Features)
|
---|
155 | * @throws URISyntaxException in case of internal error
|
---|
156 | * @since 13522
|
---|
157 | */
|
---|
158 | public static List<URI> getGenericURIs(String base, String lang) throws URISyntaxException {
|
---|
159 | return Arrays.asList(
|
---|
160 | new URI(String.format("%s%sMap_Features", base, lang)),
|
---|
161 | new URI(String.format("%sMap_Features", base))
|
---|
162 | );
|
---|
163 | }
|
---|
164 |
|
---|
165 | /**
|
---|
166 | * Display help by searching the forst valid URI in the given list.
|
---|
167 | * @param uris list of URIs to test
|
---|
168 | * @since 13522
|
---|
169 | */
|
---|
170 | public static void displayHelp(final List<URI> uris) {
|
---|
171 | try {
|
---|
172 | // find a page that actually exists in the wiki
|
---|
173 | HttpClient.Response conn;
|
---|
174 | for (URI u : uris) {
|
---|
175 | conn = HttpClient.create(u.toURL(), "HEAD").connect();
|
---|
176 |
|
---|
177 | if (conn.getResponseCode() != 200) {
|
---|
178 | conn.disconnect();
|
---|
179 | } else {
|
---|
180 | long osize = conn.getContentLength();
|
---|
181 | if (osize > -1) {
|
---|
182 | conn.disconnect();
|
---|
183 |
|
---|
184 | final URI newURI = new URI(u.toString()
|
---|
185 | .replace("=", "%3D") /* do not URLencode whole string! */
|
---|
186 | .replaceFirst("/wiki/", "/w/index.php?redirect=no&title=")
|
---|
187 | );
|
---|
188 | conn = HttpClient.create(newURI.toURL(), "HEAD").connect();
|
---|
189 | }
|
---|
190 |
|
---|
191 | /* redirect pages have different content length, but retrieving a "nonredirect"
|
---|
192 | * page using index.php and the direct-link method gives slightly different
|
---|
193 | * content lengths, so we have to be fuzzy.. (this is UGLY, recode if u know better)
|
---|
194 | */
|
---|
195 | if (osize > -1 && conn.getContentLength() != -1 && Math.abs(conn.getContentLength() - osize) > 200) {
|
---|
196 | Logging.info("{0} is a mediawiki redirect", u);
|
---|
197 | conn.disconnect();
|
---|
198 | } else {
|
---|
199 | conn.disconnect();
|
---|
200 |
|
---|
201 | OpenBrowser.displayUrl(u.toString());
|
---|
202 | break;
|
---|
203 | }
|
---|
204 | }
|
---|
205 | }
|
---|
206 | } catch (URISyntaxException | IOException e1) {
|
---|
207 | Logging.error(e1);
|
---|
208 | }
|
---|
209 | }
|
---|
210 | }
|
---|