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-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.javacore;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.LinkedHashMap;
import javax.jmi.reflect.InvalidObjectException;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.jmi.reflect.RefBaseObject;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.queries.SourceLevelQuery;
import org.netbeans.api.java.queries.SourceForBinaryQuery;
import org.netbeans.api.mdr.CreationFailedException;
import org.netbeans.api.mdr.MDRepository;
import org.netbeans.jmi.javamodel.*;
import org.netbeans.lib.java.parser.ASTree;
import org.netbeans.lib.java.parser.ParserTokens;
import org.netbeans.lib.java.parser.Token;
import org.netbeans.mdr.NBMDRepositoryImpl;
import org.netbeans.mdr.persistence.btreeimpl.btreestorage.BtreeFactory;
import org.netbeans.mdr.storagemodel.MdrStorage;
import org.netbeans.modules.javacore.internalapi.*;
import org.netbeans.modules.javacore.classpath.FilterClassPathImplementation;
import org.netbeans.modules.javacore.classpath.MergedClassPathImplementation;
import org.netbeans.modules.javacore.jmiimpl.javamodel.*;
import org.netbeans.modules.javacore.parser.ArrayRef;
import org.netbeans.modules.javacore.parser.MDRParser;
import org.netbeans.modules.javacore.parser.NameRef;
import org.netbeans.modules.javacore.parser.PrimitiveTypeRef;
import org.netbeans.modules.javacore.parser.TypeParamRef;
import org.netbeans.modules.javacore.parser.WildCardRef;
import org.netbeans.spi.java.classpath.ClassPathFactory;
import org.netbeans.spi.java.classpath.PathResourceImplementation;
import org.openide.ErrorManager;
import org.openide.filesystems.*;
import org.openide.filesystems.URLMapper;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.text.EditorSupport;
import org.openide.text.PositionBounds;
import org.openide.util.RequestProcessor;

import org.netbeans.modules.javacore.scanning.FileScanner;
import org.openide.text.IndentEngine;

/**
 *
 * @author Martin Matula
 */
public class JMManager extends JavaMetamodel implements PropertyChangeListener {
    private static final String NAME_MAIN_EXTENT = "JavaCoreMain"; // NOI18N

    private static final String PREFIX_EXTENT_CODEBASE = "codebase:"; // NOI18N
    private static final String PACKAGE_CODEBASE = "JavaModel"; // NOI18N

    private static final RequestProcessor EVENT_RP = new RequestProcessor("Parsing Event Queue"); // NOI18N
    private static final RequestProcessor SCANNING_RP = new RequestProcessor("Scanning Queue"); // NOI18N
    private static final RequestPoster RESOLVE_POSTER = new RequestPoster(SCANNING_RP);
    private static final RequestPoster CLEANUP_POSTER = new RequestPoster(SCANNING_RP);

    public static final boolean PERF_DEBUG = Boolean.getBoolean("perf.refactoring");
    public static final boolean INCONSISTENCY_DEBUG = Boolean.getBoolean("javacore.debug.inconsistency");
    public static final boolean SCAN_DEBUG = Boolean.getBoolean("javacore.debug.scanning");
    public static final boolean TRANS_DEBUG = Boolean.getBoolean("javacore.debug.transactions");
    
    private static final boolean DEBUG = false;
    private static final boolean NO_SCAN = Boolean.getBoolean("netbeans.javacore.noscan");
    private static final String[] CP_TYPES = {ClassPath.BOOT, ClassPath.SOURCE, ClassPath.COMPILE};

//    private static final LinkedHashMap MDR_CACHE = new LinkedHashMap(256, 0.5f, true) {
//            public boolean removeEldestEntry(Map.Entry entry) {
//                return size() < 128;
//            }
//        };
    
    private static final Map MDR_CACHE = new CacheClass();
    
    private static class CacheClass implements Map {
        private final Object inner[] = new Object[512];
        private int size, cursor;
        
        public Set keySet() {
            throw new UnsupportedOperationException();
        }

        public Set entrySet() {
            throw new UnsupportedOperationException();
        }

        public void putAll(Map t) {
            throw new UnsupportedOperationException();
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public boolean containsKey(Object key) {
            throw new UnsupportedOperationException();
        }

        public boolean containsValue(Object value) {
            throw new UnsupportedOperationException();
        }

        public Collection values() {
            throw new UnsupportedOperationException();
        }

        public Object put(Object key, Object value) {
            inner[cursor] = value;
            cursor++;
            if (size < inner.length) {
                size++;
            }
            if (cursor >= size) {
                cursor = 0;
            }
            return null;
        }

        public void clear() {
            Arrays.fill(inner, null);
            size = 0;
            cursor = 0;
        }

        public int size() {
            return size;
        }

        public Object get(Object key) {
            throw new UnsupportedOperationException();
        }

        public Object remove(Object key) {
            throw new UnsupportedOperationException();
        }
    }


    private JavaModelPackage mainExtent;
    // uri->storageId map for mounted partitions
    private final HashMap sids = new HashMap();
    // JavaModelPackage -> FileObject (CPRoot)
    private final HashMap filesystems = new HashMap();
    private final HashMap extents = new HashMap();

    MergedClassPathImplementation cpImpl;
    private ClassPath classPath;
    private boolean progressOpened;

    private static ProgressSupport progressSupport = null;
    
    private Cache cpForFileObjectCache = new Cache(5);
    private HashMap cpForFileObjectMap = new HashMap(10);
    
    private static ErrorManager javamodelErr = ErrorManager.getDefault().getInstance("org.netbeans.javacore"); // NOI18N
    
    private Set extentsToScan = new HashSet();

    private static final MessageDigest digest;

    static {          
        try {              
            digest = MessageDigest.getInstance("SHA");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Fatal error: " + e);
        }
    }
    
    public JMManager(MDRepository repository) {
        super(repository, Codebase.class);
        cpImpl = MergedClassPathImplementation.getDefault();
        cpImpl.addPropertyChangeListener(this);
        classPath=ClassPathFactory.createClassPath (cpImpl);
        classPath.addPropertyChangeListener (this);
    }

    private void init() {
        if (mainExtent == null) {
            repository.beginTrans(true);
            boolean fail = true;
            try {
                if (mainExtent == null) {
                    if (DEBUG) System.out.println("Initializing repository."); // NOI18N
                    String mainExtentSid = mountStorage("Main"); // NOI18N
                    JavaModelPackage main = (JavaModelPackage) repository.getExtent(NAME_MAIN_EXTENT);
                    if (main == null) {
                        try {
                            NBMDRepositoryImpl nbRep = (NBMDRepositoryImpl) repository;
                            main = (JavaModelPackage) nbRep.createExtent(NAME_MAIN_EXTENT, findRootPackage(PACKAGE_CODEBASE), null, mainExtentSid);
                        } catch (CreationFailedException e) {
                            ErrorManager.getDefault().notify(e);
                        }
                    }
                    mainExtent = main;
                }
                fail = false;
            } finally {
                repository.endTrans(fail);
            }
            RepositoryUpdater.getDefault();
        }
    }
    
    /**
     * Returns the instance of Error Manager used for logging model problems
     * and debug messages.
     *
     * @return  instance of error manager
     */
    public static ErrorManager getLog() {
        return javamodelErr;
    }
    
    private String getDirName() {
        NBMDRepositoryImpl nbRep = (NBMDRepositoryImpl) repository;
        String fileName = nbRep.getParameter(BtreeFactory.STORAGE_FILE_NAME);
        int li = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
        String folderName = getVersion();
        if (li > 0) {
            folderName = fileName.substring(0, li + 1) + folderName;
        }
        return folderName;
    }
    
    private String getFileName(String name) {
        String folderName = getDirName();
        new File(folderName).mkdirs();
        return folderName + "/" + name; // NOI18N
    }
    
    private String mountStorage(String name) {
        HashMap properties = new HashMap();
        NBMDRepositoryImpl nbRep = (NBMDRepositoryImpl) repository;
        String fileName = getFileName(name);
        properties.put(BtreeFactory.STORAGE_FILE_NAME, fileName);
        properties.put(BtreeFactory.CACHE_INSTANCE, MDR_CACHE); // NOI18N
        String sid;
        try {
            if (DEBUG) System.out.println("mounting: " + fileName); // NOI18N
            sid = nbRep.mountStorage(nbRep.getParameter("storage"), properties); // NOI18N
            MdrStorage.ValuesObject values = nbRep.getMdrStorage().storageValues(sid);
            values.store(NameRef.class.getName());
            values.store(TypeParamRef.class.getName());
            values.store(PrimitiveTypeRef.class.getName());
            values.store(WildCardRef.class.getName());
            values.store(ArrayRef.class.getName());
            return sid;
        } catch (Exception e) {
            throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(), e);
        }
    }

    public ClassPath getClassPath() {
        ClassPath result = ((ExclusiveMutex) ((NBMDRepositoryImpl) repository).getTransactionMutex()).getClassPath();

        // [TODO] remove the following line once all the usages of this method are fixed
        if (result == null) {
            result = FilterClassPathImplementation.createClassPath(Collections.singletonList(getMergedClassPath()), false);
        }
        
        return result;
    }

    public ClassPath getMergedClassPath() {
        return classPath;
    }

    /** Sets classpath for the duration of the current transaction
     *
     * @param cp Classpath to be used during the current transaction.
     */
    public void setClassPath(List/**/ cps) {
        setClassPath(cps, false);
    }
    
    public void setClassPath(List/**/ cps, boolean preferSource) {
        if (cps == null || cps.isEmpty()) return;
        ((ExclusiveMutex) ((NBMDRepositoryImpl) repository).getTransactionMutex()).setClassPath(cps, preferSource);
    }
    
    public void setClassPath(ClassPath cp) {
        setClassPath(Collections.singletonList(cp));
    }
    
    public void setClassPath(Resource rsc) {
        setClassPath(getFileObject(rsc));
    }
    
    public void setClassPath(Resource rsc, boolean preferSource) {
        setClassPath(getFileObject(rsc), preferSource);
    }
    
    private class CPList extends AbstractList implements Cache.CachedElement {
        private final FileObject fo;
        private final WeakReference inner[] = new WeakReference[CP_TYPES.length];
        
        public CPList(FileObject fo) {
            this.fo = fo;
            for (int i = 0; i < CP_TYPES.length; i++) {
                ClassPath cp = ClassPath.getClassPath(fo, CP_TYPES[i]);
                inner[i] = cp == null ? null : new WeakReference(cp);
            }
            cpForFileObjectMap.put(fo, this);
        }
        
        public Object get(int index) {
            return inner[index] == null ? null : ((WeakReference) inner[index]).get();
        }
        
        public int size() {
            return 3;
        }
        
        public boolean isValid() {
            return true;
        }
        
        public void release() {
            cpForFileObjectMap.remove(fo);
        }
    }

    public synchronized void setClassPath(FileObject fo) {
        setClassPath(fo, false);
    }

    public synchronized void setClassPath(FileObject fo, boolean preferSource) {
        if (fo == null) return;
        List cps = (List) cpForFileObjectMap.get(fo);
        if (cps == null) {
            cps = new CPList(fo);
        }
        cpForFileObjectCache.put(cps);
        setClassPath(cps, preferSource);
    }

    public JavaModelPackage getDefaultExtent() {
        init();
        return mainExtent;
    }

    public static ExclusiveMutex getTransactionMutex() {
        return (ExclusiveMutex) ((NBMDRepositoryImpl) getDefaultRepository()).getTransactionMutex();
    }

    public JavaModelPackage resolveJavaExtent(FileObject cpRoot) {
        repository.beginTrans(false);
        try {
            synchronized (this) {
                JavaModelPackage extent = getJavaExtent(cpRoot);
                if (extent == null) {
                    ClassPath cp = ClassPath.getClassPath(cpRoot, ClassPath.SOURCE);
                    if (cp!=null) {
                        resolveCPRoot(cpRoot, false);
                        extent = (JavaModelPackage) extents.get(cpRoot);
                    }
                }
                return extent;
            }
        } finally {
            repository.endTrans();
        }
    }

    public synchronized JavaModelPackage getJavaExtent(FileObject cpRoot) {
        if (Arrays.asList((Object[]) getMergedClassPath().getRoots()).contains(cpRoot)) {
            return (JavaModelPackage) extents.get(cpRoot);
        } else {
            return null;
        }
    }
    
    public Resource getResource(FileObject fo) {
        return getResource(fo, true);
    }

    public Resource getResource(FileObject fo, boolean force) {
        if (fo.isVirtual()) {
            //simply ignore virtual files
            return null;
        }
        repository.beginTrans(false);
        try {
            FileObject cpRoot = this.classPath.findOwnerRoot (fo);
            if (cpRoot==null && force) {
                String ext=fo.getExt();
                ClassPath cp=null;
                if ("java".equals(ext))         // NOI18N
                    cp = ClassPath.getClassPath(fo, ClassPath.SOURCE);
                else if ("class".equals(ext))   // NOI18N
                    cp = ClassPath.getClassPath(fo, ClassPath.EXECUTE);
               if (cp != null) {
                    cpRoot = cp.findOwnerRoot(fo);
                }
            }
            Resource result = null;
            if (cpRoot != null) {
                String resourceName = getResourceName (cpRoot, fo);
                result = getResource(cpRoot, resourceName);
            }
            return result;
        } catch (Exception ex) {
            ErrorManager.getDefault().notify(ex);
        } finally {
            repository.endTrans();
        }
        return null;
    }

    public JavaPackage getPackage(FileObject fo) {
        repository.beginTrans(false);
        try {
            FileObject cpRoot = this.classPath.findOwnerRoot (fo);
            if (cpRoot == null) return null;

            JavaModelPackage extent = getJavaExtent(cpRoot);
            if (extent == null) return null;
            
            String packageName = getResourceName (cpRoot, fo);
            packageName = packageName.replace('/', '.');
            
            return ((JavaPackageClassImpl) extent.getJavaPackage()).findRealPackage(packageName);
        } catch (Exception ex) {
            ErrorManager.getDefault().notify(ex);
        } finally {
            repository.endTrans();
        }
        return null;
    }

    public void registerExtChange(ExternalChange ch) {
        getTransactionMutex().registerExtChange(ch);
    }
    
    public Resource getResource(FileObject cpRoot, String name) {
        repository.beginTrans(false);
        try {
            if (cpRoot != null && cpRoot.isValid()) {
                JavaModelPackage pck=resolveJavaExtent(cpRoot);
                if (pck == null) {
                    return null;
                }
                ResourceClassImpl resClass=(ResourceClassImpl)pck.getResource();
                ResourceImpl result = (ResourceImpl) resClass.resolveResource(name, false);
                
                // ignore classfiles if there are corresponding java files in the same directory
                // i.e. pass resource corresponding to the java file
                if (result == null && name.endsWith(".class")) {
                    String srcName = name.substring(0, name.length() - ".class".length()) + ".java";
                    if (cpRoot.getFileObject(srcName) != null) {
                        name = srcName;
                        result = (ResourceImpl) resClass.resolveResource(name, false);
                    }
                }
                // -----------------------------------
                
                if (result == null) {
                    result = (ResourceImpl) resClass.resolveResource(name, true, false);
                    if (result != null) {
                        DataObject dobj = getDataObject(result);
                        if (dobj != null) {
                            result.updateFromDataObject(dobj, false);
                        } else {
                            result.refDelete();
                            result = null;
                        }
                    }
                }
                return result;
            } else {
                return null;
            }
        } catch (Exception ex) {
            ErrorManager.getDefault().notify(ex);
        } finally {
            repository.endTrans();
        }

        return null;
    }

    public FileObject getFileObject(Resource res) {
        repository.beginTrans(false);
        try {
            if (!res.isValid()) {
                return null;
            }
            FileObject cpRoot = getCPRoot((JavaModelPackage) res.refImmediatePackage());
            if (cpRoot != null) {
                FileObject result = cpRoot.getFileObject(res.getName());
                return result;
            }
            return null;
        } catch (InvalidObjectException e) {
            return null;
        } finally {
            repository.endTrans();
        }
    }

    public synchronized FileObject getCPRoot(JavaModelPackage javaModelPackage) {
        return (FileObject) filesystems.get(javaModelPackage);
    }

    public DataObject getDataObject(Resource res) {
        FileObject fo = getFileObject(res);
        if (fo != null) {
            try {
                return DataObject.find(fo);
            } catch (DataObjectNotFoundException e) {
                ErrorManager.getDefault().notify(e);
                return null;
            }
        }
        return null;
    }

    /**
     * Waits for scanning finish. Goes through all registered path roots and
     * looks for its extent. When there is extent for all path roots,
     * method exits. The method blocks the caller.
     * Currently there isn't any timeout.
     */
    public boolean waitScanFinished() {
        if (NO_SCAN) return false;
        boolean wait=false;
        while (cpImpl.getUnresolvedRoots().length > 0) {
            wait=true;
            try {
                if (!JavaCoreModule.isStartupInProgress() && (ProgressPanel.getVisibleProgressPanel() == null)) {
                    SCANNING_RP.post(new Runnable() {
                        public void run() {
                            refreshCodebases();
                        }
                    });
                }
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        }
        return wait;
    }
    
    // ..........................................................................
    
    public boolean isElementGuarded(Element element) {
        PositionBounds pos = getElementPosition(element);
        if (pos == null) {
            return false;
        }
        RefBaseObject refObj = element;
        while ((refObj instanceof Element) && !(refObj instanceof Resource)) {
            refObj = ((Element)refObj).refImmediateComposite();
        }
        if (!(refObj instanceof Resource)) {
            return false;
        }
        ResourceImpl resource = (ResourceImpl)refObj;
        
        DataObject dobj = JavaMetamodel.getManager().getDataObject(resource);
        EditorSupport editor = (EditorSupport) dobj.getCookie(EditorSupport.class);
        StyledDocument doc = null;
        if (editor != null) {
            doc = editor.getDocument();
            if (doc == null && resource.getParser().isFromDocument()) {
                try {
                    doc = editor.openDocument();
                } catch (IOException ex) {
                    throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(), ex);
                }
            }
        }
        if (doc != null) {
            // [PENDING] reflection
            Class docClass = doc.getClass();
            try {                
                java.lang.reflect.Method refMethod = docClass.getMethod("isPosGuarded", new Class[] {int.class}); // NOI18N
                return ((Boolean)refMethod.invoke(doc, new Object[] {new Integer(pos.getBegin().getOffset())})).booleanValue() ||
                    ((Boolean)refMethod.invoke(doc, new Object[] {new Integer(pos.getEnd().getOffset() - 1)})).booleanValue();
            } catch (NoSuchMethodException e) {
                return false;
            } catch (IllegalAccessException e) {
                throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(e.toString()), e);
            } catch (InvocationTargetException e) {
                throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(e.toString()), e);
            }
        } else {
            MDRParser parser = resource.getParser();
            if (parser.guardedBlocksBorders == null) {
                parser.guardedBlocksBorders = findGuardedBlocks(resource);
            }
            return isGuarded(parser.guardedBlocksBorders, pos.getBegin().getOffset()) ||
                isGuarded(parser.guardedBlocksBorders, pos.getEnd().getOffset());
        }
    }
    
    private static final String GEN_BEGIN = "//GEN-BEGIN"; // NOI18N
    private static final String GEN_END = "//GEN-END"; // NOI18N
    private static final String GEN_HEADER = "//GEN-HEADER"; // NOI18N
    private static final String GEN_HEADEREND = "//GEN-HEADEREND"; // NOI18N
    private static final String GEN_LINE = "//GEN-LINE"; // NOI18N
    private static final String GEN_FIRST = "//GEN-FIRST"; // NOI18N
    private static final String GEN_LAST = "//GEN-LAST"; // NOI18N
    
    private static final int GUARDED_BEGIN = 0;
    private static final int GUARDED_END = 1;
    private static final int GUARDED_LINE = 2;
    private static final int GUARDED_NONE = -1;
    
    private int[] findGuardedBlocks(Resource resource) {
        MDRParser provider = ((ResourceImpl)resource).getParser();
        int lastTokenIndex = 0;
        ASTree tree = provider.getASTree();
        if (tree != null) {
            lastTokenIndex = tree.getLastToken();
        }
        List borders = new LinkedList();
        int lineStart = 0;
        for (int x = 0; x <= lastTokenIndex; x++) {
            Token token = provider.getToken(x);
            Token[] pads = token.getPadding();
            if (pads == null)
                continue;
            for (int y = 0; y < pads.length; y++) {
                int padType = pads[y].getType();
                if (padType == ParserTokens.EOL) {
                    lineStart = pads[y].getEndOffset();
                } else if (padType == ParserTokens.EOL_COMMENT) {
                    String text = provider.getText(pads[y]);
                    int gid = guardedId(text);
                    switch (gid) {
                        case GUARDED_BEGIN:
                            borders.add(new Integer(lineStart));
                            break;
                        case GUARDED_END:
                            borders.add(new Integer(pads[y].getEndOffset()));
                            break;
                        case GUARDED_LINE:
                            borders.add(new Integer(lineStart));
                            borders.add(new Integer(pads[y].getEndOffset()));
                    } // switch
                } // if
            } // for
        } // for
        int[] result = new int[borders.size()];
        Iterator iter = borders.iterator();
        for (int x = 0; iter.hasNext(); x++) {
            result[x] = ((Integer)iter.next()).intValue();
        }
        return result;
    }
    
    private int guardedId(String text) {
        if (text == null)
            return GUARDED_NONE;
        if (text.indexOf(GEN_BEGIN) > -1 || text.indexOf(GEN_HEADER) > -1)
            return GUARDED_BEGIN;
        if (text.indexOf(GEN_END) > -1 || text.indexOf(GEN_HEADEREND) > -1)
            return GUARDED_END;
        if (text.indexOf(GEN_LINE) > -1 || text.indexOf(GEN_FIRST) > -1 || text.indexOf(GEN_LAST) > -1)
            return GUARDED_LINE;
        return GUARDED_NONE;
    }

    private boolean isGuarded(int[] borders, int start, int end) {
        if (borders.length == 0) {
            return false;
        }
        int index = 0;
        if (start < borders[0]) {
            return end < borders[0];
        }
        while (index < borders.length && borders[index] < start)
            index++;
        if (index == borders.length)
            return false;
        if (borders[index] == start)
            return true;
        if (index % 2 == 0) {
            return end < borders[index];
        } else {
            return true;
        }
    }
    
    private boolean isGuarded(int[] borders, int offset) {
        if (borders.length == 0 || offset < borders[0]) {
            return false;
        }
        for (int x = 0; x < borders.length / 2; x++) {
            if (offset >= borders[2*x] && offset <= borders[2*x+1])
                return true;
        }
        return false;
    }
    
    // ..........................................................................
    
    public PositionBounds getElementPosition(Element element) {
        if (element instanceof ParameterizedType)
            element=((ParameterizedType)element).getDefinition();
        else if (element instanceof ParameterizedTypeImpl.Wrapper)
            element=(Element)((ParameterizedTypeImpl.Wrapper)element).getWrappedObject();
        if (element instanceof MetadataElement) {
            return ((MetadataElement) element).getPosition();
        } else {
            return null;
        }
    }

    public PositionBounds getElementPartPosition(Element element, ElementPartKind part, int position) {
        if (element instanceof ParameterizedType)
            element=((ParameterizedType)element).getDefinition();
        else if (element instanceof ParameterizedTypeImpl.Wrapper)
            element=(Element)((ParameterizedTypeImpl.Wrapper)element).getWrappedObject();
        if (element instanceof MetadataElement) {
            return ((MetadataElement) element).getPartPosition(part, position);
        } else {
            return null;
        }
    }

    public Element getElementByOffset(FileObject file,int offset) {
        repository.beginTrans(false);
        try {
            Resource cr = getResource(file);
            if (cr == null) {
                return null;
            }
            ElementFinder finder = new ElementFinder(cr);

            return finder.getElementByOffset(offset);
        } finally {
            repository.endTrans();
        }
    }

    // ---- helper methods --------------------------------------------------------------------
    // THESE METHODS SHOULD NOT BE CALLED FROM OTHER MODULES

    private static String getCBExtentName(String uri) {
        return PREFIX_EXTENT_CODEBASE + uri;
    }
    
    void startResolution() {
        if (SCAN_DEBUG) System.err.println("JMManager: startResolution1");
        RESOLVE_POSTER.post(new Runnable () {
            public void run() {
                if (SCAN_DEBUG) System.err.println("JMManager: startResolution2 - runnable");
                try {
                    resolveCodebases();
                } catch (Throwable e) {
                    ErrorManager.getDefault().notify(e);
                }
                if (SCAN_DEBUG) System.err.println("JMManager: startResolution3 - runnable");
            }
        });
        if (SCAN_DEBUG) System.err.println("JMManager: startResolution4");
    }
    
    /** Resolves all extents referenced from project's classpath.
     * This method has to be called within a writable transaction.
     */
    protected void resolveCodebases() {
        boolean fail = true;
        //fix of #49020
        try {
            repository.beginTrans(true);
            repository.endTrans();
        } catch (Throwable t) {
            ErrorManager.getDefault().notify(t);
        }
        //end of fix
        repository.beginTrans(true);
        try {
            ArrayList resources = new ArrayList(Arrays.asList((Object[]) cpImpl.getUnresolvedRoots()));
            resources.addAll(cpImpl.getResources());
            this.getProgressSupport().fireProgressListenerStart(0,resources.size());
            for (Iterator it = resources.iterator(); it.hasNext();) {
                    this.getProgressSupport().fireProgressListenerStep();
                    PathResourceImplementation resource = (PathResourceImplementation) it.next();
                    URL url = resource.getRoots()[0];
                    FileObject root = URLMapper.findFileObject(url);
                    if (root != null) {
                        resolveCPRoot(root, true);
                    } else {
                        //Todo: Root does not exist physically, needs more handling
                        //Todo: some listenig is needed otherwise will cause invalid nonparseble objects, FileScanner(URL) ???
                        cpImpl.removeRoot(resource);
                    }
            }
            fail = false;
        } catch (RuntimeException e) {
            ErrorManager.getDefault().notify(e);
        } catch (AssertionError e) {
            ErrorManager.getDefault().notify(e);
        } finally {
            this.getProgressSupport().fireProgressListenerStop();
            repository.endTrans(fail);
        }
    }
    
    private void cleanUpCodebases () {
        getDefaultRepository().beginTrans(true);
        boolean fail = true;
        try {
            synchronized (this) {
                Set backup = new HashSet(sids.keySet());
                FileObject[] roots = this.classPath.getRoots();
                for (int i = 0; i < roots.length; i++) {
                    backup.remove(getRootURI(roots[i]));
                }
                PathResourceImplementation unresolved[] = this.cpImpl.getUnresolvedRoots();
                for (int i = 0; i < unresolved.length; i++) {
                    PathResourceImplementation resource = unresolved[i];
                    URL url = resource.getRoots()[0];
                    FileObject root = URLMapper.findFileObject(url);
                    if (root != null) {
                        backup.remove(getRootURI(root));
                    }
                }
                for (Iterator it = backup.iterator(); it.hasNext();) {
                    disposeCodebase((String) it.next());
                }
            }
            fail = false;
        } finally {
            getDefaultRepository().endTrans(fail);
        }
    }

    private void resolveCPRoot(FileObject root, boolean scanIfNeeded) {
        if (root == null) return;
        boolean fail = true;
        if (!scanIfNeeded) getDefaultRepository().beginTrans(true);
        try {
            JavaModelPackage extent;
            int state = 0;
            synchronized (this) {
                String uri = getRootURI(root);
                String extentName = getCBExtentName(uri);
                extent = (JavaModelPackage) repository.getExtent(extentName);
                if (extent == null) {
                    state++;
                    init();
                    String sid = mountStorage(getValidName(uri));
                    sids.put(uri, sid);
                    extent = (JavaModelPackage)repository.getExtent(extentName);
                    if (extent == null) {
                        state += 2;
                        try {
                            NBMDRepositoryImpl nbRep = (NBMDRepositoryImpl) repository;
                            extent = (JavaModelPackage) nbRep.createExtent(extentName, findRootPackage(PACKAGE_CODEBASE), null, sid);
                            extent.getCodebase().createCodebase(uri, 0, false, null);
                            ClassIndex.addIndex(extent, getFileName(getValidName(uri)));
                        } catch (Exception e) {
                            throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(), e);
                        }
                    } else {
                        // walk through all the packages in the extent and add them to the proxy packages
                        ((JavaPackageClassImpl) extent.getJavaPackage()).addAllRealPackages();
                        if (!ClassIndex.loadIndexFrom(getFileName(getValidName(uri)),extent)) {
                            state++;
                        }
                    }
                    extents.put(root, extent);
                    filesystems.put(extent, root);
                }
            }
            if (scanIfNeeded) {
                Resource[] resourcesToParse = null;
                if (state != 0 || extentsToScan.remove(extent)) { // new or mounted codebase
                    resourcesToParse = scanFiles(root, extent, state > 1);
    //                ClassIndex.saveIndex(extent);
                }
                this.cpImpl.classPathRootResolved(root.getURL());
                if (resourcesToParse != null) {
                    ProgressPanel progress = ProgressPanel.getVisibleProgressPanel();
                    if (progress != null) {
                        progress.preBuildData();
                    }
                    for (int i = 0; i < resourcesToParse.length; i++) {
                        setClassPath(resourcesToParse[i]);
                        traverseResource(resourcesToParse[i]);
                        setClassPath(resourcesToParse[i], true);
                        traverseResource(resourcesToParse[i]);
                    }
                }
            } else {
                extentsToScan.add(extent);
            }
            fail = false;
        } catch (FileStateInvalidException e) {
            ErrorManager.getDefault().notify(e);
        } finally {
            if (!scanIfNeeded) getDefaultRepository().endTrans(fail);
        }
    }
    
    private void traverseResource(Resource resource) {
        for (Iterator it = resource.getClassifiers().iterator(); it.hasNext();) {
            traverseClass((JavaClass) it.next());
        }
    }
    
    private void traverseGenericElement(GenericElement element) {
        for (Iterator it = element.getTypeParameters().iterator(); it.hasNext();) {
            ((TypeParameter) it.next()).getSuperClass();
        }
    }
    
    private void traverseCallableFeature(CallableFeature element) {
        for (Iterator it = element.getParameters().iterator(); it.hasNext();) {
            ((Parameter) it.next()).getType();
        }
    }
    
    private void traverseClass(JavaClass cls) {
        traverseGenericElement(cls);
        JavaClass superClass = cls.getSuperClass();
        if (superClass != null) traverseClass(superClass);
        for (Iterator it = cls.getInterfaces().iterator(); it.hasNext();) {
            traverseClass((JavaClass) it.next());
        }
        for (Iterator it = cls.getFeatures().iterator(); it.hasNext();) {
            Object temp = it.next();
            if (temp instanceof JavaClass) {
                traverseClass((JavaClass) temp);
            } else if (temp instanceof GenericElement) {
                traverseGenericElement((GenericElement) temp);
                if (temp instanceof CallableFeature) {
                    traverseCallableFeature((CallableFeature) temp);
                }
            } else if (temp instanceof TypedElement) {
                ((TypedElement) temp).getType();
            }
        }
    }
    
    // always called from within a transaction - no need for explicit transactions
    Resource[] scanFiles(FileObject root, JavaModelPackage extent, boolean isInitial) {
        Codebase cb = (Codebase) extent.getCodebase().refAllOfClass().iterator().next();
        try {
            ProgressPanel panel = ProgressPanel.getVisibleProgressPanel();
            if (panel != null) {
                String name = "".equals(root.getPath()) ? ((JarFileSystem) root.getFileSystem()).getJarFile().getName() : ".../" + root.getParent().getName() + '/' + root.getName(); // NOI18N
                panel.startScanning(isInitial, name);
            }
            URL url = root.getURL();
            if (url.getProtocol().equals("jar")) {    // NOI18N
                url = FileUtil.getArchiveFile(url);
            }
            String uri = null;
            long startT = 0, endT;
            if (PERF_DEBUG) {
                startT = 0;
                startT = System.currentTimeMillis();
                uri = url.toString();
            }
            String sourceLevel = SourceLevelQuery.getSourceLevel(root);
            URI rootUri = new URI(url.toExternalForm());
            Resource[] resources = new FileScanner(new File(rootUri),sourceLevel,cb).scan();
            if (PERF_DEBUG) {
                endT = System.currentTimeMillis();
                System.out.println("PERF: '" + uri + "' scanning takes " + (endT - startT) + " ms."); // NOI18N
            }
            return resources;
        } catch (Exception ex) {
            ErrorManager.getDefault().notify(ex);
        }
        return null;
    }

    private String getSourceLevel(FileObject root) {
        String result = SourceLevelQuery.getSourceLevel(root);
        return result == null ? "" : result;
    }
    
    private String getRootURI(FileObject root) {
        try {
            return root.getURL().toString() + getSourceLevel(root);
        } catch (FileStateInvalidException e) {
            throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(), e);
        }
    }

    /** Unmounts codebase - always called from within a transaction - no need for explicit transaction */
    private synchronized void disposeCodebase(String uri) {
        JavaModelPackage extent = (JavaModelPackage) repository.getExtent(getCBExtentName(uri));
        if (extent != null) {
            ((JavaPackageClassImpl) extent.getJavaPackage()).removeAllRealPackages();
            FileObject root = (FileObject) filesystems.remove(extent);
            ((NBMDRepositoryImpl) repository).unmountStorage((String) sids.remove(uri));
            JavaModelPackage oldExtent = (JavaModelPackage) extents.remove(root);
            if (!extent.equals(oldExtent)) {
                extents.put(root, oldExtent);
            }
            ClassIndex.removeIndex(extent);
        }
//        System.out.println("unmounted: " + uri);
    }

    private static String computeHash(String val) {
        int len = val.length();
        byte[] data=new byte[len*2];
        int offset=0;
        for (int i = 0; i < len; i++) {
            char ch = val.charAt(i);
            data[offset++]=(byte)(ch>>8);
            data[offset++]=(byte)ch;
        }
        byte[] hashCode = digest.digest(data);
        int len2 = hashCode.length;
        StringBuffer sb = new StringBuffer(len2 * 3);
        for (int i = 0; i < len2; i++) {
            String number = Integer.toHexString((int) hashCode[i] - Byte.MIN_VALUE);
            if (number.length() == 1) {
                sb.append('0');
            }
            sb.append(number);
        }
        return sb.toString();
    }

    /** Returns a valid name for a storage file */
    private static String getValidName(String uri) {
        String hashCode = computeHash(uri);
        StringBuffer result = new StringBuffer(uri.length());
        for (int i = 0; i < uri.length(); i++) {
            char z = uri.charAt(i);
            if ((z >= 'A' && z <= 'Z') || (z >= 'a' && z <= 'z') || (z >= '0' && z <= '9')) {
                result.append(z);
            }
        }
        int len = result.length();
        return 'f' + hashCode + (len > 18 ? result.substring(len - 18) : result.toString());
    }


    private synchronized boolean shouldOpenProgress() {
        boolean result = this.progressOpened;
        this.progressOpened = true;
        return !result;
    }

    private synchronized void progressClosed () {
        this.progressOpened = false;
    }

    public void propertyChange(PropertyChangeEvent evt) {
        String propName = evt.getPropertyName();
        if (MergedClassPathImplementation.PROP_UNRESOLVED_ROOTS.equals(propName)) {
            if (JavaCoreModule.isStartupInProgress()) {
                return;
            }
            if (SCAN_DEBUG) System.err.println("JMManager: globalpath changed");
            // refreshCodebases has to be called in a different thread
            // to prevent deadlocks (since refreshCodebases calls isProgressOpened which is synchronized)
            SCANNING_RP.post(new Runnable() {
                public void run() {
                    refreshCodebases();
                }
            });
        }
        else if (ClassPath.PROP_ROOTS.equals(propName)) {
            if (JavaCoreModule.isStartupInProgress()) {
                return;
            }
            if (SCAN_DEBUG) System.err.println("JMManager: posting cleanup");
            CLEANUP_POSTER.post(new Runnable() {
                public void run() {
                    try {
                        if (SCAN_DEBUG) System.err.println("JMManager: cleanup running");
                        cleanUpCodebases();
                    } catch (Throwable e) {
                        ErrorManager.getDefault().notify(e);
                    }
                }
            });
        }
    }

    private void refreshCodebases() {
        if (NO_SCAN) return;
        if (shouldOpenProgress()) {
            if (SCAN_DEBUG) System.err.println("JMManager: refreshCodebases1");
            SwingUtilities.invokeLater(
                    new Runnable () {
                        public void run() {
                            if (cpImpl.getUnresolvedRoots().length > 0) {
                                ProgressPanel.openProgressDialog(JMManager.this);
                            }
                            progressClosed ();
                        }
                    }
            );
        }
        if (SCAN_DEBUG) System.err.println("JMManager: refreshCodebases2");
    }
    
    /**
     * Computes the resource name of file within the classpath root
     * @param cpRoot the classpath root
     * @param file resource
     * @return String, never returns null
     */
    public static String getResourceName (FileObject cpRoot, FileObject file) {
        assert cpRoot != null && file!= null;
        return FileUtil.getRelativePath(cpRoot, file);
    }


    /**
     * Computes the resource name of file within the global merged classpath
     * @param fo resource
     * @return String, the resource name, or null when the fo is not valid resource on merged class path.
     */
    public String getResourceName (FileObject fo) {
        repository.beginTrans(false);
        try {
            FileObject cpRoot = this.classPath.findOwnerRoot (fo);
            if (cpRoot == null) {
                ClassPath cp = ClassPath.getClassPath(fo, ClassPath.SOURCE);
                if (cp!=null) {
                    cpRoot = cp.findOwnerRoot(fo);
                }
            }
            String result = null;
            if (cpRoot != null) {
                result = getResourceName(cpRoot,fo);
            }
            return cpRoot == null ? null : getResourceName(cpRoot, fo);
        } catch (Exception ex) {
            ErrorManager.getDefault().notify(ex);
        } finally {
            repository.endTrans();
        }
        return null;
    }

    void removeListener() {
        classPath.removePropertyChangeListener(this);
    }
    
    public ProgressSupport getProgressSupport() {
        if (progressSupport == null) {
            progressSupport = new ProgressSupport();
        }
        return progressSupport;
    }

    public void addModified(DataObject jdo) {
        getTransactionMutex().addModified(jdo);
    }

    public boolean removeModified(DataObject dobj) {
        return getTransactionMutex().removeModified(dobj);
    }

    public static void fireResourceParsed(final Resource rsc) {
        /** processor dedicated for parser events. */
        EVENT_RP.post(new Runnable() {
            public void run() {
                ArrayList listenersCopy;
                synchronized (listeners) {
                    listenersCopy = (ArrayList) listeners.clone();
                }
                for (Iterator it = listenersCopy.iterator(); it.hasNext();) {
                    ParsingListener listener = (ParsingListener) it.next();
                    listener.resourceParsed(rsc);
                }
            }
        });
    }
    
    public void startupFinished() {
        if (cpImpl.getUnresolvedRoots().length > 0) {
            refreshCodebases();
        } else {
            RequestProcessor.getDefault().post(new Runnable() {
                public void run() {
                    init();
                }
            });
        }
    }

    public ClassDefinition getSourceElementIfExists(ClassDefinition cls) {
        try {
            ClassDefinition realJcls=cls instanceof ParameterizedType ? ((ParameterizedType)cls).getDefinition(): cls;

            if (!(realJcls instanceof UnresolvedClass)) {
                FileObject fo = getCPRoot((JavaModelPackage)realJcls.refImmediatePackage());
                FileObject[] fos = SourceForBinaryQuery.findSourceRoots(fo.getURL()).getRoots();
                String name=realJcls.getName();

                for (int i = 0; i < fos.length; i++) {
                    FileObject fileObject = fos[i];
                    JavaModelPackage javaExtent = getJavaExtent(fileObject);
                    ClassIndex ci = ClassIndex.getIndex(javaExtent);

                    if (ci != null) {
                        JavaClass jc = ci.getClassByFqn(name);

                        if (jc != null && cls instanceof ParameterizedType) {
                            ClassDefinition outerCls = ((ParameterizedType)cls).getDeclaringClass();

                            jc = javaExtent.getParameterizedType().resolveParameterizedType(jc, ((ParameterizedType)cls).getParameters(), outerCls instanceof ParameterizedType ? (ParameterizedType)outerCls : null);
                        }
                        if (jc != null) {
                            return jc;
                        }
                    }
                }
            }
        } catch (Exception e) {
            ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
        }
        return cls;
    }
    
    /**
     * Initializes indentation. Currently, it reads editor options and sets
     * our formatting engine to respect settings. Used for indentation,
     * bracket and parenthesis formatting.
     *
     * (todo #pf): we allow much softer formatting settings, but currently
     * we do not have solved UI.
     */
    public static void initIndentation() {
        Document doc = JEditorPane.createEditorKitForContentType("text/x-java").createDefaultDocument(); // NOI18N
        IndentEngine engine = IndentEngine.find(doc);
        try {
            java.lang.reflect.Method expandTabs = engine.getClass().getMethod("isExpandTabs", null); // NOI18N
            Boolean tabs = (Boolean) expandTabs.invoke(engine, null);
            java.lang.reflect.Method spacesPerTab = engine.getClass().getMethod("getSpacesPerTab", null); // NOI18N
            Integer spaces  = (Integer) spacesPerTab.invoke(engine, null);
            setIndentSpace(spaces.intValue(), tabs.booleanValue());
            java.lang.reflect.Method newlineBeforeBrace = engine.getClass().getMethod("getJavaFormatNewlineBeforeBrace", null); // NO18N
            Boolean newlineBrace = (Boolean) newlineBeforeBrace.invoke(engine, null);
            formatCurlies(newlineBrace.booleanValue());
            java.lang.reflect.Method spaceBeforeParen = engine.getClass().getMethod("getJavaFormatSpaceBeforeParenthesis", null); // NO18N
            Boolean parenthesisSpace = (Boolean) spaceBeforeParen.invoke(engine, null);
            formatParenthesis(parenthesisSpace.booleanValue());
            // we want to listen on changes of options
            engine.addPropertyChangeListener(new PropertyChangeListener() {
                
                // listens on changes of properties which are important for
                // our formatting engine.
                public void propertyChange(PropertyChangeEvent evt) {
                    if ("spacesPerTab".equals(evt.getPropertyName())) { // NOI18N
                        setIndentSpace(((Integer) evt.getNewValue()).intValue(), MetadataElement.isExpandTab());
                    } else if ("expandTabs".equals(evt.getPropertyName())) { // NOI18N
                        setIndentSpace(MetadataElement.getIndentSpace(), ((Boolean) evt.getNewValue()).booleanValue());
                    } else if ("javaFormatNewlineBeforeBrace".equals(evt.getPropertyName())) { // NOI18N
                        formatCurlies(((Boolean) evt.getNewValue()).booleanValue());
                    } else if ("javaFormatSpaceBeforeParenthesis".equals(evt.getPropertyName())) { // NOI18N
                        formatParenthesis(((Boolean) evt.getNewValue()).booleanValue());
                    }
                }
            });
        } catch (Exception e) {
            ErrorManager.getDefault().log(e.getMessage()); // NOI18N
        }
    }
    
    /**
     * Sets the correct formatting for all brackets. If the parameter is true,
     * it sets newline before brace. Otherwise brace is on the same line
     * separated only by the space.
     *
     * @param  breakBefore  use true if you want to have bracket on the new line
     */
    private static void formatCurlies(boolean breakBefore) {
        int[] curlies = {
            MetadataElement.CLASS_OPEN_CURLY,
            MetadataElement.BODY_OPEN_CURLY,
            MetadataElement.ARRAY_OPEN_CURLY,
            MetadataElement.BLOCK_OPEN_CURLY
        };
        String before = breakBefore ? "ni" : "s";
        for (int i = 0; i < curlies.length; i++) {
            MetadataElement.setFormattingFor(curlies[i], before, MetadataElement.getFormattingFor(curlies[i])[2]);
        }
    }

    /**
     * Sets the correct formatting for the parenthesis. When parameter is true,
     * space is added before every opening parenthesis generated by our
     * code-generator. Otherwise, parenthesis is immediately after identifier.
     *
     * @param  parenthesisSpace true if you want space before opening parethesis
     */
    private static void formatParenthesis(boolean parenthesisSpace) {
        int[] parenthesis = { 
            MetadataElement.PAR_OPEN_BRACKET,
        };
        String before = parenthesisSpace ? "s" : "";
        for (int i = 0; i < parenthesis.length; i++) {
            MetadataElement.setFormattingFor(parenthesis[i], before, MetadataElement.getFormattingFor(parenthesis[i])[2]);
        }
    }
    
    /**
     * Sets the default indentation of the element.
     *
     * @param  spaces      number of spaces used for indentation
     * @param  expandTabs  if this parameter is true, tab character is used
     *                     instead of spaces
     */
    public static void setIndentSpace(int spaces, boolean expandTabs) {
        MetadataElement.setIndentSpace(spaces, expandTabs);
    }
}
... 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.