001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2006, University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA
018: */
019:
020: package edu.umd.cs.findbugs.gui2;
021:
022: import java.awt.Color;
023: import java.io.InputStream;
024: import java.io.InputStreamReader;
025: import java.lang.ref.SoftReference;
026: import java.util.HashMap;
027: import java.util.Iterator;
028: import java.util.LinkedList;
029: import java.util.Map;
030:
031: import javax.swing.JTextPane;
032: import javax.swing.text.BadLocationException;
033: import javax.swing.text.DefaultStyledDocument;
034: import javax.swing.text.Document;
035: import javax.swing.text.StyledDocument;
036:
037: import edu.umd.cs.findbugs.BugAnnotation;
038: import edu.umd.cs.findbugs.BugInstance;
039: import edu.umd.cs.findbugs.SourceLineAnnotation;
040: import edu.umd.cs.findbugs.annotations.CheckForNull;
041: import edu.umd.cs.findbugs.annotations.NonNull;
042: import edu.umd.cs.findbugs.ba.SourceFile;
043: import edu.umd.cs.findbugs.sourceViewer.JavaSourceDocument;
044:
045: public final class SourceCodeDisplay implements Runnable {
046: final MainFrame frame;
047:
048: private static final Color MAIN_HIGHLIGHT = new Color(1f, 1f, 0.5f);
049: private static final Color MAIN_HIGHLIGHT_MORE = MAIN_HIGHLIGHT
050: .brighter();
051: private static final Color ALTERNATIVE_HIGHLIGHT = new Color(0.86f,
052: 0.90f, 1.0f);
053: private static final Color FOUND_HIGHLIGHT = new Color(0.75f,
054: 0.75f, 1f);
055:
056: static final Document SOURCE_NOT_RELEVANT = new DefaultStyledDocument();;
057:
058: public JavaSourceDocument myDocument;
059: private int currentChar = -1; //for find
060:
061: private final Map<String, SoftReference<JavaSourceDocument>> map = new HashMap<String, SoftReference<JavaSourceDocument>>();
062:
063: SourceCodeDisplay(MainFrame frame) {
064: this .frame = frame;
065: Thread t = new Thread(this , "Source code display thread");
066: t.setDaemon(true);
067: t.start();
068: }
069:
070: private boolean pendingUpdate;
071:
072: @CheckForNull
073: private BugInstance bugToDisplay;
074:
075: private SourceLineAnnotation sourceToHighlight;
076:
077: public synchronized void displaySource(BugInstance bug,
078: SourceLineAnnotation source) {
079: bugToDisplay = bug;
080: sourceToHighlight = source;
081: pendingUpdate = true;
082: notifyAll();
083: }
084:
085: public void clearCache() {
086: map.clear();
087: }
088:
089: @NonNull
090: private JavaSourceDocument getDocument(SourceLineAnnotation source) {
091: try {
092: SourceFile sourceFile = frame.sourceFinder
093: .findSourceFile(source);
094: String fullFileName = sourceFile.getFullFileName();
095: SoftReference<JavaSourceDocument> resultReference = map
096: .get(fullFileName);
097: JavaSourceDocument result = null;
098: if (resultReference != null)
099: result = resultReference.get();
100: if (result != null)
101: return result;
102: try {
103: InputStream in = sourceFile.getInputStream();
104: result = new JavaSourceDocument(source.getClassName(),
105: new InputStreamReader(in), sourceFile);
106: } catch (Exception e) {
107: result = JavaSourceDocument.UNKNOWNSOURCE;
108: Debug.println(e); // e.printStackTrace();
109: }
110: map.put(fullFileName,
111: new SoftReference<JavaSourceDocument>(result));
112: return result;
113: } catch (Exception e) {
114: Debug.println(e); // e.printStackTrace();
115: return JavaSourceDocument.UNKNOWNSOURCE;
116:
117: }
118: }
119:
120: public void run() {
121: while (true) {
122: BugInstance myBug;
123: SourceLineAnnotation mySourceLine;
124: synchronized (this ) {
125: while (!pendingUpdate) {
126: try {
127: wait();
128: } catch (InterruptedException e) {
129: // we don't use these
130: }
131: }
132: myBug = bugToDisplay;
133: mySourceLine = sourceToHighlight;
134: bugToDisplay = null;
135: sourceToHighlight = null;
136: pendingUpdate = false;
137: }
138: if (myBug == null || mySourceLine == null) {
139: frame.clearSourcePane();
140: continue;
141: }
142:
143: try {
144: final JavaSourceDocument src = getDocument(mySourceLine);
145: this .myDocument = src;
146: src.getHighlightInformation().clear();
147: String primaryKind = mySourceLine.getDescription();
148: // Display myBug and mySourceLine
149: for (Iterator<BugAnnotation> i = myBug
150: .annotationIterator(); i.hasNext();) {
151: BugAnnotation annotation = i.next();
152: if (annotation instanceof SourceLineAnnotation) {
153: SourceLineAnnotation sourceAnnotation = (SourceLineAnnotation) annotation;
154: if (sourceAnnotation == mySourceLine)
155: continue;
156: if (sourceAnnotation.getDescription().equals(
157: primaryKind))
158: highlight(src, sourceAnnotation,
159: MAIN_HIGHLIGHT_MORE);
160: else
161: highlight(src, sourceAnnotation,
162: ALTERNATIVE_HIGHLIGHT);
163: }
164: }
165: highlight(src, mySourceLine, MAIN_HIGHLIGHT);
166: final BugInstance this Bug = myBug;
167: final SourceLineAnnotation this Source = mySourceLine;
168: javax.swing.SwingUtilities.invokeLater(new Runnable() {
169: public void run() {
170: frame.sourceCodeTextPane.setEditorKit(src
171: .getEditorKit());
172: StyledDocument document = src.getDocument();
173: frame.sourceCodeTextPane.setDocument(document);
174: String sourceFile = this Source.getSourceFile();
175: if (sourceFile == null
176: || sourceFile.equals("<Unknown>")) {
177: sourceFile = this Source
178: .getSimpleClassName();
179: }
180: frame.setSourceTabTitle(sourceFile + " in "
181: + this Source.getPackageName());
182: int startLine = this Source.getStartLine();
183: int endLine = this Source.getEndLine();
184: int originLine = (startLine + endLine) / 2;
185: LinkedList<Integer> otherLines = new LinkedList<Integer>();
186: //show(frame.sourceCodeTextPane, document, thisSource);
187: for (Iterator<BugAnnotation> i = this Bug
188: .annotationIterator(); i.hasNext();) {
189: BugAnnotation annotation = i.next();
190: if (annotation instanceof SourceLineAnnotation) {
191: SourceLineAnnotation sourceAnnotation = (SourceLineAnnotation) annotation;
192: if (sourceAnnotation != this Source) {
193: //show(frame.sourceCodeTextPane, document, sourceAnnotation);
194: int otherLine = sourceAnnotation
195: .getStartLine();
196: if (otherLine > originLine)
197: otherLine = sourceAnnotation
198: .getEndLine();
199: otherLines.add(otherLine);
200: }
201: }
202: }
203: //show(frame.sourceCodeTextPane, document, thisSource);
204: if (startLine >= 0 && endLine >= 0)
205: frame.sourceCodeTextPane
206: .scrollLinesToVisible(startLine,
207: endLine, otherLines);
208: }
209: });
210: } catch (Exception e) {
211: Debug.println(e); // e.printStackTrace();
212: }
213: }
214: }
215:
216: /**
217: * @param src
218: * @param sourceAnnotation
219: */
220: private void highlight(JavaSourceDocument src,
221: SourceLineAnnotation sourceAnnotation, Color color) {
222:
223: int startLine = sourceAnnotation.getStartLine();
224: if (startLine == -1)
225: return;
226: if (!sourceAnnotation.getClassName().equals(src.getTitle()))
227: return;
228: src.getHighlightInformation().setHighlight(startLine,
229: sourceAnnotation.getEndLine(), color);
230: }
231:
232: public void foundItem(int lineNum) {
233: myDocument.getHighlightInformation()
234: .updateFoundLineNum(lineNum);
235: myDocument.getHighlightInformation().setHighlight(lineNum,
236: FOUND_HIGHLIGHT);
237: frame.sourceCodeTextPane.scrollLineToVisible(lineNum);
238: frame.sourceCodeTextPane.updateUI();
239: }
240:
241: private int search(JavaSourceDocument document, String target,
242: int start, Boolean backwards) {
243: if (document == null)
244: return -1;
245:
246: String docContent = null;
247: try {
248: StyledDocument document2 = document.getDocument();
249: if (document2 == null)
250: return -1;
251: docContent = document2.getText(0, document2.getLength());
252: } catch (BadLocationException ble) {
253: System.out.println("Bad location exception");
254: } catch (NullPointerException npe) {
255: return -1;
256: }
257: if (docContent == null)
258: return -1;
259: int targetLen = target.length();
260: int sourceLen = docContent.length();
261: if (targetLen > sourceLen)
262: return -1;
263: else if (backwards) {
264: for (int i = start; i >= 0; i--)
265: if (docContent.substring(i, i + targetLen).equals(
266: target))
267: return i;
268: for (int i = (sourceLen - targetLen); i > start; i--)
269: if (docContent.substring(i, i + targetLen).equals(
270: target))
271: return i;
272: return -1;
273: } else {
274: for (int i = start; i <= (sourceLen - targetLen); i++)
275: if (docContent.substring(i, i + targetLen).equals(
276: target))
277: return i;
278: for (int i = 0; i < start; i++)
279: if (docContent.substring(i, i + targetLen).equals(
280: target))
281: return i;
282: return -1;
283: }
284: }
285:
286: private int charToLineNum(int charNum) {
287: if (charNum == -1)
288: return -1;
289: try {
290: for (int i = 1; true; i++) {
291: if (frame.sourceCodeTextPane.getLineOffset(i) > charNum)
292: return i - 1;
293: else if (frame.sourceCodeTextPane.getLineOffset(i) == -1)
294: return -1;
295: }
296: } catch (BadLocationException ble) {
297: return -1;
298: }
299: }
300:
301: public int find(String target) {
302: int charFoundAt = search(myDocument, target, 0, false);
303: currentChar = charFoundAt;
304: //System.out.println(currentChar);
305: //System.out.println(charToLineNum(currentChar));
306: return charToLineNum(currentChar);
307: }
308:
309: public int findNext(String target) {
310: int charFoundAt = search(myDocument, target, currentChar + 1,
311: false);
312: currentChar = charFoundAt;
313: //System.out.println(currentChar);
314: //System.out.println(charToLineNum(currentChar));
315: return charToLineNum(currentChar);
316: }
317:
318: public int findPrevious(String target) {
319: int charFoundAt = search(myDocument, target, currentChar - 1,
320: true);
321: currentChar = charFoundAt;
322: //System.out.println(currentChar);
323: //System.out.println(charToLineNum(currentChar));
324: return charToLineNum(currentChar);
325: }
326:
327: private void show(JTextPane pane, Document src,
328: SourceLineAnnotation sourceAnnotation) {
329:
330: int startLine = sourceAnnotation.getStartLine();
331: if (startLine == -1)
332: return;
333: frame.sourceCodeTextPane.scrollLineToVisible(startLine);
334: /*
335: Element element = src.getDefaultRootElement().getElement(sourceAnnotation.getStartLine()-1);
336: if (element == null) {
337: if (MainFrame.DEBUG) {
338: System.out.println("Couldn't display line " + sourceAnnotation.getStartLine() + " of " + sourceAnnotation.getSourceFile());
339: System.out.println("It only seems to have " + src.getDefaultRootElement().getElementCount() + " lines");
340: }
341: return;
342: }
343: pane.setCaretPosition(element.getStartOffset());
344: */
345: }
346:
347: public void showLine(int line) {
348: frame.sourceCodeTextPane.scrollLineToVisible(line);
349: /*
350: JTextPane pane = frame.sourceCodeTextPane;
351: Document doc = pane.getDocument();
352: Element element = doc.getDefaultRootElement().getElement(line-1);
353: pane.setCaretPosition(element.getStartOffset());
354: */
355: }
356: }
|