source: josm/trunk/src/org/openstreetmap/josm/gui/help/HyperlinkHandler.java@ 14807

Last change on this file since 14807 was 14807, checked in by Don-vip, 5 years ago

fix #17338 - NPE

  • Property svn:eol-style set to native
File size: 5.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.help;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Rectangle;
7import java.util.Objects;
8import java.util.regex.Matcher;
9import java.util.regex.Pattern;
10
11import javax.swing.JOptionPane;
12import javax.swing.event.HyperlinkEvent;
13import javax.swing.event.HyperlinkListener;
14import javax.swing.text.AttributeSet;
15import javax.swing.text.BadLocationException;
16import javax.swing.text.Document;
17import javax.swing.text.Element;
18import javax.swing.text.SimpleAttributeSet;
19import javax.swing.text.html.HTML.Tag;
20import javax.swing.text.html.HTMLDocument;
21
22import org.openstreetmap.josm.gui.HelpAwareOptionPane;
23import org.openstreetmap.josm.gui.widgets.JosmEditorPane;
24import org.openstreetmap.josm.tools.Logging;
25import org.openstreetmap.josm.tools.OpenBrowser;
26
27/**
28 * Handles cliks on hyperlinks inside {@link HelpBrowser}.
29 * @since 14807
30 */
31public class HyperlinkHandler implements HyperlinkListener {
32
33 private final IHelpBrowser browser;
34 private final JosmEditorPane help;
35
36 /**
37 * Constructs a new {@code HyperlinkHandler}.
38 * @param browser help browser
39 * @param help inner help pane
40 */
41 public HyperlinkHandler(IHelpBrowser browser, JosmEditorPane help) {
42 this.browser = Objects.requireNonNull(browser);
43 this.help = Objects.requireNonNull(help);
44 }
45
46 /**
47 * Scrolls the help browser to the element with id <code>id</code>
48 *
49 * @param id the id
50 * @return true, if an element with this id was found and scrolling was successful; false, otherwise
51 */
52 protected boolean scrollToElementWithId(String id) {
53 Document d = help.getDocument();
54 if (d instanceof HTMLDocument) {
55 Element element = ((HTMLDocument) d).getElement(id);
56 try {
57 if (element != null) {
58 // Deprecated API to replace only when migrating to Java 9 (replacement not available in Java 8)
59 @SuppressWarnings("deprecation")
60 Rectangle r = help.modelToView(element.getStartOffset());
61 if (r != null) {
62 Rectangle vis = help.getVisibleRect();
63 r.height = vis.height;
64 help.scrollRectToVisible(r);
65 return true;
66 }
67 }
68 } catch (BadLocationException e) {
69 Logging.warn(tr("Bad location in HTML document. Exception was: {0}", e.toString()));
70 Logging.error(e);
71 }
72 }
73 return false;
74 }
75
76 /**
77 * Checks whether the hyperlink event originated on a &lt;a ...&gt; element with
78 * a relative href consisting of a URL fragment only, i.e.
79 * &lt;a href="#thisIsALocalFragment"&gt;. If so, replies the fragment, i.e. "thisIsALocalFragment".
80 *
81 * Otherwise, replies <code>null</code>
82 *
83 * @param e the hyperlink event
84 * @return the local fragment or <code>null</code>
85 */
86 protected String getUrlFragment(HyperlinkEvent e) {
87 AttributeSet set = e.getSourceElement().getAttributes();
88 Object value = set.getAttribute(Tag.A);
89 if (!(value instanceof SimpleAttributeSet))
90 return null;
91 SimpleAttributeSet atts = (SimpleAttributeSet) value;
92 value = atts.getAttribute(javax.swing.text.html.HTML.Attribute.HREF);
93 if (value == null)
94 return null;
95 String s = (String) value;
96 Matcher m = Pattern.compile("(?:"+browser.getUrl()+")?#(.+)").matcher(s);
97 if (m.matches())
98 return m.group(1);
99 return null;
100 }
101
102 @Override
103 public void hyperlinkUpdate(HyperlinkEvent e) {
104 if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED)
105 return;
106 if (e.getURL() == null || e.getURL().toExternalForm().startsWith(browser.getUrl()+'#')) {
107 // Probably hyperlink event on a an A-element with a href consisting of a fragment only, i.e. "#ALocalFragment".
108 String fragment = getUrlFragment(e);
109 if (fragment != null) {
110 // first try to scroll to an element with id==fragment. This is the way
111 // table of contents are built in the JOSM wiki. If this fails, try to
112 // scroll to a <A name="..."> element.
113 //
114 if (!scrollToElementWithId(fragment)) {
115 help.scrollToReference(fragment);
116 }
117 } else {
118 HelpAwareOptionPane.showOptionDialog(
119 HelpBrowser.getInstance(),
120 tr("Failed to open help page. The target URL is empty."),
121 tr("Failed to open help page"),
122 JOptionPane.ERROR_MESSAGE,
123 null, /* no icon */
124 null, /* standard options, just OK button */
125 null, /* default is standard */
126 null /* no help context */
127 );
128 }
129 } else if (e.getURL().toExternalForm().endsWith("action=edit")) {
130 OpenBrowser.displayUrl(e.getURL().toExternalForm());
131 } else {
132 String url = e.getURL().toExternalForm();
133 browser.setUrl(url);
134 if (url.startsWith(HelpUtil.getWikiBaseUrl())) {
135 browser.openUrl(url);
136 } else {
137 OpenBrowser.displayUrl(url);
138 }
139 }
140 }
141}
Note: See TracBrowser for help on using the repository browser.