blob: abeda16303ffefb7db7dedc489bf0052b65aa0b5 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.editor.hints;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.netbeans.api.editor.EditorRegistry;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.Utilities;
import org.netbeans.modules.editor.lib2.highlighting.HighlightingManager;
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.Severity;
import org.openide.awt.StatusDisplayer;
import org.openide.loaders.DataObject;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
/**
*
* @author JanLahoda
*/
public class NextErrorAction extends AbstractAction implements PropertyChangeListener {
public NextErrorAction() {
putValue(NAME, NbBundle.getMessage(NextErrorAction.class, "LBL_Next_Error"));
EditorRegistry.addPropertyChangeListener(WeakListeners.propertyChange(this, EditorRegistry.class));
}
public void actionPerformed(ActionEvent e) {
final JTextComponent comp = EditorRegistry.focusedComponent();
if (comp == null) {
return ;
}
comp.getDocument().render(new Runnable() {
public void run() {
List<ErrorDescription> errors = null;
int errorOffset = -1;
int unusedOffset = -1;
int offsetToTest = comp.getCaretPosition() + 1;
if (offsetToTest < comp.getDocument().getLength()) {
errors = findNextError(comp, offsetToTest);
errorOffset = errors.isEmpty() ? -1 : errors.iterator().next().getRange().getBegin().getOffset();
unusedOffset = findNextUnused(comp, offsetToTest);
}
if (errorOffset == (-1) && unusedOffset == (-1)) {
errors = findNextError(comp, 0);
errorOffset = errors.isEmpty() ? -1 : errors.iterator().next().getRange().getBegin().getOffset();
unusedOffset = findNextUnused(comp, 0);
}
if (errorOffset == (-1) && unusedOffset == (-1)) {
Toolkit.getDefaultToolkit().beep();
} else {
if (errorOffset != (-1) && (errorOffset < unusedOffset || unusedOffset == (-1))) {
comp.getCaret().setDot(errorOffset);
Utilities.setStatusText(comp, buildText(errors), StatusDisplayer.IMPORTANCE_ERROR_HIGHLIGHT);
} else {
comp.getCaret().setDot(unusedOffset);
Utilities.setStatusText(comp,NbBundle.getMessage(NextErrorAction.class, "LBL_UnusedElement"),
StatusDisplayer.IMPORTANCE_ERROR_HIGHLIGHT);
}
}
}
});
}
private List<ErrorDescription> findNextError(JTextComponent comp, int offset) {
Document doc = Utilities.getDocument(comp);
Object stream = doc.getProperty(Document.StreamDescriptionProperty);
if (!(stream instanceof DataObject)) {
return Collections.emptyList();
}
AnnotationHolder holder = AnnotationHolder.getInstance(((DataObject) stream).getPrimaryFile());
List<ErrorDescription> errors = holder.getErrorsGE(offset);
return errors;
}
private int findNextUnused(JTextComponent comp, int offset) {
try {
BaseDocument doc = Utilities.getDocument(comp);
int lineStart = Utilities.getRowStart(doc, offset);
// "unused-browseable" in java.editor/.../ColoringManager and csl.api/.../ColoringManager
HighlightsSequence s = HighlightingManager.getInstance(comp).getBottomHighlights().getHighlights(lineStart, Integer.MAX_VALUE);
int lastUnusedEndOffset = -1;
while (s.moveNext()) {
AttributeSet attrs = s.getAttributes();
if (attrs != null && attrs.containsAttribute("unused-browseable", Boolean.TRUE)) {
if (lastUnusedEndOffset != s.getStartOffset() && s.getStartOffset() >= offset) {
return s.getStartOffset();
}
lastUnusedEndOffset = s.getEndOffset();
}
}
return -1;
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
return -1;
}
}
private String buildText(List<ErrorDescription> errors) {
List<ErrorDescription> trueErrors = new LinkedList<ErrorDescription>();
List<ErrorDescription> others = new LinkedList<ErrorDescription>();
for (ErrorDescription ed : errors) {
if (ed == null) {
continue;
}
if (ed.getSeverity() == Severity.ERROR) {
trueErrors.add(ed);
} else {
others.add(ed);
}
}
//build up the description of the annotation:
StringBuffer description = new StringBuffer();
concatDescription(trueErrors, description);
if (!trueErrors.isEmpty() && !others.isEmpty()) {
description.append(" ");
}
concatDescription(others, description);
return description.toString().replace('\n', ' ');
}
private static void concatDescription(List<ErrorDescription> errors, StringBuffer description) {
boolean first = true;
for (ErrorDescription e : errors) {
if (!first) {
description.append(" ");
}
description.append(e.getDescription());
first = false;
}
}
public void propertyChange(PropertyChangeEvent evt) {
setEnabled(EditorRegistry.focusedComponent() != null);
}
}