alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.javacore.parser;

import java.io.*;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Position.Bias;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.queries.SourceLevelQuery;
import org.netbeans.jmi.javamodel.Resource;
import org.netbeans.lib.java.parser.*;
import org.netbeans.modules.javacore.JMManager;
import org.openide.ErrorManager;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.text.EditorSupport;
import org.openide.text.PositionBounds;
import org.openide.text.PositionRef;


/**
 *
 * @author  Tomas Hurka
 */
public class ASTProvider implements ParserTokens, ASTreeTypes, ASTContext {
    DataObject dobj;
    private EditorSupport editor;
    private ASTree topNode;
    private Token[] tokens;
    private String sourceText;
    private Resource rsc;
    static ASTree[] NULL_TREE=new ASTree[0];
    boolean documentPositions;
    boolean hasSyntaxErrors;
    
    /** Creates a new instance of ASTProvider */
    public ASTProvider(Resource r,DataObject obj) {
        rsc=r;
        dobj = obj;
        if (obj == null) throw new NullPointerException();
    }
    
    protected ASTProvider(Resource r, DataObject obj, String sourceText, boolean isFromDoc) {
        this(r, obj);
        this.sourceText = sourceText;
        this.documentPositions = isFromDoc;
    }

    private void createASTree() {
        Reader r;
        int status=-1;
        hasSyntaxErrors = false;
        
        try {
            r=getReader();
        } catch (Exception ex) {
            JMManager.getLog().log("Error opening stream for: " + getResource().getName() + "; " + ex.toString());
            ErrorManager.getDefault().notify(ex);
            return;
        }
        JParser parser=Factory.getDefault().getParser(this, r, dobj.getName());
        try {
            status=parser.parse();
        } catch (Exception ex) {
            dumpSource(ex);
        }
        
        if (status!=0) {
            hasSyntaxErrors = true;
            ASTree compUnit=parser.getASTree();
            
            if (compUnit!=null && compUnit.getType()==COMPILATION_UNIT)
                topNode=compUnit;
            
//            System.out.println("Error in file: "+dobj.getPrimaryFile().getPath()+" has ASTree "+(topNode!=null));
            
            if (topNode != null) {
                fixTree(topNode);
            }
            //return;
        }
        topNode=parser.getASTree();
	tokens=parser.getTokens();
	if (tokens == null)
	    tokens = new Token[0];
        
        if (topNode == null || topNode.getSubTrees() == null) {
            // let's create a fake AST
            TokenIterator ti = new TokenIterator(ASTProvider.this);
            int i = 0;
            try {
                while (ti.getNextTokenType() != 0) i++;
            } catch (IOException e) {
                ErrorManager.getDefault().notify(e);
            }
            topNode = new ASTreeNode(COMPILATION_UNIT, 0, i - 1, new ASTree[] {null, null, null}); 
        }
    }

    private ASTree fixTree(ASTree tree) {
        if (tree == null) return null;
        int type = tree.getType();
        ASTree parts[] = tree.getSubTrees();
        
        switch (type) {
            case ENUM_BODY:
            case COMPILATION_UNIT: {
                fixChildren(parts);
                break;
            } case ENUM_CONSTANT: {
                if (parts == null) return null;
                parts[1] = parts[2] = null;
            } case TYPE_PARAMETER:
            case SUPER_:
            case CONSTRUCTOR_DECLARATOR:
            case PACKAGE_DECLARATION: {
                fixChildren(parts);
                if (parts == null || parts[0] == null || (parts[0].getType() != IDENTIFIER && parts[0].getType() != MULTI_PART_ID)) {
                    return null;
                }
                break;
            } case PRIMITIVE_TYPE: {
                fixChildren(parts);
                if (parts == null || parts[0] == null) {
                    return null;
                }
                switch (parts[0].getType()) {
                    case BOOLEAN:
                    case BYTE:
                    case CHAR:
                    case DOUBLE:
                    case FLOAT:
                    case INT:
                    case LONG:
                    case SHORT:
                    case VOID:
                        break;
                    default:
                        return null;
                }
                break;
            } case METHOD_DECLARATOR: {
                fixChildren(parts);
                if (parts == null || parts[0] == null || parts[0].getType() != IDENTIFIER) {
                    return null;
                }
                break;
            } case MULTI_PART_ID: {
                if (parts == null) return null;
                fixChildren(parts);
                if ((parts[0] != null) && (parts[0].getType() != IDENTIFIER) && (parts[0].getType() != MULTI_PART_ID)) {
                    return null;
                }
                if (parts[1] == null || parts[1].getType() != IDENTIFIER) {
                    return null;
                }
                break;
            } case SINGLE_TYPE_IMPORT:
            case TYPE_IMPORT_ON_DEMAND: {
                if (parts == null) return null;
                fixChildren(parts);
                if (parts[1] == null) {
                    return null;
                }
                break;
            } case REFERENCE_TYPE: {
                if (parts == null) return null;
                fixChildren(parts);
                if (parts[0] == null || parts[1] == null) {
                    return null;
                }
                break;
            } case ANNOTATION_TYPE_DECLARATION:
            case FIELD_DECLARATION:
            case ANNOTATION_ATTRIBUTE_DECLARATION: {
                if (parts == null) return null;
                fixChildren(parts);
                if (parts[1] == null || parts[2] == null) {
                    return null;
                }
                break;
            } case ENUM_DECLARATION: {
                if (parts == null) return null;
                fixChildren(parts);
                if (parts[1] == null || parts[1].getType() != IDENTIFIER || parts[3] == null) {
                    return null;
                }
                break;
            } case FORMAL_PARAMETER: {
                if (parts == null) return null;
                fixChildren(parts);
                if (parts[1] == null || parts[3] == null) {
                    return null;
                }
                break;
            } case CLASS_DECLARATION:
            case INTERFACE_DECLARATION: {
                if (parts == null) return null;
                fixChildren(parts);
                if (parts[1] == null || parts[1].getType() != IDENTIFIER || parts[5] == null) {
                    return null;
                }
                break;
            } case CONSTRUCTOR_DECLARATION: {
                if (parts == null) return null;
                fixChildren(parts);
                if (parts[3] == null || parts[5] == null || parts[5].getType() != BLOCK_STATEMENTS) {
                    return null;
                }
                break;
            } case METHOD_DECLARATION: {
                if (parts == null) return null;
                fixChildren(parts);
                if (parts[2] == null || parts[3] == null || parts[5] == null
                    || (parts[5].getType() != BLOCK_STATEMENTS && parts[5].getType() != SEMICOLON)) {
                    return null;
                }
                break;
            } case VARIABLE_DECLARATORS: {
                ASTree result = fixChildren(tree, type, parts);
                if (result != null && result.getSubTrees().length == 1) {
                    result = result.getSubTrees()[0];
                }
                return result;
            } case VARIABLE_DECLARATOR: {
                if (parts == null) return null;
                if (parts.length > 2) parts[2] = null;
                fixChildren(parts);
                if (parts[0] == null || parts[0].getType() != IDENTIFIER) {
                    return null;
                }
                break;
            } case TYPE_ARGUMENTS:
            case TYPE_DECLARATIONS:
            case MODIFIERS:
            case TYPE_PARAMETER_LIST:
            case BOUND_LIST:
            case FORMAL_PARAMETER_LIST:
            case ENUM_CONSTANTS:
            case IMPORT_DECLARATIONS: {
                return fixChildren(tree, type, parts);
            }
            case TYPE_LIST: { 
                return fixChildren(tree, type, parts,new int[] {MULTI_PART_ID|IDENTIFIER});
            }
	    case ANNOTATION_TYPE_BODY_DECLARATIONS:
            case ENUM_BODY_DECLARATIONS:
            case INTERFACE_MEMBER_DECLARATIONS:
            case CLASS_BODY_DECLARATIONS: {
                ASTree result = fixChildren(tree, type, parts);
                return result == null ? new ASTreeNode(type, tree.getFirstToken(), tree.getLastToken(), NULL_TREE) : result;
            } case WILDCARD: {
                if (parts == null) return null;
                fixChildren(parts);
                if ((parts[0] == null) != (parts[1] == null)) {
                    return null;
                }
                break;
            } case BLOCK_STATEMENTS: {
                return new ASTreeNode(type, tree.getFirstToken(), tree.getLastToken(), NULL_TREE);
            } case ERRONEOUS:
            case ANNOTATION:
            case INSTANCE_INITIALIZER:
            case STATIC_INITIALIZER:
            case DEFAULT_VALUE: {
                return null;
            }
        }
        return tree;
    }

    private ASTree fixChildren(ASTree tree, int type, ASTree[] parts) {
	return fixChildren(tree, type, parts, null);
    }
    
    /** NOTE !! 
     * allowedTypes must be sorted
     */
    private ASTree fixChildren(ASTree tree, int type, ASTree[] parts,int[] allowedTypes) {
        if (parts == null) return null;
        int i = 0, j = 0;
        for (; i < parts.length; i++) {
            parts[i] = fixTree(parts[i]);
            if (parts[i] != null && (allowedTypes==null || Arrays.binarySearch(allowedTypes,parts[i].getType())>=0)) {
                j++;
            }
        }
        if (j == 0) return null;
        if (j < i) {
            ASTree[] newParts = new ASTree[j];
            for (i = 0, j = 0; i < parts.length; i++) {
                if (parts[i] != null) {
                    newParts[j++] = parts[i];
                }
            }
            return new ASTreeNode(type, tree.getFirstToken(), tree.getLastToken(), newParts);
        }
        return tree;
    }

    private void fixChildren(ASTree[] parts) {
        if (parts == null) return;
        for (int i = 0; i < parts.length; i++) {
            parts[i] = fixTree(parts[i]);
        }
    }

    public ASTree getASTree() {
        if (!dobj.isValid())
            return null;
        if (topNode==null)
            createASTree();
        return topNode;
    }

    public boolean hasSyntaxError() {
        return hasSyntaxErrors;
    }
    
    DataObject getDataObject() {
        return dobj;
    }

    String getRealSource(boolean overridePositions) throws FileNotFoundException, UnsupportedEncodingException, IOException {
        EditorCookie editor;
        Document doc=null;

        editor = (EditorCookie) dobj.getCookie(EditorCookie.class);
        if (editor!=null) {
            doc = editor.getDocument();
        }
        if (doc != null) {
            if (overridePositions)
                documentPositions = true;
            // loading from the memory (Document)
            final String[] str = new String[1];
            // safely take the text from the document
            final Document doc2 = doc;
            Runnable run = new Runnable() {
                public void run() {
                    try {
                        str[0] = doc2.getText(0, doc2.getLength());
                    }
                    catch (BadLocationException e) {
                        // impossible
                    }
                }
            };
            doc.render(run);
            return str[0];
        } else {
            // loading from the file
            FileObject fo = dobj.getPrimaryFile();
            InputStream is = fo.getInputStream();
            Reader fileReader;
            //            if (filter) {
            //                reader = new GuardedReader(is, true);
            //            } else
            {
                String encoding=Util.getFileEncoding(fo);

                if (encoding == null) {
                    fileReader = new InputStreamReader(is);
                } else {
                    fileReader = new InputStreamReader(is, encoding);
                }
            }
            char[] c = Util.readContents(fileReader);
            fileReader.close();
            return new String(c);
        }
    }

    Reader getFileReader(boolean overridePositions) throws FileNotFoundException, UnsupportedEncodingException, IOException {
        return new StringReader(getRealSource(overridePositions));
    }

    /** Returns contents of DataObject obj. If the object is opened in the editor,
     * the function returns current contents of the edited document.
     * If the file is not opened in a JavaEditor, it is read from the disk and
     * guarded sections are filtered out.
     * @return contents of the file/editor document; guarded section markers are filtered out.
     *
     */
    public Reader getReader() throws FileNotFoundException, UnsupportedEncodingException, IOException {
        if (sourceText==null) {
            sourceText = getRealSource(true);
        }
        return new StringReader(sourceText);
    }
    
    public Resource getResource() {
        return rsc;
    }
    
    public String getJavaDoc(ASTree tree) {
        String javadoText=null;
        Token javaDocToken=getComment(tree);

        if (javaDocToken.getType()==DOC_COMMENT) {
            javadoText=getText(javaDocToken);
        }
        return removeJavadocStars(javadoText);
    }

    public ASTree findTree(ASTree parentTree, int firstToken, int lastToken, int type) {
        ASTree[] children = parentTree.getSubTrees();

        for (int i = 0; i < children.length; i++) {
            ASTree ch = children[i];
            if (ch != null) {
                int first = ch.getFirstToken();
                int last = ch.getLastToken();
                if (first == firstToken && last == lastToken && type == ch.getType()) {
                    return ch;
                }
                if (first <= firstToken && last >= lastToken) {
                    return findTree(ch, firstToken, lastToken, type);
                }
            }
        }
        throw new IllegalArgumentException("Child tree not found."); // NOI18N
    }

    public Token getComment(ASTree t) {
        Token first=getToken(t.getFirstToken());
        Token pad[]=first.getPadding();
        int i;
        
        for(i=pad.length-1;i>=0;i--) {
            Token c=pad[i];
            
            if (c.getType()==DOC_COMMENT)
                return c;
        }
        return first;
    }
    
    ASTree[] check(ASTree tree) {
        if (tree==null || tree.getType()==SEMICOLON)
            return null;
        return NULL_TREE;
    }
    
    public ASTree[] filterParts(ASTree parts[]) {
        if (parts!=null) {
            List typeDecl=null;
            int i;
            
            for (i=parts.length-1;i>=0;i--) {
                ASTree tree=parts[i];
                ASTree checked[]=check(tree);
                
                if (checked!=NULL_TREE) {
                    if (typeDecl==null) {
                        typeDecl=new ArrayList(Arrays.asList((Object[]) parts));
                    }
                    if (checked==null)
                        typeDecl.remove(i);
                    else
                        typeDecl.addAll(i, Arrays.asList((Object[]) checked));
                }
            }
            if (typeDecl!=null) {
                return (ASTree[])typeDecl.toArray(new ASTree[typeDecl.size()]);
            }
        }
        return parts;
    }

    public String getText(ASTree tree) {
        if (tree == null) {
            return null;
        } else {
            return getText(tree,tree);
        }
    }
    
    public String getText(ASTree first,ASTree last) {
        int start=getToken(first.getFirstToken()).getStartOffset();
        int end=getToken(last.getLastToken()).getEndOffset();

        return sourceText.substring(start,end);
    }
    
    public String getText(Token token) {
        int start=token.getStartOffset();
        int end=token.getEndOffset();

        return sourceText.substring(start,end);
    }

    public Token getToken(int index) {
	return (index >= 0 && index < tokens.length) ? tokens[index] : null;
    }
    
    public ASTree getParent(ASTree tree) {
        ASTree parent=getASTree();
        int index=tree.getFirstToken();
        
        while(true) {
            ASTree parts[]=parent.getSubTrees();
            ASTree element=null;
            int i;
        
            for (i=0;i=index)
                    break;
            }
            if (i==parts.length)
                return parent;
            if (element==tree)
                return parent;
            parent=element;
        }
    }
    
    public PositionBounds createBounds(ASTree tree) {
        return createBounds(tree, tree);
    }
    
    public boolean isFromDocument() {
        return documentPositions;
    }
    
    public PositionBounds createBounds(ASTree startTree, ASTree endTree) {
        Token startToken=getToken(startTree.getFirstToken());
        Token endToken=getToken(endTree.getLastToken());
        
        if (startToken == null || endToken == null) {
            RuntimeException ex;
            String startTreeText = null;
            String endTreeText = null;
            try {
                startTreeText = startTree.toString();
                endTreeText = endTree.toString();
            } catch (RuntimeException e) {
                // ignore
            }
            if (startToken == null) {
                ex = new RuntimeException("First token is null for ASTree: " + startTreeText + " of type: " + startTree.getType() + 
                    ";\ncalling startTree.getFirstToken() returns " + startTree.getFirstToken());
            } else {
                ex = new RuntimeException("Last token is null for ASTree: " + endTreeText + " of type: " + endTree.getType() + 
                    ";\ncalling endTree.getLastToken() returns " + endTree.getLastToken());
            }
            ex.printStackTrace();
            dumpSource(ex);
        }
        
        return createBounds(new int[] {startToken.getStartOffset(), endToken.getEndOffset()});
    }
    
    public PositionBounds createBounds(int startOffset,int endOffset) {
        return createBounds(new int[] {startOffset, endOffset});
    }
    
    public PositionBounds createBounds(int[] oldOffsets) {
        if (editor == null)
            editor = (EditorSupport) dobj.getCookie(EditorSupport.class);
        
        int[] offsets = getDocumentOffsets(oldOffsets);
        
        PositionRef start=editor.createPositionRef(offsets[0], Bias.Forward);
        PositionRef end=editor.createPositionRef(offsets[1], Bias.Backward);
        
        return new PositionBounds(start,end);
    }
    
    // this method changes the array passed as argument
    // offsets must be ordered (from lowest to highest)
    public int[] getDocumentOffsets(int[] offsets) {
        if (!isFromDocument()) {
            int pos = -1;
            int firstOffset = 0;
            
            int[] newOffsets = (int[]) offsets.clone();
            
            while (pos < offsets[offsets.length - 1]) {
                pos = sourceText.indexOf('\r',pos+1);
                if (pos == -1) break;
                while ((firstOffset < offsets.length) && (offsets[firstOffset] < pos)) {
                    firstOffset++;
                }
                for (int i = firstOffset; i < offsets.length; i++) {
                    newOffsets[i]--;
                }
            }
            return newOffsets;
        } else {
            return offsets;
        }
    }

    // todo (#pf): for the time being we supply the source string to generator.
    // In the future, we expect support methods, so clients will not need
    // to access to the source string directly.
    public String getSourceText() {
        // todo (#pf): the condition is probably functionless, because 
        // code-generator uses parser with the created tree. However,
        // there is no guarantee that another client will call this
        // method before 'getASTree()'.
        if (sourceText == null)
            getASTree();
        return sourceText;
    }

    private static final int SPACE_BEFORE=1;
    private static final int ASTERIX=2;
    private static final int SPACE_AFTER=3;
    private static final int TEXT=4;
    private static final int LINE_BREAK=5;
    
    private static String removeJavadocStars(String rawText) {
        if (rawText==null)
            return null;
        
        int len=rawText.length();
        char text[]=new char[len];
        int i,j=0;
        int state=ASTERIX;
        
        for (i=1;ijava.io.File.pathSeparatorChar.
     *
     * @return String classpath elements (roots) by path separator.
     */
    public String getClassPath() {
        ClassPath cp = org.netbeans.modules.javacore.internalapi.JavaMetamodel.getManager().getClassPath();
        FileObject[] roots = cp.getRoots();
        if (roots.length == 0)
            return "";
        StringBuffer buf = new StringBuffer(30);
        for (int i = 0; ; i++) {
            try {
                URL url=roots[i].getURL();
                if (url.getProtocol().equals("jar")) { // NOI18N
                    url=FileUtil.getArchiveFile(url);
                }
                URI uri = new URI(url.toExternalForm());
                File f =  new File(uri);
                
                buf.append(f.getAbsolutePath());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            if (i < (roots.length-1)) {
                buf.append(java.io.File.pathSeparatorChar);
            } else {
                break;
            }
        }
	return buf.toString();
    }
    
    public ErrConsumer getErrorConsumer() {
        return null;
    }

    String getFileName() {
        return getResource().getName();
    }

    /**
     * Dumps the source code to the file. Used for parser debugging. It uses
     * only five dump files: from source1.dump to source5.dump. If last
     * source5.dump file exists, methods doesn't dump anything.
     *
     * @param  src  source text which causes parser failure
     * @param  exc  exception to write to the end of dump file
     * @param  dumpDir directory where dump file will be stored
     * @return  name of created dump file or null if dump file was not created.
     */
    private void dumpSource(Exception exc) {
        String dumpDir = System.getProperty("netbeans.user") + "/var/log/"; //NOI18N
        String src = getSourceText();
        String rscName = getResource() == null ? "null" : getResource().getName();
        int pos = rscName.lastIndexOf('/');
        String origName;
        if (pos >= 0) {
            origName = rscName.substring(pos + 1);
        } else {
            origName = rscName;
        }
        String name = null;
        File f = new File(dumpDir + origName + ".dump");
        int i = 1;
        while (i < 255) {
            if (!f.exists())
                break;
            f = new File(dumpDir + origName + '_' + i + ".dump");
            i++;
        }
        if (!f.exists()) {
            try {
                OutputStream os = new FileOutputStream(f);
                try {
                    PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, "UTF-8")); // NOI18N
                    writer.println(src);
                    writer.println("----- Original exception ---------------------------------------------"); // NOI18N
                    exc.printStackTrace(writer);
                    name = f.getName();
                } finally {
                    os.close();
                }
            } catch (IOException ioe) {
                ErrorManager.getDefault().annotate(ioe, ErrorManager.UNKNOWN, "Error when writing parser dump file!", null, null, null); // NOI18N
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe);
                name = null;
            }
        }
        if (name != null) {
            ErrorManager.getDefault().notify(new RuntimeException("Invalid AST returned from parser or error parsing \'" + rscName + "\'. Please report a bug against java module and attach dump file '"  // NOI18N
                + dumpDir + name + "'.")); // NOI18N
        } else {
            ErrorManager.getDefault().log(ErrorManager.WARNING,
                "Dump could not be written. Either dump file could not " + // NOI18N
                "be created or all dump files were already used. Please " + // NOI18N
                "check that you have write permission to '" + dumpDir + "' and " + // NOI18N
                "clean all *.dump files in that directory."); // NOI18N
        }
    }
    
    public String toString() {
        return "ASTProvider@" + System.identityHashCode(this) + " for \"" + getResource().getName() + "\"";
    }

    private class ASTreeNode implements ASTree {
        private final int type, firstToken, lastToken;
        private final ASTree[] subTrees;
        
        public ASTreeNode(int type, int firstToken, int lastToken, ASTree[] subTrees) {
            this.type = type;
            this.firstToken = firstToken;
            this.lastToken = lastToken;
            this.subTrees = subTrees;
        }

        public int getType() {
            return type;
        }

        public int getFirstToken() {
            return firstToken;
        }

        public int getLastToken() {
            return lastToken;
        }

        public ASTree[] getSubTrees() {
            return subTrees;
        }

        public SymbolInfo getSymbolInfo() {
            return null;
        }

        public ASTContext getASTContext() {
            return ASTProvider.this;
        }
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.