| /** |
| * 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); |
| } |
| } |