/*
 * 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.aries.subsystem.core.internal;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import org.apache.aries.subsystem.core.archive.AriesSubsystemParentsHeader;
import org.apache.aries.subsystem.core.archive.Attribute;
import org.apache.aries.subsystem.core.archive.DeployedContentHeader;
import org.apache.aries.subsystem.core.archive.DeploymentManifest;
import org.apache.aries.subsystem.core.archive.Header;
import org.apache.aries.subsystem.core.archive.ImportPackageHeader;
import org.apache.aries.subsystem.core.archive.ImportPackageRequirement;
import org.apache.aries.subsystem.core.archive.ProvisionResourceHeader;
import org.apache.aries.subsystem.core.archive.RequireBundleHeader;
import org.apache.aries.subsystem.core.archive.RequireBundleRequirement;
import org.apache.aries.subsystem.core.archive.RequireCapabilityHeader;
import org.apache.aries.subsystem.core.archive.RequireCapabilityRequirement;
import org.apache.aries.subsystem.core.archive.SubsystemContentHeader;
import org.apache.aries.subsystem.core.archive.SubsystemExportServiceHeader;
import org.apache.aries.subsystem.core.archive.SubsystemImportServiceHeader;
import org.apache.aries.subsystem.core.archive.SubsystemImportServiceRequirement;
import org.apache.aries.subsystem.core.archive.SubsystemManifest;
import org.apache.aries.util.filesystem.FileSystem;
import org.apache.aries.util.filesystem.IDirectory;
import org.eclipse.equinox.region.Region;
import org.eclipse.equinox.region.RegionDigraph;
import org.eclipse.equinox.region.RegionFilter;
import org.eclipse.equinox.region.RegionFilterBuilder;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.NativeNamespace;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.namespace.service.ServiceNamespace;
import org.osgi.resource.Capability;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.resource.Wire;
import org.osgi.service.coordinator.Coordination;
import org.osgi.service.coordinator.Participant;
import org.osgi.service.resolver.ResolutionException;
import org.osgi.service.resolver.ResolveContext;
import org.osgi.service.subsystem.Subsystem;
import org.osgi.service.subsystem.SubsystemConstants;
import org.osgi.service.subsystem.SubsystemException;

public class SubsystemResource implements Resource {
	private Region region;

	private final List<Capability> capabilities;
	private final DeploymentManifest deploymentManifest;
	private final Collection<Resource> installableContent = new HashSet<Resource>();
	private final Collection<Resource> installableDependencies = new HashSet<Resource>();
	private final Collection<Resource> mandatoryResources = new HashSet<Resource>();
	private final Collection<DeployedContentHeader.Clause> missingResources = new HashSet<DeployedContentHeader.Clause>();
	private final Collection<Resource> optionalResources = new HashSet<Resource>();
	private final BasicSubsystem parent;
	private final RawSubsystemResource resource;
	private final Collection<Resource> sharedContent = new HashSet<Resource>();
	private final Collection<Resource> sharedDependencies = new HashSet<Resource>();

	public SubsystemResource(String location, IDirectory content, BasicSubsystem parent, Coordination coordination) throws URISyntaxException, IOException, ResolutionException, BundleException, InvalidSyntaxException {
		this(new RawSubsystemResource(location, content, parent), parent, coordination);
	}

	public SubsystemResource(RawSubsystemResource resource, BasicSubsystem parent, Coordination coordination) throws IOException, BundleException, InvalidSyntaxException, URISyntaxException {
		this.parent = parent;
		this.resource = resource;
		computeContentResources(resource.getDeploymentManifest());
		capabilities = computeCapabilities();
		if (this.getSubsystemManifest().getSubsystemTypeHeader().getAriesProvisionDependenciesDirective().isInstall()) {
		    /* compute dependencies now only if we intend to provision them during install */
	        computeDependencies(resource.getDeploymentManifest(), coordination);		    
		}
		deploymentManifest = computeDeploymentManifest();
	}

	public SubsystemResource(File file) throws IOException, URISyntaxException, ResolutionException, BundleException, InvalidSyntaxException {
		this(null, FileSystem.getFSRoot(file));
	}

	public SubsystemResource(BasicSubsystem subsystem, IDirectory directory) throws IOException, URISyntaxException, ResolutionException, BundleException, InvalidSyntaxException {
		if (subsystem == null) {
			// This is intended to only support the case where the root subsystem
			// is being initialized from a non-persistent state.
			parent = null;
		}
		else {
			parent = Utils.findScopedSubsystemInRegion(subsystem);
		}
		resource = new RawSubsystemResource(directory, parent);
		deploymentManifest = resource.getDeploymentManifest();
		computeContentResources(deploymentManifest);
		capabilities = computeCapabilities();
        if (getSubsystemManifest().getSubsystemTypeHeader().getAriesProvisionDependenciesDirective().isInstall()) {
            /* compute dependencies if we intend to provision them during install */
            computeDependencies(resource.getDeploymentManifest(), null);          
        }
	}

	@Override
	public boolean equals(Object o) {
		if (o == this)
			return true;
		if (!(o instanceof SubsystemResource))
			return false;
		SubsystemResource that = (SubsystemResource)o;
		return getLocation().equals(that.getLocation());
	}

	@Override
	public List<Capability> getCapabilities(String namespace) {
		return Collections.unmodifiableList(capabilities);
	}

	private List<Capability> computeCapabilities() throws InvalidSyntaxException {
		List<Capability> capabilities = new ArrayList<Capability>();
		if (isScoped())
			computeScopedCapabilities(capabilities);
		else
			computeUnscopedCapabilities(capabilities);
		return capabilities;
	}

	private void computeUnscopedCapabilities(List<Capability> capabilities) {
		capabilities.addAll(resource.getCapabilities(null));
		for (Resource r : getContentResources())
			capabilities.addAll(r.getCapabilities(null));
	}

	private void computeScopedCapabilities(List<Capability> capabilities) throws InvalidSyntaxException {
		capabilities.addAll(resource.getCapabilities(null));
		computeOsgiServiceCapabilities(capabilities);
	}

	private void computeOsgiServiceCapabilities(List<Capability> capabilities) throws InvalidSyntaxException {
		SubsystemExportServiceHeader header = getSubsystemManifest().getSubsystemExportServiceHeader();
		if (header == null)
			return;
		for (Resource resource : getContentResources())
			capabilities.addAll(header.toCapabilities(resource));
	}

	public DeploymentManifest getDeploymentManifest() {
		return deploymentManifest;
	}

	public long getId() {
		return resource.getId();
	}

	public Collection<Resource> getInstallableContent() {
		return installableContent;
	}

	public Collection<Resource> getInstallableDependencies() {
		return installableDependencies;
	}

	public org.apache.aries.subsystem.core.repository.Repository getLocalRepository() {
		return resource.getLocalRepository();
	}

	public String getLocation() {
		return resource.getLocation().getValue();
	}

	Collection<Resource> getMandatoryResources() {
		return mandatoryResources;
	}
	
	public Collection<DeployedContentHeader.Clause> getMissingResources() {
		return missingResources;
	}

	Collection<Resource> getOptionalResources() {
		return optionalResources;
	}
	
	public Collection<BasicSubsystem> getParents() {
		if (parent == null) {
			AriesSubsystemParentsHeader header = getDeploymentManifest().getAriesSubsystemParentsHeader();
			if (header == null)
				return Collections.emptyList();
			Collection<AriesSubsystemParentsHeader.Clause> clauses = header.getClauses();
			Collection<BasicSubsystem> result = new ArrayList<BasicSubsystem>(clauses.size());
			Subsystems subsystems = Activator.getInstance().getSubsystems();
			for (AriesSubsystemParentsHeader.Clause clause : clauses) {
				result.add(subsystems.getSubsystemById(clause.getId()));
			}
			return result;
		}
		return Collections.singleton(parent);
	}

	public synchronized Region getRegion() throws BundleException, IOException, InvalidSyntaxException, URISyntaxException {
		if (region == null) {
			region = createRegion(getId());
			Coordination coordination = Activator.getInstance().getCoordinator().peek();
			coordination.addParticipant(new Participant() {
				@Override
				public void ended(Coordination arg0) throws Exception {
					// Nothing.
				}

				@Override
				public void failed(Coordination arg0) throws Exception {
					if (isScoped())
						region.getRegionDigraph().removeRegion(region);
				}
			});
			if (!isApplication()) {
				setImportIsolationPolicy();
			}
		}
		return region;
	}

	@Override
	public List<Requirement> getRequirements(String namespace) {
		if (isScoped())
			return resource.getRequirements(namespace);
		else {
			ArrayList<Requirement> result = new ArrayList<Requirement>();
			result.addAll(resource.getRequirements(namespace));
			for (Resource r : getContentResources())
				result.addAll(r.getRequirements(namespace));
			result.trimToSize();
			return result;
		}
	}

	public Collection<Resource> getSharedContent() {
		return sharedContent;
	}

	public Collection<Resource> getSharedDependencies() {
		return sharedDependencies;
	}

	public SubsystemManifest getSubsystemManifest() {
		return resource.getSubsystemManifest();
	}

	public Collection<TranslationFile> getTranslations() {
		return resource.getTranslations();
	}
	
	@Override
	public int hashCode() {
		int result = 17;
		result = 31 * result + getLocation().hashCode();
		return result;
	}

	private void addContentResource(Resource resource) {
		if (resource == null)
			return;
		if (isMandatory(resource))
			mandatoryResources.add(resource);
		else
			optionalResources.add(resource);
		if (isInstallable(resource))
			installableContent.add(resource);
		else
			sharedContent.add(resource);
	}

	private void addMissingResource(DeployedContentHeader.Clause resource) {
		missingResources.add(resource);
	}

	private void addSubsystemServiceImportToSharingPolicy(
			RegionFilterBuilder builder) throws InvalidSyntaxException, BundleException, IOException, URISyntaxException {
		builder.allow(
				RegionFilter.VISIBLE_SERVICE_NAMESPACE,
				new StringBuilder("(&(")
						.append(org.osgi.framework.Constants.OBJECTCLASS)
						.append('=').append(Subsystem.class.getName())
						.append(")(")
						.append(Constants.SubsystemServicePropertyRegions)
						.append('=').append(getRegion().getName())
						.append("))").toString());
	}

	private void addSubsystemServiceImportToSharingPolicy(RegionFilterBuilder builder, Region to)
			throws InvalidSyntaxException, BundleException, IOException, URISyntaxException {
		Region root = Activator.getInstance().getSubsystems().getRootSubsystem().getRegion();
		if (to.getName().equals(root.getName()))
			addSubsystemServiceImportToSharingPolicy(builder);
		else {
			to = root;
			builder = to.getRegionDigraph().createRegionFilterBuilder();
			addSubsystemServiceImportToSharingPolicy(builder);
			RegionFilter regionFilter = builder.build();
			getRegion().connectRegion(to, regionFilter);
		}
	}

	private void computeContentResources(DeploymentManifest manifest) throws BundleException, IOException, InvalidSyntaxException, URISyntaxException {
		if (manifest == null)
			computeContentResources(getSubsystemManifest());
		else {
			DeployedContentHeader header = manifest.getDeployedContentHeader();
			if (header == null)
				return;
			for (DeployedContentHeader.Clause clause : header.getClauses()) {
				Resource resource = findContent(clause);
				if (resource == null)
					addMissingResource(clause);
				else
					addContentResource(resource);
			}
		}
	}

	private void computeContentResources(SubsystemManifest manifest) throws BundleException, IOException, InvalidSyntaxException, URISyntaxException {
		SubsystemContentHeader contentHeader = manifest.getSubsystemContentHeader();
		if (contentHeader == null)
			return;
		for (SubsystemContentHeader.Clause clause : contentHeader.getClauses()) {
			Requirement requirement = clause.toRequirement(this);
			Resource resource = findContent(requirement);
			if (resource == null) {
				if (clause.isMandatory())
					throw new SubsystemException("A required content resource could not be found. This means the resource was either missing or not recognized as a supported resource format due to, for example, an invalid bundle manifest or blueprint XML file. Turn on debug logging for more information. The resource was: " + requirement);
				continue;
			}
			addContentResource(resource);
		}
	}

	void computeDependencies(DeploymentManifest manifest, Coordination coordination) {
	    if (manifest == null) {
	        computeDependencies(getSubsystemManifest(), coordination);
	    }
	    else {
	        ProvisionResourceHeader header = manifest.getProvisionResourceHeader();
	        if (header == null)
	            return;
	        for (ProvisionResourceHeader.Clause clause : header.getClauses()) {
	            Resource resource = findDependency(clause);
	            if (resource == null)
	                throw new SubsystemException("A required dependency could not be found. This means the resource was either missing or not recognized as a supported resource format due to, for example, an invalid bundle manifest or blueprint XML file. Turn on debug logging for more information. The resource was: " + resource);
	            addDependency(resource);
	        }
	    }
	}
	
	private void addDependency(Resource resource) {
		if (resource == null)
			return;
		if (isInstallable(resource))
			installableDependencies.add(resource);
		else
			sharedDependencies.add(resource);
	}

	private void computeDependencies(SubsystemManifest manifest, Coordination coordination)  {
		try {
			// The following line is necessary in order to ensure that the
			// export sharing policies of composites are in place for capability
			// validation.
			StartAction.setExportPolicyOfAllInstallingSubsystemsWithProvisionDependenciesResolve(coordination);
			Map<Resource, List<Wire>> resolution = Activator.getInstance().getResolver().resolve(createResolveContext());
			setImportIsolationPolicy(resolution);
			addDependencies(resolution);
		}
		catch (Exception e) {
			Utils.handleTrowable(e);
		}
	}
	
	private void addDependencies(Map<Resource, List<Wire>> resolution) {
		for (Map.Entry<Resource, List<Wire>> entry : resolution.entrySet()) {
			addDependencies(entry, resolution);
		}
	}
	
	private void addDependencies(Map.Entry<Resource, List<Wire>> entry, Map<Resource, List<Wire>> resolution) {
		addDependencies(entry.getKey(), entry, resolution);
	}
	
	private void addDependencies(Resource resource, Map.Entry<Resource, List<Wire>> entry, Map<Resource, List<Wire>> resolution) {
		String type = ResourceHelper.getTypeAttribute(resource);
		SubsystemContentHeader contentHeader = getSubsystemManifest().getSubsystemContentHeader();
		if (!Constants.ResourceTypeSynthesized.equals(type) // Do not include synthetic resources as dependencies.
				&& !contentHeader.contains(resource)) { // Do not include content as dependencies.
			addDependency(resource);
		}
	}

	private DeployedContentHeader computeDeployedContentHeader() {
		Collection<Resource> content = getContentResources();
		if (content.isEmpty())
			return null;
		return DeployedContentHeader.newInstance(content);
	}

	private DeploymentManifest computeDeploymentManifest() throws IOException {
		DeploymentManifest result = computeExistingDeploymentManifest();
		if (result != null)
			return result;
		result = new DeploymentManifest.Builder().manifest(resource.getSubsystemManifest())
				.header(computeDeployedContentHeader())
				.header(computeProvisionResourceHeader()).build();
		return result;
	}

	private DeploymentManifest computeExistingDeploymentManifest() throws IOException {
		return resource.getDeploymentManifest();
	}

	ProvisionResourceHeader computeProvisionResourceHeader() {
		Collection<Resource> dependencies = getDependencies();
		if (dependencies.isEmpty())
			return null;
		return ProvisionResourceHeader.newInstance(dependencies);
	}

	private Region createRegion(long id) throws BundleException {
		if (!isScoped())
			return getParents().iterator().next().getRegion();
		Activator activator = Activator.getInstance();
		RegionDigraph digraph = activator.getRegionDigraph();
		if (getParents().isEmpty())
			// This is the root subsystem. Associate it with the region in which
			// the subsystems implementation bundle was installed.
			return digraph.getRegion(activator.getBundleContext().getBundle());
		String name = getSubsystemManifest()
				.getSubsystemSymbolicNameHeader().getSymbolicName()
				+ ';'
				+ getSubsystemManifest().getSubsystemVersionHeader()
						.getVersion()
				+ ';'
				+ getSubsystemManifest().getSubsystemTypeHeader()
						.getType() + ';' + Long.toString(id);
		Region region = digraph.getRegion(name);
		// TODO New regions need to be cleaned up if this subsystem fails to
		// install, but there's no access to the coordination here.
		if (region == null)
			return digraph.createRegion(name);
		return region;
	}

	private ResolveContext createResolveContext() {
		return new org.apache.aries.subsystem.core.internal.ResolveContext(this);
	}

	private Resource findContent(Requirement requirement) throws BundleException, IOException, InvalidSyntaxException, URISyntaxException {
		Map<Requirement, Collection<Capability>> map;
		// TODO System repository for scoped subsystems should be searched in
		// the case of a persisted subsystem.
		if (isUnscoped()) {
			map = Activator.getInstance().getSystemRepository().findProviders(Collections.singleton(requirement));
			if (map.containsKey(requirement)) {
				Collection<Capability> capabilities = map.get(requirement);
				for (Capability capability : capabilities) {
					Resource provider = capability.getResource();
					if (provider instanceof BundleRevision) {
						if (getRegion().contains(((BundleRevision)provider).getBundle())) {
							return provider;
						}
					}
					else if (provider instanceof BasicSubsystem) {
						if (getRegion().equals(((BasicSubsystem)provider).getRegion())) {
							return provider;
						}
					}
				}
			}
		}
		// First search the local repository.
		map = resource.getLocalRepository().findProviders(Collections.singleton(requirement));
		Collection<Capability> capabilities = map.get(requirement);
		if (capabilities.isEmpty()) {
			// Nothing found in the local repository so search the repository services.
			capabilities = new RepositoryServiceRepository().findProviders(requirement);
		}
		if (capabilities.isEmpty()) {
			// Nothing found period.
			return null;
		}
		for (Capability capability : capabilities) {
			if (!IdentityNamespace.TYPE_FRAGMENT.equals(
					capability.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE))) {
				// Favor the first resource that is not a fragment bundle.
				// See ARIES-1425.
				return capability.getResource();
			}
		}
		// Nothing here but fragment bundles. Return the first one.
		return capabilities.iterator().next().getResource();
	}

	private Resource findContent(DeployedContentHeader.Clause clause) throws BundleException, IOException, InvalidSyntaxException, URISyntaxException {
		Attribute attribute = clause.getAttribute(DeployedContentHeader.Clause.ATTRIBUTE_RESOURCEID);
		long resourceId = attribute == null ? -1 : Long.parseLong(String.valueOf(attribute.getValue()));
		if (resourceId != -1) {
			String type = clause.getType();
			if (IdentityNamespace.TYPE_BUNDLE.equals(type) || IdentityNamespace.TYPE_FRAGMENT.equals(type)) {
				Bundle resource = Activator.getInstance().getBundleContext().getBundle(0).getBundleContext().getBundle(resourceId);
				if (resource == null)
					return null;
				return resource.adapt(BundleRevision.class);
			}
			else
				return Activator.getInstance().getSubsystems().getSubsystemById(resourceId);
		}
		return findContent(clause.toRequirement(this));
	}

	private Resource findDependency(ProvisionResourceHeader.Clause clause) {
		Attribute attribute = clause.getAttribute(DeployedContentHeader.Clause.ATTRIBUTE_RESOURCEID);
		long resourceId = attribute == null ? -1 : Long.parseLong(String.valueOf(attribute.getValue()));
		if (resourceId != -1) {
			String type = clause.getType();
			if (IdentityNamespace.TYPE_BUNDLE.equals(type) || IdentityNamespace.TYPE_FRAGMENT.equals(type))
				return Activator.getInstance().getBundleContext().getBundle(0).getBundleContext().getBundle(resourceId).adapt(BundleRevision.class);
			else
				return Activator.getInstance().getSubsystems().getSubsystemById(resourceId);
		}
		OsgiIdentityRequirement requirement = new OsgiIdentityRequirement(
				clause.getPath(), clause.getDeployedVersion(),
				clause.getType(), true);
		List<Capability> capabilities = createResolveContext().findProviders(requirement);
		if (capabilities.isEmpty())
			return null;
		return capabilities.get(0).getResource();
	}

	private Collection<Resource> getContentResources() {
		Collection<Resource> result = new ArrayList<Resource>(installableContent.size() + sharedContent.size());
		result.addAll(installableContent);
		result.addAll(sharedContent);
		return result;
	}

	private Collection<Resource> getDependencies() {
		Collection<Resource> result = new ArrayList<Resource>(installableDependencies.size() + sharedDependencies.size());
		result.addAll(installableDependencies);
		result.addAll(sharedDependencies);
		return result;
	}

	boolean isApplication() {
		String type = resource.getSubsystemManifest().getSubsystemTypeHeader().getType();
		return SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION.equals(type);
	}

	boolean isComposite() {
		String type = resource.getSubsystemManifest().getSubsystemTypeHeader().getType();
		return SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE.equals(type);
	}
	
	boolean isContent(Resource resource) {
		if (installableContent.contains(resource) || sharedContent.contains(resource)) {
			return true;
		}
		// Allow for implicit subsystem installations. An implicit installation
		// occurs when a subsystem containing other subsystems as content is
		// installed. When identifying the region to be used for validation
		// purposes during resolution, resources that are content of children
		// must be treated as content of this subsystem. See ResolveContext.isValid().
		for (Resource installableResource : installableContent) {
			if (installableResource instanceof RawSubsystemResource) {
				if (((RawSubsystemResource)installableResource).getSubsystemManifest().getSubsystemContentHeader().contains(resource)) {
					return true;
				}
			}
		}
		return false;
	}
	
	private boolean isInstallable(Resource resource) {
		return !isShared(resource);
	}

	private boolean isMandatory(Resource resource) {
		SubsystemContentHeader header = this.resource.getSubsystemManifest().getSubsystemContentHeader();
		if (header == null)
			return false;
		return header.isMandatory(resource);
	}

	boolean isRoot() {
		return BasicSubsystem.ROOT_LOCATION.equals(getLocation());
	}

	private boolean isShared(Resource resource) {
		return Utils.isSharedResource(resource);
	}

	private boolean isScoped() {
		return isApplication() || isComposite();
	}

	private boolean isUnscoped() {
		return !isScoped();
	}

	private void setImportIsolationPolicy(Map<Resource, List<Wire>> resolution) throws Exception {
		if (!isApplication()) {
			return;
		}
		SubsystemContentHeader contentHeader = getSubsystemManifest().getSubsystemContentHeader();
		// Prepare the regions and filter builder to set the sharing policy.
		Region from = getRegion();
		Region to = ((BasicSubsystem)getParents().iterator().next()).getRegion();
		RegionFilterBuilder builder = from.getRegionDigraph().createRegionFilterBuilder();
		// Always provide visibility to this subsystem's service registration.
		addSubsystemServiceImportToSharingPolicy(builder, to);
		for (Resource resource : resolution.keySet()) {
			if (!contentHeader.contains(resource)) {
				continue;
			}
			// If the resource is content but the wire provider is not,
			// the sharing policy must be updated.
			List<Wire> wires = resolution.get(resource);
			for (Wire wire : wires) {
				Resource provider = wire.getProvider();
				// First check: If the provider is content there is no need to
				// update the sharing policy because the capability is already
				// visible.
				if (contentHeader.contains(provider)) {
					continue;
				}
				// Second check: If the provider is synthesized but not offering
				// a MissingCapability, then the resource is acting as a
				// placeholder as part of the Application-ImportService header
				// functionality, and the sharing policy does not need to be
				// updated.
				// Do not exclude resources providing a MissingCapability
				// even though they are synthesized. These are added by the
				// resolve context to ensure that unsatisfied optional
				// requirements become part of the sharing policy.
				if (!(wire.getCapability() instanceof DependencyCalculator.MissingCapability)
						&& Constants.ResourceTypeSynthesized.equals(ResourceHelper.getTypeAttribute(provider))) {
					continue;
				}
				// The requirement must be added to the sharing policy.
				Requirement requirement = wire.getRequirement();
				List<String> namespaces = new ArrayList<String>(2);
				namespaces.add(requirement.getNamespace());
				if (ServiceNamespace.SERVICE_NAMESPACE.equals(namespaces.get(0))) {
					// Both service capabilities and services must be visible.
					namespaces.add(RegionFilter.VISIBLE_SERVICE_NAMESPACE);
				}
				String filter = requirement.getDirectives().get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
				if (filter == null) {
					for (String namespace : namespaces)
						builder.allowAll(namespace);
				}
				else {
					for (String namespace : namespaces)
						builder.allow(namespace, filter);
				}
			}
		}
		// Always add access to osgi.ee and osgi.native namespaces
		setImplicitAccessToNativeAndEECapabilities(builder);
		// Now set the sharing policy, if the regions are different.
		RegionFilter regionFilter = builder.build();
		from.connectRegion(to, regionFilter);
	}

	private void setImportIsolationPolicy() throws BundleException, IOException, InvalidSyntaxException, URISyntaxException {
		if (isRoot() || !isScoped())
			return;
		Region region = getRegion();
		Region from = region;
		RegionFilterBuilder builder = from.getRegionDigraph().createRegionFilterBuilder();
		Region to = getParents().iterator().next().getRegion();
		addSubsystemServiceImportToSharingPolicy(builder, to);
		// TODO Is this check really necessary? Looks like it was done at the beginning of this method.
		if (isScoped()) {
			// Both applications and composites have Import-Package headers that require processing.
			// In the case of applications, the header is generated.
			Header<?> header = getSubsystemManifest().getImportPackageHeader();
			setImportIsolationPolicy(builder, (ImportPackageHeader)header);
			// Both applications and composites have Require-Capability headers that require processing.
			// In the case of applications, the header is generated.
			header = getSubsystemManifest().getRequireCapabilityHeader();
			setImportIsolationPolicy(builder, (RequireCapabilityHeader)header);
			// Both applications and composites have Subsystem-ImportService headers that require processing.
			// In the case of applications, the header is generated.
			header = getSubsystemManifest().getSubsystemImportServiceHeader();
			setImportIsolationPolicy(builder, (SubsystemImportServiceHeader)header);
			header = getSubsystemManifest().getRequireBundleHeader();
			setImportIsolationPolicy(builder, (RequireBundleHeader)header);
			// Always add access to osgi.ee and osgi.native namespaces
			setImplicitAccessToNativeAndEECapabilities(builder);
		}
		RegionFilter regionFilter = builder.build();
		from.connectRegion(to, regionFilter);
	}

	private void setImportIsolationPolicy(RegionFilterBuilder builder, ImportPackageHeader header) throws InvalidSyntaxException {
		String policy = RegionFilter.VISIBLE_PACKAGE_NAMESPACE;
		if (header == null)
			return;
		for (ImportPackageHeader.Clause clause : header.getClauses()) {
			ImportPackageRequirement requirement = new ImportPackageRequirement(clause, this);
			String filter = requirement.getDirectives().get(ImportPackageRequirement.DIRECTIVE_FILTER);
			builder.allow(policy, filter);
		}
	}
	
	private void setImportIsolationPolicy(RegionFilterBuilder builder, RequireBundleHeader header) throws InvalidSyntaxException {
		if (header == null)
			return;
		for (RequireBundleHeader.Clause clause : header.getClauses()) {
			RequireBundleRequirement requirement = new RequireBundleRequirement(clause, this);
			String policy = RegionFilter.VISIBLE_REQUIRE_NAMESPACE;
			String filter = requirement.getDirectives().get(RequireBundleRequirement.DIRECTIVE_FILTER);
			builder.allow(policy, filter);
		}
	}

	private void setImportIsolationPolicy(RegionFilterBuilder builder, RequireCapabilityHeader header) throws InvalidSyntaxException {
		if (header == null)
			return;
		for (RequireCapabilityHeader.Clause clause : header.getClauses()) {
			RequireCapabilityRequirement requirement = new RequireCapabilityRequirement(clause, this);
			String policy = requirement.getNamespace();
			String filter = requirement.getDirectives().get(RequireCapabilityRequirement.DIRECTIVE_FILTER);
			if (filter == null)
				// A null filter directive means the requirement matches any
				// capability from the same namespace.
				builder.allowAll(policy);
			else
				// Otherwise, the capabilities must be filtered accordingly.
				builder.allow(policy, filter);
		}
	}

	private void setImplicitAccessToNativeAndEECapabilities(RegionFilterBuilder builder) {
		builder.allowAll(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE);
		builder.allowAll(NativeNamespace.NATIVE_NAMESPACE);
	}

	private void setImportIsolationPolicy(RegionFilterBuilder builder, SubsystemImportServiceHeader header) throws InvalidSyntaxException {
		if (header == null)
			return;
		for (SubsystemImportServiceHeader.Clause clause : header.getClauses()) {
			SubsystemImportServiceRequirement requirement = new SubsystemImportServiceRequirement(clause, this);
			String policy = RegionFilter.VISIBLE_SERVICE_NAMESPACE;
			String filter = requirement.getDirectives().get(SubsystemImportServiceRequirement.DIRECTIVE_FILTER);
			builder.allow(policy, filter);
		}
	}
}
