/*******************************************************************************
 * 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.Platform;
import org.eclipse.core.runtime.Plugin;
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.ScriptProject;
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.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();
	public static final boolean DEBUG_PRINT_MODEL = Boolean.valueOf(
			Platform.getDebugOption("org.eclipse.dltk.core/debugPrintModel")).booleanValue();
	public static final boolean DEBUG_SCOPES = Boolean.valueOf(
			Platform.getDebugOption("org.eclipse.dltk.core/debugScopes")).booleanValue();
	public static final boolean DEBUG_SCRIPT_BUILDER = Boolean.valueOf(
			Platform.getDebugOption("org.eclipse.dltk.core/debugScriptBuilder")).booleanValue();
	public static final boolean DEBUG_COMPLETION = Boolean.valueOf(
			Platform.getDebugOption("org.eclipse.dltk.core/debugCompletion")).booleanValue();
	public static final boolean DEBUG_SELECTION = Boolean.valueOf(
			Platform.getDebugOption("org.eclipse.dltk.core/debugSelection")).booleanValue();
	public static final boolean DEBUG_PARSER = Boolean.valueOf(
			Platform.getDebugOption("org.eclipse.dltk.core/debugParser")).booleanValue();
	public static final boolean DEBUG_INDEX = Boolean.valueOf(
			Platform.getDebugOption("org.eclipse.dltk.core/debugIndex")).booleanValue();
	
	// 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";
	
	/**
	 * 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$
	
	/*
	 * 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$
		}
		return 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);
	}	
	/**
	 * 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());
							buffer.append(" {\n"); //$NON-NLS-1$
							IBuildpathEntry[] entries = container.getBuildpathEntries();
							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();
	}
	
}
