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.lang.ref.SoftReference;
import java.lang.reflect.Modifier;
import java.util.*;
import javax.jmi.reflect.RefFeatured;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.mdr.MDRepository;
import org.netbeans.jmi.javamodel.*;
import org.netbeans.lib.java.parser.*;
import org.netbeans.modules.javacore.ClassIndex;
import org.netbeans.modules.javacore.ExclusiveMutex;
import org.netbeans.modules.javacore.JMManager;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.netbeans.modules.javacore.jmiimpl.javamodel.*;
import org.openide.loaders.DataObject;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;
import org.openide.ErrorManager;

/**
 *
 * @author  Tomas Hurka
 */
public class MDRParser extends ASTProvider {
    
    private Map semanticInfo;
    private ClassPath classPath;
    private JavaMetamodel model;
    private JavaPackageClass jpckClass;
    private JavaClassClassImpl jclsClass;
    private TypeClass typeClass;
    private ArrayClass arrayClass;
    private JavaEnumClass enumClass;
    private AnnotationTypeClass annotClass;
    private ClassDefinition currentClass;
    private Feature currentFeature;
    private String jpck;
    private Stack typeScopeInfo;
    private Stack variableScope;
    private ClassIndex index;
    private TreeMap attributionToDo;
    private List localSuperInfo;
    private MDRepository rep;
    private Scope staticImpScope;
    private int javaFeatures;
    private boolean running;
    private static final boolean DEBUG=false;
    private static Map superInfoMap=new HashMap();
    private static final ModifiersInfo NO_MODIFIERS = new ModifiersInfo(0, null);
    private static final RequestProcessor PARSE_AFTER_SCAN_RP = new RequestProcessor("Parse-After-Scan Request Processor"); // NOI18N

    // enum modifier
    public static final int M_ENUM = 0x00004000;
    // annotation modifier
    public static final int M_ANNOTATION = 0x00002000;
    // feature mofid to position bounds map
    public Map mofidToBounds;
    // start and end offsets of guarded blocks
    public int[] guardedBlocksBorders;

    public MDRParser(Resource r,DataObject obj) {
        super(r,obj);
        init();
    }

    public MDRParser(Resource r, DataObject obj, String sourceText, boolean isFromDoc) {
        super(r,obj,sourceText, isFromDoc);
        init();
    }
    
    private void init() {
        JavaModelPackage srcExtent=(JavaModelPackage)getResource().refImmediatePackage();

        jpckClass=srcExtent.getJavaPackage();
        jclsClass=(JavaClassClassImpl)srcExtent.getJavaClass();
        typeClass=srcExtent.getType();
        arrayClass=srcExtent.getArray();
        enumClass=srcExtent.getJavaEnum();
        annotClass=srcExtent.getAnnotationType();
        index=ClassIndex.getIndex(srcExtent);
        semanticInfo=new HashMap();
        typeScopeInfo=new Stack();
        variableScope=new Stack();
        attributionToDo = getAttributionToDoMap();
        localSuperInfo=new ArrayList();
        model=JavaMetamodel.getManager();
        rep=JavaMetamodel.getDefaultRepository();
    }
    
    private static void reparseAfterScan(final DataObject dobj) {
        PARSE_AFTER_SCAN_RP.post(new Runnable() {
            public void run() {
                JMManager manager=(JMManager)JavaMetamodel.getManager();
                if (manager.waitScanFinished()) {
                    manager.addModified(dobj);
                    JavaMetamodel.getDefaultRepository().beginTrans(true); 
                    JavaMetamodel.getDefaultRepository().endTrans(false);
                }
            }
        });
    }
    
    private static TreeMap getAttributionToDoMap() {
        return new TreeMap(new Comparator() {
            public int compare(Object o1, Object o2) {
                int i1 = ((ASTree) o1).getFirstToken(), i2 = ((ASTree) o2).getFirstToken();
                return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1);
            }
        });        
    }

    public Object getSemanticInfo(ASTree tree, Element element) {
        Object info = semanticInfo.get(tree);
        if (info == null) {
            if (!attributionToDo.isEmpty()) {
                for (Iterator it = attributionToDo.keySet().iterator(); it.hasNext();) {
                    ASTree tmp = (ASTree) it.next();
                    if (tmp.getFirstToken() > tree.getFirstToken()) {
                        break;
                    }
                    if (tmp.getLastToken() > tree.getLastToken()) {
                        doAttribution();
                        info = semanticInfo.get(tree);
                        break;
                    }
                }
            }
            if (info == null) {
                return null;
            }
        }
        return getModelElement(tree,element,info);
    }
    
    private void doAttribution() {
        TreeMap map = attributionToDo;
        attributionToDo = getAttributionToDoMap();
//                        if (JMManager.PERF_DEBUG) {
//                            System.err.println("Attributing method bodies: " + this);
//                        }
        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            enterBody((Feature) entry.getValue(), (ASTree) entry.getKey());
        }
    }
    
    public void prepareForAttribution(Feature feature, ASTree featureTree) {
        attributionToDo.put(featureTree, feature);
    }

    public synchronized void enterBody(Feature feature,ASTree featureTree) {
        if (running) {
            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,new Exception("Recursion in enterBody"));   // NOI18N
            return;
        }
        running=true;
        if (JMManager.PERF_DEBUG) try {
            System.err.println("Attributing method: " + feature.getDeclaringClass().getName() + '.' + feature.getName() + featureTree.getFirstToken()); // NOI18N
        } catch (NullPointerException e) {
            // ignore
        }
        long time = System.currentTimeMillis();
        try {
            classPath=model.getClassPath();
            currentClass=feature.getDeclaringClass();
            computeScope(currentClass);
            semanticInfo.put(featureTree, feature);
            currentFeature=feature;
            processASTBody(featureTree);
        } finally {
            typeScopeInfo.pop();
            variableScope.pop();
            superInfoMap.keySet().removeAll(localSuperInfo);
            localSuperInfo.clear();
            currentFeature=null;
            running=false;
        }
        if (JMManager.PERF_DEBUG) System.out.println("    finished: " + (System.currentTimeMillis() - time) + "ms"); // NOI18N
    }
    
    public synchronized ResourceInfo enterMembers() {
        if (running) {
            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,new Exception("Recursion in enterMembers"));   // NOI18N
            return null;
        }
        running=true;
        try {
            classPath=model.getClassPath();
            ASTree topNode=getASTree();
 
            if (topNode!=null) {
                if (DEBUG) System.out.println("Parsing :"+dobj.getPrimaryFile().getPath()); // NOI18N
                if (getPackage("java.lang")==null) {        // no JDK found
                    reparseAfterScan(getDataObject());
                }
                return (ResourceInfo) processAST(topNode);
            }
        } finally {
            superInfoMap.keySet().removeAll(localSuperInfo);
            localSuperInfo.clear();
            running=false;
        }
        return null;
    }
    
    public int getJavaFeatures() {
        return javaFeatures;
    }
    
    private void computeScope(ClassDefinition jcls) {
        Scope classScope;
        Scope varScope;
        
        if (jcls==null) {
            Resource rsc=getResource();
            
            classScope=Scope.createTypeScope(rsc,classPath);
            varScope=new Scope(null);
            staticImpScope=Scope.createStaticImpScope(rsc);
        } else if (jcls instanceof JavaClass) {
            JavaClass javaClass=((JavaClass)jcls);
            ClassDefinition decl=javaClass.getDeclaringClass();
            Iterator typeParIt;
            
            computeScope(decl);
            classScope=new Scope((Scope)typeScopeInfo.pop());
            varScope=new Scope((Scope)variableScope.pop());

            classScope.addMember(Scope.createMemberTypeScope(jcls,this));
            varScope.addMember(Scope.createFieldScope(jcls,this));
            typeParIt=javaClass.getTypeParameters().iterator();
            while(typeParIt.hasNext()) {
                TypeParameter tp=(TypeParameter)typeParIt.next();
                
                classScope.addMember(new TypeParamScope(tp));
            }
        } else {
           ASTree parentAST=((MetadataElement)jcls).getASTree(); // CLASS_BODY_DECLARATIONS
           Token firstToken=getToken(parentAST.getFirstToken()); // first token '{' of anonymous class 
           Scope anonscope[]=(Scope[])semanticInfo.get(firstToken);
           
           classScope=new Scope(anonscope[0]);
           varScope=new Scope(anonscope[1]);
           classScope.addMember(Scope.createMemberTypeScope(jcls,this));
           varScope.addMember(Scope.createFieldScope(jcls,this));
        }
        typeScopeInfo.push(classScope);
        variableScope.push(varScope);
    }
    
    private JavaPackage getPackage(String packId) {
        return jpckClass.resolvePackage(packId);
    }

    Object processAST(ASTree tree) {
        return processAST(tree,null);
    }
    
    Object processAST(ASTree tree,String fqn) {
        ASTree parts[];
        int i,treeType;
        
        if (tree==null)
            return null;
        parts=tree.getSubTrees();
        treeType=tree.getType();
        switch (treeType) {
            case COMPILATION_UNIT: {
                Object topClasses;
                ElementInfo[] importList;
                NameRef pckRef;
                
                typeScopeInfo.push(new Scope(null));
                pckRef=(NameRef)processAST(parts[0]);
                jpck=(pckRef==null)?"":pckRef.name;
                importList=(ElementInfo[]) processAST(parts[1]);  // imports;
                typeScopeInfo.pop();
                typeScopeInfo.push(Scope.createTypeScope(jpck,classPath,importList));
                topClasses=processAST(parts[2],jpck); // declarations
                typeScopeInfo.pop();
                return new ResourceInfo(tree, treeType, getResource(), (ClassInfo[]) topClasses, importList);
            }
            case PACKAGE_DECLARATION:
                return resolveTypeName(parts[0]);
            case SINGLE_TYPE_IMPORT:
            case TYPE_IMPORT_ON_DEMAND: {
                String id;
                
                if (treeType==SINGLE_TYPE_IMPORT && parts[0]!=null) { // static single type import
                    id=resolveStaticImport(parts[1]);
                } else {
                    id=((NameRef)resolveTypeName(parts[1])).name;
                }
                return new ElementInfo(tree, treeType, id);
            }
            case IMPORT_DECLARATIONS: {
                ElementInfo imports[] = null;
                if (parts != null) {
                    imports = new ElementInfo[parts.length];
                    for (i = 0; i < imports.length; i++) {
                        imports[i] = (ElementInfo) processAST(parts[i]);
                    }
                }
                return imports;
            }
            case TYPE_DECLARATIONS: {
                ClassInfo classes[]=null;
                
                if (parts!=null) {
                    ASTree filtered[]=filterParts(parts);
                    
                    for (i=0;i=t.getFirstToken() && lastToken<=t.getLastToken()) {
                if (el instanceof GenericElement) {
                    Iterator tpIt=((GenericElement)el).getTypeParameters().iterator();
                    
                    while(tpIt.hasNext()) {
                        TypeParameter p=(TypeParameter)tpIt.next();
                        
                        if (p.getName().equals(name)) {
                            tp=p;
                        }
                    }
                }
                elIterator=el.getChildren().iterator();
            }
        }
        return null;
    }
    
    private JavaClass resolveClass(TypeParamRef name) {
        SemiPersistentElement res=(SemiPersistentElement)getResource();
        JavaClass jcls=(JavaClass)res.resolveType(name);
        
        if (jcls instanceof ParameterizedType)
            return ((ParameterizedType)jcls).getDefinition();
        return jcls;
    }
    
    private JavaClass resolveClass(String name) {
        JavaClass javaClass = (JavaClass) jclsClass.resolveClass(name, true);
        if (javaClass == null) {
            // it is acceptable that the class was not resolved in case of opening a single file (from unopened project)
            // this is because in that case javac uses its standard class reader to do the attribution
            // let's log a message but do not throw an exception
            if (DEBUG) ErrorManager.getDefault().log(ErrorManager.USER, "Class " + name + " could not be resolved.");
        }
        return javaClass;
    }

    /**
     * Looks for the model element for the specified ASTree. Method is called
     * recursively to its subtrees.
     *
     * @param   elements     root object where to start find
     * @param   findingTree  tree for which we looking for the element
     * @return  found element for the tree
     */
    private MetadataElement getModelElement(Collection elements, ASTree findingTree) {
        if (elements == null || elements.isEmpty())
            return null;
        int firstToken = findingTree.getFirstToken();
        int lastToken = findingTree.getLastToken();
        for (Iterator it = elements.iterator(); it.hasNext(); ) {
            MetadataElement element = (MetadataElement) it.next();
            ASTree elementTree = element.getASTree();
            if (elementTree == findingTree) {
                // we have found it!
                return element;
            } else if (elementTree.getFirstToken() <= firstToken && elementTree.getLastToken() >= lastToken) {
                // the element is in child tree, look for it recursively
                MetadataElement result = getModelElement(element.getChildren(), findingTree);
                if (result == null) {
                    // seems that for the provided collection of elements there isn't
                    // tree provided in findingTree parameter.
                    throw new IllegalArgumentException("Tree not found! (Tree type = " + // NOI18N
                        findingTree.getType() + "; Bounds: " + firstToken + ", " + lastToken + // NOI18N
                        "; Element type: " + elementTree.getType() + "; Bounds: " + elementTree.getFirstToken() + ", " + elementTree.getLastToken() + // NOI18N
                        ")"); // NOI18N
                }
                return result;
            }
        }
        return null;
    }

    public boolean isVariableAccess(ASTree tree) {
        if (!attributionToDo.isEmpty()) {
            doAttribution();
        }
        Object info = semanticInfo.get(tree);
        if (info == null) {
            if (tree.getType() == MULTI_PART_ID) {
                return isVariableAccess(tree.getSubTrees()[0]);
            } else {
                return false;
            }
        }
        if (info.getClass().equals(FieldRefInfo.class) || info instanceof LocalVarRefInfo)
            return true;
        if (info instanceof String) {
            String id=(String)info;
            Object jmiObject;
            
            if (id.charAt(0)!='^')
                return false;
            jmiObject=getSemanticInfo(tree, null);
            return jmiObject instanceof Variable;
        }
        return false;
    }

    /**
     * Checks wheter parameters names match. Takes callable feature parameters
     * and symbolInfo parameters and compares them. If all the type names and
     * they order is the same, returns true. Otherwise it returns false.
     *
     * @param   callableFeature  feature with parameters.
     * @param   symbolInfo       info with array of string representing
     *                           parameter type names.
     * @return  true, parameters' types match
     */
    static boolean parametersMatch(CallableFeature callableFeature, TypeRef[] parTypes) {
        List params = callableFeature.getParameters();
        int i=0;
        
        if (params.size() != parTypes.length)
            return false; // if the size of list and array differs, continue
        for (Iterator pIt = params.iterator(); pIt.hasNext();) {
            // check the parameters that they are the same
            ParameterImpl par = (ParameterImpl) pIt.next();
            ParameterInfo parInfo = (ParameterInfo) par.getElementInfo();
            if (!parTypes[i++].equals(parInfo.type)) {
                return false;
            }
        }
        return true;
    }
    
    void createTypeParametrScope(ASTree typeParsAST) {
        ASTree[] typeParams;
        int i;
        Scope currentScope;
        
        if (typeParsAST==null)
            return;
        if (typeParsAST.getType()!=TYPE_PARAMETER_LIST)
            throw new IllegalArgumentException("Type "+typeParsAST.getType()); // NOI18N
        typeParams=typeParsAST.getSubTrees();
        currentScope=(Scope)typeScopeInfo.peek();
        for (i = 0; i < typeParams.length; i++) {
            ASTree typeParAST=typeParams[i];
            String name = (String) processAST(typeParAST.getSubTrees()[0]);

            currentScope.addMember(new TypeParamScope(name));
        }        
    }
    
    void resolveSuperTypes(ASTree classTree,String fqn) {
        int treeType=classTree.getType();

        typeScopeInfo.push(new Scope((Scope)typeScopeInfo.peek()));        
        switch (treeType) {
            case ENUM_DECLARATION: {
                int i;
                ASTree parts[]=classTree.getSubTrees();
                String name=(String)processAST(parts[1]);  // class name
                ASTree ifacesAST=parts[2];  // interfaces
                NameRef interfaces[];
                List interJcls=Collections.EMPTY_LIST;
                NameRef sclass;
                String currentFqn=fqn.length()==0?name:fqn.concat(".").concat(name); // NOI18N
                
                sclass=NameRef.java_lang_Enum;
                semanticInfo.put(parts[1],sclass);
                if (ifacesAST!=null) {
                    ASTree ifaces[]=ifacesAST.getSubTrees();
                    
                    interfaces=new NameRef[ifaces.length];
                    interJcls=new LinkedList();

                    for (i=0;i0) {
                    boolean createVarScope=treeType!=BLOCK_STATEMENTS 
			|| getToken(tree.getFirstToken()).getType()==L_CURLY;
                    
                    if (createVarScope)
                        variableScope.push(((Scope)variableScope.peek()).clone());
                    for (i=0;i1)
                        usesType(varType, varDecl);
                    currVarScope.addMember(new LocalVarScope(varName,new LocalVarRefInfo(varDecl,varType)));
                    processASTBody(varDecl.getSubTrees()[2]);  //initializer
                }
                break;
            }
            case FOR_STATEMENT: {
                ASTree initAST=parts[0];
                Scope forScope=null;
                
                if (initAST!=null && initAST.getType()==LOCAL_VARIABLE_DECLARATION) {
                    forScope=new Scope((Scope)variableScope.peek());
                    variableScope.push(forScope);
                }
                for (i=0;i1)
                    methods=constructors;
            } else {
                MethodScope methodScope=Scope.createMethodScope(jcls, this);
            
                methods=methodScope.lookup(name);
            }
            if (methods instanceof List) {
                List methodList=(List)methods;
                CallableFeature[] methodArr=(CallableFeature[])methodList.toArray(new CallableFeature[methodList.size()]);
                CallableFeature closest=methodArr[0];
                Type[] parTypes=(Type[])parameters.toArray(new Type[parameters.size()]);
                int parDiff=computeParDiff(closest,parTypes.length);
                int parDistance=-1;
                int i;
                boolean isAccessible=false;
                
                if (parDiff==0)
                    isAccessible=isAccessible(closest);
                for (i=1;i0) {
                        if (localParDiff0) {
                        parDiff=0;
                        closest=candidate;
                        isAccessible=isAccessible(candidate);
                        continue;
                    }
                    if (!isAccessible(candidate)) 
                        continue;                    
                    if (!isAccessible) {
                        closest=candidate;
                        isAccessible=true;
                        continue;
                    }
                    localParDist=isApplicable(candidate,parTypes);
                    if (localParDist==0)
                        return candidate;
                    if (localParDist==Integer.MAX_VALUE)
                        continue;
                    if (parDistance==-1) {
                        parDistance=isApplicable(closest,parTypes);
                        if (parDistance==0)
                            return closest;
                    }
                    if (localParDist0) {
                            closest=candidate;
                            continue;
                        }
                        if (!Modifier.isAbstract(candidate.getModifiers()) && Modifier.isAbstract(closest.getModifiers())) {
                            closest=candidate;
                            continue;
                        }
                    }
                }
                return closest;
            }
            return (CallableFeature)methods;
        }
        return null;
    }
    
    private int isApplicable(CallableFeature method,CallableFeature bestSoFar) {
        List parTypes=new ArrayList();
        Iterator pit=bestSoFar.getParameters().iterator();
        
        while(pit.hasNext()) {
            Parameter p=(Parameter)pit.next();
            
            parTypes.add(p.getType());
        }
        return isApplicable(method,(Type[])parTypes.toArray(new Type[parTypes.size()]));
    }
    
    private int isApplicable(CallableFeature method,Type[] pars) {
        Iterator it=method.getParameters().iterator();
        int p,diff=0;
        boolean varArg=false;
        Type methodType=null;
        
        for (p=0;p
... 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.