KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > module > run > Hyperlink


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.apache.tools.ant.module.run;
21
22 import java.awt.Toolkit JavaDoc;
23 import java.beans.PropertyChangeEvent JavaDoc;
24 import java.beans.PropertyChangeListener JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.net.URL JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.Set JavaDoc;
29 import org.apache.tools.ant.module.AntModule;
30 import org.openide.ErrorManager;
31 import org.openide.awt.StatusDisplayer;
32 import org.openide.cookies.EditorCookie;
33 import org.openide.filesystems.FileObject;
34 import org.openide.filesystems.URLMapper;
35 import org.openide.loaders.DataObject;
36 import org.openide.loaders.DataObjectNotFoundException;
37 import org.openide.text.Annotatable;
38 import org.openide.text.Annotation;
39 import org.openide.text.Line;
40 import org.openide.util.WeakSet;
41 import org.openide.windows.OutputEvent;
42 import org.openide.windows.OutputListener;
43
44 /**
45  * Represents a linkable line (appears in red in Output Window).
46  * Line and column numbers start at 1, and -1 means an unknown value.
47  * Careful since org.openide.text seems to assume 0-based line and column numbers.
48  * @author Jesse Glick
49  */

50 public final class Hyperlink extends Annotation implements OutputListener, PropertyChangeListener JavaDoc {
51     
52     // #14804: detach everything before uninstalling module.
53
private static final Set JavaDoc<Hyperlink> hyperlinks = new WeakSet<Hyperlink>();
54     public static void detachAllAnnotations() {
55         synchronized (hyperlinks) {
56             Iterator JavaDoc<Hyperlink> it = hyperlinks.iterator();
57             while (it.hasNext()) {
58                 it.next().destroy();
59                 it.remove();
60             }
61         }
62     }
63
64     private final URL JavaDoc url;
65     private final String JavaDoc message;
66     private final int line1;
67     private int col1;
68     private final int line2;
69     private final int col2;
70     private Line liveLine;
71     /** #22374: do not double up annotations. (Prefer parser annotations from editor.) */
72     private boolean masked;
73     
74     private boolean dead = false;
75     
76     public Hyperlink(URL JavaDoc url, String JavaDoc message, int line1, int col1, int line2, int col2) {
77         this.url = url;
78         this.message = message;
79         this.line1 = line1;
80         this.col1 = col1;
81         this.line2 = line2;
82         this.col2 = col2;
83         synchronized (hyperlinks) {
84             hyperlinks.add(this);
85         }
86     }
87     
88     /**
89      * Enables the column number of the hyperlink to be changed after the fact.
90      * If it is already set, this is ignored.
91      */

92     public void setColumn1(int col1) {
93         if (this.col1 == -1) {
94             this.col1 = col1;
95         }
96     }
97     
98     void destroy() {
99         doDetach();
100         dead = true;
101         synchronized (hyperlinks) {
102             liveLine = null;
103         }
104     }
105     
106     public void outputLineAction(OutputEvent ev) {
107         if (dead) return;
108         FileObject file = URLMapper.findFileObject(url);
109         if (file == null) { // #13115
110
Toolkit.getDefaultToolkit().beep();
111             return;
112         }
113         try {
114             DataObject dob = DataObject.find(file);
115             EditorCookie ed = dob.getCookie(EditorCookie.class);
116             if (ed != null && /* not true e.g. for *_ja.properties */file == dob.getPrimaryFile()) {
117                 if (line1 == -1) {
118                     // OK, just open it.
119
ed.open();
120                 } else {
121                     ed.openDocument(); // XXX getLineSet does not do it for you!
122
AntModule.err.log("opened document for " + file);
123                     try {
124                         Line line = updateLines(ed);
125                         if (!line.isDeleted()) {
126                             attachAsNeeded(line);
127                             if (col1 == -1) {
128                                 line.show(Line.SHOW_GOTO);
129                             } else {
130                                 line.show(Line.SHOW_GOTO, col1 - 1);
131                             }
132                         }
133                     } catch (IndexOutOfBoundsException JavaDoc ioobe) {
134                         // Probably harmless. Bogus line number.
135
ed.open();
136                     }
137                 }
138             } else {
139                 Toolkit.getDefaultToolkit().beep();
140             }
141         } catch (DataObjectNotFoundException donfe) {
142             ErrorManager.getDefault().notify(ErrorManager.WARNING, donfe);
143         } catch (IOException JavaDoc ioe) {
144             // XXX see above, should not be necessary to call openDocument at all
145
ErrorManager.getDefault().notify(ErrorManager.WARNING, ioe);
146         }
147         if (message != null) {
148             // Try to do after opening the file, since opening a new file
149
// clears the current status message.
150
StatusDisplayer.getDefault().setStatusText(message);
151         }
152     }
153
154     /**
155      * #62623: record positions in document at time first hyperlink was clicked for this file.
156      * Otherwise an intervening save action can mess up line numbers.
157      */

158     private Line updateLines(EditorCookie ed) {
159         Line.Set lineset = ed.getLineSet();
160         synchronized (hyperlinks) {
161             assert line1 != -1;
162             boolean ran = false;
163             boolean encounteredThis = false;
164             boolean modifiedThis = false;
165             if (liveLine == null) {
166                 ran = true;
167                 for (Hyperlink h : hyperlinks) {
168                     if (h == this) {
169                         encounteredThis = true;
170                     }
171                     if (h.liveLine == null && h.url.equals(url) && h.line1 != -1) {
172                         Line l = lineset.getOriginal(h.line1 - 1);
173                         assert l != null : h;
174                         h.liveLine = l;
175                         if (h == this) {
176                             modifiedThis = true;
177                         }
178                     }
179                 }
180             }
181             assert liveLine != null : "this=" + this + " ran=" + ran +
182                     " encounteredThis=" + encounteredThis + " modifiedThis=" + modifiedThis +
183                     " hyperlinks=" + hyperlinks + " hyperlinks.contains(this)=" + hyperlinks.contains(this);
184             return liveLine;
185         }
186     }
187     
188     public void outputLineSelected(OutputEvent ev) {
189         if (dead) return;
190         FileObject file = URLMapper.findFileObject(url);
191         if (file == null) {
192             return;
193         }
194         try {
195             DataObject dob = DataObject.find(file);
196             EditorCookie ed = dob.getCookie(EditorCookie.class);
197             if (ed != null) {
198                 if (ed.getDocument() == null) {
199                     // The document is not opened, don't bother with it.
200
// The Line.Set will be corrupt anyway, currently.
201
AntModule.err.log("no document for " + file);
202                     return;
203                 }
204                 AntModule.err.log("got document for " + file);
205                 if (line1 != -1) {
206                     Line line = updateLines(ed);
207                     if (!line.isDeleted()) {
208                         attachAsNeeded(line);
209                         if (col1 == -1) {
210                             line.show(Line.SHOW_TRY_SHOW);
211                         } else {
212                             line.show(Line.SHOW_TRY_SHOW, col1 - 1);
213                         }
214                     }
215                 }
216             }
217         } catch (DataObjectNotFoundException donfe) {
218             ErrorManager.getDefault().notify(ErrorManager.WARNING, donfe);
219         } catch (IndexOutOfBoundsException JavaDoc iobe) {
220             // Probably harmless. Bogus line number.
221
}
222     }
223     
224     private synchronized void attachAsNeeded(Line l) {
225         if (getAttachedAnnotatable() == null) {
226             boolean log = AntModule.err.isLoggable(ErrorManager.INFORMATIONAL);
227             // Suppress for lifetime of hyperlink in case problem was fixed by user.
228
masked |= l.getAnnotationCount() > 0;
229             if (masked) {
230                 return;
231             }
232             Annotatable ann;
233             // Text of the line, incl. trailing newline.
234
String JavaDoc text = l.getText();
235             if (log) AntModule.err.log("Attaching to line " + l.getDisplayName() + " text=`" + text + "' line1=" + line1 + " line2=" + line2 + " col1=" + col1 + " col2=" + col2);
236             if (text != null && (line2 == -1 || line1 == line2) && col1 != -1) {
237                 int new_col1 = convertTabColumnsToCharacterColumns(text, col1 - 1, 8);
238                 int new_col2 = convertTabColumnsToCharacterColumns(text, col2 - 1, 8);
239                 if (log) AntModule.err.log("\tfits on one line");
240                 if (new_col2 != -1 && new_col2 >= new_col1 && new_col2 < text.length()) {
241                     if (log) AntModule.err.log("\tspecified section of the line");
242                     ann = l.createPart(new_col1, new_col2 - new_col1 + 1);
243                 } else if (new_col1 < text.length()) {
244                     if (log) AntModule.err.log("\tspecified column to end of line");
245                     ann = l.createPart(new_col1, text.length() - new_col1 - 1);
246                 } else {
247                     if (log) AntModule.err.log("\tcolumn numbers are bogus");
248                     ann = l;
249                 }
250             } else {
251                 if (log) AntModule.err.log("\tmultiple lines, something wrong with line, or no column given");
252                 ann = l;
253             }
254             attach(ann);
255             // #17625: detach others however
256
synchronized (hyperlinks) {
257                 for (Hyperlink h : hyperlinks) {
258                     if (h != this) {
259                         h.doDetach();
260                     }
261                 }
262             }
263             ann.addPropertyChangeListener(this);
264         }
265     }
266
267     // XXX should be handled in StandardLogger, perhaps?
268
private int convertTabColumnsToCharacterColumns(String JavaDoc text, int column, int tabSize) {
269         // #16867 - jikes is right now only compiler which reports column of the error.
270
// If the text contains 'tab' character, the jikes expects
271
// that tab character is defined as 8 spaces, and so it sets the column accordingly.
272
// This method converts jikes columns back to character columns
273
char[] textChars = text.toCharArray();
274         int i;
275         int jikes_column = 0;
276         for (i=0; i<textChars.length && jikes_column<column; i++) {
277             if (textChars[i] == 9) {
278                 jikes_column += (tabSize-(jikes_column%tabSize));
279             } else {
280                 jikes_column++;
281             }
282         }
283         return i;
284     }
285     
286     private synchronized void doDetach() {
287         Annotatable ann = getAttachedAnnotatable();
288         if (ann != null) {
289             if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
290                 AntModule.err.log("Detaching from " + ann + " `" + ann.getText() + "'");
291             }
292             ann.removePropertyChangeListener(this);
293             detach();
294         }
295     }
296     
297     public void outputLineCleared(OutputEvent ev) {
298         doDetach();
299         synchronized (hyperlinks) {
300             liveLine = null;
301         }
302     }
303     
304     public void propertyChange(PropertyChangeEvent JavaDoc ev) {
305         if (dead) return;
306         String JavaDoc prop = ev.getPropertyName();
307         if (prop == null ||
308         prop.equals(Annotatable.PROP_TEXT) ||
309         prop.equals(Annotatable.PROP_DELETED)) {
310             // Affected line has changed.
311
// Assume user has edited & corrected the error.
312
if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
313                 AntModule.err.log("Received Annotatable property change: " + prop);
314             }
315             doDetach();
316         }
317         if (Annotatable.PROP_ANNOTATION_COUNT.equals(prop)) {
318             // #22374 again: detach if another annotation is added to this line after me.
319
Annotatable ann = getAttachedAnnotatable();
320             if (ann != null) {
321                 int count = ann.getAnnotationCount();
322                 if (ann instanceof Line.Part) {
323                     // Parser annotation may be on line rather than our segment of it.
324
count += ((Line.Part) ann).getLine().getAnnotationCount();
325                 }
326                 if (count > 1) {
327                     masked = true;
328                     doDetach();
329                 }
330             }
331         }
332     }
333     
334     @Override JavaDoc
335     public String JavaDoc getAnnotationType() {
336         return "org-apache-tools-ant-module-error"; // NOI18N
337
}
338     
339     @Override JavaDoc
340     public String JavaDoc getShortDescription() {
341         return message;
342     }
343     
344     @Override JavaDoc
345     public String JavaDoc toString() {
346         return "Hyperlink[" + url + ":" + line1 + ":" + col1 + ":" + line2 + ":" + col2 + "]"; // NOI18N
347
}
348     
349 }
350
Popular Tags