Package org.apache.felix.sigil.common.core.repository

Source Code of org.apache.felix.sigil.common.core.repository.BundleResolver$ResolutionContext

/*
* 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.common.core.repository;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.felix.sigil.common.core.BldCore;
import org.apache.felix.sigil.common.model.ICapabilityModelElement;
import org.apache.felix.sigil.common.model.ICompoundModelElement;
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.eclipse.ILibrary;
import org.apache.felix.sigil.common.model.eclipse.ILibraryImport;
import org.apache.felix.sigil.common.model.eclipse.ISigilBundle;
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.progress.IProgress;
import org.apache.felix.sigil.common.repository.AbstractRepositoryManager;
import org.apache.felix.sigil.common.repository.IBundleRepository;
import org.apache.felix.sigil.common.repository.IBundleResolver;
import org.apache.felix.sigil.common.repository.IResolution;
import org.apache.felix.sigil.common.repository.IResolutionMonitor;
import org.apache.felix.sigil.common.repository.ResolutionConfig;
import org.apache.felix.sigil.common.repository.ResolutionException;
import org.osgi.framework.Version;

public class BundleResolver implements IBundleResolver
{

    private static class BundleOrderComparator implements Comparator<ISigilBundle>
    {
        private IModelElement requirement;

        public BundleOrderComparator(IModelElement requirement)
        {
            this.requirement = requirement;
        }

        public int compare(ISigilBundle o1, ISigilBundle o2)
        {
            int c = compareVersions(o1, o2);

            if (c == 0)
            {
                c = compareImports(o1, o2);
            }

            return c;
        }

        private int compareImports(ISigilBundle o1, ISigilBundle o2)
        {
            int c1 = o1.getBundleInfo().getImports().size();
            int c2 = o2.getBundleInfo().getImports().size();

            if (c1 < c2)
            {
                return -1;
            }
            else if (c2 > c1)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }

        private int compareVersions(ISigilBundle o1, ISigilBundle o2)
        {
            Version v1 = null;
            Version v2 = null;
            if (requirement instanceof IPackageImport)
            {
                v1 = findExportVersion((IPackageImport) requirement, o1);
                v2 = findExportVersion((IPackageImport) requirement, o2);
            }
            else if (requirement instanceof IRequiredBundle)
            {
                v1 = o1.getBundleInfo().getVersion();
                v2 = o1.getBundleInfo().getVersion();
            }

            if (v1 == null)
            {
                if (v2 == null)
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            }
            else
            {
                if (v2 == null)
                {
                    return -1;
                }
                else
                {
                    return v2.compareTo(v1);
                }
            }
        }

        private Version findExportVersion(IPackageImport pi, ISigilBundle o1)
        {
            for (IPackageExport pe : o1.getBundleInfo().getExports())
            {
                if (pi.getPackageName().equals(pi.getPackageName()))
                {
                    return pe.getVersion();
                }
            }

            return null;
        }

    }

    private static class ResolutionContext
    {
        private final IModelElement root;
        private final ResolutionConfig config;
        private final IResolutionMonitor monitor;

        private final Resolution resolution = new Resolution();
        private final LinkedList<IModelElement> requirements = new LinkedList<IModelElement>();

        public ResolutionContext(IModelElement root, ResolutionConfig config, IResolutionMonitor monitor)
        {
            this.root = root;
            this.config = config;
            this.monitor = monitor;
        }

        public boolean isValid()
        {
            return resolution.isSuccess();
        }

        public void setValid(boolean valid)
        {
            resolution.setSuccess(valid);
        }

        public ResolutionException newResolutionException()
        {
            return new ResolutionException(root,
                requirements.toArray(new IModelElement[requirements.size()]));
        }

        public void startRequirement(IRequirementModelElement element)
        {
            requirements.add(element);
            monitor.startResolution(element);
        }

        public void endRequirement(IRequirementModelElement element)
        {
            ISigilBundle provider = resolution.getProvider(element);

            setValid(provider != null || element.isOptional() || config.isIgnoreErrors());

            if (isValid())
            {
                // only clear stack if valid
                // else use it as an aid to trace errors
                requirements.remove(element);
            }

            monitor.endResolution(element, provider);
        }
    }

    private static class Resolution implements IResolution
    {
        private Map<ISigilBundle, List<IModelElement>> providees = new HashMap<ISigilBundle, List<IModelElement>>();
        private Map<IModelElement, ISigilBundle> providers = new HashMap<IModelElement, ISigilBundle>();
        private boolean success = true; // assume success

        boolean addProvider(IModelElement element, ISigilBundle provider)
        {
            providers.put(element, provider);

            List<IModelElement> requirements = providees.get(provider);

            boolean isNewProvider = requirements == null;

            if (isNewProvider)
            {
                requirements = new ArrayList<IModelElement>();
                providees.put(provider, requirements);
            }

            requirements.add(element);

            return isNewProvider;
        }

        void removeProvider(IModelElement element, ISigilBundle provider)
        {
            providers.remove(element);
            List<IModelElement> e = providees.get(provider);
            e.remove(element);
            if (e.isEmpty())
            {
                providees.remove(provider);
            }
        }

        void setSuccess(boolean success)
        {
            this.success = success;
        }

        public boolean isSuccess()
        {
            return success;
        }

        public ISigilBundle getProvider(IModelElement requirement)
        {
            return providers.get(requirement);
        }

        public Set<ISigilBundle> getBundles()
        {
            return providees.keySet();
        }

        public List<IModelElement> getMatchedRequirements(ISigilBundle bundle)
        {
            return providees.get(bundle);
        }

        public boolean isSynchronized()
        {
            for (ISigilBundle b : getBundles())
            {
                if (!b.isSynchronized())
                {
                    return false;
                }
            }

            return true;
        }

        public void synchronize(IProgress progress)
        {
            Set<ISigilBundle> bundles = getBundles();
            progress = progress.newTask(bundles.size()*100);

            for (ISigilBundle b : bundles)
            {
                if (progress.isCanceled())
                {
                    break;
                }

                try
                {
                    b.synchronize(progress.newChild(100));
                }
                catch (IOException e)
                {
                    BldCore.error("Failed to synchronize " + b, e);
                }
            }
        }
    }

    private static final IResolutionMonitor NULL_MONITOR = new IResolutionMonitor()
    {
        public void endResolution(IModelElement requirement, ISigilBundle sigilBundle)
        {
        }

        public boolean isCanceled()
        {
            return false;
        }

        public void startResolution(IModelElement requirement)
        {
        }
    };

    private static final Comparator<IRequirementModelElement> REQUIREMENT_COMPARATOR = new Comparator<IRequirementModelElement>()
    {
        public int compare(IRequirementModelElement o1, IRequirementModelElement o2)
        {
            if (o1 instanceof IPackageImport)
            {
                if (o2 instanceof IPackageImport)
                {
                    return 0;
                }
                else
                {
                    return -1;
                }
            }
            else
            // assumes only alternative is IRequiredBundle
            {
                if (o2 instanceof IRequiredBundle)
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            }
        }
    };

    private final AbstractRepositoryManager repositoryManager;

    public BundleResolver(AbstractRepositoryManager repositoryManager)
    {
        this.repositoryManager = repositoryManager;
    }

    public IResolution resolve(IModelElement element, ResolutionConfig config,
        IResolutionMonitor monitor) throws ResolutionException
    {
        if (monitor == null)
        {
            monitor = NULL_MONITOR;
        }
        ResolutionContext ctx = new ResolutionContext(element, config, monitor);

        resolveElement(element, ctx);

        if (!ctx.isValid())
        {
            throw ctx.newResolutionException();
        }

        return ctx.resolution;
    }

    private void resolveElement(IModelElement element, ResolutionContext ctx)
        throws ResolutionException
    {
        if (ctx.isValid())
        {
            for (IRequirementModelElement req : findRequirements(element, ctx))
            {
                if (ctx.monitor.isCanceled())
                    break;
                resolveRequirement(req, ctx);
            }
        }
    }

    private List<IRequirementModelElement> findRequirements(IModelElement element,
        final ResolutionContext ctx)
    {
        final LinkedList<IRequirementModelElement> reqs = new LinkedList<IRequirementModelElement>();

        if (element instanceof IRequirementModelElement)
        {
            reqs.add((IRequirementModelElement) element);
        }
        else if (element instanceof ICompoundModelElement)
        {
            ICompoundModelElement compound = (ICompoundModelElement) element;
            compound.visit(new IModelWalker()
            {
                public boolean visit(IModelElement element)
                {
                    if (element instanceof IRequirementModelElement)
                    {
                        reqs.add((IRequirementModelElement) element);
                    }
                    else if (element instanceof ILibrary)
                    {
                        ILibrary lib = repositoryManager.resolveLibrary((ILibraryImport) element);
                        reqs.addAll(lib.getImports());
                    }

                    return !ctx.monitor.isCanceled();
                }
            });
        }

        if (!ctx.monitor.isCanceled())
        {
            Collections.sort(reqs, REQUIREMENT_COMPARATOR);
        }
        return reqs;
    }

    private void resolveRequirement(IRequirementModelElement requirement,
        ResolutionContext ctx) throws ResolutionException
    {
        if (ctx.config.isOptional() || !requirement.isOptional())
        {
            ctx.startRequirement(requirement);

            try
            {
                if (!findInContext(requirement, ctx))
                {
                    findInRepositories(requirement, ctx);
                }
            }
            finally
            {
                ctx.endRequirement(requirement);
            }
        }
    }

    private boolean findInContext(final IRequirementModelElement requirement,
        final ResolutionContext ctx)
    {
        for (final ISigilBundle b : ctx.resolution.providees.keySet())
        {
            b.visit(new IModelWalker()
            {
                public boolean visit(IModelElement element)
                {
                    if (element instanceof ICapabilityModelElement)
                    {
                        if (requirement.accepts((ICapabilityModelElement) element))
                        {
                            ctx.resolution.addProvider(requirement, b);
                            return false;
                        }
                    }
                    return !ctx.monitor.isCanceled();
                }

            });
        }

        return ctx.resolution.getProvider(requirement) != null;
    }

    private void findInRepositories(IRequirementModelElement requirement,
        ResolutionContext ctx) throws ResolutionException
    {
        int[] priorities = repositoryManager.getPriorityLevels();

        outer: for (int i = 0; i < priorities.length; i++)
        {
            List<ISigilBundle> providers = findProvidersAtPriority(priorities[i],
                requirement, ctx);

            if (!providers.isEmpty() && !ctx.monitor.isCanceled())
            {
                if (providers.size() > 1)
                {
                    Collections.sort(providers, new BundleOrderComparator(requirement));
                }

                for (ISigilBundle provider : providers)
                {
                    // reset validity - if there's another provider it can still be solved
                    ctx.setValid(true);
                    if (ctx.resolution.addProvider(requirement, provider))
                    {
                        if (ctx.config.isDependents())
                        {
                            resolveElement(provider, ctx);
                        }

                        if (ctx.isValid())
                        {
                            break outer;
                        }
                        else
                        {
                            ctx.resolution.removeProvider(requirement, provider);
                        }
                    }
                    else
                    {
                        break outer;
                    }
                }
            }
        }
    }

    private List<ISigilBundle> findProvidersAtPriority(int i,
        IRequirementModelElement requirement, ResolutionContext ctx)
        throws ResolutionException
    {
        ArrayList<ISigilBundle> providers = new ArrayList<ISigilBundle>();

        for (IBundleRepository rep : repositoryManager.getRepositories(i))
        {
            if (ctx.monitor.isCanceled())
            {
                break;
            }
            providers.addAll(findProviders(requirement, ctx.config, rep));
        }

        return providers;
    }

    private Collection<ISigilBundle> findProviders(IRequirementModelElement requirement,
        ResolutionConfig config, IBundleRepository rep) throws ResolutionException
    {
        ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();

        if (requirement instanceof IPackageImport)
        {
            IPackageImport pi = (IPackageImport) requirement;
            found.addAll(rep.findAllProviders(pi, config.getOptions()));
        }
        else if (requirement instanceof IRequiredBundle)
        {
            IRequiredBundle rb = (IRequiredBundle) requirement;
            found.addAll(rep.findAllProviders(rb, config.getOptions()));
        }
        else
        {
            // shouldn't get here - developer error if do
            // use isRequirement before getting anywhere near this logic...
            throw new IllegalStateException("Invalid requirement type " + requirement);
        }

        return found;
    }
}
TOP

Related Classes of org.apache.felix.sigil.common.core.repository.BundleResolver$ResolutionContext

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.