001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.apache.tools.ant.module.run;
043:
044: import java.awt.Toolkit;
045: import java.io.IOException;
046: import java.net.URL;
047: import java.util.Set;
048: import org.apache.tools.ant.module.AntModule;
049: import org.openide.ErrorManager;
050: import org.openide.awt.StatusDisplayer;
051: import org.openide.cookies.EditorCookie;
052: import org.openide.filesystems.FileObject;
053: import org.openide.filesystems.URLMapper;
054: import org.openide.loaders.DataObject;
055: import org.openide.loaders.DataObjectNotFoundException;
056: import org.openide.text.Line;
057: import org.openide.util.WeakSet;
058: import org.openide.windows.OutputEvent;
059: import org.openide.windows.OutputListener;
060:
061: /**
062: * Represents a linkable line (appears in red in Output Window).
063: * Line and column numbers start at 1, and -1 means an unknown value.
064: * Careful since org.openide.text seems to assume 0-based line and column numbers.
065: * @author Jesse Glick
066: */
067: public final class Hyperlink implements OutputListener {
068:
069: static final Set<Hyperlink> hyperlinks = new WeakSet<Hyperlink>();
070:
071: private final URL url;
072: private final String message;
073: private final int line1;
074: private int col1;
075: private final int line2;
076: private final int col2;
077: private Line liveLine;
078:
079: public Hyperlink(URL url, String message, int line1, int col1,
080: int line2, int col2) {
081: this .url = url;
082: this .message = message;
083: this .line1 = line1;
084: this .col1 = col1;
085: this .line2 = line2;
086: this .col2 = col2;
087: synchronized (hyperlinks) {
088: hyperlinks.add(this );
089: }
090: }
091:
092: /**
093: * Enables the column number of the hyperlink to be changed after the fact.
094: * If it is already set, this is ignored.
095: */
096: public void setColumn1(int col1) {
097: if (this .col1 == -1) {
098: this .col1 = col1;
099: }
100: }
101:
102: public void outputLineAction(OutputEvent ev) {
103: FileObject file = URLMapper.findFileObject(url);
104: if (file == null) { // #13115
105: Toolkit.getDefaultToolkit().beep();
106: return;
107: }
108: try {
109: DataObject dob = DataObject.find(file);
110: EditorCookie ed = dob.getCookie(EditorCookie.class);
111: if (ed != null
112: && /* not true e.g. for *_ja.properties */file == dob
113: .getPrimaryFile()) {
114: if (line1 == -1) {
115: // OK, just open it.
116: ed.open();
117: } else {
118: ed.openDocument(); // XXX getLineSet does not do it for you!
119: AntModule.err.log("opened document for " + file);
120: try {
121: Line line = updateLines(ed);
122: if (!line.isDeleted()) {
123: if (col1 == -1) {
124: line.show(Line.SHOW_REUSE);
125: } else {
126: line.show(Line.SHOW_REUSE, col1 - 1);
127: }
128: }
129: } catch (IndexOutOfBoundsException ioobe) {
130: // Probably harmless. Bogus line number.
131: ed.open();
132: }
133: }
134: } else {
135: Toolkit.getDefaultToolkit().beep();
136: }
137: } catch (DataObjectNotFoundException donfe) {
138: ErrorManager.getDefault().notify(ErrorManager.WARNING,
139: donfe);
140: } catch (IOException ioe) {
141: // XXX see above, should not be necessary to call openDocument at all
142: ErrorManager.getDefault().notify(ErrorManager.WARNING, ioe);
143: }
144: if (message != null) {
145: // Try to do after opening the file, since opening a new file
146: // clears the current status message.
147: StatusDisplayer.getDefault().setStatusText(message);
148: }
149: }
150:
151: /**
152: * #62623: record positions in document at time first hyperlink was clicked for this file.
153: * Otherwise an intervening save action can mess up line numbers.
154: */
155: private Line updateLines(EditorCookie ed) {
156: Line.Set lineset = ed.getLineSet();
157: synchronized (hyperlinks) {
158: assert line1 != -1;
159: boolean ran = false;
160: boolean encounteredThis = false;
161: boolean modifiedThis = false;
162: if (liveLine == null) {
163: ran = true;
164: for (Hyperlink h : hyperlinks) {
165: if (h == this ) {
166: encounteredThis = true;
167: }
168: if (h.liveLine == null && h.url.equals(url)
169: && h.line1 != -1) {
170: Line l = lineset.getOriginal(h.line1 - 1);
171: assert l != null : h;
172: h.liveLine = l;
173: if (h == this ) {
174: modifiedThis = true;
175: }
176: }
177: }
178: }
179: assert liveLine != null : "this=" + this + " ran=" + ran
180: + " encounteredThis=" + encounteredThis
181: + " modifiedThis=" + modifiedThis + " hyperlinks="
182: + hyperlinks + " hyperlinks.contains(this)="
183: + hyperlinks.contains(this );
184: return liveLine;
185: }
186: }
187:
188: public void outputLineSelected(OutputEvent ev) {
189: FileObject file = URLMapper.findFileObject(url);
190: if (file == null) {
191: return;
192: }
193: try {
194: DataObject dob = DataObject.find(file);
195: EditorCookie ed = dob.getCookie(EditorCookie.class);
196: if (ed != null) {
197: if (ed.getDocument() == null) {
198: // The document is not opened, don't bother with it.
199: // The Line.Set will be corrupt anyway, currently.
200: AntModule.err.log("no document for " + file);
201: return;
202: }
203: AntModule.err.log("got document for " + file);
204: if (line1 != -1) {
205: Line line = updateLines(ed);
206: if (!line.isDeleted()) {
207: if (col1 == -1) {
208: line.show(Line.SHOW_TRY_SHOW);
209: } else {
210: line.show(Line.SHOW_TRY_SHOW, col1 - 1);
211: }
212: }
213: }
214: }
215: } catch (DataObjectNotFoundException donfe) {
216: ErrorManager.getDefault().notify(ErrorManager.WARNING,
217: donfe);
218: } catch (IndexOutOfBoundsException iobe) {
219: // Probably harmless. Bogus line number.
220: }
221: }
222:
223: public void outputLineCleared(OutputEvent ev) {
224: synchronized (hyperlinks) {
225: liveLine = null;
226: }
227: }
228:
229: @Override
230: public String toString() {
231: return "Hyperlink[" + url + ":" + line1 + ":" + col1 + ":"
232: + line2 + ":" + col2 + "]"; // NOI18N
233: }
234:
235: }
|