/*******************************************************************************
 * Copyright (c) 2005, 2007 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 
 *******************************************************************************/
package org.eclipse.dltk.core;

import java.util.HashMap;
import java.util.Hashtable;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.dltk.internal.core.BatchOperation;
import org.eclipse.dltk.internal.core.BuildpathAccessRule;
import org.eclipse.dltk.internal.core.BuildpathAttribute;
import org.eclipse.dltk.internal.core.BuildpathEntry;
import org.eclipse.dltk.internal.core.DLTKCorePreferenceInitializer;
import org.eclipse.dltk.internal.core.DefaultWorkingCopyOwner;
import org.eclipse.dltk.internal.core.Model;
import org.eclipse.dltk.internal.core.ModelManager;
import org.eclipse.dltk.internal.core.Region;
import org.eclipse.dltk.internal.core.ScriptProject;
import org.eclipse.dltk.internal.core.util.MementoTokenizer;
import org.eclipse.dltk.internal.core.util.Util;
import org.osgi.framework.BundleContext;

/**
 * The main plugin class to be used in the desktop.
 */
public class DLTKCore extends Plugin {

	// Debug/Verbose constants
	public static final boolean VERBOSE = false;
	public static final boolean VERBOSE_MODEL_MANAGER = false;
	public static final boolean VERBOSE_BP_RESOLVE = false;
	public static final boolean VERBOSE_ZIP_ACCESS = false;
	public static final boolean VERBOSE_EXTERNAL_FRAGMENT = false;
	public static final boolean VERBOSE_JOBMANAGER = false;
	public static final boolean VERBOSE_SEARCH = false;
	public static final boolean VERBOSE_SEARCH_NAMELOOKUP = false;
	public static final boolean VERBOSE_COMPLETION = false;
	public static final boolean VERBOSE_MIXIN = false;

	public static final boolean PERFOMANCE = false;

	public static final boolean DEBUG = Boolean
			.valueOf(Platform.getDebugOption("org.eclipse.dltk.core/debug")).booleanValue(); //$NON-NLS-1$

	public static final boolean SHOW_REINDEX = Boolean
			.valueOf(
					Platform
							.getDebugOption("org.eclipse.dltk.core/show_reindex")).booleanValue(); //$NON-NLS-1$

	public static final boolean DEBUG_PRINT_MODEL = Boolean
			.valueOf(
					Platform
							.getDebugOption("org.eclipse.dltk.core/debugPrintModel")).booleanValue(); //$NON-NLS-1$
	public static final boolean DEBUG_SCOPES = Boolean
			.valueOf(
					Platform
							.getDebugOption("org.eclipse.dltk.core/debugScopes")).booleanValue(); //$NON-NLS-1$
	public static final boolean DEBUG_SCRIPT_BUILDER = Boolean
			.valueOf(
					Platform
							.getDebugOption("org.eclipse.dltk.core/debugScriptBuilder")).booleanValue(); //$NON-NLS-1$

	public static final boolean TRACE_SCRIPT_BUILDER = Boolean
			.valueOf(
					Platform
							.getDebugOption("org.eclipse.dltk.core/traceScriptBuilder")).booleanValue(); //$NON-NLS-1$

	public static final boolean DEBUG_COMPLETION = Boolean
			.valueOf(
					Platform
							.getDebugOption("org.eclipse.dltk.core/debugCompletion")).booleanValue(); //$NON-NLS-1$
	public static final boolean DEBUG_SELECTION = Boolean
			.valueOf(
					Platform
							.getDebugOption("org.eclipse.dltk.core/debugSelection")).booleanValue(); //$NON-NLS-1$
	public static final boolean DEBUG_PARSER = Boolean
			.valueOf(
					Platform
							.getDebugOption("org.eclipse.dltk.core/debugParser")).booleanValue(); //$NON-NLS-1$
	public static final boolean DEBUG_INDEX = Boolean
			.valueOf(
					Platform.getDebugOption("org.eclipse.dltk.core/debugIndex")).booleanValue(); //$NON-NLS-1$

	// Log errors into log.
	public static final boolean DEBUG_LOG = false;

	// The shared instance.
	private static DLTKCore plugin;

	public final static String PLUGIN_ID = "org.eclipse.dltk.core"; //$NON-NLS-1$

	/**
	 * Name of the User Library Container id.
	 * 
	 */
	public static final String USER_LIBRARY_CONTAINER_ID = "org.eclipse.dltk.USER_LIBRARY"; //$NON-NLS-1$

	/**
	 * Possible configurable option value.
	 */
	public static final String ERROR = "error"; //$NON-NLS-1$

	public static final String BUILDER_ID = PLUGIN_ID + ".scriptbuilder"; //$NON-NLS-1$

	/**
	 * Possible configurable option value.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String ABORT = "abort"; //$NON-NLS-1$	
	/**
	 * Possible configurable option value.
	 * 
	 * @see #getDefaultOptions()
	 */
	public static final String WARNING = "warning"; //$NON-NLS-1$
	/**
	 * Possible configurable option value.
	 * 
	 * @see #getDefaultOptions()
	 */
	public static final String IGNORE = "ignore"; //$NON-NLS-1$

	/**
	 * Possible configurable option value.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String ENABLED = "enabled"; //$NON-NLS-1$
	/**
	 * Possible configurable option value.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String DISABLED = "disabled"; //$NON-NLS-1$
	/**
	 * Possible configurable option value.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CLEAN = "clean"; //$NON-NLS-1$

	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String COMPILER_PB_FORBIDDEN_REFERENCE = PLUGIN_ID
			+ ".compiler.problem.forbiddenReference"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String COMPILER_PB_DISCOURAGED_REFERENCE = PLUGIN_ID
			+ ".compiler.problem.discouragedReference"; //$NON-NLS-1$	

	/**
	 * Possible configurable option ID.
	 */
	public static final String CORE_ENCODING = PLUGIN_ID + ".encoding"; //$NON-NLS-1$

	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 */
	public static final String CORE_ENABLE_BUILDPATH_EXCLUSION_PATTERNS = PLUGIN_ID
			+ ".buildpath.exclusionPatterns"; //$NON-NLS-1$

	/**
	 * Possible configurable option ID.
	 */
	public static final String CORE_CIRCULAR_BUILDPATH = PLUGIN_ID
			+ ".circularBuildpath"; //$NON-NLS-1$

	/**
	 * public static final boolean DEBUG_PRINT_MODEL = false; Possible
	 * configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CORE_INCOMPLETE_BUILDPATH = PLUGIN_ID
			+ ".incompleteBuildpath"; //$NON-NLS-1$

	public static final String CORE_NON_LOCAL_EMPTY_FILE_CONTENT_TYPE_CHECKING = PLUGIN_ID
			+ ".nonLocalEmptyFileContentCheking"; //$NON-NLS-1$

	/*
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 */
	public static final String CODEASSIST_VISIBILITY_CHECK = PLUGIN_ID
			+ ".codeComplete.visibilityCheck"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_DEPRECATION_CHECK = PLUGIN_ID
			+ ".codeComplete.deprecationCheck"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_CAMEL_CASE_MATCH = PLUGIN_ID
			+ ".codeComplete.camelCaseMatch"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.public static final boolean DEBUG_PARSER
	 * = false;
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_IMPLICIT_QUALIFICATION = PLUGIN_ID
			+ ".codeComplete.forceImplicitQualification"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_FIELD_PREFIXES = PLUGIN_ID
			+ ".codeComplete.fieldPrefixes"; //$NON-NLS-1$	
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_LOCAL_PREFIXES = PLUGIN_ID
			+ ".codeComplete.localPrefixes"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_ARGUMENT_PREFIXES = PLUGIN_ID
			+ ".codeComplete.argumentPrefixes"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_FIELD_SUFFIXES = PLUGIN_ID
			+ ".codeComplete.fieldSuffixes"; //$NON-NLS-1$	
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_LOCAL_SUFFIXES = PLUGIN_ID
			+ ".codeComplete.localSuffixes"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_ARGUMENT_SUFFIXES = PLUGIN_ID
			+ ".codeComplete.argumentSuffixes"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_FORBIDDEN_REFERENCE_CHECK = PLUGIN_ID
			+ ".codeComplete.forbiddenReferenceCheck"; //$NON-NLS-1$
	/**
	 * Possible configurable option ID.
	 * 
	 * @see #getDefaultOptions()
	 * 
	 */
	public static final String CODEASSIST_DISCOURAGED_REFERENCE_CHECK = PLUGIN_ID
			+ ".codeComplete.discouragedReferenceCheck"; //$NON-NLS-1$					

	/**
	 * The constructor.
	 */
	public DLTKCore() {
		plugin = this;
	}

	/**
	 * Returns the single instance of the DLTK core plug-in runtime class.
	 * 
	 * @return the single instance of the DLTK core plug-in runtime class
	 */
	public static Plugin getPlugin() {
		return plugin;
	}

	/**
	 * Adds the given listener for changes to script elements. Has no effect if
	 * an identical listener is already registered.
	 * 
	 * This listener will only be notified during the POST_CHANGE resource
	 * change notification and any reconcile operation (POST_RECONCILE). For
	 * finer control of the notification, use
	 * <code>addElementChangedListener(IElementChangedListener,int)</code>,
	 * which allows to specify a different eventMask.
	 * 
	 * @param listener
	 * 		the listener
	 * @see ElementChangedEvent
	 */
	public static void addElementChangedListener(
			IElementChangedListener listener) {
		addElementChangedListener(listener, ElementChangedEvent.POST_CHANGE
				| ElementChangedEvent.POST_RECONCILE);
	}

	/**
	 * Adds the given listener for changes to script elements. Has no effect if
	 * an identical listener is already registered. After completion of this
	 * method, the given listener will be registered for exactly the specified
	 * events. If they were previously registered for other events, they will be
	 * deregistered.
	 * <p>
	 * Once registered, a listener starts receiving notification of changes to
	 * elements in the model. The listener continues to receive notifications
	 * until it is replaced or removed.
	 * </p>
	 * <p>
	 * Listeners can listen for several types of event as defined in
	 * <code>ElementChangeEvent</code>. Clients are free to register for any
	 * number of event types however if they register for more than one, it is
	 * their responsibility to ensure they correctly handle the case where the
	 * same element change shows up in multiple notifications. Clients are
	 * guaranteed to receive only the events for which they are registered.
	 * </p>
	 * 
	 * @param listener
	 * 		the listener
	 * @param eventMask
	 * 		the bit-wise OR of all event types of interest to the listener
	 * @see IElementChangedListener
	 * @see ElementChangedEvent
	 * @see #removeElementChangedListener(IElementChangedListener)
	 * 
	 */
	public static void addElementChangedListener(
			IElementChangedListener listener, int eventMask) {
		ModelManager.getModelManager().deltaState.addElementChangedListener(
				listener, eventMask);
	}

	/**
	 * Removes the given element changed listener. Has no affect if an identical
	 * listener is not registered.
	 * 
	 * @param listener
	 * 		the listener
	 */
	public static void removeElementChangedListener(
			IElementChangedListener listener) {
		ModelManager.getModelManager().deltaState
				.removeElementChangedListener(listener);
	}

	/**
	 * Returns the workspace root default charset encoding.
	 * 
	 * @return the name of the default charset encoding for workspace root.
	 * @see IContainer#getDefaultCharset()
	 * @see ResourcesPlugin#getEncoding()
	 * 
	 */
	public static String getEncoding() {
		// Verify that workspace is not shutting down (see bug
		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=60687)
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		if (workspace != null) {
			try {
				return workspace.getRoot().getDefaultCharset();
			} catch (CoreException e) {
				// fails silently and return plugin global encoding if core
				// exception occurs
			}
		}
		return ResourcesPlugin.getEncoding();
	}

	/**
	 * Returns the model element corresponding to the given file, or
	 * <code>null</code> if unable to associate the given file with a model
	 * element.
	 * 
	 * Creating a model element has the side effect of creating and opening all
	 * of the element's parents if they are not yet open.
	 * 
	 * @param file
	 * 		the given file
	 * @return the model element corresponding to the given file, or
	 * 	<code>null</code> if unable to associate the given file with a model
	 * 	element
	 */
	public static IModelElement create(IFile file) {
		return ModelManager.create(file, null/* unknown dltk project */);
	}

	public static IModelElement create(IResource resource) {
		return ModelManager.create(resource, null/* unknown dltk project */);
	}

	/**
	 * Returns the model.
	 * 
	 * @param root
	 * 		the given root
	 * @return the model, or <code>null</code> if the root is null
	 */
	public static IScriptModel create(IWorkspaceRoot root) {
		if (root == null) {
			return null;
		}
		return ModelManager.getModelManager().getModel();
	}

	/**
	 * Returns the dylan project corresponding to the given project.
	 * <p>
	 * Creating a Dylan Project has the side effect of creating and opening all
	 * of the project's parents if they are not yet open.
	 * <p>
	 * Note that no check is done at this time on the existence or the nature of
	 * this project.
	 * 
	 * @param project
	 * 		the given project
	 * @return the Dylan project corresponding to the given project, null if the
	 * 	given project is null
	 */
	public static IScriptProject create(IProject project) {
		if (project == null) {
			return null;
		}
		Model model = ModelManager.getModelManager().getModel();
		return model.getScriptProject(project);
	}

	/**
	 * Creates and returns a new buildpath entry of kind <code>BPE_SOURCE</code>
	 * for all files in the project's source folder identified by the given
	 * absolute workspace-relative path.
	 * <p>
	 * The convenience method is fully equivalent to:
	 * 
	 * <pre>
	 * newSourceEntry(path, new IPath[] {}, new IPath[] {});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param path
	 * 		the absolute workspace-relative path of a source folder
	 * @return a new source buildpath entry
	 * @see #newSourceEntry(IPath, IPath[], IPath[])
	 */
	public static IBuildpathEntry newSourceEntry(IPath path) {

		return newSourceEntry(path, BuildpathEntry.INCLUDE_ALL,
				BuildpathEntry.EXCLUDE_NONE);
	}

	/**
	 * Creates and returns a new buildpath entry of kind <code>BPE_SOURCE</code>
	 * for the project's source folder identified by the given absolute
	 * workspace-relative path but excluding all source files with paths
	 * matching any of the given patterns.
	 * <p>
	 * The convenience method is fully equivalent to:
	 * 
	 * <pre>
	 * newSourceEntry(path, new IPath[] {}, exclusionPatterns);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param path
	 * 		the absolute workspace-relative path of a source folder
	 * @param exclusionPatterns
	 * 		the possibly empty list of exclusion patterns represented as
	 * 		relative paths
	 * @return a new source buildpath entry
	 * @see #newSourceEntry(IPath, IPath[], IPath[], IPath)
	 * 
	 */
	public static IBuildpathEntry newSourceEntry(IPath path,
			IPath[] exclusionPatterns) {

		return newSourceEntry(path, BuildpathEntry.INCLUDE_ALL,
				exclusionPatterns);
	}

	/**
	 * Creates and returns a new buildpath entry of kind <code>CPE_SOURCE</code>
	 * for the project's source folder identified by the given absolute
	 * workspace-relative path but excluding all source files with paths
	 * matching any of the given patterns, and associated with a specific output
	 * location (that is, ".class" files are not going to the project default
	 * output location).
	 * <p>
	 * The convenience method is fully equivalent to:
	 * 
	 * <pre>
	 * newSourceEntry(path, new IPath[] {}, exclusionPatterns, specificOutputLocation,
	 * 		new IBuildpathAttribute[] {});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param path
	 * 		the absolute workspace-relative path of a source folder
	 * @param inclusionPatterns
	 * 		the possibly empty list of inclusion patterns represented as
	 * 		relative paths
	 * @param exclusionPatterns
	 * 		the possibly empty list of exclusion patterns represented as
	 * 		relative paths
	 * @param specificOutputLocation
	 * 		the specific output location for this source entry (
	 * 		<code>null</code> if using project default ouput location)
	 * @return a new source buildpath entry
	 * @see #newSourceEntry(IPath, IPath[], IPath[], IPath,
	 * 	IBuildpathAttribute[])
	 * 
	 */
	public static IBuildpathEntry newSourceEntry(IPath path,
			IPath[] inclusionPatterns, IPath[] exclusionPatterns) {
		return newSourceEntry(path, inclusionPatterns, exclusionPatterns,
				BuildpathEntry.NO_EXTRA_ATTRIBUTES);
	}

	/**
	 * Creates and returns a new buildpath entry of kind <code>CPE_SOURCE</code>
	 * for the project's source folder identified by the given absolute
	 * workspace-relative path using the given inclusion and exclusion patterns
	 * to determine which source files are included, and the given output path
	 * to control the output location of generated files.
	 * <p>
	 * The source folder is referred to using an absolute path relative to the
	 * workspace root, e.g. <code>/Project/src</code>. A project's source
	 * folders are located with that project. That is, a source buildpath entry
	 * specifying the path <code>/P1/src</code> is only usable for project
	 * <code>P1</code>.
	 * </p>
	 * <p>
	 * The inclusion patterns determines the initial set of source files that
	 * are to be included; the exclusion patterns are then used to reduce this
	 * set. When no inclusion patterns are specified, the initial file set
	 * includes all relevent files in the resource tree rooted at the source
	 * entry's path. On the other hand, specifying one or more inclusion
	 * patterns means that all <b>and only</b> files matching at least one of
	 * the specified patterns are to be included. If exclusion patterns are
	 * specified, the initial set of files is then reduced by eliminating files
	 * matched by at least one of the exclusion patterns. Inclusion and
	 * exclusion patterns look like relative file paths with wildcards and are
	 * interpreted relative to the source entry's path. File patterns are
	 * case-sensitive can contain '**', '*' or '?' wildcards (see {@link
	 * IBuildpathEntry#getExclusionPatterns()} for the full description of their
	 * syntax and semantics). The resulting set of files are included in the
	 * corresponding package fragment root; all package fragments within the
	 * root will have children of type <code>ISourceModule</code>.
	 * </p>
	 * <p>
	 * For example, if the source folder path is <code>/Project/src</code>,
	 * there are no inclusion filters, and the exclusion pattern is
	 * <code>com/xyz/tests/&#42;&#42;</code>, then source files like
	 * <code>/Project/src/com/xyz/Foo.java</code> and
	 * <code>/Project/src/com/xyz/utils/Bar.java</code> would be included,
	 * whereas <code>/Project/src/com/xyz/tests/T1.java</code> and
	 * <code>/Project/src/com/xyz/tests/quick/T2.java</code> would be excluded.
	 * </p>
	 * <p>
	 * Additionally, a source entry can be associated with a specific output
	 * location. By doing so, the script builder will ensure that the generated
	 * ".class" files will be issued inside this output location, as opposed to
	 * be generated into the project default output location (when output
	 * location is <code>null</code>). Note that multiple source entries may
	 * target the same output location. The output location is referred to using
	 * an absolute path relative to the workspace root, e.g.
	 * <code>"/Project/bin"</code>, it must be located inside the same project
	 * as the source folder.
	 * </p>
	 * <p>
	 * Also note that all sources/binaries inside a project are contributed as a
	 * whole through a project entry (see <code>DLTKCore.newProjectEntry</code>).
	 * Particular source entries cannot be selectively exported.
	 * </p>
	 * <p>
	 * The <code>extraAttributes</code> list contains name/value pairs that must
	 * be persisted with this entry. If no extra attributes are provided, an
	 * empty array must be passed in.<br>
	 * Note that this list should not contain any duplicate name.
	 * </p>
	 * 
	 * @param path
	 * 		the absolute workspace-relative path of a source folder
	 * @param inclusionPatterns
	 * 		the possibly empty list of inclusion patterns represented as
	 * 		relative paths
	 * @param exclusionPatterns
	 * 		the possibly empty list of exclusion patterns represented as
	 * 		relative paths
	 * @param specificOutputLocation
	 * 		the specific output location for this source entry (
	 * 		<code>null</code> if using project default ouput location)
	 * @param extraAttributes
	 * 		the possibly empty list of extra attributes to persist with this
	 * 		entry
	 * @return a new source buildpath entry with the given exclusion patterns
	 * @see IBuildpoathEntry#getInclusionPatterns()
	 * @see IBuildpoathEntry#getExclusionPatterns()
	 * @see IBuildpoathEntry#getOutputLocation()
	 * 
	 */
	public static IBuildpathEntry newSourceEntry(IPath path,
			IPath[] inclusionPatterns, IPath[] exclusionPatterns,
			IBuildpathAttribute[] extraAttributes) {

		if (path == null)
			Assert.isTrue(false, "Source path cannot be null"); //$NON-NLS-1$
		if (!path.isAbsolute())
			Assert.isTrue(false, "Path for IBuildpathEntry must be absolute"); //$NON-NLS-1$
		if (exclusionPatterns == null)
			Assert.isTrue(false, "Exclusion pattern set cannot be null"); //$NON-NLS-1$
		if (inclusionPatterns == null)
			Assert.isTrue(false, "Inclusion pattern set cannot be null"); //$NON-NLS-1$

		return new BuildpathEntry(IProjectFragment.K_SOURCE,
				IBuildpathEntry.BPE_SOURCE, path, false, inclusionPatterns,
				exclusionPatterns, null, false, // no access rules to combine
				extraAttributes, false);
	}

	/**
	 * Creates and returns a new non-exported buildpath entry of kind
	 * <code>BPE_PROJECT</code> for the project identified by the given absolute
	 * path. This method is fully equivalent to calling {@link
	 * #newProjectEntry(IPath, boolean, boolean) newProjectEntry(path, true,
	 * false)}.
	 * 
	 * @param path
	 * 		the absolute path of the binary archive
	 * @return a new project buildpath entry
	 */
	public static IBuildpathEntry newProjectEntry(IPath path) {
		return newProjectEntry(path, false);
	}

	/**
	 * Creates and returns a new buildpath entry of kind
	 * <code>CPE_PROJECT</code> for the project identified by the given absolute
	 * path. This method is fully equivalent to calling {@link
	 * #newProjectEntry(IPath, IAccessRule[], boolean, IBuildpathAttribute[],
	 * boolean) newProjectEntry(path, new IAccessRule[0], true, new
	 * IBuildpathAttribute[0], isExported)}.
	 * 
	 * @param path
	 * 		the absolute path of the prerequisite project
	 * @param isExported
	 * 		indicates whether this entry is contributed to dependent projects in
	 * 		addition to the output location
	 * @return a new project buildpath entry
	 * 
	 */
	public static IBuildpathEntry newProjectEntry(IPath path, boolean isExported) {

		if (!path.isAbsolute())
			Assert.isTrue(false, "Path for IBuildpathEntry must be absolute"); //$NON-NLS-1$

		return newProjectEntry(path, BuildpathEntry.NO_ACCESS_RULES, true,
				BuildpathEntry.NO_EXTRA_ATTRIBUTES, isExported);
	}

	/**
	 * Creates and returns a new buildpath entry of kind
	 * <code>CPE_PROJECT</code> for the project identified by the given absolute
	 * path.
	 * <p>
	 * A project entry is used to denote a prerequisite project on a buildpath.
	 * The referenced project will be contributed as a whole, either as sources
	 * (in the script Model, it contributes all its package fragment roots) or
	 * as binaries (when building, it contributes its whole output location).
	 * </p>
	 * <p>
	 * A project reference allows to indirect through another project,
	 * independently from its internal layout.
	 * </p>
	 * <p>
	 * The prerequisite project is referred to using an absolute path relative
	 * to the workspace root.
	 * </p>
	 * <p>
	 * The access rules determine the set of accessible source files in the
	 * project. If the list of access rules is empty then all files in this
	 * project are accessible. See {@link IAccessRule} for a detailed
	 * description of access rules.
	 * </p>
	 * <p>
	 * The <code>combineAccessRules</code> flag indicates whether access rules
	 * of one (or more) exported entry of the project should be combined with
	 * the given access rules. If they should be combined, the given access
	 * rules are considered first, then the entry's access rules are considered.
	 * </p>
	 * <p>
	 * The <code>extraAttributes</code> list contains name/value pairs that must
	 * be persisted with this entry. If no extra attributes are provided, an
	 * empty array must be passed in.<br>
	 * Note that this list should not contain any duplicate name.
	 * </p>
	 * <p>
	 * The <code>isExported</code> flag indicates whether this entry is
	 * contributed to dependent projects. If not exported, dependent projects
	 * will not see any of the classes from this entry. If exported, dependent
	 * projects will concatenate the accessible files patterns of this entry
	 * with the accessible files patterns of the projects, and they will
	 * concatenate the non accessible files patterns of this entry with the non
	 * accessible files patterns of the project.
	 * </p>
	 * 
	 * @param path
	 * 		the absolute path of the prerequisite project
	 * @param accessRules
	 * 		the possibly empty list of access rules for this entry
	 * @param combineAccessRules
	 * 		whether the access rules of the project's exported entries should be
	 * 		combined with the given access rules
	 * @param extraAttributes
	 * 		the possibly empty list of extra attributes to persist with this
	 * 		entry
	 * @param isExported
	 * 		indicates whether this entry is contributed to dependent projects in
	 * 		addition to the output location
	 * @return a new project buildpath entry
	 * 
	 */
	public static IBuildpathEntry newProjectEntry(IPath path,
			IAccessRule[] accessRules, boolean combineAccessRules,
			IBuildpathAttribute[] extraAttributes, boolean isExported) {

		if (!path.isAbsolute())
			Assert.isTrue(false, "Path for IBuildpathEntry must be absolute"); //$NON-NLS-1$

		return new BuildpathEntry(IProjectFragment.K_SOURCE,
				IBuildpathEntry.BPE_PROJECT, path, isExported,
				BuildpathEntry.INCLUDE_ALL, // inclusion patterns
				BuildpathEntry.EXCLUDE_NONE, // exclusion patterns
				accessRules, combineAccessRules, extraAttributes, false);
	}

	/**
	 * Creates and returns a new access rule with the given file pattern and
	 * kind.
	 * <p>
	 * The rule kind is one of {@link IAccessRule#K_ACCESSIBLE}, {@link
	 * IAccessRule#K_DISCOURAGED}, or {@link IAccessRule#K_NON_ACCESSIBLE},
	 * optionally combined with {@link IAccessRule#IGNORE_IF_BETTER}, e..g.
	 * <code>IAccessRule.K_NON_ACCESSIBLE | IAccessRule.IGNORE_IF_BETTER</code>.
	 * </p>
	 * 
	 * @param filePattern
	 * 		the file pattern this access rule should match
	 * @param kind
	 * 		one of {@link IAccessRule#K_ACCESSIBLE}, {@link
	 * 		IAccessRule#K_DISCOURAGED}, or {@link IAccessRule#K_NON_ACCESSIBLE},
	 * 		optionally combined with {@link IAccessRule#IGNORE_IF_BETTER}
	 * @return a new access rule
	 * 
	 */
	public static IAccessRule newAccessRule(IPath filePattern, int kind) {
		return new BuildpathAccessRule(filePattern, kind);
	}

	/**
	 * Creates and returns a new buildpath attribute with the given name and the
	 * given value.
	 * 
	 * @return a new buildpath attribute
	 * 
	 */
	public static IBuildpathAttribute newBuildpathAttribute(String name,
			String value) {
		return new BuildpathAttribute(name, value);
	}

	/**
	 * Creates and returns a new buildpath entry of kind
	 * <code>CPE_CONTAINER</code> for the given path. This method is fully
	 * equivalent to calling {@link #newContainerEntry(IPath, IAccessRule[],
	 * IBuildpathAttribute[], boolean) newContainerEntry(containerPath, new
	 * IAccessRule[0], new IBuildpathAttribute[0], false)}.
	 * <p>
	 * 
	 * @param containerPath
	 * 		the path identifying the container, it must be formed of two
	 * 		segments
	 * @return a new container buildpath entry
	 * 
	 * @see DLTKCore#getBuildpathContainer(IPath, IScriptProject)
	 * 
	 */
	public static IBuildpathEntry newContainerEntry(IPath containerPath) {
		return newContainerEntry(containerPath, BuildpathEntry.NO_ACCESS_RULES,
				BuildpathEntry.NO_EXTRA_ATTRIBUTES, false/* not exported */);
	}

	/**
	 * Creates and returns a new buildpath entry of kind
	 * <code>CPE_CONTAINER</code> for the given path. This method is fully
	 * equivalent to calling {@link #newContainerEntry(IPath, IAccessRule[],
	 * IBuildpathAttribute[], boolean) newContainerEntry(containerPath, new
	 * IAccessRule[0], new IBuildpathAttribute[0], isExported)}.
	 * 
	 * @param containerPath
	 * 		the path identifying the container, it must be formed of at least
	 * 		one segment (ID+hints)
	 * @param isExported
	 * 		a boolean indicating whether this entry is contributed to dependent
	 * 		projects in addition to the output location
	 * @return a new container buildpath entry
	 * 
	 * @see DLTKCore#getBuildpathContainer(IPath, IScriptProject)
	 * @see DLTKCore#setBuildpathContainer(IPath, IScriptProject[],
	 * 	IBuildpathContainer[], IProgressMonitor)
	 * 
	 */
	public static IBuildpathEntry newContainerEntry(IPath containerPath,
			boolean isExported) {
		return newContainerEntry(containerPath, BuildpathEntry.NO_ACCESS_RULES,
				BuildpathEntry.NO_EXTRA_ATTRIBUTES, isExported);
	}

	/**
	 * Creates and returns a new buildpath entry of kind
	 * <code>CPE_CONTAINER</code> for the given path. The path of the container
	 * will be used during resolution so as to map this container entry to a set
	 * of other buildpath entries the container is acting for.
	 * <p>
	 * A container entry allows to express indirect references to a set of
	 * libraries, projects and variable entries, which can be interpreted
	 * differently for each script project where it is used. A buildpath
	 * container entry can be resolved using
	 * <code>DLTKCore.getResolvedBuildpathContainer</code>, and updated with
	 * <code>DLTKCore.buildpathContainerChanged</code>
	 * <p>
	 * A container is exclusively resolved by a
	 * <code>BuildpathContainerInitializer</code> registered onto the extension
	 * point "org.eclipse.dltk.core.buildpathContainerInitializer".
	 * <p>
	 * A container path must be formed of at least one segment, where:
	 * <ul>
	 * <li>the first segment is a unique ID identifying the target container,
	 * there must be a container initializer registered onto this ID through the
	 * extension point "org.eclipse.dltk.core.buildpathContainerInitializer".
	 * </li>
	 * <li>the remaining segments will be passed onto the initializer, and can
	 * be used as additional hints during the initialization phase.</li>
	 * </ul>
	 * The access rules determine the set of accessible source and source files
	 * in the container. If the list of access rules is empty, then all files in
	 * this container are accessible. See {@link IAccessRule} for a detailed
	 * description of access rules. Note that if an entry defined by the
	 * container defines access rules, then these access rules are combined with
	 * the given access rules. The given access rules are considered first, then
	 * the entry's access rules are considered.
	 * </p>
	 * <p>
	 * The <code>extraAttributes</code> list contains name/value pairs that must
	 * be persisted with this entry. If no extra attributes are provided, an
	 * empty array must be passed in.<br>
	 * Note that this list should not contain any duplicate name.
	 * </p>
	 * <p>
	 * The <code>isExported</code> flag indicates whether this entry is
	 * contributed to dependent projects. If not exported, dependent projects
	 * will not see any of the classes from this entry. If exported, dependent
	 * projects will concatenate the accessible files patterns of this entry
	 * with the accessible files patterns of the projects, and they will
	 * concatenate the non accessible files patterns of this entry with the non
	 * accessible files patterns of the project.
	 * </p>
	 * <p>
	 * Note that this operation does not attempt to validate buildpath
	 * containers or access the resources at the given paths.
	 * </p>
	 * 
	 * @param containerPath
	 * 		the path identifying the container, it must be formed of at least
	 * 		one segment (ID+hints)
	 * @param accessRules
	 * 		the possibly empty list of access rules for this entry
	 * @param extraAttributes
	 * 		the possibly empty list of extra attributes to persist with this
	 * 		entry
	 * @param isExported
	 * 		a boolean indicating whether this entry is contributed to dependent
	 * 		projects in addition to the output location
	 * @return a new container buildpath entry
	 * 
	 * @see DLTKCore#getBuildpathContainer(IPath, IScriptProject)
	 * @see DLTKCore#setBuildpathContainer(IPath, IScriptProject[],
	 * 	IBuildpathContainer[], IProgressMonitor)
	 * @see DLTKCore#newContainerEntry(IPath, boolean)
	 * @see DLTKCore#newAccessRule(IPath, int)
	 * 
	 */
	public static IBuildpathEntry newContainerEntry(IPath containerPath,
			IAccessRule[] accessRules, IBuildpathAttribute[] extraAttributes,
			boolean isExported) {

		if (containerPath == null) {
			Assert.isTrue(false, "Container path cannot be null"); //$NON-NLS-1$
		} else if (containerPath.segmentCount() < 1) {
			Assert
					.isTrue(
							false,
							"Illegal buildpath container path: \'" + containerPath.makeRelative().toString() + "\', must have at least one segment (containerID+hints)"); //$NON-NLS-1$//$NON-NLS-2$
		}
		BuildpathEntry entry = new BuildpathEntry(IProjectFragment.K_SOURCE,
				IBuildpathEntry.BPE_CONTAINER, containerPath, isExported,
				BuildpathEntry.INCLUDE_ALL, // inclusion patterns
				BuildpathEntry.EXCLUDE_NONE, // exclusion patterns
				accessRules, true, // comsbine access rules
				extraAttributes, false);
		
		entry.setIsContainerEntry(true);
		return entry;
	}

	/**
	 * Helper method for returning one option value only. Equivalent to
	 * <code>(String)ScriptCore.getOptions().get(optionName)</code> Note that it
	 * may answer <code>null</code> if this option does not exist.
	 * <p>
	 * For a complete description of the configurable options, see
	 * <code>getDefaultOptions</code>.
	 * </p>
	 * 
	 * @param optionName
	 * 		the name of an option
	 * @return the String value of a given option
	 * @see ScriptCore#getDefaultOptions()
	 */
	public static String getOption(String optionName) {
		return ModelManager.getModelManager().getOption(optionName);
	}

	/**
	 * Returns the table of the current options. Initially, all options have
	 * their default values, and this method returns a table that includes all
	 * known options.
	 * <p>
	 * For a complete description of the configurable options, see
	 * <code>getDefaultOptions</code>.
	 * </p>
	 * <p>
	 * Returns a default set of options even if the platform is not running.
	 * </p>
	 * 
	 * @return table of current settings of all options (key type:
	 * 	<code>String</code>; value type: <code>String</code>)
	 * @see #getDefaultOptions()
	 * @see DLTKCorePreferenceInitializer for changing default settings
	 */
	public static Hashtable getOptions() {
		return ModelManager.getModelManager().getOptions();
	}

	/**
	 * This method is called upon plug-in activation
	 */
	public void start(BundleContext context) throws Exception {
		super.start(context);
		ModelManager.getModelManager().startup();
	}

	/**
	 * This method is called when the plug-in is stopped
	 */
	public void stop(BundleContext context) throws Exception {
		try {
			ModelManager.getModelManager().shutdown();
		} finally {
			super.stop(context);
			plugin = null;
		}
	}

	/**
	 * Returns the shared instance.
	 */
	public static DLTKCore getDefault() {
		return plugin;
	}

	/**
	 * Runs the given action as an atomic script model operation.
	 * <p>
	 * After running a method that modifies elements, registered listeners
	 * receive after-the-fact notification of what just transpired, in the form
	 * of a element changed event. This method allows clients to call a number
	 * of methods that modify elements and only have element changed event
	 * notifications reported at the end of the entire batch.
	 * </p>
	 * <p>
	 * If this method is called outside the dynamic scope of another such call,
	 * this method runs the action and then reports a single element changed
	 * event describing the net effect of all changes done to elements by the
	 * action.
	 * </p>
	 * <p>
	 * If this method is called in the dynamic scope of another such call, this
	 * method simply runs the action.
	 * </p>
	 * 
	 * @param action
	 * 		the action to perform
	 * @param monitor
	 * 		a progress monitor, or <code>null</code> if progress reporting and
	 * 		cancellation are not desired
	 * @exception CoreException
	 * 		if the operation failed.
	 * 
	 */
	public static void run(IWorkspaceRunnable action, IProgressMonitor monitor)
			throws CoreException {
		run(action, ResourcesPlugin.getWorkspace().getRoot(), monitor);
	}

	/**
	 * Runs the given action as an atomic script model operation.
	 * <p>
	 * After running a method that modifies elements, registered listeners
	 * receive after-the-fact notification of what just transpired, in the form
	 * of a element changed event. This method allows clients to call a number
	 * of methods that modify elements and only have element changed event
	 * notifications reported at the end of the entire batch.
	 * </p>
	 * <p>
	 * If this method is called outside the dynamic scope of another such call,
	 * this method runs the action and then reports a single element changed
	 * event describing the net effect of all changes done to elements by the
	 * action.
	 * </p>
	 * <p>
	 * If this method is called in the dynamic scope of another such call, this
	 * method simply runs the action.
	 * </p>
	 * <p>
	 * The supplied scheduling rule is used to determine whether this operation
	 * can be run simultaneously with workspace changes in other threads. See
	 * <code>IWorkspace.run(...)</code> for more details.
	 * </p>
	 * 
	 * @param action
	 * 		the action to perform
	 * @param rule
	 * 		the scheduling rule to use when running this operation, or
	 * 		<code>null</code> if there are no scheduling restrictions for this
	 * 		operation.
	 * @param monitor
	 * 		a progress monitor, or <code>null</code> if progress reporting and
	 * 		cancellation are not desired
	 * @exception CoreException
	 * 		if the operation failed.
	 * 
	 */
	public static void run(IWorkspaceRunnable action, ISchedulingRule rule,
			IProgressMonitor monitor) throws CoreException {
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		if (workspace.isTreeLocked()) {
			new BatchOperation(action).run(monitor);
		} else {
			// use IWorkspace.run(...) to ensure that a build will be done in
			// autobuild mode
			workspace.run(new BatchOperation(action), rule,
					IWorkspace.AVOID_UPDATE, monitor);
		}
	}

	/**
	 * Answers the project specific value for a given buildpath container. In
	 * case this container path could not be resolved, then will answer
	 * <code>null</code>. Both the container path and the project context are
	 * supposed to be non-null.
	 * <p>
	 * The containerPath is a formed by a first ID segment followed with extra
	 * segments, which can be used as additional hints for resolution. If no
	 * container was ever recorded for this container path onto this project
	 * (using <code>setBuildpathContainer</code>, then a
	 * <code>buildpathContainerInitializer</code> will be activated if any was
	 * registered for this container ID onto the extension point
	 * "org.eclipse.dltk.core.buildpathContainerInitializer".
	 * <p>
	 * There is no assumption that the returned container must answer the exact
	 * same containerPath when requested
	 * <code>IBuildpathContainer#getPath</code>. Indeed, the containerPath is
	 * just an indication for resolving it to an actual container object.
	 * <p>
	 * buildpath container values are persisted locally to the workspace, but
	 * are not preserved from a session to another. It is thus highly
	 * recommended to register a <code>buildpathContainerInitializer</code> for
	 * each referenced container (through the extension point
	 * "org.eclipse.dltk.core.buildpathContainerInitializer").
	 * <p>
	 * 
	 * @param containerPath
	 * 		the name of the container, which needs to be resolved
	 * @param project
	 * 		a specific project in which the container is being resolved
	 * @return the corresponding buildpath container or <code>null</code> if
	 * 	unable to find one.
	 * 
	 * @exception ModelException
	 * 		if an exception occurred while resolving the container, or if the
	 * 		resolved container contains illegal entries (contains BPE_CONTAINER
	 * 		entries or null entries).
	 * 
	 * @see buildpathContainerInitializer
	 * @see IBuildpathContainer
	 * @see #setbuildpathContainer(IPath, IScriptProject[],
	 * 	IBuildpathContainer[], IProgressMonitor)
	 * 
	 */
	public static IBuildpathContainer getBuildpathContainer(
			IPath containerPath, IScriptProject project) throws ModelException {
		ModelManager manager = ModelManager.getModelManager();
		IBuildpathContainer container = manager.getBuildpathContainer(
				containerPath, project);
		if (container == ModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) {
			return manager.getPreviousSessionContainer(containerPath, project);
		}
		return container;
	}

	/**
	 * Helper method finding the buildpath container initializer registered for
	 * a given buildpath container ID or <code>null</code> if none was found
	 * while iterating over the contributions to extension point to the
	 * extension point "org.eclipse.dltk.core.buildpathContainerInitializer".
	 * <p>
	 * A containerID is the first segment of any container path, used to
	 * identify the registered container initializer.
	 * <p>
	 * 
	 * @param containerID
	 * 		- a containerID identifying a registered initializer
	 * @return BuildpathContainerInitializer - the registered buildpath
	 * 	container initializer or <code>null</code> if none was found.
	 */
	public static BuildpathContainerInitializer getBuildpathContainerInitializer(
			String containerID) {
		HashMap containerInitializersCache = ModelManager.getModelManager().containerInitializersCache;
		BuildpathContainerInitializer initializer = (BuildpathContainerInitializer) containerInitializersCache
				.get(containerID);
		if (initializer == null) {
			initializer = computeBuildpathContainerInitializer(containerID);
			if (initializer == null)
				return null;
			containerInitializersCache.put(containerID, initializer);
		}
		return initializer;
	}

	private static BuildpathContainerInitializer computeBuildpathContainerInitializer(
			String containerID) {
		Plugin dltkCorePlugin = DLTKCore.getPlugin();
		if (dltkCorePlugin == null)
			return null;

		IExtensionPoint extension = Platform.getExtensionRegistry()
				.getExtensionPoint(DLTKCore.PLUGIN_ID,
						ModelManager.BPCONTAINER_INITIALIZER_EXTPOINT_ID);
		if (extension != null) {
			IExtension[] extensions = extension.getExtensions();
			for (int i = 0; i < extensions.length; i++) {
				IConfigurationElement[] configElements = extensions[i]
						.getConfigurationElements();
				for (int j = 0; j < configElements.length; j++) {
					String initializerID = configElements[j].getAttribute("id"); //$NON-NLS-1$
					if (initializerID != null
							&& initializerID.equals(containerID)) {
						if (ModelManager.BP_RESOLVE_VERBOSE) {
							org.eclipse.dltk.internal.core.util.Util
									.verbose("BPContainer INIT - found initializer\n" + //$NON-NLS-1$
											"	container ID: "
											+ containerID
											+ '\n'
											+ //$NON-NLS-1$
											"	class: "
											+ configElements[j]
													.getAttribute("class")); //$NON-NLS-1$ //$NON-NLS-2$
						}
						try {
							Object execExt = configElements[j]
									.createExecutableExtension("class"); //$NON-NLS-1$
							if (execExt instanceof BuildpathContainerInitializer) {
								return (BuildpathContainerInitializer) execExt;
							}
						} catch (CoreException e) {
							// executable extension could not be created: ignore
							// this initializer
							if (ModelManager.BP_RESOLVE_VERBOSE) {
								Util
										.verbose(
												"BPContainer INIT - failed to instanciate initializer\n" + //$NON-NLS-1$
														"	container ID: "
														+ containerID + '\n'
														+ //$NON-NLS-1$
														"	class: "
														+ configElements[j]
																.getAttribute("class"), //$NON-NLS-1$ //$NON-NLS-2$
												System.err);
								e.printStackTrace();
							}
						}
					}
				}
			}
		}
		return null;
	}

	/**
	 * Bind a container reference path to some actual containers (
	 * <code>IBuildpathContainer</code>). This API must be invoked whenever
	 * changes in container need to be reflected onto the Model. Containers can
	 * have distinct values in different projects, therefore this API considers
	 * a set of projects with their respective containers.
	 * <p>
	 * <code>containerPath</code> is the path under which these values can be
	 * referenced through container buildpath entries (
	 * <code>IBuildpathEntry#BPE_CONTAINER</code>). A container path is formed
	 * by a first ID segment followed with extra segments, which can be used as
	 * additional hints for the resolution. The container ID is used to identify
	 * a <code>BuildpathContainerInitializer</code> registered on the extension
	 * point "org.eclipse.dltk.core.buildpathContainerInitializer".
	 * <p>
	 * There is no assumption that each individual container value passed in
	 * argument (<code>respectiveContainers</code>) must answer the exact same
	 * path when requested <code>IBuildpathContainer#getPath</code>. Indeed, the
	 * containerPath is just an indication for resolving it to an actual
	 * container object. It can be delegated to a
	 * <code>BuildpathContainerInitializer</code>, which can be activated
	 * through the extension point
	 * "org.eclipse.dltk.core.buildpathContainerInitializer").
	 * <p>
	 * In reaction to changing container values, the Model will be updated to
	 * reflect the new state of the updated container. A combined script element
	 * delta will be notified to describe the corresponding buildpath changes
	 * resulting from the container update. This operation is batched, and
	 * automatically eliminates unnecessary updates (new container is same as
	 * old one). This operation acquires a lock on the workspace's root.
	 * <p>
	 * This functionality cannot be used while the workspace is locked, since it
	 * may create/remove some resource markers.
	 * <p>
	 * Buildpath container values are persisted locally to the workspace, but
	 * are not preserved from a session to another. It is thus highly
	 * recommended to register a <code>BuildpathContainerInitializer</code> for
	 * each referenced container (through the extension point
	 * "org.eclipse.dltk.core.BuildpathContainerInitializer").
	 * <p>
	 * Note: setting a container to <code>null</code> will cause it to be lazily
	 * resolved again whenever its value is required. In particular, this will
	 * cause a registered initializer to be invoked again.
	 * <p>
	 * 
	 * @param containerPath
	 * 		- the name of the container reference, which is being updated
	 * @param affectedProjects
	 * 		- the set of projects for which this container is being bound
	 * @param respectiveContainers
	 * 		- the set of respective containers for the affected projects
	 * @param monitor
	 * 		a monitor to report progress
	 * @throws ModelException
	 * @see BuildpathContainerInitializer
	 * @see #getBuildpathContainer(IPath, IScriptProject)
	 * @see IBuildpathContainer
	 * 
	 */
	public static void setBuildpathContainer(final IPath containerPath,
			IScriptProject[] affectedProjects,
			IBuildpathContainer[] respectiveContainers, IProgressMonitor monitor)
			throws ModelException {

		if (affectedProjects.length != respectiveContainers.length)
			Assert
					.isTrue(false,
							"Projects and containers collections should have the same size"); //$NON-NLS-1$

		if (monitor != null && monitor.isCanceled())
			return;

		if (ModelManager.BP_RESOLVE_VERBOSE) {
			Util
					.verbose("BPContainer SET  - setting container\n" + //$NON-NLS-1$
							"	container path: "
							+ containerPath
							+ '\n'
							+ //$NON-NLS-1$
							"	projects: {"
							+ //$NON-NLS-1$
							org.eclipse.dltk.internal.core.util.Util
									.toString(
											affectedProjects,
											new org.eclipse.dltk.internal.core.util.Util.Displayable() {
												public String displayString(
														Object o) {
													return ((IScriptProject) o)
															.getElementName();
												}
											})
							+ "}\n	values: {\n" + //$NON-NLS-1$
							org.eclipse.dltk.internal.core.util.Util
									.toString(
											respectiveContainers,
											new org.eclipse.dltk.internal.core.util.Util.Displayable() {
												public String displayString(
														Object o) {
													StringBuffer buffer = new StringBuffer(
															"		"); //$NON-NLS-1$
													if (o == null) {
														buffer.append("<null>"); //$NON-NLS-1$
														return buffer
																.toString();
													}
													IBuildpathContainer container = (IBuildpathContainer) o;
													buffer
															.append(container
																	.getDescription(null));
													buffer.append(" {\n"); //$NON-NLS-1$
													IBuildpathEntry[] entries = container
															.getBuildpathEntries(null);
													if (entries != null) {
														for (int i = 0; i < entries.length; i++) {
															buffer
																	.append(" 			"); //$NON-NLS-1$
															buffer
																	.append(entries[i]);
															buffer.append('\n');
														}
													}
													buffer.append(" 		}"); //$NON-NLS-1$
													return buffer.toString();
												}
											})
							+ "\n	}\n	invocation stack trace:"); //$NON-NLS-1$
			new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
		}

		ModelManager manager = ModelManager.getModelManager();
		if (manager.containerPutIfInitializingWithSameEntries(containerPath,
				affectedProjects, respectiveContainers))
			return;

		final int projectLength = affectedProjects.length;
		final IScriptProject[] modifiedProjects;
		System.arraycopy(affectedProjects, 0,
				modifiedProjects = new IScriptProject[projectLength], 0,
				projectLength);
		final IBuildpathEntry[][] oldResolvedPaths = new IBuildpathEntry[projectLength][];

		// filter out unmodified project containers
		int remaining = 0;
		for (int i = 0; i < projectLength; i++) {

			if (monitor != null && monitor.isCanceled())
				return;

			ScriptProject affectedProject = (ScriptProject) affectedProjects[i];
			IBuildpathContainer newContainer = respectiveContainers[i];
			if (newContainer == null)
				newContainer = ModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS; // 30920
			// -
			// prevent
			// infinite
			// loop
			boolean found = false;
			if (ScriptProject.hasScriptNature(affectedProject.getProject())) {
				IBuildpathEntry[] rawClasspath = affectedProject
						.getRawBuildpath();
				for (int j = 0, cpLength = rawClasspath.length; j < cpLength; j++) {
					IBuildpathEntry entry = rawClasspath[j];
					if (entry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER
							&& entry.getPath().equals(containerPath)) {
						found = true;
						break;
					}
				}
			}
			if (!found) {
				modifiedProjects[i] = null; // filter out this project - does
				// not reference the container path,
				// or isnt't yet script project
				manager.containerPut(affectedProject, containerPath,
						newContainer);
				continue;
			}
			IBuildpathContainer oldContainer = manager.containerGet(
					affectedProject, containerPath);
			if (oldContainer == ModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) {
				oldContainer = null;
			}
			if (oldContainer != null
					&& oldContainer.equals(respectiveContainers[i])) {
				modifiedProjects[i] = null; // filter out this project -
				// container did not change
				continue;
			}
			remaining++;
			oldResolvedPaths[i] = affectedProject.getResolvedBuildpath(
					true/* ignoreUnresolvedEntry */, false/*
														 * don't
														 * generateMarkerOnError
														 */, false/*
																 * don't
																 * returnResolutionInProgress
																 */);
			manager.containerPut(affectedProject, containerPath, newContainer);
		}

		if (remaining == 0)
			return;

		// trigger model refresh
		try {
			final boolean canChangeResources = !ResourcesPlugin.getWorkspace()
					.isTreeLocked();
			DLTKCore.run(new IWorkspaceRunnable() {
				public void run(IProgressMonitor progressMonitor)
						throws CoreException {
					for (int i = 0; i < projectLength; i++) {

						if (progressMonitor != null
								&& progressMonitor.isCanceled())
							return;

						ScriptProject affectedProject = (ScriptProject) modifiedProjects[i];
						if (affectedProject == null)
							continue; // was filtered out

						if (ModelManager.BP_RESOLVE_VERBOSE) {
							Util
									.verbose("BPContainer SET  - updating affected project due to setting container\n" + //$NON-NLS-1$
											"	project: "
											+ affectedProject.getElementName()
											+ '\n' + //$NON-NLS-1$
											"	container path: " + containerPath); //$NON-NLS-1$
						}

						// force a refresh of the affected project (will compute
						// deltas)
						affectedProject.setRawBuildpath(affectedProject
								.getRawBuildpath(), progressMonitor,
								canChangeResources, oldResolvedPaths[i], false, // updating
								// -
								// no
								// need
								// for
								// early
								// validation
								false); // updating - no need to save
					}
				}
			}, null/* no need to lock anything */, monitor);
		} catch (CoreException e) {
			if (ModelManager.BP_RESOLVE_VERBOSE) {
				Util.verbose("CPContainer SET  - FAILED DUE TO EXCEPTION\n" + //$NON-NLS-1$
						"	container path: " + containerPath, //$NON-NLS-1$
						System.err);
				e.printStackTrace();
			}
			if (e instanceof ModelException) {
				throw (ModelException) e;
			} else {
				throw new ModelException(e);
			}
		} finally {
			for (int i = 0; i < projectLength; i++) {
				if (respectiveContainers[i] == null) {
					manager.containerPut(affectedProjects[i], containerPath,
							null); // reset init in progress marker
				}
			}
		}
	}

	/**
	 * Creates and returns a new non-exported buildpath entry of kind
	 * <code>CPE_LIBRARY</code> for the ZIP or folder identified by the given
	 * absolute path. This specifies that all package fragments within the root
	 * will have children of type <code>IClassFile</code>. This method is fully
	 * equivalent to calling {@link #newLibraryEntry(IPath, IPath, IPath,
	 * IAccessRule[], IBuildpathAttribute[], boolean) newLibraryEntry(path,
	 * sourceAttachmentPath, sourceAttachmentRootPath, new IAccessRule[0], new
	 * IBuildpathAttribute[0], false)}.
	 * 
	 * @param path
	 * 		the absolute path of the binary archive
	 * @param sourceAttachmentPath
	 * 		the absolute path of the corresponding source archive or folder, or
	 * 		<code>null</code> if none. Note an empty path is allowed to denote no
	 * 		source attachment. and will be automatically converted to
	 * 		<code>null</code>.
	 * @param sourceAttachmentRootPath
	 * 		the location of the root of the source files within the source
	 * 		archive or folder or <code>null</code> if this location should be
	 * 		automatically detected.
	 * @return a new library buildpath entry
	 */
	public static IBuildpathEntry newLibraryEntry(IPath path) {

		return newLibraryEntry(path, BuildpathEntry.NO_ACCESS_RULES,
				BuildpathEntry.NO_EXTRA_ATTRIBUTES, false/* not exported */,
				false);
	}

	public static IBuildpathEntry newExtLibraryEntry(IPath path) {

		return newLibraryEntry(path, BuildpathEntry.NO_ACCESS_RULES,
				BuildpathEntry.NO_EXTRA_ATTRIBUTES, false/* not exported */,
				true);
	}

	/**
	 * Creates and returns a new buildpath entry of kind
	 * <code>CPE_LIBRARY</code> for the ZIP or folder identified by the given
	 * absolute path. This specifies that all package fragments within the root
	 * will have children of type <code>IClassFile</code>. This method is fully
	 * equivalent to calling {@link #newLibraryEntry(IPath, IPath, IPath,
	 * IAccessRule[], IBuildpathAttribute[], boolean) newLibraryEntry(path,
	 * sourceAttachmentPath, sourceAttachmentRootPath, new IAccessRule[0], new
	 * IBuildpathAttribute[0], isExported)}.
	 * 
	 * @param path
	 * 		the absolute path of the binary archive
	 * @param sourceAttachmentPath
	 * 		the absolute path of the corresponding source archive or folder, or
	 * 		<code>null</code> if none. Note an empty path is allowed to denote no
	 * 		source attachment. and will be automatically converted to
	 * 		<code>null</code>.
	 * @param sourceAttachmentRootPath
	 * 		the location of the root of the source files within the source
	 * 		archive or folder or <code>null</code> if this location should be
	 * 		automatically detected.
	 * @param isExported
	 * 		indicates whether this entry is contributed to dependent projects in
	 * 		addition to the output location
	 * @return a new library buildpath entry
	 * 
	 */
	public static IBuildpathEntry newLibraryEntry(IPath path, boolean isExported) {

		return newLibraryEntry(path, BuildpathEntry.NO_ACCESS_RULES,
				BuildpathEntry.NO_EXTRA_ATTRIBUTES, isExported, false);
	}

	public static IBuildpathEntry newLibraryEntry(IPath path,
			boolean isExported, boolean externalLib) {

		return newLibraryEntry(path, BuildpathEntry.NO_ACCESS_RULES,
				BuildpathEntry.NO_EXTRA_ATTRIBUTES, isExported, externalLib);
	}

	/**
	 * Creates and returns a new buildpath entry of kind
	 * <code>BPE_LIBRARY</code> for the ZIp or folder identified by the given
	 * absolute path. This specifies that all package fragments within the root
	 * will have readonly children.
	 * <p>
	 * A library entry is used to denote a prerequisite ZIP or root folder
	 * containing sources. The target ZIP can either be defined internally to
	 * the workspace (absolute path relative to the workspace root) or
	 * externally to the workspace (absolute path in the file system). The
	 * target root folder can only be defined internally to the workspace
	 * (absolute path relative to the workspace root). To use a binary folder
	 * external to the workspace, it must first be linked (see
	 * IFolder#createLink(...)). Note that on non-Windows platform, a path
	 * <code>"/some/lib.zip"</code> is ambiguous. It can be a path to an
	 * external ZIP (its file system path being <code>"/some/lib.jar"</code>) or
	 * it can be a path to an internal ZIP (<code>"some"</code> being a project
	 * in the workspace). Such an ambiguity is solved when the buildpath entry
	 * is used (e.g. in {@link IScriptProject#getProjectFragments()}). If the
	 * resource <code>"lib.jar"</code> exists in project <code>"some"</code>,
	 * then it is considered an internal ZIP. Otherwise it is an external ZIP.
	 * <p>
	 * Also note that this operation does not attempt to validate or access the
	 * resources at the given paths.
	 * </p>
	 * <p>
	 * The access rules determine the set of accessible source files in the
	 * library. If the list of access rules is empty then all files in this
	 * library are accessible. See {@link IAccessRule} for a detailed
	 * description of access rules.
	 * </p>
	 * <p>
	 * The <code>extraAttributes</code> list contains name/value pairs that must
	 * be persisted with this entry. If no extra attributes are provided, an
	 * empty array must be passed in.<br>
	 * Note that this list should not contain any duplicate name.
	 * </p>
	 * <p>
	 * The <code>isExported</code> flag indicates whether this entry is
	 * contributed to dependent projects. If not exported, dependent projects
	 * will not see any of the classes from this entry. If exported, dependent
	 * projects will concatenate the accessible files patterns of this entry
	 * with the accessible files patterns of the projects, and they will
	 * concatenate the non accessible files patterns of this entry with the non
	 * accessible files patterns of the project.
	 * </p>
	 * 
	 * @param path
	 * 		the absolute path of the binary archive
	 * @param accessRules
	 * 		the possibly empty list of access rules for this entry
	 * @param extraAttributes
	 * 		the possibly empty list of extra attributes to persist with this
	 * 		entry
	 * @param isExported
	 * 		indicates whether this entry is contributed to dependent projects in
	 * 		addition to the output location
	 * @param externalLib
	 * 		TODO
	 * @param sourceAttachmentPath
	 * 		the absolute path of the corresponding source archive or folder, or
	 * 		<code>null</code> if none. Note an empty path is allowed to denote no
	 * 		source attachment. and will be automatically converted to
	 * 		<code>null</code>.
	 * @param sourceAttachmentRootPath
	 * 		the location of the root of the source files within the source
	 * 		archive or folder or <code>null</code> if this location should be
	 * 		automatically detected.
	 * @return a new library buildpath entry
	 * 
	 */
	public static IBuildpathEntry newLibraryEntry(IPath path,
			IAccessRule[] accessRules, IBuildpathAttribute[] extraAttributes,
			boolean isExported, boolean externalLib) {

		if (path == null)
			Assert.isTrue(false, "Library path cannot be null"); //$NON-NLS-1$
		if (!path.isAbsolute())
			Assert.isTrue(false, "Path for IBuildpathEntry must be absolute"); //$NON-NLS-1$
		return new BuildpathEntry(IProjectFragment.K_BINARY,
				IBuildpathEntry.BPE_LIBRARY, ScriptProject
						.canonicalizedPath(path), isExported,
				BuildpathEntry.INCLUDE_ALL, // inclusion patterns
				BuildpathEntry.EXCLUDE_NONE, // exclusion patterns
				accessRules, false, // no access rules to combine
				extraAttributes, externalLib);
	}

	public static IBuildpathEntry newLibraryEntry(IPath path,
			IAccessRule[] accessRules, IBuildpathAttribute[] extraAttributes,
			IPath[] include, IPath[] exclude, boolean isExported,
			boolean externalLib) {

		if (path == null)
			Assert.isTrue(false, "Library path cannot be null"); //$NON-NLS-1$
		if (!path.isAbsolute())
			Assert.isTrue(false, "Path for IBuildpathEntry must be absolute"); //$NON-NLS-1$
		return new BuildpathEntry(IProjectFragment.K_BINARY,
				IBuildpathEntry.BPE_LIBRARY, ScriptProject
						.canonicalizedPath(path), isExported, include, // inclusion
				// patterns
				exclude, // exclusion patterns
				accessRules, false, // no access rules to combine
				extraAttributes, externalLib);
	}

	public static IBuildpathEntry newBuiltinEntry(IPath path,
			IAccessRule[] accessRules, IBuildpathAttribute[] extraAttributes,
			IPath[] include, IPath[] exclude, boolean isExported,
			boolean externalLib) {

		if (path == null)
			Assert.isTrue(false, "Library path cannot be null"); //$NON-NLS-1$
		return new BuildpathEntry(IProjectFragment.K_BINARY,
				IBuildpathEntry.BPE_LIBRARY, path, isExported, include, // inclusion
				// patterns
				exclude, // exclusion patterns
				accessRules, false, // no access rules to combine
				extraAttributes, externalLib);
	}

	/**
	 * Returns the script model element corresponding to the given handle
	 * identifier generated by <code>IModelElement.getHandleIdentifier()</code>,
	 * or <code>null</code> if unable to create the associated element.
	 * 
	 * @param handleIdentifier
	 * 		the given handle identifier
	 * @return the script element corresponding to the handle identifier
	 */
	public static IModelElement create(String handleIdentifier) {
		return create(handleIdentifier, DefaultWorkingCopyOwner.PRIMARY);
	}

	/**
	 * Returns the script model element corresponding to the given handle
	 * identifier generated by <code>IModelElement.getHandleIdentifier()</code>,
	 * or <code>null</code> if unable to create the associated element. If the
	 * returned script element is an <code>ISourceModule</code>, its owner is
	 * the given owner if such a working copy exists, otherwise the compilation
	 * unit is a primary compilation unit.
	 * 
	 * @param handleIdentifier
	 * 		the given handle identifier
	 * @param owner
	 * 		the owner of the returned compilation unit, ignored if the returned
	 * 		element is not a compilation unit
	 * @return the script element corresponding to the handle identifier
	 * 
	 */
	public static IModelElement create(String handleIdentifier,
			WorkingCopyOwner owner) {
		if (handleIdentifier == null) {
			return null;
		}
		MementoTokenizer memento = new MementoTokenizer(handleIdentifier);
		Model model = ModelManager.getModelManager().getModel();
		return model.getHandleFromMemento(memento, owner);
	}

	public static ISourceModule createSourceModuleFrom(IFile file) {
		return ModelManager.createSourceModuleFrom(file, null/*
															 * unknown script
															 * project
															 */);
	}

	public static Hashtable getDefaultOptions() {
		return ModelManager.getModelManager().getDefaultOptions();
	}

	public static void setOptions(Hashtable defaultOptions) {
		ModelManager.getModelManager().setOptions(defaultOptions);
	}

	public static IRegion newRegion() {
		return new Region();
	}

	public static String[] getUserLibraryNames(IDLTKLanguageToolkit toolkit) {
		return ModelManager.getUserLibraryManager()
				.getUserLibraryNames(toolkit);
	}

	public static void error(String message) {
		plugin.getLog()
				.log(
						new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK,
								message, null));
	}

	public static void error(String message, Throwable t) {
		plugin.getLog().log(
				new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, message, t));
	}

}
