Package org.apache.felix.sigil.eclipse.internal.model.project

Source Code of org.apache.felix.sigil.eclipse.internal.model.project.SigilProject

/*
* 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.internal.model.project;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;

import org.apache.felix.sigil.common.config.BldFactory;
import org.apache.felix.sigil.common.config.IBldProject;
import org.apache.felix.sigil.common.config.IRepositoryConfig;
import org.apache.felix.sigil.common.model.AbstractCompoundModelElement;
import org.apache.felix.sigil.common.model.ICapabilityModelElement;
import org.apache.felix.sigil.common.model.IModelElement;
import org.apache.felix.sigil.common.model.IModelWalker;
import org.apache.felix.sigil.common.model.IRequirementModelElement;
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.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.job.ThreadProgressMonitor;
import org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel;
import org.apache.felix.sigil.eclipse.model.util.JavaHelper;
import org.apache.felix.sigil.eclipse.progress.ProgressAdapter;
import org.apache.felix.sigil.eclipse.repository.ResolutionMonitorAdapter;
import org.apache.felix.sigil.utils.GlobCompiler;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.ClasspathContainerInitializer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.osgi.framework.Version;

/**
* @author dave
*
*/
public class SigilProject extends AbstractCompoundModelElement implements ISigilProjectModel
{

    private static final long serialVersionUID = 1L;

    private IFile bldProjectFile;
    private IProject project;
    private IBldProject bldProject;

    private ISigilBundle bundle;

    private List<IRequirementModelElement> lastReqs = new LinkedList<IRequirementModelElement>();
    private List<ICapabilityModelElement> lastCaps = new LinkedList<ICapabilityModelElement>();

    public SigilProject()
    {
        super("Sigil Project");
    }

    public SigilProject(IProject project) throws CoreException
    {
        this();
        this.project = project;
        bldProjectFile = project.getFile(new Path(SigilCore.SIGIL_PROJECT_FILE));
    }

    public void save(IProgressMonitor monitor) throws CoreException
    {
        save(monitor, true);
    }

    public void save(IProgressMonitor monitor, boolean rebuildDependencies)
        throws CoreException
    {
        SubMonitor progress = SubMonitor.convert(monitor, 1000);

        if (bldProjectFile.getLocation().toFile().exists())
        {
            bldProjectFile.setContents(buildContents(), IFile.KEEP_HISTORY,
                progress.newChild(10));
        }
        else
        {
            bldProjectFile.create(buildContents(), true /* force */,
                progress.newChild(5));
            project.refreshLocal(IResource.DEPTH_ONE, progress.newChild(5));
        }

        if (rebuildDependencies)
        {
            rebuildDependencies(progress.newChild(900));
        }
    }

    public void rebuildDependencies(IProgressMonitor monitor) throws CoreException
    {
        SubMonitor progress = SubMonitor.convert(monitor, 1000);

        HashSet<ICapabilityModelElement> changes = new HashSet<ICapabilityModelElement>(
            lastCaps);

        LinkedList<IRequirementModelElement> reqs = new LinkedList<IRequirementModelElement>();
        LinkedList<ICapabilityModelElement> caps = new LinkedList<ICapabilityModelElement>();

        checkChanges(progress.newChild(100), reqs, caps);

        boolean reqsChanged;
        boolean capsChanged;

        synchronized (this)
        {
            reqsChanged = isRequirementsChanged(reqs);
            capsChanged = isCapabilitiesChanged(caps);
        }

        if (reqsChanged)
        {
            processRequirementsChanges(progress.newChild(600));
            SigilCore.rebuild(this, progress.newChild(50));
        }

        progress.setWorkRemaining(250);

        if (capsChanged)
        {
            changes.addAll(caps);
            SigilCore.rebuildBundleDependencies(this, changes, progress.newChild(250));
        }
    }

    public void flushDependencyState()
    {
        synchronized (this)
        {
            lastReqs.clear();
        }
    }

    private void processRequirementsChanges(IProgressMonitor monitor)
        throws CoreException
    {
        SubMonitor progress = SubMonitor.convert(monitor, 100);

        IRepositoryManager manager = getRepositoryManager();
        ResolutionConfig config = new ResolutionConfig(ResolutionConfig.INCLUDE_OPTIONAL
            | ResolutionConfig.IGNORE_ERRORS);

        try
        {
            IResolution resolution = manager.getBundleResolver().resolve(this, config,
                new ResolutionMonitorAdapter(progress.newChild(20)));

            markProblems(resolution);

            // pull remote bundles from repositories to be added to classpath
            if (!resolution.isSynchronized())
            {
                resolution.synchronize(new ProgressAdapter(progress.newChild(80)));
            }
        }
        catch (ResolutionException e)
        {
            throw SigilCore.newCoreException("Failed to resolve dependencies", e);
        }
    }

    private void markProblems(IResolution resolution)
    {
        try
        {
            getProject().deleteMarkers(SigilCore.MARKER_UNRESOLVED_DEPENDENCY, true,
                IResource.DEPTH_ONE);

            // Find missing imports
            Collection<IPackageImport> imports = getBundle().getBundleInfo().getImports();
            for (IPackageImport pkgImport : imports)
            {
                if (resolution.getProvider(pkgImport) == null)
                {
                    markMissingImport(pkgImport, getProject());
                }
            }

            // Find missing required bundles
            Collection<IRequiredBundle> requiredBundles = getBundle().getBundleInfo().getRequiredBundles();
            for (IRequiredBundle requiredBundle : requiredBundles)
            {
                if (resolution.getProvider(requiredBundle) == null)
                {
                    markMissingRequiredBundle(requiredBundle, getProject());
                }
            }
        }
        catch (CoreException e)
        {
            SigilCore.error("Failed to update problems", e);
        }
    }

    private void checkChanges(IProgressMonitor monitor,
        final List<IRequirementModelElement> reqs,
        final List<ICapabilityModelElement> caps)
    {
        visit(new IModelWalker()
        {
            public boolean visit(IModelElement element)
            {
                if (element instanceof IRequirementModelElement)
                {
                    reqs.add((IRequirementModelElement) element);
                }
                else if (element instanceof ICapabilityModelElement)
                {
                    // also calculate uses during this pass to save multi pass on model
                    if (element instanceof IPackageExport)
                    {
                        IPackageExport pe = (IPackageExport) element;
                        try
                        {
                            pe.setUses(Arrays.asList(JavaHelper.findUses(
                                pe.getPackageName(), SigilProject.this)));
                        }
                        catch (CoreException e)
                        {
                            SigilCore.error("Failed to build uses list for " + pe, e);
                        }
                    }

                    caps.add((ICapabilityModelElement) element);
                }
                return true;
            }
        });
    }

    private boolean isRequirementsChanged(List<IRequirementModelElement> dependencies)
    {
        if (lastReqs.equals(dependencies))
        {
            return false;
        }
        else
        {
            lastReqs = dependencies;
            return true;
        }
    }

    private boolean isCapabilitiesChanged(List<ICapabilityModelElement> capabilites)
    {
        if (lastCaps.equals(capabilites))
        {
            return false;
        }
        else
        {
            lastCaps = capabilites;
            return true;
        }
    }

    private static void markMissingImport(IPackageImport pkgImport, IProject project)
        throws CoreException
    {
        IMarker marker = project.getProject().createMarker(
            SigilCore.MARKER_UNRESOLVED_IMPORT_PACKAGE);
        marker.setAttribute("element", pkgImport.getPackageName());
        marker.setAttribute("versionRange", pkgImport.getVersions().toString());
        marker.setAttribute(IMarker.MESSAGE, "Cannot resolve imported package \""
            + pkgImport.getPackageName() + "\" with version range "
            + pkgImport.getVersions());
        marker.setAttribute(IMarker.SEVERITY,
            pkgImport.isOptional() ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR);
        marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
    }

    private static void markMissingRequiredBundle(IRequiredBundle req, IProject project)
        throws CoreException
    {
        IMarker marker = project.getProject().createMarker(
            SigilCore.MARKER_UNRESOLVED_REQUIRE_BUNDLE);
        marker.setAttribute("element", req.getSymbolicName());
        marker.setAttribute("versionRange", req.getVersions().toString());
        marker.setAttribute(IMarker.MESSAGE, "Cannot resolve required bundle \""
            + req.getSymbolicName() + "\" with version range " + req.getVersions());
        marker.setAttribute(IMarker.SEVERITY, req.isOptional() ? IMarker.SEVERITY_WARNING
            : IMarker.SEVERITY_ERROR);
        marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
    }

    public Collection<IClasspathEntry> findExternalClasspath(IProgressMonitor monitor)
        throws CoreException
    {
        return JavaHelper.resolveClasspathEntrys(this, monitor);
    }

    public Version getVersion()
    {
        ISigilBundle bundle = getBundle();
        return bundle == null ? null : bundle.getBundleInfo() == null ? null
            : bundle.getBundleInfo().getVersion();
    }

    public String getSymbolicName()
    {
        ISigilBundle bundle = getBundle();
        return bundle == null ? null : bundle.getBundleInfo() == null ? null
            : bundle.getBundleInfo().getSymbolicName();
    }

    public IProject getProject()
    {
        return project;
    }

    public ISigilBundle getBundle()
    {
        ISigilBundle b = null;

        try
        {
            synchronized (bldProjectFile)
            {
                if (bundle == null)
                {                  
                    IPath loc = bldProjectFile.getLocation();
                    if (loc == null) {
                        // callers can protect against this by using
                        // checking exists()
                        throw new IllegalStateException("Sigil project does not exist");
                    }
                    else if (loc.toFile().exists())
                    {
                        bundle = parseContents(bldProjectFile);
                    }
                    else
                    {
                        bundle = setupDefaults();
                    }
                }

                b = bundle;
            }
        }
        catch (CoreException e)
        {
            SigilCore.error("Failed to build bundle", e);
        }

        return b;
    }

    public void setBundle(ISigilBundle bundle)
    {
        synchronized (bldProjectFile)
        {
            this.bundle = bundle;
        }
    }

    public IJavaProject getJavaModel()
    {
        return JavaCore.create(project);
    }

    @Override
    public boolean equals(Object obj)
    {
        if (obj == null)
            return false;

        if (obj == this)
            return true;

        try
        {
            SigilProject p = (SigilProject) obj;
            return getSymbolicName().equals(p.getSymbolicName())
                && (getVersion() == null ? p.getVersion() == null : getVersion().equals(
                    p.getVersion()));
        }
        catch (ClassCastException e)
        {
            return false;
        }
    }

    @Override
    public int hashCode()
    {
        String bsn = getSymbolicName();
        int hc = bsn == null ? 1 : bsn.hashCode();
        if (getVersion() != null)
        {
            hc *= getVersion().hashCode();
        }
        hc *= 7;
        return hc;
    }

    @Override
    public String toString()
    {
        return "SigilProject[" + getSymbolicName() + ":" + getVersion() + "]";
    }

    public void resetClasspath(IProgressMonitor monitor, boolean forceResolve) throws CoreException
    {
        if (forceResolve) {
            processRequirementsChanges(monitor);
        }
       
        Path containerPath = new Path(SigilCore.CLASSPATH_CONTAINER_PATH);
        IJavaProject java = getJavaModel();
        ClasspathContainerInitializer init = JavaCore.getClasspathContainerInitializer(SigilCore.CLASSPATH_CONTAINER_PATH);
        ThreadProgressMonitor.setProgressMonitor(monitor);
        try
        {
            init.requestClasspathContainerUpdate(containerPath, java, null);
        }
        finally
        {
            ThreadProgressMonitor.setProgressMonitor(null);
        }
    }

    public IPath findBundleLocation() throws CoreException
    {
        IPath p = PathUtil.newPathIfExists(getBundle().getLocation());
        if (p == null)
        {
            p = SigilCore.getDefault().findDefaultBundleLocation(this);
        }
        return p;
    }

    public IModelElement findImport(final String packageName,
        final IProgressMonitor monitor)
    {
        final IModelElement[] found = new IModelElement[1];

        visit(new IModelWalker()
        {
            public boolean visit(IModelElement element)
            {
                if (element instanceof IPackageImport)
                {
                    IPackageImport pi = (IPackageImport) element;
                    if (pi.getPackageName().equals(packageName))
                    {
                        found[0] = pi;
                        return false;
                    }
                }
                else if (element instanceof IRequiredBundle)
                {
                    IRequiredBundle rb = (IRequiredBundle) element;
                    try
                    {
                        IRepositoryManager manager = SigilProject.this.getRepositoryManager();
                        ResolutionConfig config = new ResolutionConfig(
                            ResolutionConfig.IGNORE_ERRORS);
                        IResolution res = manager.getBundleResolver().resolve(rb, config,
                            new ResolutionMonitorAdapter(monitor));
                        ISigilBundle b = res.getProvider(rb);
                        for (IPackageExport pe : b.getBundleInfo().getExports())
                        {
                            if (pe.getPackageName().equals(packageName))
                            {
                                found[0] = rb;
                                return false;
                            }
                        }
                    }
                    catch (ResolutionException e)
                    {
                        SigilCore.error("Failed to resolve " + rb, e);
                    }
                }
                return true;
            }

        });

        return found[0];
    }

    public boolean isInClasspath(String packageName, IProgressMonitor monitor)
        throws CoreException
    {
        if (findImport(packageName, monitor) != null)
        {
            return true;
        }

        for (String path : getBundle().getClasspathEntrys())
        {
            IClasspathEntry cp = getJavaModel().decodeClasspathEntry(path);
            for (IPackageFragmentRoot root : getJavaModel().findPackageFragmentRoots(cp))
            {
                if (findPackage(packageName, root))
                {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean isInClasspath(ISigilBundle bundle)
    {
        for (String path : getBundle().getClasspathEntrys())
        {
            IClasspathEntry cp = getJavaModel().decodeClasspathEntry(path);
            switch (cp.getEntryKind())
            {
                case IClasspathEntry.CPE_PROJECT:
                    ISigilProjectModel p = bundle.getAncestor(ISigilProjectModel.class);
                    return p != null && cp.getPath().equals(p.getProject().getFullPath());
                case IClasspathEntry.CPE_LIBRARY:
                    return cp.getPath().equals(bundle.getLocation());
            }
        }

        return false;
    }

    private boolean findPackage(String packageName, IParent parent)
        throws JavaModelException
    {
        for (IJavaElement e : parent.getChildren())
        {
            if (e.getElementType() == IJavaElement.PACKAGE_FRAGMENT)
            {
                return e.getElementName().equals(packageName);
            }

            if (e instanceof IParent)
            {
                if (findPackage(packageName, (IParent) e))
                {
                    return true;
                }
            }
        }

        return false;
    }

    private ISigilBundle setupDefaults()
    {
        ISigilBundle bundle = ModelElementFactory.getInstance().newModelElement(
            ISigilBundle.class);
        IBundleModelElement info = ModelElementFactory.getInstance().newModelElement(
            IBundleModelElement.class);
        bundle.setBundleInfo(info);
        bundle.setParent(this);
        return bundle;
    }

    private ISigilBundle parseContents(IFile projectFile) throws CoreException
    {
        if (projectFile.getName().equals(SigilCore.SIGIL_PROJECT_FILE))
        {
            return parseBldContents(projectFile.getLocationURI());
        }
        else
        {
            throw SigilCore.newCoreException("Unexpected project file: "
                + projectFile.getName(), null);
        }
    }

    private ISigilBundle parseBldContents(URI uri) throws CoreException
    {
        try
        {
            bldProject = BldFactory.getProject(uri, true);
            ISigilBundle bundle = bldProject.getDefaultBundle();

            if (bundle == null)
            {
                throw SigilCore.newCoreException("No default bundle", null);
            }

            bundle.setParent(this);
            return bundle;
        }
        catch (IOException e)
        {
            throw SigilCore.newCoreException("Failed to parse " + uri, e);
        }
    }

    private InputStream buildContents() throws CoreException
    {
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        try
        {
            if (bldProject == null)
            {
                bldProject = BldFactory.newProject(bldProjectFile.getLocationURI(), null);
            }
            bldProject.setDefaultBundle(getBundle());
            bldProject.saveTo(buf);
        }
        catch (IOException e)
        {
            throw SigilCore.newCoreException("Failed to save project file", e);
        }
        return new ByteArrayInputStream(buf.toByteArray());
    }

    public String getName()
    {
        return getProject().getName();
    }

    public IPath findOutputLocation() throws CoreException
    {
        return getProject().getLocation().append(
            getJavaModel().getOutputLocation().removeFirstSegments(1));
    }

    public IBldProject getBldProject() throws CoreException
    {
        try
        {
            return BldFactory.getProject(project.getFile(IBldProject.PROJECT_FILE).getLocationURI());
        }
        catch (IOException e)
        {
            throw SigilCore.newCoreException("Failed to get project file: ", e);
        }
    }

    public IRepositoryConfig getRepositoryConfig() throws CoreException
    {
        try
        {
            return BldFactory.getConfig(project.getFile(IBldProject.PROJECT_FILE).getLocationURI());
        }
        catch (IOException e)
        {
            throw SigilCore.newCoreException("Failed to get project file: ", e);
        }
    }
   
    public boolean isInBundleClasspath(IPackageFragment root) throws JavaModelException
    {
        if (getBundle().getClasspathEntrys().isEmpty())
        {
            for (String p : getBundle().getPackages())
            {
                SigilCore.log("Checking " + p + "->" + root.getElementName());
                Matcher m = GlobCompiler.compile(p).matcher(root.getElementName());
                if (m.matches())
                {
                    return true;
                }
            }
            return false;
        }
        else
        {
            IPackageFragmentRoot parent = (IPackageFragmentRoot) root.getParent();
            String enc = getJavaModel().encodeClasspathEntry(
                parent.getRawClasspathEntry());
            return getBundle().getClasspathEntrys().contains(enc.trim());
        }
    }

    /* (non-Javadoc)
     * @see org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel#getRepositoryManager()
     */
    public IRepositoryManager getRepositoryManager()
    {
        return SigilCore.getRepositoryManager(this);
    }

    /* (non-Javadoc)
     * @see org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel#exists()
     */
    public boolean exists()
    {
        return project.exists() && project.getFile(IBldProject.PROJECT_FILE).exists();
    }
}
TOP

Related Classes of org.apache.felix.sigil.eclipse.internal.model.project.SigilProject

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.