Package org.apache.felix.sigil.eclipse.model.util

Source Code of org.apache.felix.sigil.eclipse.model.util.JavaHelper

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.felix.sigil.eclipse.model.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.regex.Pattern;

import org.apache.felix.sigil.common.config.Resource;
import org.apache.felix.sigil.common.model.IModelElement;
import org.apache.felix.sigil.common.model.IModelWalker;
import org.apache.felix.sigil.common.model.ModelElementFactory;
import org.apache.felix.sigil.common.model.eclipse.ISigilBundle;
import org.apache.felix.sigil.common.model.osgi.IBundleModelElement;
import org.apache.felix.sigil.common.model.osgi.IPackageExport;
import org.apache.felix.sigil.common.model.osgi.IPackageImport;
import org.apache.felix.sigil.common.model.osgi.IRequiredBundle;
import org.apache.felix.sigil.common.osgi.VersionRange;
import org.apache.felix.sigil.common.repository.IRepositoryManager;
import org.apache.felix.sigil.common.repository.IResolution;
import org.apache.felix.sigil.common.repository.ResolutionConfig;
import org.apache.felix.sigil.common.repository.ResolutionException;
import org.apache.felix.sigil.eclipse.PathUtil;
import org.apache.felix.sigil.eclipse.SigilCore;
import org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel;
import org.apache.felix.sigil.eclipse.progress.ProgressAdapter;
import org.apache.felix.sigil.eclipse.repository.ResolutionMonitorAdapter;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.osgi.framework.Version;

/**
* @author dave
*
*/
public class JavaHelper
{

    public static final IAccessRule DENY_RULE = JavaCore.newAccessRule(new Path("**"),
        IAccessRule.K_NON_ACCESSIBLE | IAccessRule.IGNORE_IF_BETTER);

    public static final IAccessRule ALLOW_ALL_RULE = JavaCore.newAccessRule(
        new Path("**"), IAccessRule.K_ACCESSIBLE);

    private static Map<String, Collection<IClasspathEntry>> entryCache = new HashMap<String, Collection<IClasspathEntry>>();

    public static Collection<IClasspathEntry> findClasspathEntries(ISigilBundle bundle)
    {
        LinkedList<IClasspathEntry> cp = new LinkedList<IClasspathEntry>();

        ISigilProjectModel sp = bundle.getAncestor(ISigilProjectModel.class);

        if (sp != null)
        {
            IJavaProject p = sp.getJavaModel();

            for (String enc : bundle.getClasspathEntrys())
            {
                IClasspathEntry e = p.decodeClasspathEntry(enc);
                if (e != null)
                {
                    cp.add(e);
                }
            }
        }

        return cp;
    }

    public static Collection<ICompilationUnit> findCompilationUnits(
        ISigilProjectModel project) throws JavaModelException
    {
        LinkedList<ICompilationUnit> ret = new LinkedList<ICompilationUnit>();

        IJavaProject java = project.getJavaModel();
        for (IClasspathEntry cp : findClasspathEntries(project.getBundle()))
        {
            IPackageFragmentRoot[] roots = java.findPackageFragmentRoots(cp);
            for (IPackageFragmentRoot rt : roots)
            {
                for (IJavaElement j : rt.getChildren())
                {
                    IPackageFragment p = (IPackageFragment) j;
                    ICompilationUnit[] units = p.getCompilationUnits();
                    for (ICompilationUnit u : units)
                    {
                        ret.add(u);
                    }
                }
            }
        }

        return ret;
    }

    /**
     * @param project
     * @param packageName
     * @return
     */
    public static Collection<IPackageExport> findExportsForPackage(
        ISigilProjectModel project, final String packageName)
    {
        final LinkedList<IPackageExport> results = new LinkedList<IPackageExport>();

        project.getRepositoryManager().visit(new IModelWalker()
        {
            public boolean visit(IModelElement element)
            {
                if (element instanceof IPackageExport)
                {
                    IPackageExport e = (IPackageExport) element;
                    if (e.getPackageName().equals(packageName))
                    {
                        results.add(e);
                    }
                }
                return true;
            }
        });

        return results;
    }

    public static String[] findUses(String packageName, ISigilProjectModel projectModel)
        throws CoreException
    {
        ArrayList<String> uses = new ArrayList<String>();

        for (final String dependency : findPackageDependencies(packageName, projectModel))
        {
            if (!dependency.equals(packageName))
            {
                final boolean[] found = new boolean[1];

                projectModel.visit(new IModelWalker()
                {

                    public boolean visit(IModelElement element)
                    {
                        if (element instanceof IPackageImport)
                        {
                            IPackageImport pi = (IPackageImport) element;
                            if (pi.getPackageName().equals(dependency))
                            {
                                found[0] = true;
                            }
                        }
                        return !found[0];
                    }
                });

                if (found[0])
                {
                    uses.add(dependency);
                }
            }
        }

        return uses.toArray(new String[uses.size()]);
    }

    private static String[] findPackageDependencies(String packageName,
        ISigilProjectModel projectModel) throws CoreException
    {
        HashSet<String> imports = new HashSet<String>();

        IPackageFragment p = (IPackageFragment) projectModel.getJavaModel().findElement(
            new Path(packageName.replace('.', '/')));

        if (p == null)
        {
            throw SigilCore.newCoreException("Unknown package " + packageName, null);
        }
        for (ICompilationUnit cu : p.getCompilationUnits())
        {
            scanImports(cu, imports);
        }
        for (IClassFile cf : p.getClassFiles())
        {
            scanImports(cf, imports);
        }

        return imports.toArray(new String[imports.size()]);
    }

    /**
     * @param project
     * @param monitor
     * @return
     */
    public static List<IPackageImport> findRequiredImports(ISigilProjectModel project,
        IProgressMonitor monitor)
    {
        LinkedList<IPackageImport> imports = new LinkedList<IPackageImport>();
        Set<String> names = findJavaImports(project, monitor);
        if ( !monitor.isCanceled() ) {
            for (String packageName : names)
            {
                if (!ProfileManager.isBootDelegate(project, packageName))
                { // these must come from boot classloader
                    try
                    {
                        if (!project.isInClasspath(packageName, monitor))
                        {
                            Collection<IPackageExport> exports = findExportsForPackage(
                                project, packageName);
                            if (!exports.isEmpty())
                            {
                                imports.add(select(exports));
                            }
                        }
                    }
                    catch (CoreException e)
                    {
                        SigilCore.error("Failed to check classpath", e);
                    }
                }
            }
        }
        return imports;
    }

    /**
     * @param project
     * @param monitor
     * @return
     */
    public static Collection<IModelElement> findUnusedReferences(
        final ISigilProjectModel project, final IProgressMonitor monitor)
    {
        final LinkedList<IModelElement> unused = new LinkedList<IModelElement>();

        final Set<String> packages = findJavaImports(project, monitor);

        if (!monitor.isCanceled()) {
            project.visit(new IModelWalker()
            {
                public boolean visit(IModelElement element)
                {
                    if (element instanceof IPackageImport)
                    {
                        IPackageImport pi = (IPackageImport) element;
                        if (!packages.contains(pi.getPackageName()))
                        {
                            unused.add(pi);
                        }
                    }
                    else if (element instanceof IRequiredBundle)
                    {
                        IRequiredBundle rb = (IRequiredBundle) element;
                        IRepositoryManager manager = project.getRepositoryManager();
                        ResolutionConfig config = new ResolutionConfig(
                            ResolutionConfig.INCLUDE_OPTIONAL
                                | ResolutionConfig.IGNORE_ERRORS);
                        try
                        {
                            IResolution r = manager.getBundleResolver().resolve(rb, config,
                                new ResolutionMonitorAdapter(monitor));
                            ISigilBundle bundle = r.getProvider(rb);
                            boolean found = false;
                            for (IPackageExport pe : bundle.getBundleInfo().getExports())
                            {
                                if (packages.contains(pe.getPackageName()))
                                {
                                    found = true;
                                    break;
                                }
                            }

                            if (!found)
                            {
                                unused.add(rb);
                            }
                        }
                        catch (ResolutionException e)
                        {
                            SigilCore.error("Failed to resolve " + rb, e);
                        }
                    }
                    return !monitor.isCanceled();
                }
            });
        }

        return unused;
    }

    public static Collection<IClasspathEntry> resolveClasspathEntrys(
        ISigilProjectModel sigil, IProgressMonitor monitor) throws CoreException
    {
        if (monitor == null)
        {
            monitor = Job.getJobManager().createProgressGroup();
            monitor.beginTask("Resolving classpath for " + sigil.getSymbolicName(), 2);
        }

        ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();

        ResolutionConfig config = new ResolutionConfig(ResolutionConfig.INCLUDE_OPTIONAL
            | ResolutionConfig.IGNORE_ERRORS | ResolutionConfig.INDEXED_ONLY
            | ResolutionConfig.LOCAL_ONLY | ResolutionConfig.COMPILE_TIME);

        IResolution resolution;
        try
        {
            resolution = sigil.getRepositoryManager().getBundleResolver().resolve(
                sigil, config, new ResolutionMonitorAdapter(monitor));
        }
        catch (ResolutionException e)
        {
            throw SigilCore.newCoreException("Failed to resolve dependencies", e);
        }

        monitor.worked(1);

        Set<ISigilBundle> bundles = resolution.getBundles();
        for (ISigilBundle bundle : bundles)
        {
            if (!sigil.getSymbolicName().equals(bundle.getBundleInfo().getSymbolicName()))
            { // discard self reference...
                List<IModelElement> matched = resolution.getMatchedRequirements(bundle);
                for (IClasspathEntry cpe : buildClassPathEntry(sigil, bundle, bundles,
                    matched, monitor))
                {
                    entries.add(cpe);
                }
            }
        }

        Collections.sort(entries, new Comparator<IClasspathEntry>()
        {
            public int compare(IClasspathEntry o1, IClasspathEntry o2)
            {
                return o1.toString().compareTo(o2.toString());
            }
        });

        monitor.worked(1);
        monitor.done();

        return entries;
    }

    private static Collection<IClasspathEntry> buildClassPathEntry(
        ISigilProjectModel project, ISigilBundle provider, Set<ISigilBundle> all,
        List<IModelElement> requirements, IProgressMonitor monitor) throws CoreException
    {
        IAccessRule[] rules = buildAccessRules(project, provider, all, requirements);
        IClasspathAttribute[] attrs = new IClasspathAttribute[0];

        ISigilProjectModel other = provider.getAncestor(ISigilProjectModel.class);

        try
        {
            if (other == null)
            {
                provider.synchronize(new ProgressAdapter(monitor));
                return newBundleEntry(provider, rules, attrs, false);
            }
            else
            {
                return newProjectEntry(other, rules, attrs, false);
            }
        }
        catch (IOException e)
        {
            throw SigilCore.newCoreException("Failed to synchronize " + provider, e);
        }
    }

    private static Collection<IClasspathEntry> newProjectEntry(ISigilProjectModel n,
        IAccessRule[] rules, IClasspathAttribute[] attributes, boolean export)
        throws CoreException
    {
        ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
        entries.add(JavaCore.newProjectEntry(n.getProject().getFullPath(), rules, false,
            attributes, export));
        for (IClasspathEntry e : n.getJavaModel().getRawClasspath())
        {
            String encoded = n.getJavaModel().encodeClasspathEntry(e);
            if ( n.getBundle().getClasspathEntrys().contains(encoded) ) {
                switch (e.getEntryKind())
                {
                    case IClasspathEntry.CPE_LIBRARY:
                        entries.add(JavaCore.newLibraryEntry(e.getPath(),
                            e.getSourceAttachmentPath(), e.getSourceAttachmentRootPath(),
                            rules, attributes, export));
                        break;
                    case IClasspathEntry.CPE_VARIABLE:
                        IPath path = JavaCore.getResolvedVariablePath(e.getPath());
                        if (path != null)
                        {
                            IPath spath = e.getSourceAttachmentPath();
                            if (spath != null) {
                                spath = JavaCore.getResolvedVariablePath(spath);
                            }
                           
                            entries.add(JavaCore.newLibraryEntry(path,
                                spath, e.getSourceAttachmentRootPath(),
                                rules, attributes, export));
                        }
                        break;
                }
            }
        }

        return entries;
    }

    private static Collection<IClasspathEntry> newBundleEntry(ISigilBundle bundle,
        IAccessRule[] rules, IClasspathAttribute[] attributes, boolean exported)
        throws CoreException
    {
        String name = bundle.getBundleInfo().getSymbolicName();

        if (bundle.getBundleInfo().getVersion() != null)
        {
            name += "_version_" + bundle.getBundleInfo().getVersion();
        }

        String cacheName = name + rules.toString();

        Collection<IClasspathEntry> entries = null;

        synchronized (entryCache)
        {
            entries = entryCache.get(cacheName);

            if (entries == null)
            {
                IPath path = PathUtil.newPathIfExists(bundle.getLocation());

                if (path == null)
                {
                    SigilCore.error("Found null path for " + bundle.getSymbolicName());
                    entries = Collections.emptyList();
                }
                else
                {
                    entries = buildEntries(path, name, bundle, rules, attributes,
                        exported);
                }

                entryCache.put(cacheName, entries);
            }
        }

        return entries;
    }

    private static IPath bundleCache = SigilCore.getDefault().getStateLocation().append(
        "bundle-cache");

    public static boolean isCachedBundle(String bp)
    {
        return bp.startsWith(bundleCache.toOSString());
    }

    private static Collection<IClasspathEntry> buildEntries(IPath path, String name,
        ISigilBundle bundle, IAccessRule[] rules, IClasspathAttribute[] attributes,
        boolean exported) throws CoreException
    {
        if (path.toFile().isDirectory())
        {
            throw SigilCore.newCoreException("Bundle location cannot be a directory",
                null);
        }
        else
        {
            // ok it's a jar could contain libs etc
            try
            {
                IPath cache = bundleCache.append(name);
                Collection<String> classpath = bundle.getBundleInfo().getClasspaths();
                ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(
                    classpath.size());
                IPath source = PathUtil.newPathIfExists(bundle.getSourcePathLocation());
                IPath rootPath = PathUtil.newPathIfNotNull(bundle.getSourceRootPath());

                if (source != null && !source.toFile().exists())
                {
                    source = null;
                }

                if (!classpath.isEmpty())
                {
                    unpack(cache, bundle, classpath);
                    for (String cp : classpath)
                    {
                        IPath p = ".".equals(cp) ? path : cache.append(cp);
                        if (p.toFile().exists())
                        {
                            IClasspathEntry e = JavaCore.newLibraryEntry(p, source,
                                rootPath, rules, attributes, exported);
                            entries.add(e);
                        }
                    }
                }
                else
                { // default classpath is .
                    IClasspathEntry e = JavaCore.newLibraryEntry(path, source, rootPath,
                        rules, attributes, exported);
                    entries.add(e);
                }
                return entries;
            }
            catch (IOException e)
            {
                throw SigilCore.newCoreException("Failed to unpack bundle", e);
            }
        }
    }

    private static HashMap<IPath, Collection<String>> unpacked = new HashMap<IPath, Collection<String>>();

    private static synchronized void unpack(IPath cache, ISigilBundle bundle,
        Collection<String> classpath) throws IOException
    {
        Collection<String> check = unpacked.get(cache);

        if (check == null || !check.equals(classpath))
        {
            if (classpath.size() == 1 && classpath.contains("."))
            {
                unpacked.put(cache, classpath);
            }
            else
            {
                // trim . from path to avoid check later in inClasspath
                check = new HashSet<String>(classpath);
                check.remove(".");

                File dir = createEmptyDir(cache);
                FileInputStream fin = null;
                try
                {
                    fin = new FileInputStream(bundle.getLocation());
                    JarInputStream in = new JarInputStream(fin);
                    JarEntry entry;
                    while ((entry = in.getNextJarEntry()) != null)
                    {
                        if (inClasspath(check, entry))
                        {
                            File f = new File(dir, entry.getName());
                            if (entry.isDirectory())
                            {
                                createDir(f);
                            }
                            else
                            {
                                try
                                {
                                    File p = f.getParentFile();
                                    createDir(p);
                                    streamTo(in, f);
                                }
                                catch (RuntimeException e)
                                {
                                    SigilCore.error("Failed to unpack " + entry, e);
                                }
                            }
                        }
                    }
                    unpacked.put(cache, classpath);
                }
                finally
                {
                    if (fin != null)
                    {
                        fin.close();
                    }
                }
            }
        }
    }

    /**
     * @param classpath
     * @param entry
     * @return
     */
    private static boolean inClasspath(Collection<String> classpath, JarEntry entry)
    {
        for (String s : classpath)
        {
            if (entry.getName().startsWith(s))
            {
                return true;
            }
        }
        return false;
    }

    private static void createDir(File p) throws IOException
    {
        if (!p.exists())
        {
            if (!p.mkdirs())
                throw new IOException("Failed to create directory " + p);
        }
    }

    private static void streamTo(InputStream in, File f) throws IOException
    {
        FileOutputStream fos = new FileOutputStream(f);
        try
        {
            byte[] buf = new byte[1024];
            for (;;)
            {
                int r = in.read(buf);

                if (r == -1)
                    break;

                fos.write(buf, 0, r);
            }

            fos.flush();
        }
        finally
        {
            try
            {
                fos.close();
            }
            catch (IOException e)
            {
                SigilCore.error("Failed to close stream", e);
            }
        }
    }

    private static File createEmptyDir(IPath cache)
    {
        File dir = cache.toFile();
        if (dir.exists())
        {
            deleteAll(dir);
        }

        dir.mkdirs();
        return dir;
    }

    private static void deleteAll(File file)
    {
        File[] sub = file.listFiles();
        if (sub != null)
        {
            for (File f : sub)
            {
                deleteAll(f);
            }
        }
        file.delete();
    }

    private static IAccessRule[] buildAccessRules(ISigilProjectModel project,
        ISigilBundle bundle, Set<ISigilBundle> all, List<IModelElement> requirements)
        throws JavaModelException
    {
        ArrayList<IAccessRule> rules = new ArrayList<IAccessRule>();

        for (IModelElement e : requirements)
        {
            if (e instanceof IRequiredBundle)
            {
                IRequiredBundle host = project.getBundle().getBundleInfo().getFragmentHost();
                if (host != null)
                {
                    if (host.equals(e))
                    {
                        return new IAccessRule[] { ALLOW_ALL_RULE };
                    }
                    else
                    {
                        return buildExportRules(bundle, all, requirements);
                    }
                }
                else
                {
                    return buildExportRules(bundle, all, requirements);
                }
            }
            else if (e instanceof IPackageImport)
            {
                IPackageImport pi = (IPackageImport) e;
                String pckg = pi.getPackageName();
                HashSet<String> pckgs = new HashSet<String>();
                pckgs.add(pckg);
                //findIndirectReferences(pckgs, pckg, project.getJavaModel(), project.getJavaModel());

                for (String p : pckgs)
                {
                    rules.add(newPackageAccess(p));
                }
            }
        }

        rules.add(DENY_RULE);

        return rules.toArray(new IAccessRule[rules.size()]);
    }

    private static IAccessRule[] buildExportRules(ISigilBundle bundle,
        Set<ISigilBundle> all, List<IModelElement> requirements)
    {
        Set<IPackageExport> ex = mergeExports(bundle, all, requirements);

        IAccessRule[] rules = new IAccessRule[ex.size() + 1];

        Iterator<IPackageExport> iter = ex.iterator();
        for (int i = 0; i < rules.length - 1; i++)
        {
            IPackageExport p = iter.next();
            rules[i] = JavaCore.newAccessRule(new Path(p.getPackageName().replace('.',
                '/')).append("*"), IAccessRule.K_ACCESSIBLE);
        }

        rules[rules.length - 1] = DENY_RULE;

        return rules;
    }

    private static Set<IPackageExport> mergeExports(ISigilBundle bundle,
        Set<ISigilBundle> all, List<IModelElement> requirements)
    {
        IBundleModelElement headers = bundle.getBundleInfo();
        // FIXME treeset as PackageExport does not implement equals/hashCode
        TreeSet<IPackageExport> exports = new TreeSet<IPackageExport>(
            headers.getExports());
        IRequiredBundle host = headers.getFragmentHost();
        if (host != null)
        {
            for (ISigilBundle b : all)
            {
                if (host.accepts(b.getBundleCapability()))
                {
                    exports.addAll(b.getBundleInfo().getExports());
                    break;
                }
            }
        }
        return exports;
    }

    /*
     * Searches for C (and D, E, etc) in case:
     * A extends B extends C where A, B and C are in different packages and A is in this bundle
     * and B and C are in one or more external bundles
     */
    private static void findIndirectReferences(Set<String> indirect, String pckg,
        IParent parent, IJavaProject p) throws JavaModelException
    {
        for (IJavaElement e : parent.getChildren())
        {
            boolean skip = false;
            switch (e.getElementType())
            {
                case IJavaElement.PACKAGE_FRAGMENT_ROOT:
                    IPackageFragmentRoot rt = (IPackageFragmentRoot) e;
                    IClasspathEntry ce = rt.getRawClasspathEntry();
                    IPath path = ce.getPath();
                    skip = "org.eclipse.jdt.launching.JRE_CONTAINER".equals(path.toString());
                    break;
                case IJavaElement.CLASS_FILE:
                    IClassFile cf = (IClassFile) e;
                    if (cf.getElementName().startsWith(pckg))
                    {
                        findIndirectReferences(indirect,
                            findPackage(cf.getType().getSuperclassName()), p, p);
                    }
                    break;
                case IJavaElement.COMPILATION_UNIT:
                    ICompilationUnit cu = (ICompilationUnit) e;
                    break;
            }

            if (!skip && e instanceof IParent)
            {
                IParent newParent = (IParent) e;
                findIndirectReferences(indirect, pckg, newParent, p);
            }
        }
    }

    private static IAccessRule newPackageAccess(String packageName)
    {
        return JavaCore.newAccessRule(
            new Path(packageName.replace('.', '/')).append("*"), IAccessRule.K_ACCESSIBLE);
    }

    private static Set<String> findJavaImports(ISigilProjectModel project,
        IProgressMonitor monitor)
    {
        Set<String> imports = new HashSet<String>();

        findJavaModelImports(project, imports, monitor);
        if ( !monitor.isCanceled() ) {
            findTextImports(project, imports, monitor);
        }

        return imports;
    }

    private static void findTextImports(ISigilProjectModel project, Set<String> imports,
        IProgressMonitor monitor)
    {
        IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
        IContentType txt = contentTypeManager.getContentType("org.eclipse.core.runtime.text");
        for (Resource p : project.getBundle().getSourcePaths())
        {
            IFile f = project.getProject().getFile(p.getLocalFile());
            if (f.exists())
            {
                try
                {
                    IContentDescription desc = f.getContentDescription();
                    if (desc != null)
                    {
                        IContentType type = desc.getContentType();
                        if (type != null)
                        {
                            if (type.isKindOf(txt))
                            {
                                parseText(f, imports);
                            }
                        }
                    }
                }
                catch (CoreException e)
                {
                    SigilCore.error("Failed to parse text file " + f, e);
                }
            }
        }
    }

    private static void findJavaModelImports(ISigilProjectModel project,
        Set<String> imports, IProgressMonitor monitor)
    {
        try
        {
            for (IPackageFragment root : project.getJavaModel().getPackageFragments())
            {
                if ( monitor.isCanceled() ) {
                    return;
                }
               
                IPackageFragmentRoot rt = (IPackageFragmentRoot) root.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);

                if (isInClassPath(project, rt))
                {
                    for (ICompilationUnit cu : root.getCompilationUnits())
                    {
                        scanImports(cu, imports);
                    }

                    for (IClassFile cf : root.getClassFiles())
                    {
                        scanImports(cf, imports);
                    }
                }
            }
        }
        catch (JavaModelException e)
        {
            SigilCore.error("Failed to parse java model", e);
        }
    }

    // matches word.word.word.word.Word
    private static final Pattern JAVA_CLASS_PATTERN = Pattern.compile("((\\w*\\.\\w*)+?)\\.[A-Z]\\w*");

    private static void parseText(IFile f, Set<String> imports) throws CoreException
    {
        for (String result : Grep.grep(JAVA_CLASS_PATTERN, f))
        {
            findImport(result, imports);
        }
    }

    private static boolean isInClassPath(ISigilProjectModel project,
        IPackageFragmentRoot rt) throws JavaModelException
    {
        String path = encode(project, rt.getRawClasspathEntry());
        return project.getBundle().getClasspathEntrys().contains(path);
    }

    private static String encode(ISigilProjectModel project, IClasspathEntry cp)
    {
        return project.getJavaModel().encodeClasspathEntry(cp).trim();
    }

    private static void scanImports(IParent parent, Set<String> imports)
        throws JavaModelException
    {
        for (IJavaElement e : parent.getChildren())
        {
            switch (e.getElementType())
            {
                case IJavaElement.TYPE:
                    handleType((IType) e, imports);
                    break;
                case IJavaElement.IMPORT_DECLARATION:
                    handleImport((IImportDeclaration) e, imports);
                    break;
                case IJavaElement.FIELD:
                    handleField((IField) e, imports);
                    break;
                case IJavaElement.LOCAL_VARIABLE:
                    handleLocalVariable((ILocalVariable) e, imports);
                    break;
                case IJavaElement.ANNOTATION:
                    handleAnnotation((IAnnotation) e, imports);
                    break;
                case IJavaElement.METHOD:
                    handleMethod((IMethod) e, imports);
                    break;
                default:
                    // no action
                    break;
            }

            if (e instanceof IParent)
            {
                scanImports((IParent) e, imports);
            }
        }
    }

    private static void handleType(IType e, Set<String> imports)
        throws JavaModelException
    {
        findImportFromType(e.getSuperclassTypeSignature(), imports);
        for (String sig : e.getSuperInterfaceTypeSignatures())
        {
            findImportFromType(sig, imports);
        }
        //findImportsForSuperTypes(e, imports);
    }

    /*private static void findImportsForSuperTypes(IType e, Set<String> imports) throws JavaModelException {
      IJavaProject project = (IJavaProject) e.getAncestor(IJavaModel.JAVA_PROJECT);
      LinkedList<String> types = new LinkedList<String>();
      types.add( decodeSignature(e.getSuperclassTypeSignature()) );
      for ( String sig : e.getSuperInterfaceTypeSignatures() ) {
        types.add( decodeSignature(sig) );
      }
     
      for ( IPackageFragmentRoot root : project.getPackageFragmentRoots() ) {
        // only need to search binary files for inheritance as source will automatically be searched
        if ( root.getKind() == IPackageFragmentRoot.K_BINARY ) {
          for ( String t : types ) {
            String pac = findPackage(t);
            if ( pac != null ) {
              IPackageFragment fragment = root.getPackageFragment(pac);
              if ( fragment != null ) {
                IClassFile c = fragment.getClassFile(findClass(t));
                if ( c != null ) {
                  findImportsForSuperTypes(c.getType(), imports);
                }
              }
            }
          }
        }
      }
    } */

    private static void handleMethod(IMethod e, Set<String> imports)
        throws JavaModelException
    {
        findImportFromType(e.getReturnType(), imports);

        for (String param : e.getParameterTypes())
        {
            findImportFromType(param, imports);
        }

        for (String ex : e.getExceptionTypes())
        {
            findImportFromType(ex, imports);
        }
    }

    private static void handleAnnotation(IAnnotation e, Set<String> imports)
    {
        findImport(e.getElementName(), imports);
    }

    private static void handleLocalVariable(ILocalVariable e, Set<String> imports)
    {
        findImportFromType(e.getTypeSignature(), imports);
    }

    private static void handleField(IField e, Set<String> imports)
        throws IllegalArgumentException, JavaModelException
    {
        findImportFromType(Signature.getElementType(e.getTypeSignature()), imports);
    }

    private static void handleImport(IImportDeclaration id, Set<String> imports)
    {
        findImport(id.getElementName(), imports);
    }

    private static void findImportFromType(String type, Set<String> imports)
    {
        String element = decodeSignature(type);
        findImport(element, imports);
    }

    private static String decodeSignature(String type)
    {
        return decodeSignature(type, false);
    }

    private static String decodeSignature(String type, boolean resolve)
    {
        if (type == null)
        {
            return null;
        }

        if (type.length() > 0)
        {
            switch (type.charAt(0))
            {
                case Signature.C_ARRAY:
                    return decodeSignature(type.substring(1));
                case Signature.C_UNRESOLVED:
                    return resolve ? resolve(type.substring(1, type.length() - 1)) : null;
                case Signature.C_RESOLVED:
                    return type.substring(1);
            }
        }
        return type;
    }

    private static String resolve(String substring)
    {
        // TODO Auto-generated method stub
        return null;
    }

    private static void findImport(String clazz, Set<String> imports)
    {
        String packageName = findPackage(clazz);
        if (packageName != null)
        {
            imports.add(packageName);
        }
    }

    private static String findPackage(String clazz)
    {
        if (clazz == null)
            return null;
        int pos = clazz.lastIndexOf('.');
        return pos == -1 ? null : clazz.substring(0, pos);
    }

    private static String findClass(String clazz)
    {
        if (clazz == null)
            return null;
        int pos = clazz.lastIndexOf('.');
        return pos == -1 ? null : clazz.substring(pos + 1);
    }

    private static IPackageImport select(Collection<IPackageExport> proposals)
    {
        IPackageExport pe = null;

        for (IPackageExport check : proposals)
        {
            if (pe == null || check.getVersion().compareTo(pe.getVersion()) > 0)
            {
                pe = check;
            }
        }

        String packageName = pe.getPackageName();

        Version version = pe.getVersion();
        VersionRange versions = ModelHelper.getDefaultRange(version);

        IPackageImport pi = ModelElementFactory.getInstance().newModelElement(
            IPackageImport.class);
        pi.setPackageName(packageName);
        pi.setVersions(versions);

        return pi;
    }

    public static Iterable<IJavaElement> findTypes(IParent parent, int... type)
        throws JavaModelException
    {
        LinkedList<IJavaElement> found = new LinkedList<IJavaElement>();
        scanForElement(parent, type, found, false);
        return found;
    }

    public static IJavaElement findType(IParent parent, int... type)
        throws JavaModelException
    {
        LinkedList<IJavaElement> found = new LinkedList<IJavaElement>();
        scanForElement(parent, type, found, true);
        return found.isEmpty() ? null : found.getFirst();
    }

    private static void scanForElement(IParent parent, int[] type,
        List<IJavaElement> roots, boolean thereCanBeOnlyOne) throws JavaModelException
    {
        for (IJavaElement e : parent.getChildren())
        {
            if (isType(type, e))
            {
                roots.add(e);
                if (thereCanBeOnlyOne)
                {
                    break;
                }
            }
            else if (e instanceof IParent)
            {
                scanForElement((IParent) e, type, roots, thereCanBeOnlyOne);
            }
        }
    }

    private static boolean isType(int[] type, IJavaElement e)
    {
        for (int i : type)
        {
            if (i == e.getElementType())
            {
                return true;
            }
        }
        return false;
    }

    public static boolean isAssignableTo(String ifaceOrParentClass, IType type)
        throws JavaModelException
    {
        if (ifaceOrParentClass == null)
            return true;

        ITypeHierarchy h = type.newSupertypeHierarchy(null);

        for (IType superType : h.getAllClasses())
        {
            String name = superType.getFullyQualifiedName();
            if (name.equals(ifaceOrParentClass))
            {
                return true;
            }
        }
        for (IType ifaceType : h.getAllInterfaces())
        {
            String name = ifaceType.getFullyQualifiedName();
            if (name.equals(ifaceOrParentClass))
            {
                return true;
            }
        }

        return false;
    }

    private static IType findType(ITypeRoot root) throws JavaModelException
    {
        // TODO Auto-generated method stub
        for (IJavaElement child : root.getChildren())
        {
            if (child.getElementType() == IJavaElement.TYPE)
            {
                return (IType) child;
            }
        }

        throw new JavaModelException(
            new IllegalStateException("Missing type for " + root), IStatus.ERROR);
    }

    public static Set<String> findLocalPackageDependencies(ISigilProjectModel project,
        String packageName, IProgressMonitor monitor) throws JavaModelException
    {
        Set<String> imports = findJavaImports(project, monitor);
        imports.remove(packageName);
        return imports;
    }

    public static Set<String> findLocalPackageUsers(ISigilProjectModel project,
        String packageName, IProgressMonitor monitor) throws JavaModelException
    {
        Set<String> imports = new HashSet<String>();
        Set<String> check = new HashSet<String>();
        for (IPackageFragment root : project.getJavaModel().getPackageFragments())
        {
            IPackageFragmentRoot rt = (IPackageFragmentRoot) root.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);

            if (isInClassPath(project, rt))
            {
                for (ICompilationUnit cu : root.getCompilationUnits())
                {
                    IPackageFragment pack = (IPackageFragment) cu.getAncestor(IJavaModel.PACKAGE_FRAGMENT);
                    if (!pack.getElementName().equals(packageName))
                    {
                        scanImports(cu, check);
                        if (check.contains(packageName))
                        {
                            imports.add(pack.getElementName());
                        }
                    }
                    check.clear();
                }

                for (IClassFile cf : root.getClassFiles())
                {
                    IPackageFragment pack = (IPackageFragment) cf.getAncestor(IJavaModel.PACKAGE_FRAGMENT);
                    if (!pack.getElementName().equals(packageName))
                    {
                        scanImports(cf, check);
                        if (check.contains(packageName))
                        {
                            imports.add(pack.getElementName());
                        }
                    }
                    check.clear();
                }
            }
        }

        return imports;
    }
}
TOP

Related Classes of org.apache.felix.sigil.eclipse.model.util.JavaHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.