/*******************************************************************************
 * Copyright (c) 2000, 2008 QNX Software Systems 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
 *
 * Contributors:
 *     QNX Software Systems - Initial API and implementation
 *     Anton Leherbauer (Wind River Systems)
 *     Markus Schorn (Wind River Systems)
 *     IBM Corporation
 *******************************************************************************/
package org.eclipse.cdt.internal.core.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ElementChangedEvent;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICElementDelta;
import org.eclipse.cdt.core.model.ICModelMarker;
import org.eclipse.cdt.core.model.ICModelStatus;
import org.eclipse.cdt.core.model.ICModelStatusConstants;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IContainerEntry;
import org.eclipse.cdt.core.model.IElementChangedListener;
import org.eclipse.cdt.core.model.IIncludeEntry;
import org.eclipse.cdt.core.model.IIncludeFileEntry;
import org.eclipse.cdt.core.model.ILibraryEntry;
import org.eclipse.cdt.core.model.IMacroEntry;
import org.eclipse.cdt.core.model.IMacroFileEntry;
import org.eclipse.cdt.core.model.IOutputEntry;
import org.eclipse.cdt.core.model.IPathEntry;
import org.eclipse.cdt.core.model.IPathEntryContainer;
import org.eclipse.cdt.core.model.IPathEntryContainerExtension;
import org.eclipse.cdt.core.model.IProjectEntry;
import org.eclipse.cdt.core.model.ISourceEntry;
import org.eclipse.cdt.core.model.ISourceRoot;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.core.model.PathEntryContainerChanged;
import org.eclipse.cdt.core.model.PathEntryContainerInitializer;
import org.eclipse.cdt.core.resources.IPathEntryStore;
import org.eclipse.cdt.core.resources.IPathEntryStoreListener;
import org.eclipse.cdt.core.resources.PathEntryStoreChangedEvent;
import org.eclipse.cdt.core.settings.model.util.PathEntryResolveInfo;
import org.eclipse.cdt.core.settings.model.util.PathEntryResolveInfoElement;
import org.eclipse.cdt.core.settings.model.util.ThreadLocalMap;
import org.eclipse.cdt.internal.core.settings.model.AbstractCExtensionProxy;
import org.eclipse.cdt.internal.core.settings.model.ConfigBasedPathEntryStore;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.WorkspaceJob;
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.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;

/**
 * @author alain
 *  
 */
public class PathEntryManager implements IPathEntryStoreListener, IElementChangedListener {

	// PathEntry extension
	public final static String PATHENTRY_STORE_ID = "PathEntryStore"; //$NON-NLS-1$
	public final static String PATHENTRY_STORE_UNIQ_ID = CCorePlugin.PLUGIN_ID + "." + PATHENTRY_STORE_ID; //$NON-NLS-1$

	static String CONTAINER_INITIALIZER_EXTPOINT_ID = "PathEntryContainerInitializer"; //$NON-NLS-1$
	/**
	 * An empty array of strings indicating that a project doesn't have any
	 * prerequesite projects.
	 */
	static final String[] NO_PREREQUISITES = new String[0];
	/**
	 * pathentry containers pool accessing the Container is done synch with the
	 * class
	 */
	private static HashMap<ICProject, Map<IPath, IPathEntryContainer>> Containers = new HashMap<ICProject, Map<IPath, IPathEntryContainer>>(5);

	static final IPathEntry[] NO_PATHENTRIES = new IPathEntry[0];

	static final IIncludeEntry[] NO_INCLUDE_ENTRIES = new IIncludeEntry[0];

	static final IIncludeFileEntry[] NO_INCLUDE_FILE_ENTRIES = new IIncludeFileEntry[0];

	static final IMacroEntry[] NO_MACRO_ENTRIES = new IMacroEntry[0];

	static final IMacroFileEntry[] NO_MACRO_FILE_ENTRIES = new IMacroFileEntry[0];

	static final IPathEntryContainer[] NO_PATHENTRYCONTAINERS = new IPathEntryContainer[0];

	// Synchronized the access of the cache entries.
	protected Map<ICProject, ArrayList<IPathEntry>> resolvedMap = new Hashtable<ICProject, ArrayList<IPathEntry>>();
	private Map<ICProject, PathEntryResolveInfo> resolvedInfoMap = new Hashtable<ICProject, PathEntryResolveInfo>();
	private ThreadLocalMap resolveInfoValidState = new ThreadLocalMap(); 

	// Accessing the map is synch with the class
	private Map<IProject, IPathEntryStore> storeMap = new HashMap<IProject, IPathEntryStore>();

	private static PathEntryManager pathEntryManager;

	protected ConcurrentLinkedQueue<PathEntryProblem> markerProblems = new ConcurrentLinkedQueue<PathEntryProblem>();
	
	//Setting up a generate markers job, it does not get scheduled
	Job markerTask = new GenerateMarkersJob("PathEntry Marker Job"); //$NON-NLS-1$

	private PathEntryManager() {
	}

	private class PathEntryProblem {
		IProject project;
		ICModelStatus[] problems;
		
		public PathEntryProblem(IProject project, ICModelStatus[] problems) {
			this.project = project;
			this.problems = problems;
		}		
	}

	private class PathEntryContainerLock implements IPathEntryContainer {

		boolean runInitializer;

		public boolean isContainerInitialize() {
			return runInitializer;
		}

		public void setContainerInitialize(boolean init) {
			runInitializer = init;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.eclipse.cdt.core.model.IPathEntryContainer#getPathEntries()
		 */
		public IPathEntry[] getPathEntries() {
			return NO_PATHENTRIES;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.eclipse.cdt.core.model.IPathEntryContainer#getDescription()
		 */
		public String getDescription() {
			return new String("Lock container"); //$NON-NLS-1$
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.eclipse.cdt.core.model.IPathEntryContainer#getPath()
		 */
		public IPath getPath() {
			return Path.EMPTY;
		}
	}

	/**
	 * Return the singleton.
	 */
	public static synchronized PathEntryManager getDefault() {
		if (pathEntryManager == null) {
			pathEntryManager = new PathEntryManager();
			CoreModel.getDefault().addElementChangedListener(pathEntryManager);
		}
		return pathEntryManager;
	}

	public IIncludeFileEntry[] getIncludeFileEntries(IPath resPath) throws CModelException {
		ICElement celement = CoreModel.getDefault().create(resPath);
		if (celement instanceof ITranslationUnit) {
			return getIncludeFileEntries((ITranslationUnit)celement);
		}
		if (celement != null) {
			// get project include file entries
			List<IPathEntry> entryList = new ArrayList<IPathEntry>();
			ICProject cproject = celement.getCProject();
			ArrayList<IPathEntry> resolvedListEntries = getResolvedPathEntries(cproject, false);
			IPathEntry[] pathEntries= getCachedResolvedPathEntries(resolvedListEntries, cproject);
			for (int i = 0; i < pathEntries.length; ++i) {
				IPathEntry entry = pathEntries[i];
				if ((entry.getEntryKind() & IPathEntry.CDT_INCLUDE_FILE) != 0) {
					entryList.add(entry);
				}
			}
			IIncludeFileEntry[] incFiles = entryList.toArray(new IIncludeFileEntry[entryList.size()]);
			return incFiles;
		}
		return NO_INCLUDE_FILE_ENTRIES;
	}

	public IIncludeFileEntry[] getIncludeFileEntries(ITranslationUnit cunit) throws CModelException {
		List<IPathEntry> list = getPathEntries(cunit, IPathEntry.CDT_INCLUDE_FILE);
		IIncludeFileEntry[] incFiles = list.toArray(new IIncludeFileEntry[list.size()]);
		return incFiles;
	}

	public IIncludeEntry[] getIncludeEntries(IPath resPath) throws CModelException {
		ICElement celement = CoreModel.getDefault().create(resPath);
		if (celement instanceof ITranslationUnit) {
			return getIncludeEntries((ITranslationUnit)celement);
		}
		if (celement != null) {
			// get project include entries
			List<IPathEntry> entryList = new ArrayList<IPathEntry>();
			ICProject cproject = celement.getCProject();
			ArrayList<IPathEntry> resolvedListEntries = getResolvedPathEntries(cproject, false);
			IPathEntry[] pathEntries= getCachedResolvedPathEntries(resolvedListEntries, cproject);
			for (int i = 0; i < pathEntries.length; ++i) {
				IPathEntry entry = pathEntries[i];
				if ((entry.getEntryKind() & IPathEntry.CDT_INCLUDE) != 0) {
					entryList.add(entry);
				}
			}
			IIncludeEntry[] includes = entryList.toArray(new IIncludeEntry[entryList.size()]);
			return includes;
		}
		return NO_INCLUDE_ENTRIES;
	}

	public IIncludeEntry[] getIncludeEntries(ITranslationUnit cunit) throws CModelException {
		List<IPathEntry> list = getPathEntries(cunit, IPathEntry.CDT_INCLUDE);
		IIncludeEntry[] includes = list.toArray(new IIncludeEntry[list.size()]);
		return includes;
	}

	public IMacroEntry[] getMacroEntries(IPath resPath)  throws CModelException {
		ICElement celement = CoreModel.getDefault().create(resPath);
		if (celement instanceof ITranslationUnit) {
			return getMacroEntries((ITranslationUnit)celement);
		}
		if (celement != null) {
			// get project macro entries
			List<IPathEntry> entryList = new ArrayList<IPathEntry>();
			ICProject cproject = celement.getCProject();
			ArrayList<IPathEntry> resolvedListEntries = getResolvedPathEntries(cproject, false);
			IPathEntry[] pathEntries= getCachedResolvedPathEntries(resolvedListEntries, cproject);
			for (int i = 0; i < pathEntries.length; ++i) {
				IPathEntry entry = pathEntries[i];
				if ((entry.getEntryKind() & IPathEntry.CDT_MACRO) != 0) {
					entryList.add(entry);
				}
			}
			IMacroEntry[] macros= entryList.toArray(new IMacroEntry[entryList.size()]);
			return macros;
		}
		return NO_MACRO_ENTRIES;
	}

	private IMacroEntry[] getMacroEntries(ITranslationUnit cunit) throws CModelException  {
		ArrayList<IPathEntry> macroList = new ArrayList<IPathEntry>();
		ICProject cproject = cunit.getCProject();
		IPath resPath = cunit.getPath();
		// Do this first so the containers get inialized.
		ArrayList<IPathEntry> resolvedListEntries = getResolvedPathEntries(cproject, false);
		for (int i = 0; i < resolvedListEntries.size(); ++i) {
			IPathEntry entry = resolvedListEntries.get(i);
			if (entry.getEntryKind() == IPathEntry.CDT_MACRO) {
				macroList.add(entry);
			}
		}
		IPathEntryContainer[] containers = getPathEntryContainers(cproject);
		for (int i = 0; i < containers.length; ++i) {
			if (containers[i] instanceof IPathEntryContainerExtension) {
				IPathEntryContainerExtension extension = (IPathEntryContainerExtension)containers[i];
                IPathEntry[] incs = extension.getPathEntries(resPath, IPathEntry.CDT_MACRO);
				macroList.addAll(Arrays.asList(incs));
			}
		}

		IMacroEntry[] macros = macroList.toArray(new IMacroEntry[macroList.size()]);
		macroList.clear();
		
		// For the macros the closest symbol will override 
		// /projec/src/file.c --> NDEBUG=1
		// /project/src       --> NDEBUG=0
		//
		// We will use NDEBUG=1 only
		int count = resPath.segmentCount();
		Map<String, IMacroEntry> symbolMap = new HashMap<String, IMacroEntry>();
		for (int i = 0; i < count; i++) {
			IPath newPath = resPath.removeLastSegments(i);
			for (IMacroEntry macro : macros) {
				IPath otherPath = macro.getPath();
				if (newPath.equals(otherPath)) {
					String key = macro.getMacroName();
					if (!symbolMap.containsKey(key)) {
						symbolMap.put(key, macro);
					}
				}
			}
		}

		// Add the Project contributions last.
		for (int i = 0; i < resolvedListEntries.size(); i++) {
			IPathEntry entry = resolvedListEntries.get(i);
			if (entry != null && entry.getEntryKind() == IPathEntry.CDT_PROJECT) {
				IResource res = cproject.getCModel().getWorkspace().getRoot().findMember(entry.getPath());
				if (res != null && res.getType() == IResource.PROJECT) {
					ICProject refCProject = CoreModel.getDefault().create((IProject)res);
					if (refCProject != null) {
						IPathEntry[] projEntries = refCProject.getResolvedPathEntries();
						for (IPathEntry projEntry : projEntries) {
							if (projEntry.isExported()) {
								if (projEntry.getEntryKind() == IPathEntry.CDT_MACRO) {
									IMacroEntry macro = (IMacroEntry)entry;
									String key = macro.getMacroName();
									if (!symbolMap.containsKey(key)) {
										symbolMap.put(key, macro);
									}
								}
							}
						}
					}
				}
			}
		}

		return symbolMap.values().toArray(NO_MACRO_ENTRIES);

	}

	public IMacroFileEntry[] getMacroFileEntries(IPath resPath)  throws CModelException {
		ICElement celement = CoreModel.getDefault().create(resPath);
		if (celement instanceof ITranslationUnit) {
			return getMacroFileEntries((ITranslationUnit)celement);
		}
		return NO_MACRO_FILE_ENTRIES;
	}

	public IMacroFileEntry[] getMacroFileEntries(ITranslationUnit cunit) throws CModelException  {
		List<IPathEntry> list = getPathEntries(cunit, IPathEntry.CDT_MACRO_FILE);
		IMacroFileEntry[] macFiles = list.toArray(new IMacroFileEntry[list.size()]);
		return macFiles;
	}

	private List<IPathEntry> getPathEntries(ITranslationUnit cunit, int type) throws CModelException {
		ArrayList<IPathEntry> entryList = new ArrayList<IPathEntry>();
		ICProject cproject = cunit.getCProject();
		IPath resPath = cunit.getPath();
		// Do this first so the containers get inialized.
		ArrayList<IPathEntry> resolvedListEntries = getResolvedPathEntries(cproject, false);
		for (int i = 0; i < resolvedListEntries.size(); ++i) {
			IPathEntry entry = resolvedListEntries.get(i);
			if ((entry.getEntryKind() & type) != 0) {
				entryList.add(entry);
			}
		}
		IPathEntryContainer[] containers = getPathEntryContainers(cproject);
		for (int i = 0; i < containers.length; ++i) {
			if (containers[i] instanceof IPathEntryContainerExtension) {
				IPathEntryContainerExtension extension = (IPathEntryContainerExtension)containers[i];
				IPathEntry[] incs = extension.getPathEntries(resPath, type);
				entryList.addAll(Arrays.asList(incs));
			}
		}

		IPathEntry[] entries = entryList.toArray(new IPathEntry[entryList.size()]);
		// Clear the list since we are reusing it.
		entryList.clear();

		// We need to reorder the include/macros:
		// includes with the closest match to the resource will come first
		// /project/src/file.c  --> /usr/local/include
		// /project             --> /usr/include
		// 
		//  /usr/local/include must come first.
		//
		int count = resPath.segmentCount();
		for (int i = 0; i < count; i++) {
			IPath newPath = resPath.removeLastSegments(i);
			for (IPathEntry entrie : entries) {
				IPath otherPath = entrie.getPath();
				if (newPath.equals(otherPath)) {
					entryList.add(entrie);
				}
			}
		}

		// Since the include that comes from a project contribution are not
		// tied to a resource they are added last.
		for (int i = 0; i < resolvedListEntries.size(); i++) {
			IPathEntry entry = resolvedListEntries.get(i);
			if (entry != null && entry.getEntryKind() == IPathEntry.CDT_PROJECT) {
				IResource res = cproject.getCModel().getWorkspace().getRoot().findMember(entry.getPath());
				if (res != null && res.getType() == IResource.PROJECT) {
					ICProject refCProject = CoreModel.getDefault().create((IProject)res);
					if (refCProject != null) {
						IPathEntry[] projEntries = refCProject.getResolvedPathEntries();
						for (IPathEntry projEntry : projEntries) {
							if (projEntry.isExported()) {
								if ((projEntry.getEntryKind() & type) != 0) {
									entryList.add(projEntry);
								}
							}
						}
					}
				}
			}
		}
		return entryList;		
	}

	/**
	 * Return the cached entries, if no cache null.
	 * @param cproject
	 */
	protected IPathEntry[] getCachedResolvedPathEntries(ICProject cproject) {
		ArrayList<IPathEntry> resolvedListEntries = resolvedMap.get(cproject);
		if (resolvedListEntries != null) {
			try {
				return getCachedResolvedPathEntries(resolvedListEntries, cproject);
			} catch (CModelException e) {
				//
			}
		}
		return null;
	}
	
	public PathEntryResolveInfo getResolveInfo(ICProject cproject, boolean useCache) throws CModelException{
		PathEntryResolveInfo info = resolvedInfoMap.get(cproject);
		if(info == null && useCache){
			getResolvedPathEntries(cproject);
			info = resolvedInfoMap.get(cproject);
		}
		if(info == null || !useCache || !getResolveInfoValidState(cproject)){
			Object[] resolved = getResolvedPathEntries(cproject, false, false);
			if(resolved != null)
				info = (PathEntryResolveInfo)resolved[1]; 
		}
		return info;
	}
	
	private void setResolveInfoValidState(ICProject cproject, boolean valid){
		Object v = valid ? null : Boolean.FALSE;
		resolveInfoValidState.set(cproject, v);
	}
	
	private boolean getResolveInfoValidState(ICProject cproject){
		return resolveInfoValidState.get(cproject) == null;
	}
	
	protected IPathEntry[] removeCachedResolvedPathEntries(ICProject cproject) {
		ArrayList<IPathEntry> resolvedListEntries = resolvedMap.remove(cproject);
		resolvedInfoMap.remove(cproject);
		if (resolvedListEntries != null) {
			try {
				return getCachedResolvedPathEntries(resolvedListEntries, cproject);
			} catch (CModelException e) {
				//
			}
		}
		return null;
	}

	private IPathEntry[] getCachedResolvedPathEntries(ArrayList<IPathEntry> resolvedListEntries, ICProject cproject) throws CModelException {
		IPathEntry[] entries = resolvedListEntries.toArray(NO_PATHENTRIES);
		boolean hasContainerExtension = false;
		for (IPathEntry entrie : entries) {
			if (entrie.getEntryKind() == IPathEntry.CDT_CONTAINER) {
				IContainerEntry centry = (IContainerEntry)entrie;
				IPathEntryContainer container = getPathEntryContainer(centry, cproject);
				if (container instanceof IPathEntryContainerExtension) {
					hasContainerExtension = true;
					break;
				}
			}
		}
		if (hasContainerExtension) {
			IPath projectPath = cproject.getPath();
			ArrayList<IPathEntry> listEntries = new ArrayList<IPathEntry>(entries.length);
			for (int i = 0; i < entries.length; ++i) {
				if (entries[i].getEntryKind() == IPathEntry.CDT_CONTAINER) {
					IContainerEntry centry = (IContainerEntry)entries[i];
					IPathEntryContainer container = getPathEntryContainer(centry, cproject);
					if (container != null) {
						IPathEntry[] containerEntries = container.getPathEntries();
						if (containerEntries != null) {
							for (IPathEntry containerEntrie : containerEntries) {
								IPathEntry newEntry = PathEntryUtil.cloneEntryAndExpand(projectPath, containerEntrie);
								listEntries.add(newEntry);
							}
						}
					}
				} else {
					listEntries.add(entries[i]);
				}
			}
			entries = listEntries.toArray(NO_PATHENTRIES);
		}
		return entries;
	}

	public IPathEntry[] getResolvedPathEntries(ICProject cproject) throws CModelException {
		boolean treeLock = cproject.getProject().getWorkspace().isTreeLocked();
		ArrayList<IPathEntry> resolvedListEntries = getResolvedPathEntries(cproject, !treeLock);
		return getCachedResolvedPathEntries(resolvedListEntries, cproject);
	}

	/**
	 * This method will not expand container extending IPathEntryContainerExtension
	 * 
	 * @param cproject
	 * @param generateMarkers
	 * @return
	 * @throws CModelException
	 */
	@SuppressWarnings("unchecked")
	private ArrayList<IPathEntry> getResolvedPathEntries(ICProject cproject, boolean generateMarkers) throws CModelException {
		Object[] result = getResolvedPathEntries(cproject, generateMarkers, true);
		if(result != null)
			return (ArrayList<IPathEntry>)result[0];
		return null;
	}

	private Object[] getResolvedPathEntries(ICProject cproject, boolean generateMarkers, boolean useCache) throws CModelException {
		ArrayList<IPathEntry> resolvedEntries = null;
		PathEntryResolveInfo rInfo = null;
		if(useCache){
			resolvedEntries = resolvedMap.get(cproject);
			rInfo = resolvedInfoMap.get(cproject);
		}
		if (resolvedEntries == null) {
			List<PathEntryResolveInfoElement> resolveInfoList = new ArrayList<PathEntryResolveInfoElement>();
			IPath projectPath = cproject.getPath();
			IPathEntry[] rawEntries = getRawPathEntries(cproject);
			resolvedEntries = new ArrayList<IPathEntry>();
			for (IPathEntry entry : rawEntries) {
				// Expand the containers.
				if (entry.getEntryKind() == IPathEntry.CDT_CONTAINER) {
					IContainerEntry centry = (IContainerEntry)entry;
					IPathEntryContainer container = getPathEntryContainer(centry, cproject);
					if (container != null) {
						// For backward compatibility we need to expand and cache container that
						// are not IPathEntryContainerExtension.
						if (!(container instanceof IPathEntryContainerExtension)) {
							IPathEntry[] containerEntries = container.getPathEntries();
							List<IPathEntry> resolvedList = new ArrayList<IPathEntry>();
							if (containerEntries != null) {
								for (IPathEntry containerEntrie : containerEntries) {
									IPathEntry newEntry = PathEntryUtil.cloneEntryAndExpand(projectPath, containerEntrie);
									resolvedEntries.add(newEntry);
									resolvedList.add(newEntry);
								}
							}
							resolveInfoList.add(new PathEntryResolveInfoElement(centry, resolvedList));
						} else {
							IPathEntry resolved = PathEntryUtil.cloneEntryAndExpand(projectPath, entry);
							resolvedEntries.add(resolved);
							resolveInfoList.add(new PathEntryResolveInfoElement(entry, resolved));
						}
					}
				} else {
					IPathEntry clone = PathEntryUtil.cloneEntryAndExpand(projectPath, entry);
					IPathEntry e = PathEntryUtil.getExpandedPathEntry(clone, cproject);
					if (e != null) {
						resolvedEntries.add(e);
					}
					resolveInfoList.add(new PathEntryResolveInfoElement(entry, e));
				}
			}
			resolvedEntries.trimToSize();

			if (generateMarkers) {
				IPathEntry[] finalEntries = resolvedEntries.toArray(NO_PATHENTRIES);
				ArrayList<ICModelStatus> problemList = new ArrayList<ICModelStatus>();
				ICModelStatus status = validatePathEntry(cproject, finalEntries);
				if (!status.isOK()) {
					problemList.add(status);
				}
				for (IPathEntry finalEntrie : finalEntries) {
					status = PathEntryUtil.validatePathEntry(cproject, finalEntrie, true, false);
					if (!status.isOK()) {
						problemList.add(status);
					}
				}
				ICModelStatus[] problems = new ICModelStatus[problemList.size()];
				problemList.toArray(problems);
				IProject project = cproject.getProject();
				if (PathEntryUtil.hasPathEntryProblemMarkersChange(project, problems)) {
					addProblemMarkers(project, problems);
				}
			}

			// Check for duplication in the sources
			List<IPathEntry> dups = PathEntryUtil.checkForDuplication(resolvedEntries, IPathEntry.CDT_SOURCE);
			if (dups.size() > 0) {
				resolvedEntries.removeAll(dups);
			}

			// Check for duplication in the outputs
			dups = PathEntryUtil.checkForDuplication(resolvedEntries, IPathEntry.CDT_OUTPUT);
			if (dups.size() > 0) {
				resolvedEntries.removeAll(dups);
			}

			rInfo = new PathEntryResolveInfo(resolveInfoList);
			if(useCache){
				resolvedMap.put(cproject, resolvedEntries);
				resolvedInfoMap.put(cproject, rInfo);
			}
		}
		return new Object[]{resolvedEntries, rInfo};
	}

	public void setRawPathEntries(ICProject cproject, IPathEntry[] newEntries, IProgressMonitor monitor) throws CModelException {
		try {
			IPathEntry[] oldResolvedEntries = getCachedResolvedPathEntries(cproject);
			SetPathEntriesOperation op = new SetPathEntriesOperation(cproject, oldResolvedEntries, newEntries);
			op.runOperation(monitor);
		} catch (CoreException e) {
			throw new CModelException(e);
		}
	}

	public IPathEntry[] getRawPathEntries(ICProject cproject) throws CModelException {
		IProject project = cproject.getProject();
		// Check if the Project is accesible.
		if (! (CoreModel.hasCNature(project) || CoreModel.hasCCNature(project))) {
			throw new CModelException(new CModelStatus(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST));
		}
		IPathEntry[] pathEntries;
		try {
			IPathEntryStore store = getPathEntryStore(project, true);
			pathEntries = store.getRawPathEntries();
		} catch (CoreException e) {
			throw new CModelException(e);
		}

		// Checks/hacks for backward compatibility ..
		// if no output is specified we return the project
		// if no source is specified we return the project
		boolean foundSource = false;
		boolean foundOutput = false;
		for (IPathEntry rawEntry : pathEntries) {
			if (rawEntry.getEntryKind() == IPathEntry.CDT_SOURCE) {
				foundSource = true;
			}
			if (rawEntry.getEntryKind() == IPathEntry.CDT_OUTPUT) {
				foundOutput = true;
			}
		}

		if (!foundSource) {
			IPathEntry[] newEntries = new IPathEntry[pathEntries.length + 1];
			System.arraycopy(pathEntries, 0, newEntries, 0, pathEntries.length);
			newEntries[pathEntries.length] = CoreModel.newSourceEntry(cproject.getPath());
			pathEntries = newEntries;
		}
		if (!foundOutput) {
			IPathEntry[] newEntries = new IPathEntry[pathEntries.length + 1];
			System.arraycopy(pathEntries, 0, newEntries, 0, pathEntries.length);
			newEntries[pathEntries.length] = CoreModel.newOutputEntry(cproject.getPath());
			pathEntries = newEntries;
		}
		return pathEntries;
	}

	public void setPathEntryContainer(ICProject[] affectedProjects, IPathEntryContainer newContainer, IProgressMonitor monitor)
			throws CModelException {

		SetPathEntryContainerOperation op = new SetPathEntryContainerOperation(affectedProjects, newContainer);
		op.runOperation(monitor);
	}

	public void clearPathEntryContainer(ICProject[] affectedProjects, IPath containerPath , IProgressMonitor monitor)
		throws CModelException {

		SetPathEntryContainerOperation op = new SetPathEntryContainerOperation(affectedProjects, containerPath);
		op.runOperation(monitor);
	}

	public synchronized IPathEntryContainer[] getPathEntryContainers(ICProject cproject) {
		IPathEntryContainer[] pcs = NO_PATHENTRYCONTAINERS;
		Map<IPath, IPathEntryContainer> projectContainers = Containers.get(cproject);
		if (projectContainers != null) {
			Collection<IPathEntryContainer> collection = projectContainers.values();
			pcs= collection.toArray(NO_PATHENTRYCONTAINERS);
		}
		return pcs;
	}

	public IPathEntryContainer getPathEntryContainer(IContainerEntry entry, ICProject cproject) throws CModelException {
		return getPathEntryContainer(entry.getPath(), cproject);
	}

	public IPathEntryContainer getPathEntryContainer(final IPath containerPath, final ICProject project) throws CModelException {
		// Try the cache.
		IPathEntryContainer container = containerGet(project, containerPath, true);
		if (container instanceof PathEntryContainerLock) {
			boolean runInitializer = false;
			PathEntryContainerLock lock = (PathEntryContainerLock)container;
			synchronized (lock) {
				if (!lock.isContainerInitialize()) {
					runInitializer = true;
					lock.setContainerInitialize(runInitializer);
				} else if (! Thread.holdsLock(lock)){
					// FIXME: Use Thread.holdsLock(lock) to break the cycle.
					// This seem to happend when the container(say the auto discovery)
					// trigger a resource change, the CoreModel will try to get the pathentries .. deadlock.

					// Wait for the inialization to finish.
					while (containerGet(project, containerPath, true) instanceof PathEntryContainerLock) {
						try {
							lock.wait();
						} catch (InterruptedException e) {
							//e.printStackTrace();
						}
					}
				}
			}
			if (runInitializer) {
				// remove the lock.
				final PathEntryContainerInitializer initializer = getPathEntryContainerInitializer(containerPath.segment(0));
				final boolean[] ok = {false};
				if (initializer != null) {
					// wrap initializer call with Safe runnable in case
					// initializer would be
					// causing some grief
					SafeRunner.run(new ISafeRunnable() {

						public void handleException(Throwable exception) {
							IStatus status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, IStatus.ERROR,
									"Exception occurred in container initializer: " + initializer, exception); //$NON-NLS-1$
							CCorePlugin.log(status);
						}

						public void run() throws Exception {
							initializer.initialize(containerPath, project);
							ok[0] = true;
						}
					});
				}
				if (!ok[0]) {
					containerPut(project, containerPath, null); // flush and
					// notify
				}
			}
			// retrieve new value
			container = containerGet(project, containerPath, false);
		}
		return container;
	}

	/**
	 * Helper method finding the container initializer registered for a given
	 * container ID or <code>null</code> if none was found while iterating
	 * over the contributions to extension point to the extension point
	 * "org.eclipse.cdt.core.PathEntryContainerInitializer".
	 * <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 PathEntryContainerInitializer - the registered container
	 *         initializer or <code>null</code> if none was found.
	 */
	public PathEntryContainerInitializer getPathEntryContainerInitializer(String containerID) {
		Plugin core = CCorePlugin.getDefault();
		if (core == null) {
			return null;
		}
		IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(CCorePlugin.PLUGIN_ID,
				CONTAINER_INITIALIZER_EXTPOINT_ID);
		if (extension != null) {
			IExtension[] extensions = extension.getExtensions();
			for (IExtension extension2 : extensions) {
				IConfigurationElement[] configElements = extension2.getConfigurationElements();
				for (IConfigurationElement configElement : configElements) {
					String initializerID = configElement.getAttribute("id"); //$NON-NLS-1$
					if (initializerID != null && initializerID.equals(containerID)) {
						try {
							Object execExt = configElement.createExecutableExtension("class"); //$NON-NLS-1$
							if (execExt instanceof PathEntryContainerInitializer) {
								return (PathEntryContainerInitializer)execExt;
							}
						} catch (CoreException e) {
							// executable extension could not be created:
							// ignore this initializer if
							//e.printStackTrace();
						}
					}
				}
			}
		}
		return null;
	}

	synchronized IPathEntryContainer containerGet(ICProject cproject, IPath containerPath, boolean bCreateLock) {
		Map<IPath, IPathEntryContainer> projectContainers = Containers.get(cproject);
		if (projectContainers == null) {
			projectContainers = new HashMap<IPath, IPathEntryContainer>();
			Containers.put(cproject, projectContainers);
		}
		IPathEntryContainer container = projectContainers.get(containerPath);
		// Initialize the first time with a lock
		if (bCreateLock && container == null) {
			container = new PathEntryContainerLock();
			projectContainers.put(containerPath, container);
		}
		return container;
	}

	synchronized void containerPut(ICProject cproject, IPath containerPath, IPathEntryContainer container) {
		Map<IPath, IPathEntryContainer> projectContainers = Containers.get(cproject);
		if (projectContainers == null) {
			projectContainers = new HashMap<IPath, IPathEntryContainer>();
			Containers.put(cproject, projectContainers);
		}
		IPathEntryContainer oldContainer;
		if (container == null) {
			oldContainer = projectContainers.remove(containerPath);
		} else {
			oldContainer = projectContainers.put(containerPath, container);
		}
		if (oldContainer instanceof PathEntryContainerLock) {
			synchronized (oldContainer) {
				oldContainer.notifyAll();
			}
		}
	}

	synchronized void containerRemove(ICProject cproject) {
		Containers.remove(cproject);
	}


	public void pathEntryContainerUpdates(IPathEntryContainerExtension container, PathEntryContainerChanged[] events, IProgressMonitor monitor) {

		PathEntryContainerUpdatesOperation op = new PathEntryContainerUpdatesOperation(container, events);
		try {
			op.runOperation(monitor);
		} catch (CModelException e) {
			//
		}
	}

	public String[] projectPrerequisites(IPathEntry[] entries) throws CModelException {
		if (entries != null) {
			ArrayList<String> prerequisites = new ArrayList<String>();
			for (IPathEntry entrie : entries) {
				if (entrie.getEntryKind() == IPathEntry.CDT_PROJECT) {
					IProjectEntry entry = (IProjectEntry)entrie;
					prerequisites.add(entry.getPath().lastSegment());
				}
			}
			int size = prerequisites.size();
			if (size != 0) {
				String[] result = new String[size];
				prerequisites.toArray(result);
				return result;
			}
		}
		return NO_PREREQUISITES;
	}

	public void saveRawPathEntries(ICProject cproject, IPathEntry[] entries) throws CModelException {
		// sanity
		if (entries == null) {
			entries = NO_PATHENTRIES;
		}

		ArrayList<IPathEntry> list = new ArrayList<IPathEntry>(entries.length);
		IPath projectPath = cproject.getPath();
		for (IPathEntry entrie : entries) {
			IPathEntry entry;

			int kind = entrie.getEntryKind();

			// translate the project prefix.
			IPath resourcePath = entrie.getPath();
			if (resourcePath == null) {
				resourcePath = Path.EMPTY;
			}

			// Do not do this for container, the path is the ID.
			if (kind != IPathEntry.CDT_CONTAINER) {
				// translate to project relative from absolute (unless a device
				// path)
				if (resourcePath.isAbsolute()) {
					if (projectPath != null && projectPath.isPrefixOf(resourcePath)) {
						if (resourcePath.segment(0).equals(projectPath.segment(0))) {
							resourcePath = resourcePath.removeFirstSegments(1);
							resourcePath = resourcePath.makeRelative();
						} else {
							resourcePath = resourcePath.makeAbsolute();
						}
					}
				}
			}

			// Specifics to the entries
			switch (kind) {
				case IPathEntry.CDT_INCLUDE : {
					IIncludeEntry include = (IIncludeEntry)entrie;
					IPath baseRef = include.getBaseReference();
					if (baseRef == null || baseRef.isEmpty()) {
						entry = CoreModel.newIncludeEntry(resourcePath, include.getBasePath(), include.getIncludePath(),
								include.isSystemInclude(), include.getExclusionPatterns(), include.isExported());
					} else {
						entry = CoreModel.newIncludeRefEntry(resourcePath, baseRef, include.getIncludePath());
					}
					break;
				}
				case IPathEntry.CDT_INCLUDE_FILE : {
					IIncludeFileEntry includeFile = (IIncludeFileEntry)entrie;
					entry = CoreModel.newIncludeFileEntry(resourcePath, includeFile.getBasePath(),
							includeFile.getBaseReference(), includeFile.getIncludeFilePath(),
							includeFile.getExclusionPatterns(), includeFile.isExported());
					break;
				}
				case IPathEntry.CDT_LIBRARY : {
					ILibraryEntry library = (ILibraryEntry)entrie;
					IPath sourcePath = library.getSourceAttachmentPath();
					if (sourcePath != null) {
						// translate to project relative from absolute
						if (projectPath != null && projectPath.isPrefixOf(sourcePath)) {
							if (sourcePath.segment(0).equals(projectPath.segment(0))) {
								sourcePath = sourcePath.removeFirstSegments(1);
								sourcePath = sourcePath.makeRelative();
							}
						}
					}
					IPath baseRef = library.getBaseReference();
					if (baseRef == null || baseRef.isEmpty()) {
						entry = CoreModel.newLibraryEntry(resourcePath, library.getBasePath(), library.getLibraryPath(),
								sourcePath, library.getSourceAttachmentRootPath(), library.getSourceAttachmentPrefixMapping(),
								library.isExported());
					} else {
						entry = CoreModel.newLibraryRefEntry(resourcePath, baseRef, library.getLibraryPath());
					}
					break;
				}
				case IPathEntry.CDT_MACRO : {
					IMacroEntry macro = (IMacroEntry)entrie;
					IPath baseRef = macro.getBaseReference();
					if (baseRef == null || baseRef.isEmpty()) {
						entry = CoreModel.newMacroEntry(resourcePath, macro.getMacroName(), macro.getMacroValue(),
								macro.getExclusionPatterns(), macro.isExported());
					} else {
						entry = CoreModel.newMacroRefEntry(resourcePath, baseRef, macro.getMacroName());
					}
					break;
				}
				case IPathEntry.CDT_MACRO_FILE : {
					IMacroFileEntry macro = (IMacroFileEntry)entrie;
					entry = CoreModel.newMacroFileEntry(resourcePath, macro.getBasePath(),
							macro.getBaseReference(), macro.getMacroFilePath(),
							macro.getExclusionPatterns(), macro.isExported());
					break;
				}
				case IPathEntry.CDT_OUTPUT : {
					IOutputEntry out = (IOutputEntry)entrie;
					entry = CoreModel.newOutputEntry(resourcePath, out.getExclusionPatterns());
					break;
				}
				case IPathEntry.CDT_PROJECT : {
					IProjectEntry projEntry = (IProjectEntry)entrie;
					entry = CoreModel.newProjectEntry(projEntry.getPath(), projEntry.isExported());
					break;
				}
				case IPathEntry.CDT_SOURCE : {
					ISourceEntry source = (ISourceEntry)entrie;
					entry = CoreModel.newSourceEntry(resourcePath, source.getExclusionPatterns());
					break;
				}
				case IPathEntry.CDT_CONTAINER :
					entry = CoreModel.newContainerEntry(entrie.getPath(), entrie.isExported());
					break;
				default :
					entry = entrie;
			}
			list.add(entry);
		}
		try {
			IPathEntry[] newRawEntries = new IPathEntry[list.size()];
			list.toArray(newRawEntries);
			IProject project = cproject.getProject();
			IPathEntryStore store = getPathEntryStore(project, true);
			setResolveInfoValidState(cproject, false);
			store.setRawPathEntries(newRawEntries);
			setResolveInfoValidState(cproject, true);
		} catch (CoreException e) {
			throw new CModelException(e);
		}
	}
	
	/**
	 * Collects path entry errors for each project and generate error markers for these errors
	 * @param project - Project with path entry errors
	 * @param problems - The path entry errors associated with the project
	 */
	public void addProblemMarkers(final IProject project, final ICModelStatus[] problems) {
		PathEntryProblem problem = new PathEntryProblem(project, problems);
		//queue up the problems to be logged
		markerProblems.add(problem);
		//generate the error markers
		markerTask.schedule();
	}

	private class GenerateMarkersJob extends WorkspaceJob {
		public GenerateMarkersJob(String name) {
			super(name);
		}

		@Override
		public IStatus runInWorkspace(IProgressMonitor monitor) {								
			while (markerProblems.peek() != null && !monitor.isCanceled()) {
				PathEntryProblem problem = markerProblems.poll();
				IProject project = problem.project;
				ICModelStatus[] problems = problem.problems;
				PathEntryUtil.flushPathEntryProblemMarkers(project);
				for (int i = 0; i < problems.length; ++i) {
					PathEntryUtil.createPathEntryProblemMarker(project, problems[i]);
				}
			}
			return Status.OK_STATUS;
		}
	}
	
	private boolean needDelta(ICProject cproject){
		try {
			PathEntryStoreProxy store = (PathEntryStoreProxy)getPathEntryStore(cproject.getProject(), false);
			return store == null || !(store.getStore() instanceof ConfigBasedPathEntryStore);
		} catch (CoreException e) {
		}
		return false;
	}

	public ICElementDelta[] generatePathEntryDeltas(ICProject cproject, IPathEntry[] oldEntries, IPathEntry[] newEntries) {
		if(!needDelta(cproject))
			return new ICElementDelta[0];

		ArrayList<ICElementDelta> list = new ArrayList<ICElementDelta>();

		// if nothing was known before do not generate any deltas.
		if (oldEntries == null) {
			return new ICElementDelta[0];
		}
		// Sanity checks
		if (newEntries == null) {
			newEntries = NO_PATHENTRIES;
		}

		// Check the removed entries.
		for (IPathEntry oldEntrie : oldEntries) {
			boolean found = false;
			for (IPathEntry newEntrie : newEntries) {
				if (oldEntrie.equals(newEntrie)) {
					found = true;
					break;
				}
			}
			// Was it deleted.
			if (!found) {
				ICElementDelta delta = makePathEntryDelta(cproject, oldEntrie, true);
				if (delta != null) {
					list.add(delta);
				}
			}
		}

		// Check the new entries.
		for (IPathEntry newEntrie : newEntries) {
			boolean found = false;
			for (IPathEntry oldEntrie : oldEntries) {
				if (newEntrie.equals(oldEntrie)) {
					found = true;
					break;
				}
			}
			// is it new?
			if (!found) {
				ICElementDelta delta = makePathEntryDelta(cproject, newEntrie, false);
				if (delta != null) {
					list.add(delta);
				}
			}
		}

		// Check for reorder
		if (list.size() == 0 && oldEntries.length == newEntries.length) {
			for (int i = 0; i < newEntries.length; i++) {
				if (!newEntries[i].equals(oldEntries[i])) {
					ICElementDelta delta = makePathEntryDelta(cproject, null, false);
					if (delta != null) {
						list.add(delta);
					}
				}
			}
		}
		// They may have remove some duplications, catch here .. consider it as reordering.
		if (list.size() == 0 && oldEntries.length != newEntries.length) {
			ICElementDelta delta = makePathEntryDelta(cproject, null, true);
			if (delta != null) {
				list.add(delta);
			}
		}
		ICElementDelta[] deltas = new ICElementDelta[list.size()];
		list.toArray(deltas);
		return deltas;
	}

	/**
	 * return a delta, with the specified change flag.
	 */
	protected ICElementDelta makePathEntryDelta(ICProject cproject, IPathEntry entry, boolean removed) {
		ICElement celement = null;
		int flag = ICElementDelta.F_PATHENTRY_REORDER;
		if (entry == null) {
			celement = cproject;
			flag = ICElementDelta.F_PATHENTRY_REORDER;
		} else {
			int kind = entry.getEntryKind();
			switch (kind) {
				case IPathEntry.CDT_SOURCE : {
					ISourceEntry source = (ISourceEntry)entry;
					IPath path = source.getPath();
					celement = CoreModel.getDefault().create(path);
					flag = (removed) ? ICElementDelta.F_REMOVED_PATHENTRY_SOURCE : ICElementDelta.F_ADDED_PATHENTRY_SOURCE;
					break;
				}
				case IPathEntry.CDT_LIBRARY : {
					celement = cproject;
					flag = (removed) ? ICElementDelta.F_REMOVED_PATHENTRY_LIBRARY : ICElementDelta.F_ADDED_PATHENTRY_LIBRARY;
					break;
				}
				case IPathEntry.CDT_PROJECT : {
					//IProjectEntry pentry = (IProjectEntry) entry;
					celement = cproject;
					flag = ICElementDelta.F_CHANGED_PATHENTRY_PROJECT;
					break;
				}
				case IPathEntry.CDT_INCLUDE : {
					IIncludeEntry include = (IIncludeEntry)entry;
					IPath path = include.getPath();
					celement = CoreModel.getDefault().create(path);
					flag = ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE;
					break;
				}
				case IPathEntry.CDT_INCLUDE_FILE: {
					IIncludeFileEntry includeFile = (IIncludeFileEntry)entry;
					IPath path = includeFile.getPath();
					celement = CoreModel.getDefault().create(path);
					flag = ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE;
					break;
				}
				case IPathEntry.CDT_MACRO : { 
					IMacroEntry macro = (IMacroEntry)entry;
					IPath path = macro.getPath();
					celement = CoreModel.getDefault().create(path);
					flag = ICElementDelta.F_CHANGED_PATHENTRY_MACRO;
					break;
				}
				case IPathEntry.CDT_MACRO_FILE: {
					IMacroFileEntry macro = (IMacroFileEntry)entry;
					IPath path = macro.getPath();
					celement = CoreModel.getDefault().create(path);
					flag = ICElementDelta.F_CHANGED_PATHENTRY_MACRO;
					break;
				}
				case IPathEntry.CDT_CONTAINER : {
					//IContainerEntry container = (IContainerEntry) entry;
					//celement = cproject;
					//SHOULD NOT BE HERE Container are resolved.
					break;
				}
			}
		}
		if (celement == null) {
			celement = cproject;
		}
		CElementDelta delta = new CElementDelta(cproject.getCModel());
		delta.changed(celement, flag);
		return delta;
	}

	static String[] getRegisteredContainerIDs() {
		Plugin core = CCorePlugin.getDefault();
		if (core == null) {
			return null;
		}
		ArrayList<String> containerIDList = new ArrayList<String>(5);
		IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(CCorePlugin.PLUGIN_ID,
				CONTAINER_INITIALIZER_EXTPOINT_ID);
		if (extension != null) {
			IExtension[] extensions = extension.getExtensions();
			for (IExtension extension2 : extensions) {
				IConfigurationElement[] configElements = extension2.getConfigurationElements();
				for (IConfigurationElement configElement : configElements) {
					String idAttribute = configElement.getAttribute("id"); //$NON-NLS-1$
					if (idAttribute != null)
						containerIDList.add(idAttribute);
				}
			}
		}
		String[] containerIDs = new String[containerIDList.size()];
		containerIDList.toArray(containerIDs);
		return containerIDs;
	}

	public void setPathEntryStore(IProject project, IPathEntryStore newStore) {
		IPathEntryStore oldStore = null;
		synchronized (storeMap) {
			oldStore = storeMap.remove(project);
			if (newStore != null) {
				storeMap.put(project, newStore);
			}
		}
		if (oldStore != null) {
			// remove are self before closing
			oldStore.removePathEntryStoreListener(this);
			oldStore.close();
		}
	}

	public IPathEntryStore getPathEntryStore(IProject project, boolean create) throws CoreException {
		synchronized (storeMap){
			IPathEntryStore store = storeMap.get(project);
			if (store == null) {
				if(create == true){
					store = createPathEntryStore(project);
					storeMap.put(project, store);
					store.addPathEntryStoreListener(this);
				}
			} else if (store instanceof AbstractCExtensionProxy){
				((AbstractCExtensionProxy)store).updateProject(project);
			}
			return store;
		}
	}

	public IPathEntryStore createPathEntryStore(IProject project) throws CoreException {
		return new PathEntryStoreProxy(project);
//		IPathEntryStore store = null;
//		if (project != null) {
//			try {
//				ICDescriptor cdesc = CCorePlugin.getDefault().getCProjectDescription(project, false);
//				if (cdesc != null) {
//					ICExtensionReference[] cextensions = cdesc.get(PATHENTRY_STORE_UNIQ_ID, true);
//					if (cextensions.length > 0) {
//						for (int i = 0; i < cextensions.length; i++) {
//							try {
//								store = (IPathEntryStore)cextensions[i].createExtension();
//								break;
//							} catch (ClassCastException e) {
//								//
//								CCorePlugin.log(e);
//							}
//						}
//					}
//				}
//			} catch (CoreException e) {
//				// ignore since we fall back to a default....
//			}
//		}
//		if (store == null) {
//			store = createDefaultStore(project);
//		}
//		return store;
	}
	
//	private IPathEntryStore createDefaultStore(IProject project){
//		if(CProjectDescriptionManager.getInstance().isNewStyleIndexCfg(project)){
//			return new ConfigBasedPathEntryStore(project);
//		}
//		return  new DefaultPathEntryStore(project);
//	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.cdt.core.resources.IPathEntryStoreListener#pathEntryStoreChanged(org.eclipse.cdt.core.resources.PathEntryChangedEvent)
	 */
	public void pathEntryStoreChanged(PathEntryStoreChangedEvent event) {
		IProject project = event.getProject();

		// sanity
		if (project == null) {
			return;
		}

		CModelManager manager = CModelManager.getDefault();
		final ICProject cproject = manager.create(project);
		if (event.hasClosed()) {
			setPathEntryStore(project, null);
			containerRemove(cproject);
		}
		if (project.isAccessible()) {
			try {
				CModelOperation op = new PathEntryStoreChangedOperation(cproject);
				op.runOperation(null);
			} catch (CModelException e) {
				CCorePlugin.log(e);
			}
		} else {
			resolvedMap.remove(cproject);
			resolvedInfoMap.remove(cproject);
			containerRemove(cproject);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.cdt.core.model.IElementChangedListener#elementChanged(org.eclipse.cdt.core.model.ElementChangedEvent)
	 */
	public void elementChanged(ElementChangedEvent event) {
		try {
			if (processDelta(event.getDelta()) == true) {
				ICProject[] cProjects = resolvedMap.keySet().toArray(new ICProject[0]);
				for (ICProject project2 : cProjects) {
					IPathEntry[] entries = getCachedResolvedPathEntries(project2);
					if (entries != null) {
						IProject project = project2.getProject();
						try {
							IMarker[] markers = project.findMarkers(ICModelMarker.PATHENTRY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
							if (markers != null && markers.length > 0) {
								ArrayList<ICModelStatus> problemList = new ArrayList<ICModelStatus>();
								for (IPathEntry entrie : entries) {
									ICModelStatus status = PathEntryUtil.validatePathEntry(project2, entrie, true, false);
									if (!status.isOK()) {
										problemList.add(status);
									}
								}
								ICModelStatus[] problems = new ICModelStatus[problemList.size()];
								problemList.toArray(problems);
								if (PathEntryUtil.hasPathEntryProblemMarkersChange(project, problems)) {
									addProblemMarkers(project, problems);
								}
							}
						} catch (CoreException e) {
							// ignore the exception.
						}
					}
				}
			}
		} catch (CModelException e) {
		}
	}

	protected boolean processDelta(ICElementDelta delta) throws CModelException {
		int kind = delta.getKind();
		ICElement element = delta.getElement();
		int type = element.getElementType(); 

		// handle open, closing and removing of projects
		if ( type == ICElement.C_PROJECT) {
			ICProject cproject = (ICProject)element;
			if ((kind == ICElementDelta.REMOVED || kind == ICElementDelta.ADDED)) {
				if (kind == ICElementDelta.REMOVED) {
					IProject project = cproject.getProject();
					IPathEntryStore store = null;
					try {
						store = getPathEntryStore(project, false);
						if (store != null) {
							store.close();
						}
					} catch (CoreException e) {
						throw new CModelException(e);
					} finally {
						if (store == null) {
							resolvedMap.remove(cproject);
							resolvedInfoMap.remove(cproject);
							containerRemove(cproject);
						}
					}
				} 
				return true;
			}
			// project change, traverse children.
		} 
		if (element instanceof IWorkingCopy) {
			return false;
		}
		if (kind == ICElementDelta.ADDED || kind == ICElementDelta.REMOVED) {
			// If the element path maps to the some IPathEntry we need to remove that entry
			// but I'm not sure of the side effects so lets just do this for the ISourceEntry type
			if (element instanceof ISourceRoot) {
				ISourceRoot sourceRoot = (ISourceRoot)element;
				if (kind == ICElementDelta.REMOVED) {
					updatePathEntryFromDeleteSource(sourceRoot);
				}
			}
			return true; // add/remove we validate all paths
		}
		if (type == ICElement.C_MODEL || type == ICElement.C_CCONTAINER || type == ICElement.C_PROJECT) {
			ICElementDelta[] affectedChildren = delta.getAffectedChildren();
			for (ICElementDelta element2 : affectedChildren) {
				if (processDelta(element2) == true) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * The sourceroot been deleted update the path entries
	 * @param sourceRoot
	 * @throws CModelException
	 */
	void updatePathEntryFromDeleteSource(ISourceRoot sourceRoot) throws CModelException {
		final ICProject cproject = sourceRoot.getCProject();
		IPathEntry[] rawEntries = getRawPathEntries(cproject);
		boolean change = false;
		ArrayList<IPathEntry> list = new ArrayList<IPathEntry>(rawEntries.length);
		for (int i = 0; i < rawEntries.length; ++i) {
			if (rawEntries[i].getEntryKind() == IPathEntry.CDT_SOURCE) {
				if (sourceRoot.getPath().equals(rawEntries[i].getPath())) {
					change = true;
				} else {
					list.add(rawEntries[i]);
				}
			} else {
				list.add(rawEntries[i]);
			}
		}
		if (change) {
			IPathEntry[] newEntries = new IPathEntry[list.size()];
			list.toArray(newEntries);
			final IPathEntry[] finalEntries = newEntries;
			Job updatePathEntry = new Job("PathEntry Update source roots") { //$NON-NLS-1$
				
				/*
				 * (non-Javadoc)
				 * 
				 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
				 */
				@Override
				protected IStatus run(IProgressMonitor monitor) {
					try {
						CCorePlugin.getWorkspace().run(new IWorkspaceRunnable() {
							
							/* (non-Javadoc)
							 * @see org.eclipse.core.resources.IWorkspaceRunnable#run(org.eclipse.core.runtime.IProgressMonitor)
							 */
							public void run(IProgressMonitor mon) throws CoreException {
								setRawPathEntries(cproject, finalEntries, mon);
							}
						}, null);
					} catch (CoreException e) {
						return e.getStatus();
					}
					
					return Status.OK_STATUS;
				}
			};
			IProject project = cproject.getProject();
			ISchedulingRule rule = project.getWorkspace().getRoot();
			updatePathEntry.setRule(rule);
			updatePathEntry.schedule();
		}
	}
		

	public ICModelStatus validatePathEntry(ICProject cProject, IPathEntry[] entries) {
		return PathEntryUtil.validatePathEntry(cProject, entries);
	}

	public ICModelStatus validatePathEntry(ICProject cProject, IPathEntry entry, boolean checkSourceAttachment,
			boolean recurseInContainers) {
		return PathEntryUtil.validatePathEntry(cProject, entry, checkSourceAttachment, recurseInContainers);
	}

}
