/*******************************************************************************
 * Copyright (c) 2011, 2013 VMware Inc.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    SpringSource, a division of VMware - initial API and implementation and/or initial documentation
 *******************************************************************************/

package org.eclipse.equinox.region;

import java.util.Set;
import org.osgi.framework.*;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;

/**
 * {@link RegionDigraph} is a <a href="http://en.wikipedia.org/wiki/Directed_graph">directed graph</a>, or
 * <i>digraph</i>, of {@link Region Regions}. The regions form the nodes of the graph and the edges connect regions to
 * other regions.
 * <p>
 * Each edge (r, s) of the digraph is directed from region r, known as the <i>tail</i> of the edge, to region s, known
 * as the <i>head</i> of the edge.
 * <p>
 * Each edge is associated with a {@link RegionFilter}, making the digraph a <i>labelled</i> digraph. The region filter
 * for edge (r, s) allows region r to see certain bundles, packages, and services visible in region s.
 * <p>
 * Although the digraph may contain cycles it does not contain any <i>loops</i> which are edges of the form (r, r) for
 * some region r. Loopless digraphs are known as <i>simple</i> digraphs. So the digraph is a simple, labelled digraph.
 * <p>
 * The region digraph extends <code>Iterable<Region></code> and so a foreach statement may be used to iterate over (a
 * snapshot of) the regions in the digraph, e.g.
 * 
 * <pre>
 * for (Region r : regionDigraph) {
 *   ...
 * }
 * </pre>
 * <p>
 * <strong>Concurrent Semantics</strong><br />
 * 
 * Implementations of this interface must be thread safe.
 * @noimplement This interface is not intended to be implemented by clients.
 */
public interface RegionDigraph extends Iterable<Region> {
	/**
	 * A {@link FilteredRegion} represents the head region and the {@link RegionFilter} used 
	 * in a connection with a tail region.
	 */
	public interface FilteredRegion {
		/**
		 * The head {@link Region} for which the filter is being applied.
		 * @return the head region.
		 */
		Region getRegion();

		/**
		 * The {@link RegionFilter} used to determine capabilities which are visible from the
		 * head region.
		 * @return the region filter.
		 */
		RegionFilter getFilter();
	}

	/**
	 * Create a {@link Region} with the given name. If a region with the given name already exists, then BundleException
	 * with exception type UNSUPPORTED_OPERATION is thrown.  If the region name is not valid then an
	 * IllegalArgumentException is thrown.  A valid region name contains none of the following 
	 * characters:
	 * <ul>
	 *   <li> : (colon)</li>
	 *   <li> = (equals)</li>
	 *   <li> \n (newline)</li>
	 *   <li> * (asterisk)</li>
	 *   <li> ? (question mark)</li>
	 *   <li> , (comma)</li>
	 *   <li> &quot; (double quotes)</li>
	 *   <li> \ (backslash)</li>
	 * </ul>
	 * 
	 * @param regionName the name of the region
	 * @return the {@link Region} created
	 * @throws BundleException if the region was not created
	 * @throws IllegalArgumentException if the region name is not valid
	 */
	Region createRegion(String regionName) throws BundleException;

	/**
	 * Create a {@link RegionFilterBuilder} instance.
	 * 
	 * @return a region filter builder
	 */
	RegionFilterBuilder createRegionFilterBuilder();

	/**
	 * Removes the given {@link Region} from the digraph along with any edges which have the given region as head or
	 * tail. If the given region is not present in the digraph, this is not an error and there is no effect.
	 * 
	 * @param region the {@link Region} to be removed
	 */
	void removeRegion(Region region);

	/**
	 * Gets all the {@link Region Regions} in the digraph.
	 * 
	 * @return a set of {@link Region Regions}
	 */
	Set<Region> getRegions();

	/**
	 * Gets the {@link Region} in the digraph with the given name.
	 * 
	 * @param regionName the name of the region
	 * @return the {@link Region} or <code>null</code> if no such region is present in the digraph
	 */
	Region getRegion(String regionName);

	/**
	 * Gets the {@link Region} in the digraph containing the given bundle.
	 * 
	 * @param bundle the bundle to search for
	 * @return the {@link Region} which contains the given bundle or <code>null</code> if there is no such region
	 */
	Region getRegion(Bundle bundle);

	/**
	 * Gets the {@link Region} in the digraph containing a bundle with the given bundle id.
	 * 
	 * @param bundleId the bundleId of the bundle to search for
	 * @return the {@link Region} which contains a bundle with the given bundle or <code>null</code> if there is no such
	 *         region
	 */
	Region getRegion(long bundleId);

	/**
	 * Connects a given tail region to a given head region via an edge labeled with the given {@link RegionFilter filter}. The
	 * tail region may then, subject to the region filter, see bundles, packages, and services visible in the head
	 * region.
	 * <p>
	 * The given head and tail regions are added to the digraph if they are not already present.
	 * <p>
	 * If the given tail region is already connected to the given head region, then BundleException with exception type
	 * UNSUPPORTED_OPERATION is thrown.
	 * <p>
	 * If the given head and the given tail are identical, then BundleException with exception type
	 * UNSUPPORTED_OPERATION is thrown.
	 * 
	 * @param tailRegion the region at the tail of the new edge
	 * @param filter a {@link RegionFilter} which labels the new edge
	 * @param headRegion the region at the head of the new edge
	 * @throws BundleException if the edge was not created
	 */
	void connect(Region tailRegion, RegionFilter filter, Region headRegion) throws BundleException;

	/**
	 * Replaces or creates a connection between a given tail region to a given head region via an edge
	 * labeled with the given {@link RegionFilter filter}. The tail region may then, subject to the
	 * filter, see bundles, packages, and services visible in the head region.
	 * <p>
	 * The given head and tail regions are added to the digraph if they are not already present.
	 * <p>
	 * If the given tail region is already connected to the given head region, then the existing 
	 * {@link RegionFilter filter} is replaced by the given {@link RegionFilter filter}.  If the given
	 * {@link RegionFilter filter} is {@code null} then the existing connection is removed.
	 * <p>
	 * If the given head and the given tail are identical, then BundleException with exception type
	 * UNSUPPORTED_OPERATION is thrown.
	 * 
	 * @param tailRegion the region at the tail of the new edge
	 * @param filter a {@link RegionFilter} which labels the new edge, or {@code null} to remove
	 *        an existing connection.
	 * @param headRegion the region at the head of the new edge
	 * @return the existing filter that was replaced or {@code null} if there was no existing filter
	 *         for the connection.
	 * @throws BundleException if the edge was not created
	 */
	RegionFilter replaceConnection(Region tailRegion, RegionFilter filter, Region headRegion) throws BundleException;

	/**
	 * Gets a {@link Set} containing a snapshot of the {@link FilteredRegion FilteredRegions} attached to the given tail
	 * region.
	 * 
	 * @param tailRegion the tail region whose edges are gotten
	 * @return a {@link Set} of {@link FilteredRegion FilteredRegions} of head regions and region filters
	 */
	Set<FilteredRegion> getEdges(Region tailRegion);

	/**
	 * Visit the subgraph connected to the given region.
	 * 
	 * @param startingRegion the region at which to start
	 * @param visitor a {@link RegionDigraphVisitor} to be called as the subgraph is navigated
	 */
	void visitSubgraph(Region startingRegion, RegionDigraphVisitor visitor);

	/**
	 * Gets a {@link RegionDigraphPersistence} object which can be used to save and load a {@link RegionDigraph} to and
	 * from persistent storage.
	 * 
	 * @return a {@link RegionDigraphPersistence} object.
	 */
	RegionDigraphPersistence getRegionDigraphPersistence();

	/**
	 * Creates a copy of this {@link RegionDigraph}.  Modifying the returned copy has no effect on this
	 * digraph.
	 * @return a copy of this digraph.
	 * @throws BundleException if the digraph could not be copied
	 */
	RegionDigraph copy() throws BundleException;

	/**
	 * Replaces the content of this digraph with the content of the supplied digraph.
	 * The supplied digraph must have been returned by a call to this digraph 
	 * {@link #copy()} method.  If this digraph has been modified between the 
	 * call to {@link #copy()} and {@link #replace(RegionDigraph)} then an
	 * exception is thrown.
	 * @param digraph the digraph to replace this digraph with.
	 * @throws BundleException if the digraph could not be replaced
	 */
	void replace(RegionDigraph digraph) throws BundleException;

	/**
	 * Gets the resolver hook factory associated with this digraph.
	 * @return the resolver hook factory
	 */
	ResolverHookFactory getResolverHookFactory();

	/**
	 * Gets the bundle event hook associated with this digraph.
	 * @return the bundle event hook
	 */
	org.osgi.framework.hooks.bundle.EventHook getBundleEventHook();

	/**
	 * Gets the bundle find hook associated with this digraph.
	 * @return the bundle find hook
	 */
	org.osgi.framework.hooks.bundle.FindHook getBundleFindHook();

	/**
	 * Gets the service event hook associated with this digraph.
	 * @return the service event hook
	 */
	@SuppressWarnings("deprecation")
	org.osgi.framework.hooks.service.EventHook getServiceEventHook();

	/**
	 * Gets the service find hook associated with this digraph.
	 * @return the service find hook
	 */
	org.osgi.framework.hooks.service.FindHook getServiceFindHook();

	/**
	 * Sets a {@link Region} as default one, where all bundles installed via {@link BundleContext} will be included.
	 * If the default {@link Region} isn't set newly installed bundles are assigned to their installer's region.
	 * 
	 * @param defaultRegion the region where all bundles installed via {@link BundleContext} will be assigned to
	 */
	void setDefaultRegion(Region defaultRegion);

	/**
	 * Gets the default {@link Region}, where all bundles installed via {@link BundleContext} are assigned.
	 * If the default {@link Region} isn't set newly installed bundles are assigned to their installer's region.
	 * 
	 * @return The default region to assign to or <b>null</b> if it isn't set
	 */
	Region getDefaultRegion();

}
