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.netbeans.modules.editor.hints;
043:
044: import java.awt.Toolkit;
045: import java.awt.event.ActionEvent;
046: import java.beans.PropertyChangeEvent;
047: import java.beans.PropertyChangeListener;
048: import java.util.Collections;
049: import java.util.LinkedList;
050: import java.util.List;
051: import javax.swing.AbstractAction;
052: import javax.swing.text.BadLocationException;
053: import javax.swing.text.Document;
054: import javax.swing.text.JTextComponent;
055: import org.netbeans.api.editor.EditorRegistry;
056: import org.netbeans.editor.BaseDocument;
057: import org.netbeans.editor.Utilities;
058:
059: import org.netbeans.modules.editor.lib2.highlighting.HighlightingManager;
060: import org.netbeans.spi.editor.highlighting.HighlightsSequence;
061: import org.netbeans.spi.editor.hints.ErrorDescription;
062: import org.netbeans.spi.editor.hints.Severity;
063: import org.openide.loaders.DataObject;
064: import org.openide.util.Exceptions;
065: import org.openide.util.NbBundle;
066: import org.openide.util.WeakListeners;
067:
068: /**
069: *
070: * @author JanLahoda
071: */
072: public class NextErrorAction extends AbstractAction implements
073: PropertyChangeListener {
074:
075: public NextErrorAction() {
076: putValue(NAME, NbBundle.getMessage(NextErrorAction.class,
077: "LBL_Next_Error"));
078: EditorRegistry.addPropertyChangeListener(WeakListeners
079: .propertyChange(this , EditorRegistry.class));
080: }
081:
082: public void actionPerformed(ActionEvent e) {
083: final JTextComponent comp = EditorRegistry.focusedComponent();
084:
085: if (comp == null) {
086: return;
087: }
088:
089: comp.getDocument().render(new Runnable() {
090: public void run() {
091: List<ErrorDescription> errors = null;
092: int errorOffset = -1;
093: int unusedOffset = -1;
094: int offsetToTest = comp.getCaretPosition() + 1;
095:
096: if (offsetToTest < comp.getDocument().getLength()) {
097: errors = findNextError(comp, offsetToTest);
098: errorOffset = errors.isEmpty() ? -1 : errors
099: .iterator().next().getRange().getBegin()
100: .getOffset();
101: unusedOffset = findNextUnused(comp, offsetToTest);
102: }
103:
104: if (errorOffset == (-1) && unusedOffset == (-1)) {
105: errors = findNextError(comp, 0);
106: errorOffset = errors.isEmpty() ? -1 : errors
107: .iterator().next().getRange().getBegin()
108: .getOffset();
109: unusedOffset = findNextUnused(comp, 0);
110: }
111:
112: if (errorOffset == (-1) && unusedOffset == (-1)) {
113: Toolkit.getDefaultToolkit().beep();
114: } else {
115: if (errorOffset != (-1)
116: && (errorOffset < unusedOffset || unusedOffset == (-1))) {
117: comp.getCaret().setDot(errorOffset);
118:
119: Utilities
120: .setStatusText(comp, buildText(errors));
121: } else {
122: comp.getCaret().setDot(unusedOffset);
123:
124: Utilities.setStatusText(comp, NbBundle
125: .getMessage(NextErrorAction.class,
126: "LBL_UnusedElement"));
127: }
128: }
129: }
130: });
131: }
132:
133: private List<ErrorDescription> findNextError(JTextComponent comp,
134: int offset) {
135: Document doc = Utilities.getDocument(comp);
136: Object stream = doc
137: .getProperty(Document.StreamDescriptionProperty);
138:
139: if (!(stream instanceof DataObject)) {
140: return Collections.emptyList();
141: }
142:
143: AnnotationHolder holder = AnnotationHolder
144: .getInstance(((DataObject) stream).getPrimaryFile());
145:
146: List<ErrorDescription> errors = holder.getErrorsGE(offset);
147:
148: return errors;
149: }
150:
151: private int findNextUnused(JTextComponent comp, int offset) {
152: try {
153: BaseDocument doc = Utilities.getDocument(comp);
154: int lineStart = Utilities.getRowStart(doc, offset);
155: HighlightsSequence s = HighlightingManager.getInstance()
156: .getHighlights(comp, null).getHighlights(lineStart,
157: Integer.MAX_VALUE);
158: int lastUnusedEndOffset = -1;
159:
160: while (s.moveNext()) {
161: if (s.getAttributes().containsAttribute(
162: "unused-browseable", Boolean.TRUE)) {
163:
164: if (lastUnusedEndOffset != s.getStartOffset()
165: && s.getStartOffset() >= offset) {
166: return s.getStartOffset();
167: }
168: lastUnusedEndOffset = s.getEndOffset();
169: }
170: }
171:
172: return -1;
173: } catch (BadLocationException ex) {
174: Exceptions.printStackTrace(ex);
175: return -1;
176: }
177: }
178:
179: private String buildText(List<ErrorDescription> errors) {
180: List<ErrorDescription> trueErrors = new LinkedList<ErrorDescription>();
181: List<ErrorDescription> others = new LinkedList<ErrorDescription>();
182:
183: for (ErrorDescription ed : errors) {
184: if (ed == null) {
185: continue;
186: }
187:
188: if (ed.getSeverity() == Severity.ERROR) {
189: trueErrors.add(ed);
190: } else {
191: others.add(ed);
192: }
193: }
194:
195: //build up the description of the annotation:
196: StringBuffer description = new StringBuffer();
197:
198: concatDescription(trueErrors, description);
199:
200: if (!trueErrors.isEmpty() && !others.isEmpty()) {
201: description.append(" ");
202: }
203:
204: concatDescription(others, description);
205:
206: return description.toString().replace('\n', ' ');
207: }
208:
209: private static void concatDescription(
210: List<ErrorDescription> errors, StringBuffer description) {
211: boolean first = true;
212:
213: for (ErrorDescription e : errors) {
214: if (!first) {
215: description.append(" ");
216: }
217: description.append(e.getDescription());
218: first = false;
219: }
220: }
221:
222: public void propertyChange(PropertyChangeEvent evt) {
223: setEnabled(EditorRegistry.focusedComponent() != null);
224: }
225: }
|