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.Relation;
|
---|
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<Relation> 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 | */
|
---|
52 | public HelpAction(JTable tagTable, IntFunction<String> tagKeySupplier, IntFunction<Map<String, Integer>> tagValuesSupplier,
|
---|
53 | JTable membershipTable, IntFunction<Relation> memberValueSupplier) {
|
---|
54 | this.tagTable = Objects.requireNonNull(tagTable);
|
---|
55 | this.tagKeySupplier = Objects.requireNonNull(tagKeySupplier);
|
---|
56 | this.tagValuesSupplier = Objects.requireNonNull(tagValuesSupplier);
|
---|
57 | this.membershipTable = membershipTable;
|
---|
58 | this.memberValueSupplier = memberValueSupplier;
|
---|
59 | putValue(NAME, tr("Go to OSM wiki for tag help"));
|
---|
60 | putValue(SHORT_DESCRIPTION, tr("Launch browser with wiki help for selected object"));
|
---|
61 | new ImageProvider("dialogs", "search").getResource().attachImageIcon(this, true);
|
---|
62 | putValue(ACCELERATOR_KEY, getKeyStroke());
|
---|
63 | }
|
---|
64 |
|
---|
65 | /**
|
---|
66 | * Returns the keystroke launching this action (F1).
|
---|
67 | * @return the keystroke launching this action
|
---|
68 | */
|
---|
69 | public KeyStroke getKeyStroke() {
|
---|
70 | return KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0);
|
---|
71 | }
|
---|
72 |
|
---|
73 | @Override
|
---|
74 | public void actionPerformed(ActionEvent e) {
|
---|
75 | try {
|
---|
76 | String base = Config.getPref().get("url.openstreetmap-wiki", "https://wiki.openstreetmap.org/wiki/");
|
---|
77 | String lang = LanguageInfo.getWikiLanguagePrefix();
|
---|
78 | final List<URI> uris = new ArrayList<>();
|
---|
79 | if (tagTable.getSelectedRowCount() == 1) {
|
---|
80 | int row = tagTable.getSelectedRow();
|
---|
81 | String key = Utils.encodeUrl(tagKeySupplier.apply(row));
|
---|
82 | Map<String, Integer> m = tagValuesSupplier.apply(row);
|
---|
83 | if (!m.isEmpty()) {
|
---|
84 | String val = Utils.encodeUrl(m.entrySet().iterator().next().getKey());
|
---|
85 | uris.addAll(getTagURIs(base, lang, key, val));
|
---|
86 | }
|
---|
87 | } else if (membershipTable != null && membershipTable.getSelectedRowCount() == 1) {
|
---|
88 | int row = membershipTable.getSelectedRow();
|
---|
89 | uris.addAll(getRelationURIs(base, lang, memberValueSupplier.apply(row)));
|
---|
90 | } else {
|
---|
91 | // give the generic help page, if more than one element is selected
|
---|
92 | uris.addAll(getGenericURIs(base, lang));
|
---|
93 | }
|
---|
94 |
|
---|
95 | MainApplication.worker.execute(() -> displayHelp(uris));
|
---|
96 | } catch (URISyntaxException e1) {
|
---|
97 | Logging.error(e1);
|
---|
98 | }
|
---|
99 | }
|
---|
100 |
|
---|
101 | /**
|
---|
102 | * Returns a list of URIs for the given key/value.
|
---|
103 | * @param base OSM wiki base URL
|
---|
104 | * @param lang Language prefix
|
---|
105 | * @param key Key
|
---|
106 | * @param val Value
|
---|
107 | * @return a list of URIs for the given key/value by order of relevance
|
---|
108 | * @throws URISyntaxException in case of internal error
|
---|
109 | * @since 13522
|
---|
110 | */
|
---|
111 | public static List<URI> getTagURIs(String base, String lang, String key, String val) throws URISyntaxException {
|
---|
112 | return Arrays.asList(
|
---|
113 | new URI(String.format("%s%sTag:%s=%s", base, lang, key, val)),
|
---|
114 | new URI(String.format("%sTag:%s=%s", base, key, val)),
|
---|
115 | new URI(String.format("%s%sKey:%s", base, lang, key)),
|
---|
116 | new URI(String.format("%sKey:%s", base, key)),
|
---|
117 | new URI(String.format("%s%sMap_Features", base, lang)),
|
---|
118 | new URI(String.format("%sMap_Features", base))
|
---|
119 | );
|
---|
120 | }
|
---|
121 |
|
---|
122 | /**
|
---|
123 | * Returns a list of URIs for the given relation.
|
---|
124 | * @param base OSM wiki base URL
|
---|
125 | * @param lang Language prefix
|
---|
126 | * @param rel Relation
|
---|
127 | * @return a list of URIs for the given relation by order of relevance
|
---|
128 | * @throws URISyntaxException in case of internal error
|
---|
129 | * @since 13522
|
---|
130 | */
|
---|
131 | public static List<URI> getRelationURIs(String base, String lang, Relation rel) throws URISyntaxException {
|
---|
132 | List<URI> uris = new ArrayList<>();
|
---|
133 | String type = rel.get("type");
|
---|
134 | if (type != null) {
|
---|
135 | type = Utils.encodeUrl(type);
|
---|
136 | }
|
---|
137 |
|
---|
138 | if (type != null && !type.isEmpty()) {
|
---|
139 | uris.add(new URI(String.format("%s%sRelation:%s", base, lang, type)));
|
---|
140 | uris.add(new URI(String.format("%sRelation:%s", base, type)));
|
---|
141 | }
|
---|
142 |
|
---|
143 | uris.add(new URI(String.format("%s%sRelations", base, lang)));
|
---|
144 | uris.add(new URI(String.format("%sRelations", base)));
|
---|
145 | return uris;
|
---|
146 | }
|
---|
147 |
|
---|
148 | /**
|
---|
149 | * Returns a list of generic URIs (Map Features).
|
---|
150 | * @param base OSM wiki base URL
|
---|
151 | * @param lang Language prefix
|
---|
152 | * @return a list of generic URIs (Map Features)
|
---|
153 | * @throws URISyntaxException in case of internal error
|
---|
154 | * @since 13522
|
---|
155 | */
|
---|
156 | public static List<URI> getGenericURIs(String base, String lang) throws URISyntaxException {
|
---|
157 | return Arrays.asList(
|
---|
158 | new URI(String.format("%s%sMap_Features", base, lang)),
|
---|
159 | new URI(String.format("%sMap_Features", base))
|
---|
160 | );
|
---|
161 | }
|
---|
162 |
|
---|
163 | /**
|
---|
164 | * Display help by searching the forst valid URI in the given list.
|
---|
165 | * @param uris list of URIs to test
|
---|
166 | * @since 13522
|
---|
167 | */
|
---|
168 | public static void displayHelp(final List<URI> uris) {
|
---|
169 | try {
|
---|
170 | // find a page that actually exists in the wiki
|
---|
171 | HttpClient.Response conn;
|
---|
172 | for (URI u : uris) {
|
---|
173 | conn = HttpClient.create(u.toURL(), "HEAD").connect();
|
---|
174 |
|
---|
175 | if (conn.getResponseCode() != 200) {
|
---|
176 | conn.disconnect();
|
---|
177 | } else {
|
---|
178 | long osize = conn.getContentLength();
|
---|
179 | if (osize > -1) {
|
---|
180 | conn.disconnect();
|
---|
181 |
|
---|
182 | final URI newURI = new URI(u.toString()
|
---|
183 | .replace("=", "%3D") /* do not URLencode whole string! */
|
---|
184 | .replaceFirst("/wiki/", "/w/index.php?redirect=no&title=")
|
---|
185 | );
|
---|
186 | conn = HttpClient.create(newURI.toURL(), "HEAD").connect();
|
---|
187 | }
|
---|
188 |
|
---|
189 | /* redirect pages have different content length, but retrieving a "nonredirect"
|
---|
190 | * page using index.php and the direct-link method gives slightly different
|
---|
191 | * content lengths, so we have to be fuzzy.. (this is UGLY, recode if u know better)
|
---|
192 | */
|
---|
193 | if (osize > -1 && conn.getContentLength() != -1 && Math.abs(conn.getContentLength() - osize) > 200) {
|
---|
194 | Logging.info("{0} is a mediawiki redirect", u);
|
---|
195 | conn.disconnect();
|
---|
196 | } else {
|
---|
197 | conn.disconnect();
|
---|
198 |
|
---|
199 | OpenBrowser.displayUrl(u.toString());
|
---|
200 | break;
|
---|
201 | }
|
---|
202 | }
|
---|
203 | }
|
---|
204 | } catch (URISyntaxException | IOException e1) {
|
---|
205 | Logging.error(e1);
|
---|
206 | }
|
---|
207 | }
|
---|
208 | }
|
---|