All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.languages.features.AnnotationManager Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * 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.languages.features;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.text.Position;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

import org.netbeans.api.languages.ParserManager;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.api.languages.ASTEvaluator;
import org.netbeans.api.languages.ASTItem;
import org.netbeans.api.languages.ASTPath;
import org.netbeans.api.languages.ParserManager.State;
import org.netbeans.api.languages.SyntaxContext;
import org.netbeans.api.languages.ASTNode;
import org.netbeans.api.lexer.Token;
import org.netbeans.modules.editor.NbEditorDocument;
import org.netbeans.modules.languages.Feature;
import org.netbeans.modules.languages.Language;
import org.openide.ErrorManager;
import org.openide.text.Annotation;


/**
 *
 * @author Jan Jancura
 */
public class AnnotationManager extends ASTEvaluator {
    
    private NbEditorDocument            doc;
    private ParserManager               parser;
    private List               items;
    private List               marks;
    private List   annotations = new ArrayList ();

    
    /** Creates a new instance of AnnotationManager */
    public AnnotationManager (Document doc) {
        this.doc = (NbEditorDocument) doc;
        if (doc == null) throw new NullPointerException ();
        parser = ParserManager.get (doc);
        parser.addASTEvaluator (this);
    }
    
    public String getFeatureName () {
        return "MARK";
    }

    public void beforeEvaluation (State state, ASTNode root) {
        items = new ArrayList ();
        marks = new ArrayList ();
    }

    public void afterEvaluation (State state, ASTNode root) {
        refresh (items, marks);
    }

    public void evaluate (State state, List path, Feature feature) {
        if (feature.getBoolean ("condition", SyntaxContext.create (doc, ASTPath.create (path)), true)) {
            items.add (path.get (path.size () - 1));
            marks.add (feature);
        }
    }
    
    public void remove () {
        removeAnnotations ();
        parser.removeASTEvaluator (this);
    }
    
    private void removeAnnotations () {
        Iterator it = annotations.iterator ();
        while (it.hasNext ())
            doc.removeAnnotation (it.next ());
    }
    
    private void refresh (final List items, final List marks) {
        SwingUtilities.invokeLater (new Runnable () {
            public void run () {
                try {
                    List newAnnotations = new ArrayList ();
                    Iterator it = annotations.iterator ();
                    LanguagesAnnotation oldAnnotation = it.hasNext () ? it.next () : null;
                    Iterator it2 = items.iterator ();
                    Iterator it3 = marks.iterator ();
                    int count = 0;
                    while (it2.hasNext () && count < 100) {
                        ASTItem item = it2.next ();
                        Feature mark = it3.next ();
                        String message = (String) mark.getValue ("message");
                        Language language = (Language) item.getLanguage ();
                        message = LocalizationSupport.localize (language, message);
                        String type = (String) mark.getValue ("type");
                        while (
                            oldAnnotation != null &&
                            oldAnnotation.getPosition ().getOffset () < item.getOffset ()
                        ) {
                            doc.removeAnnotation (oldAnnotation);
                            oldAnnotation = it.hasNext () ? it.next () : null;
                        }
                        count++;
                        if (
                            oldAnnotation != null &&
                            oldAnnotation.getPosition ().getOffset () == item.getOffset () &&
                            oldAnnotation.getAnnotationType ().equals (type) &&
                            oldAnnotation.getShortDescription ().equals (message)
                        ) {
                            newAnnotations.add (oldAnnotation);
                            oldAnnotation = it.hasNext () ? it.next () : null;
                            continue;
                        }
                        LanguagesAnnotation la = new LanguagesAnnotation (
                            type,
                            message
                        );
   
                        if (item.getLength() == 0) {
                            //when the ASTItem length is zero we need to find an appropriate token to signal the error 
                            TokenHierarchy hi = TokenHierarchy.get(doc);
                            TokenSequence ts = hi.tokenSequence();
                            ts.move(item.getOffset());
                            //test if next token contains the ASTItem's language embedding
                            if(!(ts.moveNext() && testCreateAnnotation(hi, ts, item, la)))
                                //if not, do the same with previous token
                                if(!(ts.movePrevious() && testCreateAnnotation(hi, ts, item, la))) {
                                    //give up - use default annotation location
                                    Position position = doc.createPosition(item.getOffset ());
                                    la.setPosition (position);
                                    doc.addAnnotation(doc.createPosition(item.getOffset()), item.getLength(), la);
                                }
                        } else {
                            Position position = doc.createPosition(item.getOffset ());
                            la.setPosition (position);
                            doc.addAnnotation(doc.createPosition(item.getOffset()), item.getLength(), la);
                        }
                        newAnnotations.add (la);
                    } // while
                    if (oldAnnotation != null)
                        doc.removeAnnotation (oldAnnotation);
                    while (it.hasNext ())
                        doc.removeAnnotation (it.next ());
                    annotations = newAnnotations;
                } catch (BadLocationException ex) {
                    ErrorManager.getDefault ().notify (ex);
                }
            }
        });
    }

    private boolean testCreateAnnotation(TokenHierarchy hi, TokenSequence ts, ASTItem item, LanguagesAnnotation la) throws BadLocationException {
        if (ts.language () == null)
            throw new NullPointerException ("ts.language()==null");
        if (ts.language ().mimeType () == null)
            throw new NullPointerException ("TokenSequence.mimeType==null");
        if (ts.language().mimeType().equals(item.getMimeType())) {
                Token t = ts.token();
                if (t == null) throw new NullPointerException ();
                Position position = doc.createPosition(t.offset(hi));
                la.setPosition (position);
                doc.addAnnotation(position, t.length(), la);
                return true;
            } else {
                ts = ts.embedded();
                if(ts == null) {
                    return false;
                } else {
                    return ts.moveNext() ? testCreateAnnotation(hi, ts, item, la) : false;
                }
            }
    }
    
    
    // innerclasses ............................................................
    
    static class LanguagesAnnotation extends Annotation {

        private String type;
        private String description;

        /** Creates a new instance of ToolsAnotation */
        LanguagesAnnotation (
            String type,
            String description
        ) {
            this.type = type;
            this.description = description;
            if (type == null) throw new NullPointerException ();
            if (description == null) throw new NullPointerException ();
        }

        /** Returns name of the file which describes the annotation type.
         * The file must be defined in module installation layer in the
         * directory "Editors/AnnotationTypes"
         * @return  name of the anotation type
         */
        public String getAnnotationType () {
            return type;
        }

        /** Returns the tooltip text for this annotation.
         * @return  tooltip for this annotation
         */
        public String getShortDescription () {
            return description;
        }
        
        private Position position;
        
        void setPosition (Position position) {
            this.position = position;
        }
        
        Position getPosition () {
            return position;
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy