/*******************************************************************************
 * Copyright (c) 2007, 2013 Intel Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 * Intel Corporation - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.core.settings.model.util;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.cdtvariables.CdtVariableException;
import org.eclipse.cdt.core.cdtvariables.ICdtVariable;
import org.eclipse.cdt.core.cdtvariables.ICdtVariableManager;
import org.eclipse.cdt.core.language.settings.providers.ScannerDiscoveryLegacySupport;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.CoreModelUtil;
import org.eclipse.cdt.core.model.ICModelStatus;
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.ISourceEntry;
import org.eclipse.cdt.core.resources.IPathEntryVariableManager;
import org.eclipse.cdt.core.settings.model.CExternalSetting;
import org.eclipse.cdt.core.settings.model.COutputEntry;
import org.eclipse.cdt.core.settings.model.CSourceEntry;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICExclusionPatternPathEntry;
import org.eclipse.cdt.core.settings.model.ICExternalSetting;
import org.eclipse.cdt.core.settings.model.ICIncludePathEntry;
import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
import org.eclipse.cdt.core.settings.model.ICOutputEntry;
import org.eclipse.cdt.core.settings.model.ICSettingBase;
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
import org.eclipse.cdt.core.settings.model.ICSourceEntry;
import org.eclipse.cdt.core.settings.model.ICStorageElement;
import org.eclipse.cdt.core.settings.model.extension.CBuildData;
import org.eclipse.cdt.core.settings.model.extension.CConfigurationData;
import org.eclipse.cdt.core.settings.model.extension.CFileData;
import org.eclipse.cdt.core.settings.model.extension.CFolderData;
import org.eclipse.cdt.core.settings.model.extension.CLanguageData;
import org.eclipse.cdt.core.settings.model.extension.CResourceData;
import org.eclipse.cdt.internal.core.CdtVarPathEntryVariableManager;
import org.eclipse.cdt.internal.core.CharOperation;
import org.eclipse.cdt.internal.core.cdtvariables.CoreVariableSubstitutor;
import org.eclipse.cdt.internal.core.cdtvariables.DefaultVariableContextInfo;
import org.eclipse.cdt.internal.core.cdtvariables.ICoreVariableContextInfo;
import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer;
import org.eclipse.cdt.internal.core.model.APathEntry;
import org.eclipse.cdt.internal.core.model.CModelStatus;
import org.eclipse.cdt.internal.core.model.PathEntry;
import org.eclipse.cdt.internal.core.settings.model.CConfigurationDescriptionCache;
import org.eclipse.cdt.internal.core.settings.model.IInternalCCfgInfo;
import org.eclipse.cdt.utils.cdtvariables.CdtVariableResolver;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;

public class PathEntryTranslator {
	public static final int OP_ADD = 1;
	public static final int OP_REMOVE = 2;
	public static final int OP_REPLACE = 3;

	public static final int INCLUDE_BUILT_INS = 1;
	public static final int INCLUDE_USER = 1 << 1;
	public static final int INCLUDE_ALL = INCLUDE_BUILT_INS | INCLUDE_USER;

	static String PATH_ENTRY = "pathentry"; //$NON-NLS-1$
	static String ATTRIBUTE_KIND = "kind"; //$NON-NLS-1$
	static String ATTRIBUTE_PATH = "path"; //$NON-NLS-1$
	static String ATTRIBUTE_BASE_PATH = "base-path"; //$NON-NLS-1$
	static String ATTRIBUTE_BASE_REF = "base-ref"; //$NON-NLS-1$
	static String ATTRIBUTE_EXPORTED = "exported"; //$NON-NLS-1$
	static String ATTRIBUTE_SOURCEPATH = "sourcepath"; //$NON-NLS-1$
	static String ATTRIBUTE_ROOTPATH = "roopath"; //$NON-NLS-1$
	static String ATTRIBUTE_PREFIXMAPPING = "prefixmapping"; //$NON-NLS-1$
	static String ATTRIBUTE_EXCLUDING = "excluding"; //$NON-NLS-1$
	static String ATTRIBUTE_INCLUDE = "include"; //$NON-NLS-1$
	static String ATTRIBUTE_INCLUDE_FILE = "include-file"; //$NON-NLS-1$
	static String ATTRIBUTE_LIBRARY = "library"; //$NON-NLS-1$
	static String ATTRIBUTE_SYSTEM = "system"; //$NON-NLS-1$
	static String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
	static String ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$
	static String ATTRIBUTE_MACRO_FILE = "macro-file"; //$NON-NLS-1$
	static String VALUE_TRUE = "true"; //$NON-NLS-1$

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

	private static final char[] SPEC_CHARS = new char[] { '*', '?' };
	private PathSettingsContainer fRcDataHolder;
	private IProject fProject;
	private CConfigurationData fCfgData;
	private PathSettingsContainer fTranslatedFilters;
	private Map<IPath, ResourceInfo> fResourceMap = new HashMap<>();
	private IWorkspaceRoot fRoot = ResourcesPlugin.getWorkspace().getRoot();

	private static class VarSubstitutor extends CoreVariableSubstitutor {
		ICConfigurationDescription fCfg;
		ICdtVariableManager fMngr = CCorePlugin.getDefault().getCdtVariableManager();

		public VarSubstitutor(ICConfigurationDescription cfgDescription) {
			super(new DefaultVariableContextInfo(ICoreVariableContextInfo.CONTEXT_CONFIGURATION, cfgDescription), "", //$NON-NLS-1$
					" "); //$NON-NLS-1$
			fCfg = cfgDescription;
		}

		@Override
		protected ResolvedMacro resolveMacro(ICdtVariable macro) throws CdtVariableException {
			if (!CdtVarPathEntryVariableManager.isPathEntryVariable(macro, fCfg, fMngr))
				return super.resolveMacro(macro);
			return new ResolvedMacro(macro.getName(), CdtVariableResolver.createVariableReference(macro.getName()));
		}
	}

	public static final class ReferenceSettingsInfo {
		private IPath[] fRefProjPaths;
		private ICExternalSetting[] fExtSettings;

		public ReferenceSettingsInfo(ICConfigurationDescription cfgDescription) {
			fExtSettings = cfgDescription.getExternalSettings();
			Map<String, String> map = cfgDescription.getReferenceInfo();
			fRefProjPaths = new IPath[map.size()];
			int num = 0;
			for (String proj : map.keySet()) {
				fRefProjPaths[num++] = new Path(proj).makeAbsolute();
			}
		}

		public ReferenceSettingsInfo(IPath[] projPaths, ICExternalSetting extSettings[]) {
			if (projPaths != null)
				fRefProjPaths = projPaths.clone();
			if (extSettings != null)
				fExtSettings = extSettings.clone();
		}

		public IPath[] getReferencedProjectsPaths() {
			if (fRefProjPaths != null)
				return fRefProjPaths.clone();
			return new IPath[0];
		}

		public Map<String, String> getRefProjectsMap() {
			if (fRefProjPaths != null && fRefProjPaths.length != 0) {
				Map<String, String> map = new HashMap<>(fRefProjPaths.length);
				for (IPath fRefProjPath : fRefProjPaths) {
					map.put(fRefProjPath.segment(0), ""); //$NON-NLS-1$
				}
				return map;
			}
			return new HashMap<>(0);
		}

		public ICExternalSetting[] getExternalSettings() {
			if (fExtSettings != null)
				return fExtSettings.clone();
			return new ICExternalSetting[0];
		}
	}

	private static class PathEntryKyndStore {
		private static final int INDEX_CDT_LIBRARY = 0;
		private static final int INDEX_CDT_PROJECT = 1;
		private static final int INDEX_CDT_SOURCE = 2;
		private static final int INDEX_CDT_INCLUDE = 3;
		private static final int INDEX_CDT_CONTAINER = 4;
		private static final int INDEX_CDT_MACRO = 5;
		private static final int INDEX_CDT_OUTPUT = 6;
		private static final int INDEX_CDT_INCLUDE_FILE = 7;
		private static final int INDEX_CDT_MACRO_FILE = 8;

		private static final int STORAGE_SIZE = 9;

		private static final int ENTRY_KINDS[] = new int[] { IPathEntry.CDT_LIBRARY, IPathEntry.CDT_PROJECT,
				IPathEntry.CDT_SOURCE, IPathEntry.CDT_INCLUDE, IPathEntry.CDT_CONTAINER, IPathEntry.CDT_MACRO,
				IPathEntry.CDT_OUTPUT, IPathEntry.CDT_INCLUDE_FILE, IPathEntry.CDT_MACRO_FILE, };
		//		private static final int INEXISTENT_INDEX = -1;

		@SuppressWarnings("unchecked")
		private Map<String, IPathEntry>[] fEntryStorage = new Map[STORAGE_SIZE];

		private int kindToIndex(int kind) {
			switch (kind) {
			case IPathEntry.CDT_LIBRARY:
				return INDEX_CDT_LIBRARY;
			case IPathEntry.CDT_PROJECT:
				return INDEX_CDT_PROJECT;
			case IPathEntry.CDT_SOURCE:
				return INDEX_CDT_SOURCE;
			case IPathEntry.CDT_INCLUDE:
				return INDEX_CDT_INCLUDE;
			case IPathEntry.CDT_CONTAINER:
				return INDEX_CDT_CONTAINER;
			case IPathEntry.CDT_MACRO:
				return INDEX_CDT_MACRO;
			case IPathEntry.CDT_OUTPUT:
				return INDEX_CDT_OUTPUT;
			case IPathEntry.CDT_INCLUDE_FILE:
				return INDEX_CDT_INCLUDE_FILE;
			case IPathEntry.CDT_MACRO_FILE:
				return INDEX_CDT_MACRO_FILE;
			}
			throw new IllegalArgumentException(UtilMessages.getString("PathEntryTranslator.0")); //$NON-NLS-1$
		}

		public static int[] getSupportedKinds() {
			return ENTRY_KINDS.clone();
		}

		public Map<String, IPathEntry> get(int kind) {
			return fEntryStorage[kindToIndex(kind)];
		}

		public Map<String, IPathEntry> put(int kind, Map<String, IPathEntry> object) {
			int index = kindToIndex(kind);
			Map<String, IPathEntry> old = fEntryStorage[index];
			fEntryStorage[index] = object;
			return old;
		}
	}

	private static class LangEntryInfo {
		ICLanguageSettingEntry fLangEntry;
		ResolvedEntry fResolvedEntry;

		public LangEntryInfo(ICLanguageSettingEntry lEntry, ResolvedEntry re) {
			fLangEntry = lEntry;
			fResolvedEntry = re;
		}
	}

	private class RcDesInfo {
		List<ResolvedEntry> fResolvedEntries;
		KindBasedStore<List<LangEntryInfo>> fLangEntries;

		private RcDesInfo(ResourceInfo rcInfo) {
			fResolvedEntries = new ArrayList<>();
			fLangEntries = new KindBasedStore<>();
		}

		public void add(LangEntryInfo info) {
			List<LangEntryInfo> list = fLangEntries.get(info.fLangEntry.getKind());
			if (list == null) {
				list = new ArrayList<>();
				fLangEntries.put(info.fLangEntry.getKind(), list);
			}
			list.add(info);
		}

		public ICLanguageSettingEntry[] getEntries(int kind) {
			List<LangEntryInfo> list = fLangEntries.get(kind);
			if (list != null) {
				ICLanguageSettingEntry[] entries = new ICLanguageSettingEntry[list.size()];
				for (int i = 0; i < entries.length; i++) {
					LangEntryInfo info = list.get(i);
					entries[i] = info.fLangEntry;
				}
				return entries;
			}
			return new ICLanguageSettingEntry[0];

		}
	}

	private static ICLanguageSettingEntry createLangEntry(ResolvedEntry entry) {
		PathEntryValueInfo pathEntryValue = entry.getResolvedValue();
		String name = pathEntryValue.getName();

		int flags = ICSettingEntry.RESOLVED;
		if (entry.isReadOnly())
			flags |= ICSettingEntry.READONLY;
		if (entry.isBuiltIn())
			flags |= ICSettingEntry.BUILTIN;

		IPath path = pathEntryValue.getFullPath();
		if (path != null) {
			flags |= ICSettingEntry.VALUE_WORKSPACE_PATH;
		} else {
			path = pathEntryValue.getLocation();
		}

		int kind = entry.fEntry.getEntryKind();
		switch (kind) {
		case IPathEntry.CDT_LIBRARY: {
			if (path != null) {
				ILibraryEntry libEntry = (ILibraryEntry) entry.fEntry;
				return (ICLanguageSettingEntry) CDataUtil.createEntry(ICSettingEntry.LIBRARY_FILE, name, null, null,
						flags, libEntry.getSourceAttachmentPath(), libEntry.getSourceAttachmentRootPath(),
						libEntry.getSourceAttachmentPrefixMapping());
			}
			break;
		}
		//			case IPathEntry.CDT_PROJECT:
		//				return ICLanguageSettingEntry;
		//			case IPathEntry.CDT_SOURCE:
		//				return INDEX_CDT_SOURCE;
		case IPathEntry.CDT_INCLUDE: {
			if (path != null) {
				return CDataUtil.createCIncludePathEntry(name, flags);
			}
			break;
		}
		//			case IPathEntry.CDT_CONTAINER:
		//				return INDEX_CDT_CONTAINER;
		case IPathEntry.CDT_MACRO:
			if (name.length() != 0) {
				String value = pathEntryValue.getValue();
				return CDataUtil.createCMacroEntry(name, value, flags);
			}
			break;
		//			case IPathEntry.CDT_OUTPUT:
		//				return INDEX_CDT_OUTPUT;
		case IPathEntry.CDT_INCLUDE_FILE: {
			if (path != null) {
				return CDataUtil.createCIncludeFileEntry(name, flags);
			}
			break;
		}
		case IPathEntry.CDT_MACRO_FILE: {
			if (path != null) {
				return CDataUtil.createCMacroFileEntry(name, flags);
			}
			break;
		}
		}
		return null;
	}

	private class ResourceInfo {
		IResource fRc;
		boolean fExists;

		public ResourceInfo(IResource rc, boolean exists) {
			fRc = rc;
			fExists = exists;
		}
	}

	private class PathEntryValueInfo {
		private ResourceInfo fResourceInfo;
		private IPath fLocation;
		private String fName;
		private String fValue;
		private ResolvedEntry fResolvedEntry;

		private PathEntryValueInfo(ResolvedEntry rEntry) {
			fResolvedEntry = rEntry;

			init();
		}

		public IPath getFullPath() {
			if (fResourceInfo != null)
				return fResourceInfo.fRc.getFullPath();
			return null;
		}

		public IPath getLocation() {
			if (fResourceInfo != null)
				return fResourceInfo.fRc.getLocation();
			return fLocation;
		}

		public String getName() {
			if (fName != null)
				return fName;
			return ""; //$NON-NLS-1$
		}

		public String getValue() {
			if (fValue != null)
				return fValue;
			return ""; //$NON-NLS-1$
		}

		private void init() {
			IPathEntry entry = fResolvedEntry.fEntry;
			int peKind = entry.getEntryKind();
			IPath basePath = null, valuePath = null;
			boolean isFile = false;
			boolean calcPath = false;
			switch (peKind) {
			case IPathEntry.CDT_MACRO:
				IMacroEntry me = (IMacroEntry) entry;
				fName = me.getMacroName();
				fValue = me.getMacroValue();
				break;
			case IPathEntry.CDT_LIBRARY:
				isFile = true;
				calcPath = true;
				ILibraryEntry le = (ILibraryEntry) entry;
				basePath = le.getBasePath();
				valuePath = le.getLibraryPath();
				break;
			case IPathEntry.CDT_INCLUDE:
				isFile = false;
				calcPath = true;
				IIncludeEntry ie = (IIncludeEntry) entry;
				basePath = ie.getBasePath();
				valuePath = ie.getIncludePath();
				break;
			case IPathEntry.CDT_INCLUDE_FILE:
				isFile = true;
				calcPath = true;
				IIncludeFileEntry ife = (IIncludeFileEntry) entry;
				basePath = ife.getBasePath();
				valuePath = ife.getIncludeFilePath();
				break;
			case IPathEntry.CDT_MACRO_FILE:
				isFile = true;
				calcPath = true;
				IMacroFileEntry mfe = (IMacroFileEntry) entry;
				basePath = mfe.getBasePath();
				valuePath = mfe.getMacroFilePath();
				break;
			case IPathEntry.CDT_PROJECT:
			case IPathEntry.CDT_SOURCE:
			case IPathEntry.CDT_CONTAINER:
			case IPathEntry.CDT_OUTPUT:
				fResourceInfo = fResolvedEntry.getResourceInfo();
				break;
			}

			if (calcPath) {
				do {
					IPath unresolvedBase = basePath;
					IPath unresolvedValue = valuePath;
					IPathEntryVariableManager mngr = CCorePlugin.getDefault().getPathEntryVariableManager();

					basePath = mngr.resolvePath(basePath);
					valuePath = mngr.resolvePath(valuePath);

					fName = unresolvedBase.isEmpty() ? unresolvedValue.toString()
							: unresolvedBase.append(unresolvedValue).toString();
					fValue = fName;

					if (!basePath.isEmpty()) {
						IPath loc = basePath;
						if (!loc.isAbsolute()) {
							ResourceInfo rcInfo = findResourceInfo(fRoot, loc.append(valuePath), !isFile);
							if (rcInfo.fExists) {
								fResourceInfo = rcInfo;
								fName = unresolvedBase.append(unresolvedValue).toString();
								fValue = fName;
								break;
							}
						}
						fLocation = loc.append(valuePath);
						break;
					}

					if (!valuePath.isAbsolute()) {
						ResourceInfo rcInfo = fResolvedEntry.getResourceInfo();
						if (rcInfo.fExists) {
							if (rcInfo.fRc.getType() == IResource.FILE) {
								rcInfo = findResourceInfo(fRoot, rcInfo.fRc.getFullPath().removeLastSegments(1), true);
							}
							IPath location = rcInfo.fRc.getLocation();
							if (location != null && rcInfo.fRc.getType() != IResource.FILE) {
								rcInfo = findResourceInfo((IContainer) rcInfo.fRc, valuePath, !isFile);
								fResourceInfo = rcInfo;
								break;
							}
						}
					}

					fLocation = valuePath;
				} while (false);
			}
		}
	}

	private class ResolvedEntry {
		private IPathEntry fEntry;
		private ResourceInfo fResourceInfo;
		private ResourceInfo[] fFilterInfos;
		private PathEntryValueInfo fResolvedValue;
		private PathEntryResolveInfoElement fResolveElement;
		private boolean fIsReadOnly;
		private boolean fIsBuiltIn;

		public ResolvedEntry(IPathEntry entry, PathEntryResolveInfoElement resolveElement) {
			fEntry = entry;
			fResolveElement = resolveElement;
			fIsReadOnly = areEntriesReadOnly(fResolveElement);
			fIsBuiltIn = fIsReadOnly;
		}

		public ResolvedEntry(IPathEntry entry, boolean isReadOnly) {
			fEntry = entry;
			fIsReadOnly = isReadOnly;
		}

		public boolean isReadOnly() {
			return fIsReadOnly;
		}

		public boolean isBuiltIn() {
			return fIsBuiltIn;
		}

		public ResourceInfo getResourceInfo() {
			if (fResourceInfo == null) {
				fResourceInfo = findResourceInfo(fRoot, getEntryFullPath(fEntry), true);
			}
			return fResourceInfo;
		}

		public ResourceInfo[] getFilterInfos() {
			if (fFilterInfos == null) {
				IPath[] paths = obtainFilters(fEntry);
				if (paths.length == 0) {
					fFilterInfos = new ResourceInfo[0];
				} else {
					ResourceInfo rcInfo = getResourceInfo();
					if (rcInfo.fExists) {
						if (rcInfo.fRc.getType() == IResource.FILE) {
							fFilterInfos = new ResourceInfo[0];
						} else {
							List<ResourceInfo> list = new ArrayList<>();
							for (IPath path : paths) {
								list.addAll(Arrays.asList(processFilter((IContainer) rcInfo.fRc, path)));
							}
							fFilterInfos = new ResourceInfo[list.size()];
							list.toArray(fFilterInfos);
						}
					} else {
						fFilterInfos = new ResourceInfo[paths.length];
						for (int i = 0; i < paths.length; i++) {
							fFilterInfos[i] = processInexistingResourceFilter((IContainer) rcInfo.fRc, paths[i]);
						}
					}
				}
			}
			return fFilterInfos;
		}

		private ResourceInfo[] processFilter(IContainer container, IPath path) {
			return resolveFilter(container, path);
		}

		private ResourceInfo processInexistingResourceFilter(IContainer container, IPath path) {
			IFolder f = container.getFolder(path);
			ResourceInfo rcInfo = new ResourceInfo(f, false);
			addRcInfoToMap(rcInfo);

			addResolvedFilterToMap(container.getFullPath(), new ResourceInfo[] { rcInfo }, true);
			return rcInfo;
		}

		public IPathEntry getEntry() {
			return fEntry;
		}

		public PathEntryValueInfo getResolvedValue() {
			if (fResolvedValue == null) {
				fResolvedValue = new PathEntryValueInfo(this);
			}
			return fResolvedValue;
		}

	}

	private static class PathEntryComposer {
		private IPath fPath;
		private ICSettingEntry fLangEntry;
		private Set<IPath> fFiltersSet;
		private boolean fIsExported;
		private IProject fProject;

		PathEntryComposer(IPath path, IProject project/*, ICConfigurationDescription cfgDescription*/) {
			fPath = toProjectPath(path);
			fProject = project;
		}

		private static IPath toProjectPath(IPath path) {
			if (path.segmentCount() > 1)
				path = new Path(path.segment(0));

			return path.makeAbsolute();
		}

		PathEntryComposer(ICExclusionPatternPathEntry entry,
				IProject project/*, ICConfigurationDescription cfgDescription*/) {
			fPath = new Path(entry.getValue());
			fLangEntry = entry;
			fProject = project;
			IPath[] exclusions = entry.getExclusionPatterns();
			if (exclusions.length != 0) {
				fFiltersSet = new HashSet<>(exclusions.length);
				fFiltersSet.addAll(Arrays.asList(entry.getExclusionPatterns()));
			}
		}

		PathEntryComposer(IPath path, ICLanguageSettingEntry entry, boolean exported,
				IProject project/*, ICConfigurationDescription cfgDescription*/) {
			fPath = path;
			fLangEntry = entry;
			fIsExported = exported;
			fProject = project;
		}

		public void addFilter(IPath path) {
			if (fFiltersSet == null)
				fFiltersSet = new HashSet<>();

			fFiltersSet.add(path);
		}

		public ICSettingEntry getSettingEntry() {
			return fLangEntry;
		}

		public IPath getPath() {
			return fPath;
		}

		public IPath[] getExclusionPatterns() {
			if (fFiltersSet != null)
				return fFiltersSet.toArray(new IPath[fFiltersSet.size()]);
			return new IPath[0];
		}

		private IPath[][] getEntryPath(ICSettingEntry entry, ICConfigurationDescription cfgDescription) {
			return valueToEntryPath(entry.getName(), (entry.getFlags() & ICSettingEntry.VALUE_WORKSPACE_PATH) != 0,
					cfgDescription);
		}

		private IPath[][] valueToEntryPath(String value, boolean isWsp, ICConfigurationDescription cfgDescription) {
			String[] pathVarValues = resolveKeepingPathEntryFars(value, cfgDescription);
			IPath result[][] = new IPath[2][pathVarValues.length];
			for (int i = 0; i < pathVarValues.length; i++) {
				String resolvedValue = resolveAll(value, cfgDescription);
				IPath resolvedPath = new Path(resolvedValue);
				IPath pathVarPath = new Path(pathVarValues[i]);
				if (isWsp) {
					if (!resolvedPath.isAbsolute()) {
						result[0][i] = fProject.getFullPath().makeRelative();
						result[1][i] = pathVarPath;
					} else {
						if (resolvedPath.segmentCount() != 0) {
							String projName = resolvedPath.segment(0);
							IPath valuePath = resolvedPath.removeFirstSegments(1).makeRelative();
							if (pathVarPath.segmentCount() != 0) {
								String resolvedProjName = projName;
								String varProjName = pathVarPath.segment(0);
								IPath resolvedProjPath = CCorePlugin.getDefault().getPathEntryVariableManager()
										.resolvePath(new Path(varProjName));
								if (resolvedProjPath.segmentCount() == 1
										&& resolvedProjName.equals(resolvedProjPath.segment(0))) {
									projName = varProjName;
									valuePath = pathVarPath.removeFirstSegments(1).makeRelative();
								}
							}

							result[0][i] = new Path(projName);
							result[1][i] = valuePath;
						} else if (pathVarPath.isRoot()) {
							result[1][i] = ResourcesPlugin.getWorkspace().getRoot().getLocation();
						}
					}
				} else {
					if (!resolvedPath.isAbsolute()) {
						IPath location = fProject.getLocation();
						if (location != null)
							pathVarPath = location.append(pathVarPath);
					}
					result[1][i] = pathVarPath;
				}
			}

			return result;
		}

		public IPathEntry[] toPathEntry(ICConfigurationDescription cfgDescription, boolean keepPathInfo) {
			IPath path = keepPathInfo ? fPath : fProject.getFullPath();
			IPathEntry[] result = new IPathEntry[0];
			if (fLangEntry != null) {
				switch (fLangEntry.getKind()) {
				case ICSettingEntry.INCLUDE_FILE: {
					IPath paths[][] = getEntryPath(fLangEntry, cfgDescription);
					result = new IPathEntry[paths[0].length];
					for (int i = 0; i < result.length; i++)
						result[i] = CoreModel.newIncludeFileEntry(path, null, paths[0][i], paths[1][i],
								getExclusionPatterns(), fIsExported);
					return result;
				}
				case ICSettingEntry.INCLUDE_PATH: {
					IPath paths[][] = getEntryPath(fLangEntry, cfgDescription);
					ICIncludePathEntry ipe = (ICIncludePathEntry) fLangEntry;

					result = new IPathEntry[paths[0].length];
					for (int i = 0; i < result.length; i++)
						result[i] = CoreModel.newIncludeEntry(path, paths[0][i], paths[1][i], !ipe.isLocal(),
								getExclusionPatterns(), fIsExported);
					return result;
				}
				case ICSettingEntry.MACRO:
					result = new IPathEntry[1];
					result[0] = CoreModel.newMacroEntry(path, fLangEntry.getName(), fLangEntry.getValue(),
							getExclusionPatterns(), fIsExported);
					return result;
				case ICSettingEntry.MACRO_FILE: {
					IPath paths[][] = getEntryPath(fLangEntry, cfgDescription);
					result = new IPathEntry[paths[0].length];
					for (int i = 0; i < result.length; i++)
						result[i] = CoreModel.newMacroFileEntry(path, paths[0][i], null, paths[1][i],
								getExclusionPatterns(), fIsExported);
					return result;
				}
				case ICSettingEntry.LIBRARY_PATH:
				case ICSettingEntry.LIBRARY_FILE:
					// Bug 100844 don't contribute library files back to the CModel as a library files, as supplied by the build system,
					// aren't currently resolved
					return null;
				//				case ICSettingEntry.LIBRARY_FILE:{
				//						IPath paths[][] = getEntryPath(fLangEntry, cfgDescription);
				//						result = new IPathEntry[paths[0].length];
				//						for (int i = 0; i < result.length; i++)
				//							result[i] = CoreModel.newLibraryEntry(path, paths[0][i], paths[1][i], null, null, null, fIsExported);
				//						return result;
				//					}
				case ICSettingEntry.OUTPUT_PATH:
					result = new IPathEntry[1];
					result[0] = CoreModel.newOutputEntry(fPath, getExclusionPatterns());
					return result;
				case ICSettingEntry.SOURCE_PATH:
					result = new IPathEntry[1];
					result[0] = CoreModel.newSourceEntry(fPath, getExclusionPatterns());
					return result;
				default:
					return result; // empty
				}
			} else if (fPath != null) {
				result = new IPathEntry[1];
				result[0] = CoreModel.newProjectEntry(fPath, fIsExported);
				return result;
			}
			return result; // empty
		}
	}

	private static String resolveAll(String value, ICConfigurationDescription cfgDescription) {
		try {
			return CCorePlugin.getDefault().getCdtVariableManager().resolveValue(value, "", " ", cfgDescription); //$NON-NLS-1$ //$NON-NLS-2$
		} catch (CdtVariableException e) {
			CCorePlugin.log(e);
		}
		return value;
	}

	private static String[] resolveKeepingPathEntryFars(String value, ICConfigurationDescription cfgDescription) {
		String[] result = new String[] { value }; // default value;
		try {
			VarSubstitutor substitutor = new VarSubstitutor(cfgDescription);

			result = CdtVariableResolver.resolveToStringList(value, substitutor);
			if (result == null || result.length == 0)
				result = new String[] { CdtVariableResolver.resolveToString(value, substitutor) };
		} catch (CdtVariableException e) {
			CCorePlugin.log(e);
		}
		return result;
	}

	public static class PathEntryCollector {
		private PathSettingsContainer fStorage;
		private KindBasedStore<LinkedHashMap<ICSettingEntry, PathEntryComposer>> fStore;
		private KindBasedStore<LinkedHashMap<EntryNameKey, PathEntryComposer>> fNameKeyMapStore; //utility map, does not contain all entries, only those added explicitly
		private LinkedHashMap<IPath, PathEntryComposer> fRefProjMap;
		private IProject fProject;

		private PathEntryCollector(IProject project/*, ICConfigurationDescription cfgDescription*/) {
			fStorage = PathSettingsContainer.createRootContainer();
			fStorage.setValue(this);
			fStore = new KindBasedStore<>(false);
			fNameKeyMapStore = new KindBasedStore<>(false);
			fProject = project;
		}

		private PathEntryCollector(PathSettingsContainer container,
				KindBasedStore<LinkedHashMap<ICSettingEntry, PathEntryComposer>> store,
				IProject project/*, ICConfigurationDescription cfgDescription*/) {
			fStorage = container;
			fStore = store;
			fNameKeyMapStore = new KindBasedStore<>(false);
			fProject = project;
		}

		public void setSourceOutputEntries(int kind, ICExclusionPatternPathEntry entries[]) {
			Map<ICSettingEntry, PathEntryComposer> map = getEntriesMap(kind, true);
			Map<EntryNameKey, PathEntryComposer> nameKeyMap = getEntriesNameKeyMap(kind, true);
			for (ICExclusionPatternPathEntry entry : entries) {
				entry = CDataUtil.makeAbsolute(fProject, entry, true);
				EntryNameKey nameKey = new EntryNameKey(entry);
				PathEntryComposer old = nameKeyMap.get(nameKey);
				if (old != null) {
					entry = CDataUtil.addRemoveExclusionsToEntry(entry,
							((ICExclusionPatternPathEntry) old.fLangEntry).getExclusionPatterns(), true);
				}
				PathEntryComposer newComposer = new PathEntryComposer(entry, fProject/*, fCfg*/);
				map.put(entry, newComposer);
				nameKeyMap.put(nameKey, newComposer);
			}
		}

		public void setRefProjects(IPath[] paths) {
			if (paths == null || paths.length == 0) {
				fRefProjMap = null;
			} else {
				fRefProjMap = new LinkedHashMap<>();
				for (IPath path : paths) {
					PathEntryComposer cs = new PathEntryComposer(path, fProject/*, fCfg*/);
					IPath composerPath = cs.getPath();
					fRefProjMap.put(composerPath, cs);
				}
			}
		}

		public PathEntryCollector createChild(IPath path) {
			if (path.segmentCount() == 0)
				return this;

			PathEntryCollector cr = (PathEntryCollector) fStorage.getChildContainer(path, false, false).getValue();
			if (cr != this) {
				IPath basePath = cr.getPath();
				path = path.removeFirstSegments(basePath.segmentCount());
				return cr.createChild(path);
			}

			PathSettingsContainer newContainer = fStorage.getChildContainer(path, true, true);
			@SuppressWarnings("unchecked")
			KindBasedStore<LinkedHashMap<ICSettingEntry, PathEntryComposer>> cloneStore = (KindBasedStore<LinkedHashMap<ICSettingEntry, PathEntryComposer>>) fStore
					.clone();
			IKindBasedInfo<LinkedHashMap<ICSettingEntry, PathEntryComposer>> info[] = cloneStore.getContents();
			for (IKindBasedInfo<LinkedHashMap<ICSettingEntry, PathEntryComposer>> kindInfo : info) {
				LinkedHashMap<ICSettingEntry, PathEntryComposer> map = kindInfo.getInfo();
				if (map != null) {
					@SuppressWarnings("unchecked")
					LinkedHashMap<ICSettingEntry, PathEntryComposer> clone = (LinkedHashMap<ICSettingEntry, PathEntryComposer>) map
							.clone();
					kindInfo.setInfo(clone);
				}
			}
			PathEntryCollector newCr = new PathEntryCollector(newContainer, cloneStore, fProject/*, fCfg*/);
			newContainer.setValue(newCr);
			return newCr;
		}

		public IPath getPath() {
			return fStorage.getPath();
		}

		public void setEntries(int kind, ICLanguageSettingEntry entries[], Set<ICSettingEntry> exportedEntries) {
			IPath path = getPath();
			HashSet<ICSettingEntry> parentSet = getEntriesSetCopy(kind);
			@SuppressWarnings("unchecked")
			HashSet<ICSettingEntry> removedParentSet = (HashSet<ICSettingEntry>) parentSet.clone();
			HashSet<ICLanguageSettingEntry> addedThisSet = new HashSet<>(Arrays.asList(entries));
			removedParentSet.removeAll(addedThisSet);
			addedThisSet.removeAll(parentSet);

			if (removedParentSet.size() != 0) {
				PathEntryCollector parent = getParent();
				IPath parentPath = parent.getPath();

				int segsToRemove = parentPath.segmentCount();
				if (segsToRemove > path.segmentCount())
					segsToRemove = path.segmentCount() - 1;
				if (segsToRemove < 0)
					segsToRemove = 0;

				IPath filterPath = path.removeFirstSegments(segsToRemove);

				parent.addFilter(kind, filterPath, removedParentSet);

				Map<ICSettingEntry, PathEntryComposer> map = getEntriesMap(kind, true);
				for (ICSettingEntry item : removedParentSet) {
					map.remove(item);
				}
			}

			if (addedThisSet.size() != 0) {
				Map<ICSettingEntry, PathEntryComposer> map = getEntriesMap(kind, true);
				IPath fullPath = fProject.getFullPath().append(path);
				for (int i = 0; i < entries.length; i++) {
					if (!addedThisSet.remove(entries[i]))
						continue;

					ICLanguageSettingEntry entry = entries[i];
					map.put(entry, new PathEntryComposer(fullPath, entry, exportedEntries.contains(entry),
							fProject/*, fCfg*/));
				}
			}
		}

		private LinkedHashMap<ICSettingEntry, PathEntryComposer> getEntriesMap(int kind, boolean create) {
			LinkedHashMap<ICSettingEntry, PathEntryComposer> map = fStore.get(kind);
			if (map == null && create) {
				map = new LinkedHashMap<>();
				fStore.put(kind, map);
			}
			return map;
		}

		private LinkedHashMap<EntryNameKey, PathEntryComposer> getEntriesNameKeyMap(int kind, boolean create) {
			LinkedHashMap<EntryNameKey, PathEntryComposer> map = fNameKeyMapStore.get(kind);
			if (map == null && create) {
				map = new LinkedHashMap<>();
				fNameKeyMapStore.put(kind, map);
			}
			return map;
		}

		private void addFilter(int kind, IPath path, Set<ICSettingEntry> entriesSet) {
			if (entriesSet.size() == 0)
				return;

			Map<ICSettingEntry, PathEntryComposer> map = fStore.get(kind);
			for (ICSettingEntry fltPath : entriesSet) {
				PathEntryComposer cs = map.get(fltPath);
				cs.addFilter(path);
			}
		}

		public PathEntryCollector getParent() {
			if (fStorage.isRoot())
				return null;
			PathSettingsContainer cr = fStorage.getParentContainer();
			return (PathEntryCollector) cr.getValue();
		}

		private HashSet<ICSettingEntry> getEntriesSetCopy(int kind) {
			Map<ICSettingEntry, PathEntryComposer> map = getEntriesMap(kind, false);
			if (map != null) {
				return new HashSet<>(map.keySet());
			}
			return new HashSet<>(0);
		}

		private List<PathEntryComposer> getCollectedEntriesList(final int kind) {
			final List<PathEntryComposer> list = new ArrayList<>();
			final Set<PathEntryComposer> set = new HashSet<>();
			fStorage.accept(new IPathSettingsContainerVisitor() {

				@Override
				public boolean visit(PathSettingsContainer container) {
					PathEntryCollector clr = (PathEntryCollector) container.getValue();
					clr.getLocalCollectedEntries(kind, list, set);
					return true;
				}

			});

			return list;
		}

		private void getLocalCollectedEntries(int kind, List<PathEntryComposer> list,
				Set<PathEntryComposer> addedEntries) {
			Map<ICSettingEntry, PathEntryComposer> map = getEntriesMap(kind, false);
			if (map == null)
				return;

			for (PathEntryComposer pathEntryComposer : map.values()) {
				if (addedEntries.add(pathEntryComposer)) {
					list.add(pathEntryComposer);
				}
			}
		}

		public List<IPathEntry> getEntries(int peKind, List<IPathEntry> list, int flags,
				ICConfigurationDescription cfgDescription) {
			Set<IPathEntry> set = new LinkedHashSet<>();

			int sKind = peKindToSettingKind(peKind);
			List<PathEntryComposer> composerList = null;
			if (sKind != 0) {
				composerList = getCollectedEntriesList(sKind);
			} else if (peKind == IPathEntry.CDT_PROJECT) {
				if (fRefProjMap != null && fRefProjMap.size() != 0) {
					composerList = new ArrayList<>(fRefProjMap.values());
				}
			}
			if (composerList != null) {
				PathEntryKyndStore store = new PathEntryKyndStore();
				for (PathEntryComposer cs : composerList) {
					ICSettingEntry entry = cs.getSettingEntry();
					if (checkFilter(cs, entry, flags)) {
						IPathEntry[] pe = null;
						if (isBuiltIn(entry) && cs.getPath().segmentCount() > 1) {
							String name = entry.getName();
							Map<String, IPathEntry> map = store.get(peKind);
							if (map == null) {
								map = new HashMap<>();
								store.put(peKind, map);
							}
							if (!map.containsKey(name)) {
								pe = cs.toPathEntry(cfgDescription, false);
								if (pe != null) {
									if (pe.length > 1) {
										System.out.println();
									}
									map.put(name, pe[0]);
								}
							}
						} else {
							pe = cs.toPathEntry(cfgDescription, true);
						}
						if (pe != null)
							set.addAll(Arrays.asList(pe));
					}
				}
			}

			if (list == null) {
				list = new ArrayList<>(set);
			} else {
				list.addAll(set);
			}
			return list;
		}

		private static boolean checkFilter(PathEntryComposer cs, ICSettingEntry entry, int flags) {
			boolean builtIn = isBuiltIn(entry);

			//			if (builtIn && cs.getPath().segmentCount() > 1)
			//				return false;
			if ((flags & INCLUDE_BUILT_INS) != 0 && builtIn)
				return true;
			if ((flags & INCLUDE_USER) != 0 && !builtIn)
				return true;
			return false;
		}

		private static boolean isBuiltIn(ICSettingEntry entry) {
			return entry != null && (entry.isBuiltIn() || entry.isReadOnly());
		}

		public List<IPathEntry> getEntries(List<IPathEntry> list, int flags,
				ICConfigurationDescription cfgDescription) {
			if (list == null)
				list = new ArrayList<>();
			int peKinds[] = PathEntryKyndStore.getSupportedKinds();
			for (int peKind : peKinds) {
				getEntries(peKind, list, flags, cfgDescription);
			}

			return list;
		}

		public IPathEntry[] getEntries(int flags, ICConfigurationDescription cfgDescription) {
			List<IPathEntry> list = getEntries(null, flags, cfgDescription);
			IPathEntry[] entries = list.toArray(new IPathEntry[list.size()]);
			return entries;
		}
	}

	private static LangEntryInfo createLangEntryInfo(ResolvedEntry entry) {
		ICLanguageSettingEntry le = createLangEntry(entry);
		if (le != null) {
			return new LangEntryInfo(le, entry);
		}
		return null;
	}

	private static boolean areEntriesReadOnly(PathEntryResolveInfoElement el) {
		switch (el.getRawEntry().getEntryKind()) {
		case IPathEntry.CDT_LIBRARY:
		case IPathEntry.CDT_INCLUDE:
		case IPathEntry.CDT_MACRO:
		case IPathEntry.CDT_INCLUDE_FILE:
		case IPathEntry.CDT_MACRO_FILE:
		case IPathEntry.CDT_SOURCE:
		case IPathEntry.CDT_OUTPUT:
			return false;
		//		case IPathEntry.CDT_PROJECT:
		//		case IPathEntry.CDT_CONTAINER:
		}
		return true;
	}

	private IPath getEntryFullPath(IPathEntry entry) {
		IPath path = entry.getPath();
		if (path == null)
			return fProject.getFullPath();
		else if (path.isAbsolute())
			return path;
		return fProject.getFullPath().append(path);

	}

	private IPath[] obtainFilters(IPathEntry entry) {
		switch (entry.getEntryKind()) {
		case IPathEntry.CDT_INCLUDE:
		case IPathEntry.CDT_INCLUDE_FILE:
		case IPathEntry.CDT_MACRO:
		case IPathEntry.CDT_OUTPUT:
		case IPathEntry.CDT_SOURCE:
			return ((APathEntry) entry).getExclusionPatterns();
		}
		return new IPath[0];
	}

	public PathEntryTranslator(IProject project, CConfigurationData cfgData) {
		fProject = project;
		fCfgData = cfgData;
		fRcDataHolder = createRcDataHolder(cfgData);
		fTranslatedFilters = PathSettingsContainer.createRootContainer();
		fTranslatedFilters.setValue(new ResourceInfo[] { new ResourceInfo(fRoot, true) });
	}

	private static PathSettingsContainer createRcDataHolder(CConfigurationData cfgData) {
		return CDataUtil.createRcDataHolder(cfgData);
	}

	public ReferenceSettingsInfo applyPathEntries(PathEntryResolveInfo info, int op) {
		ResolvedEntry[] rEntries = getResolvedEntries(info);
		return addPathEntries(rEntries, op);
	}

	private RcDesInfo getRcDesInfo(PathSettingsContainer cr, ResourceInfo rcInfo) {
		IResource rc = rcInfo.fRc;
		IPath projPath = rc.getProjectRelativePath();
		PathSettingsContainer child = cr.getChildContainer(projPath, true, true);
		RcDesInfo rcDes = (RcDesInfo) child.getValue();
		if (rcDes == null) {
			rcDes = new RcDesInfo(rcInfo);
			child.setValue(rcDes);
		}
		return rcDes;
	}

	private ReferenceSettingsInfo addPathEntries(ResolvedEntry[] rEntries, int op) {
		PathSettingsContainer cr = PathSettingsContainer.createRootContainer();
		cr.setValue(new RcDesInfo(new ResourceInfo(fProject, true)));
		List<IPathEntry> srcList = new ArrayList<>();
		List<IPathEntry> outList = new ArrayList<>();
		List<ResolvedEntry> projList = new ArrayList<>();
		List<ResolvedEntry> exportSettingsList = new ArrayList<>();
		ICSourceEntry srcEntries[] = null;
		ICOutputEntry outEntries[] = null;
		ResourceInfo rcInfo;
		for (ResolvedEntry rEntry : rEntries) {
			if (rEntry.isReadOnly())
				continue;
			if (toLanguageEntryKind(rEntry.fEntry.getEntryKind()) == 0) {
				switch (rEntry.fEntry.getEntryKind()) {
				case IPathEntry.CDT_SOURCE:
					srcList.add(rEntry.fEntry);
					break;
				case IPathEntry.CDT_OUTPUT:
					outList.add(rEntry.fEntry);
					break;
				case IPathEntry.CDT_PROJECT:
					projList.add(rEntry);
					break;
				}
				continue;
			}

			if (rEntry.getEntry().isExported()) {
				exportSettingsList.add(rEntry);
			}
			rcInfo = rEntry.getResourceInfo();
			RcDesInfo rcDes = getRcDesInfo(cr, rcInfo);

			rcDes.fResolvedEntries.add(rEntry);

			ResourceInfo[] fInfos = rEntry.getFilterInfos();
			for (ResourceInfo fInfo : fInfos) {
				getRcDesInfo(cr, fInfo);
			}
		}

		if (srcList.size() != 0) {
			srcEntries = toCSourceEntries(srcList);
		}
		if (outList.size() != 0) {
			outEntries = toCOutputEntries(outList);
		}

		propagateValues(cr, new ArrayList<LangEntryInfo>(0));

		//applying settings
		applyOutputEntries(outEntries, op);
		applySourceEntries(srcEntries, op);
		applyLangSettings(cr, op);

		IPath refProjPaths[] = new IPath[projList.size()];
		for (int i = 0; i < refProjPaths.length; i++) {
			ResolvedEntry e = projList.get(i);
			refProjPaths[i] = e.getResourceInfo().fRc.getFullPath();
		}

		ICExternalSetting extSettings[];
		if (exportSettingsList.size() != 0) {
			extSettings = new ICExternalSetting[1];
			List<ICLanguageSettingEntry> list = new ArrayList<>(exportSettingsList.size());
			for (int i = 0; i < exportSettingsList.size(); i++) {
				ResolvedEntry re = exportSettingsList.get(i);
				ICLanguageSettingEntry le = createLangEntry(re);
				if (le != null)
					list.add(le);
			}
			ICLanguageSettingEntry expEntries[] = list.toArray(new ICLanguageSettingEntry[list.size()]);
			extSettings[0] = new CExternalSetting(null, null, null, expEntries);
		} else {
			extSettings = new ICExternalSetting[0];
		}

		return new ReferenceSettingsInfo(refProjPaths, extSettings);
	}

	private static ICSourceEntry[] toCSourceEntries(List<IPathEntry> list) {
		ICSourceEntry[] entries = new ICSourceEntry[list.size()];
		for (int i = 0; i < entries.length; i++) {
			entries[i] = toCSourceEntry((ISourceEntry) list.get(i), true);
		}
		return entries;
	}

	private static ICOutputEntry[] toCOutputEntries(List<IPathEntry> list) {
		ICOutputEntry[] entries = new ICOutputEntry[list.size()];
		for (int i = 0; i < entries.length; i++) {
			entries[i] = toCOutputEntry((IOutputEntry) list.get(i), true);
		}
		return entries;
	}

	private static ICSourceEntry toCSourceEntry(ISourceEntry entry, boolean makeProjRelative) {
		IPath path = entry.getPath();
		if (makeProjRelative && path.isAbsolute())
			path = path.removeFirstSegments(1);
		return new CSourceEntry(path, entry.getExclusionPatterns(),
				ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED);
	}

	private static ICOutputEntry toCOutputEntry(IOutputEntry entry, boolean makeProjRelative) {
		IPath path = entry.getPath();
		if (makeProjRelative && path.isAbsolute())
			path = path.removeFirstSegments(1);
		return new COutputEntry(path, entry.getExclusionPatterns(),
				ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED);
	}

	private static ICSettingEntry[] replaceUserEntries(ICSettingEntry[] oldEntries, ICSettingEntry[] newUsrEntries) {
		Set<ICSettingEntry> set = new LinkedHashSet<>();
		Class<?> componentType = null;

		if (newUsrEntries != null) {
			for (ICSettingEntry entry : newUsrEntries) {
				if (entry.isBuiltIn() || entry.isReadOnly())
					continue;
				set.add(entry);
			}
			componentType = newUsrEntries.getClass().getComponentType();
		}

		if (oldEntries != null) {
			for (ICSettingEntry entry : oldEntries) {
				if (entry.isBuiltIn() || entry.isReadOnly())
					set.add(entry);
			}
			if (componentType == null)
				componentType = oldEntries.getClass().getComponentType();
		}

		if (componentType != null) {
			ICSettingEntry[] result = (ICSettingEntry[]) Array.newInstance(componentType, set.size());
			set.toArray(result);
			return result;
		}
		return null;
	}

	private void applySourceEntries(ICSourceEntry entries[], int op) {
		ICSourceEntry[] oldEntries = fCfgData.getSourceEntries();
		oldEntries = (ICSourceEntry[]) CDataUtil.makeRelative(fProject, oldEntries, true);
		entries = (ICSourceEntry[]) CDataUtil.makeRelative(fProject, entries, true);
		entries = (ICSourceEntry[]) replaceUserEntries(oldEntries, entries);

		switch (op) {
		case OP_ADD:
			if (entries != null && entries.length != 0) {
				Set<ICSourceEntry> set = new LinkedHashSet<>();
				set.addAll(Arrays.asList(oldEntries));
				set.addAll(Arrays.asList(entries));
				fCfgData.setSourceEntries(set.toArray(new ICSourceEntry[set.size()]));
			}
			break;
		case OP_REMOVE:
			if (entries != null && entries.length != 0) {
				Set<ICSourceEntry> set = new HashSet<>();
				set.addAll(Arrays.asList(oldEntries));
				set.removeAll(Arrays.asList(entries));
				fCfgData.setSourceEntries(set.toArray(new ICSourceEntry[set.size()]));
			}
			break;
		case OP_REPLACE:
		default:
			if (entries != null) {
				fCfgData.setSourceEntries(entries);
			} else {
				fCfgData.setSourceEntries(new ICSourceEntry[0]);
			}
			break;
		}
	}

	private void applyOutputEntries(ICOutputEntry entries[], int op) {
		CBuildData bData = fCfgData.getBuildData();
		if (bData == null) {
			CCorePlugin.log(UtilMessages.getString("PathEntryTranslator.2")); //$NON-NLS-1$
			return;
		}

		ICOutputEntry[] oldEntries = bData.getOutputDirectories();
		oldEntries = (ICOutputEntry[]) CDataUtil.makeRelative(fProject, oldEntries, true);
		entries = (ICOutputEntry[]) CDataUtil.makeRelative(fProject, entries, true);
		entries = (ICOutputEntry[]) replaceUserEntries(oldEntries, entries);

		switch (op) {
		case OP_ADD:
			if (entries != null && entries.length != 0) {
				Set<ICOutputEntry> set = new LinkedHashSet<>();
				set.addAll(Arrays.asList(oldEntries));
				set.addAll(Arrays.asList(entries));
				bData.setOutputDirectories(set.toArray(new ICOutputEntry[set.size()]));
			}
			break;
		case OP_REMOVE:
			if (entries != null && entries.length != 0) {
				Set<ICOutputEntry> set = new HashSet<>();
				set.addAll(Arrays.asList(oldEntries));
				set.removeAll(Arrays.asList(entries));
				bData.setOutputDirectories(set.toArray(new ICOutputEntry[set.size()]));
			}
			break;
		case OP_REPLACE:
		default:
			if (entries != null) {
				bData.setOutputDirectories(entries);
			} else {
				bData.setOutputDirectories(new ICOutputEntry[0]);
			}
			break;
		}
	}

	private void applyLangSettings(PathSettingsContainer cr, int op) {
		PathSettingsContainer crs[] = cr.getChildren(true);
		for (PathSettingsContainer cur : crs) {
			RcDesInfo desInfo = (RcDesInfo) cur.getValue();
			try {
				CResourceData rcData = getResourceData(cur.getPath(), true, true);
				applyEntries(rcData, desInfo, op);
			} catch (CoreException e) {
				CCorePlugin.log(e);
			}
		}

		CResourceData[] rcDatas = getResourceDatas();
		for (CResourceData rcData : rcDatas) {
			PathSettingsContainer c = cr.getChildContainer(rcData.getPath(), false, false);
			if (cr.getPath().makeRelative().equals(rcData.getPath().makeRelative())) {
				continue;
			}

			RcDesInfo desInfo = (RcDesInfo) c.getValue();
			applyEntries(rcData, desInfo, op);
		}
	}

	private CResourceData[] getResourceDatas() {
		PathSettingsContainer crs[] = fRcDataHolder.getChildren(true);
		List<CResourceData> list = new ArrayList<>(crs.length);
		for (PathSettingsContainer cur : crs) {
			list.add((CResourceData) cur.getValue());
		}
		return list.toArray(new CResourceData[list.size()]);
	}

	private CResourceData getResourceData(IPath path, boolean create, boolean exactPath) throws CoreException {
		PathSettingsContainer rcDataH = fRcDataHolder.getChildContainer(path, false, exactPath);
		if (rcDataH != null) {
			return (CResourceData) rcDataH.getValue();
		} else if (create) {
			ResourceInfo rcInfo = findResourceInfo(fProject, path, true);
			CResourceData base = getResourceData(path, false, false);

			CResourceData newRcData;
			if (rcInfo.fRc.getType() == IResource.FILE) {
				if (base.getType() == ICSettingBase.SETTING_FILE) {
					newRcData = fCfgData.createFileData(path, (CFileData) base);
				} else {
					CFolderData folderData = (CFolderData) base;
					CLanguageData lDatas[] = folderData.getLanguageDatas();
					CLanguageData baseLData = CDataUtil.findLanguagDataForFile(rcInfo.fRc.getFullPath().lastSegment(),
							fProject, lDatas);
					newRcData = fCfgData.createFileData(path, folderData, baseLData);
				}
			} else {
				while (base.getType() == ICSettingBase.SETTING_FILE) {
					base = getResourceData(base.getPath().removeLastSegments(1), false, false);
				}

				newRcData = fCfgData.createFolderData(path, (CFolderData) base);
			}

			fRcDataHolder.getChildContainer(path, true, true).setValue(newRcData);
			return newRcData;
		}
		return null;
	}

	private void applyEntries(CResourceData rcData, RcDesInfo info, int op) {
		CLanguageData lDatas[] = rcData.getType() == ICSettingBase.SETTING_FILE
				? new CLanguageData[] { ((CFileData) rcData).getLanguageData() }
				: ((CFolderData) rcData).getLanguageDatas();

		for (CLanguageData lData : lDatas) {
			if (lData == null)
				continue;

			applyEntries(lData, info, op);
		}
	}

	private void applyEntries(CLanguageData lData, RcDesInfo info, int op) {
		int kinds[] = KindBasedStore.getLanguageEntryKinds();
		int supported = lData.getSupportedEntryKinds();
		for (int kind : kinds) {
			if ((supported & kind) == 0)
				continue;

			ICLanguageSettingEntry opEntries[] = info.getEntries(kind);
			ICLanguageSettingEntry oldEntries[] = lData.getEntries(kind);
			opEntries = (ICLanguageSettingEntry[]) replaceUserEntries(oldEntries, opEntries);

			if (op == OP_REPLACE)
				oldEntries = null;
			ICLanguageSettingEntry result[] = composeNewEntries(oldEntries, opEntries, op);
			lData.setEntries(kind, result);
		}
	}

	private ICLanguageSettingEntry[] composeNewEntries(ICLanguageSettingEntry oldEntries[],
			ICLanguageSettingEntry newEntries[], int op) {
		ICLanguageSettingEntry result[];
		switch (op) {
		case OP_ADD: {
			Set<ICLanguageSettingEntry> oldSet = new HashSet<>(Arrays.asList(oldEntries));
			Set<ICLanguageSettingEntry> newSet = new HashSet<>(Arrays.asList(newEntries));
			newSet.removeAll(oldSet);
			if (newSet.size() == 0) {
				result = oldEntries;
			} else {
				result = new ICLanguageSettingEntry[oldEntries.length + newSet.size()];
				newSet.toArray(result);
				System.arraycopy(oldEntries, 0, result, newSet.size(), oldEntries.length);
			}
			break;
		}
		case OP_REMOVE: {
			Set<ICLanguageSettingEntry> oldSet = new HashSet<>(Arrays.asList(oldEntries));
			Set<ICLanguageSettingEntry> newSet = new HashSet<>(Arrays.asList(newEntries));
			oldSet.removeAll(newSet);
			if (oldSet.size() == 0) {
				result = new ICLanguageSettingEntry[0];
			} else {
				result = new ICLanguageSettingEntry[oldSet.size()];
				oldSet.toArray(result);
			}
			break;
		}
		case OP_REPLACE:
		default:
			result = newEntries;
			break;
		}

		return result;
	}

	public ReferenceSettingsInfo applyPathEntries(IPathEntry[] usrEntries, IPathEntry[] sysEntries, int op) {
		ResolvedEntry[] rEntries = getResolvedEntries(usrEntries, sysEntries);
		return addPathEntries(rEntries, op);
	}

	private void propagateValues(PathSettingsContainer cr, List<LangEntryInfo> langEntryInfoList) {

		RcDesInfo rcDes = (RcDesInfo) cr.getValue();
		if (rcDes != null) {
			List<ResolvedEntry> rEntries = rcDes.fResolvedEntries;
			List<LangEntryInfo> curLanfInfos = new ArrayList<>(rEntries.size() + langEntryInfoList.size());
			for (ResolvedEntry re : rEntries) {
				LangEntryInfo li = createLangEntryInfo(re);
				if (li != null) {
					curLanfInfos.add(li);
				}
			}

			curLanfInfos.addAll(langEntryInfoList);
			langEntryInfoList = curLanfInfos;

			for (LangEntryInfo li : langEntryInfoList) {
				rcDes.add(li);
			}
		}

		PathSettingsContainer directChildren[] = cr.getDirectChildren();
		for (PathSettingsContainer directChild : directChildren) {
			filterAndPropagate(directChild, langEntryInfoList);
		}
	}

	private void filterAndPropagate(PathSettingsContainer cr, List<LangEntryInfo> list) {
		list = new ArrayList<>(list);
		IPath path = cr.getPath();
		for (Iterator<LangEntryInfo> iter = list.iterator(); iter.hasNext();) {
			LangEntryInfo li = iter.next();
			ResolvedEntry re = li.fResolvedEntry;
			ResourceInfo[] filters = re.getFilterInfos();
			for (ResourceInfo filter : filters) {
				IResource rc = filter.fRc;
				IPath projPath = rc.getProjectRelativePath();
				if (projPath.isPrefixOf(path.makeRelative())) {
					iter.remove();
					break;
				}
			}
		}

		propagateValues(cr, list);
	}

	private int toLanguageEntryKind(int peKind) {
		switch (peKind) {
		case IPathEntry.CDT_LIBRARY:
			return ICSettingEntry.LIBRARY_FILE;
		//		case IPathEntry.CDT_PROJECT:
		//			return ICLanguageSettingEntry;
		//		case IPathEntry.CDT_SOURCE:
		//			return INDEX_CDT_SOURCE;
		case IPathEntry.CDT_INCLUDE:
			return ICSettingEntry.INCLUDE_PATH;
		//		case IPathEntry.CDT_CONTAINER:
		//			return INDEX_CDT_CONTAINER;
		case IPathEntry.CDT_MACRO:
			return ICSettingEntry.MACRO;
		//		case IPathEntry.CDT_OUTPUT:
		//			return INDEX_CDT_OUTPUT;
		case IPathEntry.CDT_INCLUDE_FILE:
			return ICSettingEntry.INCLUDE_FILE;
		case IPathEntry.CDT_MACRO_FILE:
			return ICSettingEntry.MACRO_FILE;
		}
		return 0;
	}

	private static int peKindToSettingKind(int peKind) {
		switch (peKind) {
		case IPathEntry.CDT_LIBRARY:
			return ICSettingEntry.LIBRARY_FILE;
		//		case IPathEntry.CDT_PROJECT:
		//			return ICLanguageSettingEntry;
		case IPathEntry.CDT_SOURCE:
			return ICSettingEntry.SOURCE_PATH;
		case IPathEntry.CDT_INCLUDE:
			return ICSettingEntry.INCLUDE_PATH;
		//		case IPathEntry.CDT_CONTAINER:
		//			return INDEX_CDT_CONTAINER;
		case IPathEntry.CDT_MACRO:
			return ICSettingEntry.MACRO;
		case IPathEntry.CDT_OUTPUT:
			return ICSettingEntry.OUTPUT_PATH;
		case IPathEntry.CDT_INCLUDE_FILE:
			return ICSettingEntry.INCLUDE_FILE;
		case IPathEntry.CDT_MACRO_FILE:
			return ICSettingEntry.MACRO_FILE;
		}
		return 0;
	}

	private ResolvedEntry[] getResolvedEntries(PathEntryResolveInfo info) {
		PathEntryResolveInfoElement els[] = info.getElements();
		List<ResolvedEntry> list = new ArrayList<>();
		for (PathEntryResolveInfoElement el : els) {
			getResolvedEntries(el, list);
		}
		return list.toArray(new ResolvedEntry[list.size()]);
	}

	private List<ResolvedEntry> getResolvedEntries(PathEntryResolveInfoElement el, List<ResolvedEntry> list) {
		if (list == null)
			list = new ArrayList<>();

		IPathEntry[] rpEntries = el.getResolvedEntries();
		ResolvedEntry resolvedE;
		IPathEntry rawEntry = el.getRawEntry();
		if (rawEntry.getEntryKind() == IPathEntry.CDT_PROJECT) {
			resolvedE = createResolvedEntry(rawEntry, el);
			if (resolvedE != null)
				list.add(resolvedE);
		}
		for (IPathEntry rpEntry : rpEntries) {
			resolvedE = createResolvedEntry(rpEntry, el);
			if (resolvedE != null)
				list.add(resolvedE);
		}
		return list;
	}

	private ResolvedEntry createResolvedEntry(IPathEntry entry, PathEntryResolveInfoElement el) {
		switch (entry.getEntryKind()) {
		//		case IPathEntry.CDT_PROJECT:
		//			//should not be here
		case IPathEntry.CDT_CONTAINER:
			//the case of extension path entry container
			return null;
		}
		return new ResolvedEntry(entry, el);
	}

	private ResolvedEntry[] getResolvedEntries(IPathEntry[] usrEntries, IPathEntry[] sysEntries) {
		int length = usrEntries != null ? usrEntries.length : 0;
		if (sysEntries != null)
			length += sysEntries.length;
		ResolvedEntry[] rEntries = new ResolvedEntry[length];
		int num = 0;
		if (usrEntries != null) {
			for (IPathEntry usrEntry : usrEntries) {
				rEntries[num++] = new ResolvedEntry(usrEntry, false);
			}
		}

		if (sysEntries != null) {
			for (IPathEntry sysEntry : sysEntries) {
				rEntries[num++] = new ResolvedEntry(sysEntry, true);
			}
		}
		return rEntries;
	}

	private ResourceInfo[] resolveFilter(IContainer container, IPath path) {
		IPath containerFullPath = container.getFullPath();
		IPath fullPath = containerFullPath.append(path);
		PathSettingsContainer cr = fTranslatedFilters.getChildContainer(fullPath, false, false);
		ResourceInfo[] baseInfos = (ResourceInfo[]) cr.getValue();
		ResourceInfo[] result;
		if (!baseInfos[0].fExists) {
			// resource does not exist, always create new rc info and not add it to map
			ResourceInfo inexistent = new ResourceInfo(container.getFolder(path), false);
			result = new ResourceInfo[] { inexistent };
		} else {
			// base exists
			IPath baseTranslatedPath = cr.getPath();
			if (baseTranslatedPath.equals(fullPath)) {
				result = baseInfos;
			} else if (containerFullPath.isPrefixOf(baseTranslatedPath)) {
				IPath filterToTranslate = fullPath.removeFirstSegments(baseTranslatedPath.segmentCount());
				result = performTranslation(baseTranslatedPath, baseInfos, filterToTranslate);
			} else {
				// should never be here
				throw new IllegalStateException();
			}
		}

		return result;
	}

	private ResourceInfo[] performTranslation(IPath basePath, ResourceInfo baseInfos[], IPath filter) {
		ResourceInfo result[];
		int segCount = filter.segmentCount();
		String seg;
		int i = 0;
		for (; i < segCount; i++) {
			if (!baseInfos[0].fExists)
				break;
			seg = filter.segment(0);
			baseInfos = performTranslation(basePath, baseInfos, seg);
			basePath = basePath.append(seg);
			filter = filter.removeFirstSegments(1);
		}

		if (i < segCount) {
			result = new ResourceInfo[baseInfos.length];
			ResourceInfo baseInfo;
			IFolder rc;

			for (int k = 0; k < baseInfos.length; k++) {
				baseInfo = baseInfos[k];
				rc = (IFolder) baseInfo.fRc;
				rc = rc.getFolder(filter);
				result[k] = new ResourceInfo(rc, false);
			}
		} else {
			result = baseInfos;
		}

		return result;
	}

	private ResourceInfo[] performTranslation(IPath basePath, ResourceInfo[] baseInfos, String seg) {
		IPath filterFullPath = basePath.append(seg);
		boolean needsParsing = hasSpecChars(seg);
		List<ResourceInfo> list = new ArrayList<>();
		char[] segChars = seg.toCharArray();
		for (ResourceInfo baseInfo : baseInfos) {
			IResource baseRc = baseInfo.fRc;
			if (baseRc.getType() == IResource.FILE) {
				continue;
			} else {
				IContainer baseCr = (IContainer) baseRc;
				IResource rc = baseCr.findMember(seg);
				if (rc != null) {
					ResourceInfo rcInfo = new ResourceInfo(rc, true);
					addRcInfoToMap(rcInfo);
					list.add(rcInfo);
				} else if (needsParsing) {
					try {
						IResource children[] = baseCr.members();
						ResourceInfo rcInfo;
						for (IResource child : children) {
							if (CoreModelUtil.match(segChars, child.getName().toCharArray(), true)) {
								rcInfo = new ResourceInfo(child, true);
								addRcInfoToMap(rcInfo);
								list.add(rcInfo);
							}
						}
					} catch (CoreException e) {
						CCorePlugin.log(e);
					}
				}
			}
		}

		if (list.size() == 0) {
			IFolder f = fRoot.getFolder(filterFullPath);
			ResourceInfo rcInfo = new ResourceInfo(f, false);
			addRcInfoToMap(rcInfo);
			list.add(rcInfo);
		}

		ResourceInfo[] result = new ResourceInfo[list.size()];
		list.toArray(result);
		addResolvedFilterToMap(filterFullPath, result, false);
		return result;
	}

	private boolean hasSpecChars(String str) {
		for (char ch : SPEC_CHARS) {
			if (str.indexOf(ch) != -1)
				return true;
		}
		return false;
	}

	private void addResolvedFilterToMap(IPath fullFilterPath, ResourceInfo[] resolved, boolean check) {
		if (check) {
			PathSettingsContainer cr = fTranslatedFilters.getChildContainer(fullFilterPath, false, false);
			ResourceInfo[] infos = (ResourceInfo[]) cr.getValue();
			if (!infos[0].fExists)
				return;
		}

		PathSettingsContainer cr = fTranslatedFilters.getChildContainer(fullFilterPath, true, true);
		cr.setValue(resolved);
	}

	private ResourceInfo findResourceInfo(IContainer container, IPath relPath, boolean folderIfNotExist) {
		IPath fullPath = container.getFullPath().append(relPath);
		ResourceInfo rcInfo = fResourceMap.get(fullPath);

		if (rcInfo == null) {
			IResource rc = container.findMember(relPath);
			boolean exists = true;
			if (rc == null) {
				exists = false;
				if (container.getType() == IResource.ROOT && relPath.segmentCount() == 1) {
					rc = fRoot.getProject(relPath.segment(0));
				} else if (folderIfNotExist) {
					rc = container.getFolder(relPath);
				} else {
					rc = container.getFile(relPath);
				}
			}

			rcInfo = new ResourceInfo(rc, exists);
			addRcInfoToMap(rcInfo);
		}
		return rcInfo;
	}

	private void addRcInfoToMap(ResourceInfo rcInfo) {
		IPath fullPath = rcInfo.fRc.getFullPath();
		fResourceMap.put(fullPath, rcInfo);
		addResolvedFilterToMap(fullPath, new ResourceInfo[] { rcInfo }, true);
	}

	public static IPathEntry[] decodePathEntries(IProject project, ICStorageElement el) {
		ArrayList<IPathEntry> pathEntries = new ArrayList<>();
		ICStorageElement children[] = el.getChildren();
		for (ICStorageElement child : children) {
			if (child.getName().equals(PATH_ENTRY)) {
				try {
					pathEntries.add(decodePathEntry(project, child));
				} catch (CModelException e) {
					CCorePlugin.log(e);
				}
			}
		}
		IPathEntry[] entries = new IPathEntry[pathEntries.size()];
		pathEntries.toArray(entries);
		return entries;
	}

	private static String getAttribute(ICStorageElement el, String attr) {
		String v = el.getAttribute(attr);
		if (v != null)
			return v;
		return ""; //$NON-NLS-1$
	}

	static IPathEntry decodePathEntry(IProject project, ICStorageElement element) throws CModelException {
		IPath projectPath = project.getFullPath();

		// kind
		String kindAttr = getAttribute(element, ATTRIBUTE_KIND);
		int kind = PathEntry.kindFromString(kindAttr);

		// exported flag
		boolean isExported = false;
		if (element.getAttribute(ATTRIBUTE_EXPORTED) != null) {
			isExported = element.getAttribute(ATTRIBUTE_EXPORTED).equals(VALUE_TRUE);
		}

		// get path and ensure it is absolute
		IPath path;
		if (element.getAttribute(ATTRIBUTE_PATH) != null) {
			path = new Path(element.getAttribute(ATTRIBUTE_PATH));
		} else {
			path = new Path(""); //$NON-NLS-1$
		}
		if (!path.isAbsolute()) {
			path = projectPath.append(path);
		}

		// check fo the base path
		IPath basePath = new Path(getAttribute(element, ATTRIBUTE_BASE_PATH));

		// get the base ref
		IPath baseRef = new Path(getAttribute(element, ATTRIBUTE_BASE_REF));

		// exclusion patterns (optional)
		String exclusion = getAttribute(element, ATTRIBUTE_EXCLUDING);
		IPath[] exclusionPatterns = APathEntry.NO_EXCLUSION_PATTERNS;
		if (exclusion != null && exclusion.length() > 0) {
			char[][] patterns = CharOperation.splitOn('|', exclusion.toCharArray());
			int patternCount;
			if ((patternCount = patterns.length) > 0) {
				exclusionPatterns = new IPath[patternCount];
				for (int j = 0; j < patterns.length; j++) {
					exclusionPatterns[j] = new Path(new String(patterns[j]));
				}
			}
		}

		// recreate the entry
		switch (kind) {
		case IPathEntry.CDT_PROJECT:
			return CoreModel.newProjectEntry(path, isExported);
		case IPathEntry.CDT_LIBRARY: {
			IPath libraryPath = new Path(getAttribute(element, ATTRIBUTE_LIBRARY));
			// source attachment info (optional)
			IPath sourceAttachmentPath = element.getAttribute(ATTRIBUTE_SOURCEPATH) != null
					? new Path(element.getAttribute(ATTRIBUTE_SOURCEPATH))
					: null;
			IPath sourceAttachmentRootPath = element.getAttribute(ATTRIBUTE_ROOTPATH) != null
					? new Path(element.getAttribute(ATTRIBUTE_ROOTPATH))
					: null;
			IPath sourceAttachmentPrefixMapping = element.getAttribute(ATTRIBUTE_PREFIXMAPPING) != null
					? new Path(element.getAttribute(ATTRIBUTE_PREFIXMAPPING))
					: null;

			if (!baseRef.isEmpty()) {
				return CoreModel.newLibraryRefEntry(path, baseRef, libraryPath);
			}
			return CoreModel.newLibraryEntry(path, basePath, libraryPath, sourceAttachmentPath,
					sourceAttachmentRootPath, sourceAttachmentPrefixMapping, isExported);
		}
		case IPathEntry.CDT_SOURCE: {
			// must be an entry in this project or specify another
			// project
			String projSegment = path.segment(0);
			if (projSegment != null && projSegment.equals(project.getName())) { // this
				// project
				return CoreModel.newSourceEntry(path, exclusionPatterns);
			}
			// another project
			return CoreModel.newProjectEntry(path, isExported);
		}
		case IPathEntry.CDT_OUTPUT:
			return CoreModel.newOutputEntry(path, exclusionPatterns);
		case IPathEntry.CDT_INCLUDE: {
			// include path info
			IPath includePath = new Path(getAttribute(element, ATTRIBUTE_INCLUDE));
			// isSysteminclude
			boolean isSystemInclude = false;
			if (element.getAttribute(ATTRIBUTE_SYSTEM) != null) {
				isSystemInclude = getAttribute(element, ATTRIBUTE_SYSTEM).equals(VALUE_TRUE);
			}
			if (!baseRef.isEmpty()) {
				return CoreModel.newIncludeRefEntry(path, baseRef, includePath);
			}
			return CoreModel.newIncludeEntry(path, basePath, includePath, isSystemInclude, exclusionPatterns,
					isExported);
		}
		case IPathEntry.CDT_INCLUDE_FILE: {
			// include path info
			IPath includeFilePath = new Path(getAttribute(element, ATTRIBUTE_INCLUDE_FILE));
			return CoreModel.newIncludeFileEntry(path, basePath, baseRef, includeFilePath, exclusionPatterns,
					isExported);
		}
		case IPathEntry.CDT_MACRO: {
			String macroName = getAttribute(element, ATTRIBUTE_NAME);
			String macroValue = getAttribute(element, ATTRIBUTE_VALUE);
			if (!baseRef.isEmpty()) {
				return CoreModel.newMacroRefEntry(path, baseRef, macroName);
			}
			return CoreModel.newMacroEntry(path, macroName, macroValue, exclusionPatterns, isExported);
		}
		case IPathEntry.CDT_MACRO_FILE: {
			IPath macroFilePath = new Path(getAttribute(element, ATTRIBUTE_MACRO_FILE));
			return CoreModel.newMacroFileEntry(path, basePath, baseRef, macroFilePath, exclusionPatterns, isExported);
		}
		case IPathEntry.CDT_CONTAINER: {
			IPath id = new Path(getAttribute(element, ATTRIBUTE_PATH));
			return CoreModel.newContainerEntry(id, isExported);
		}
		default: {
			ICModelStatus status = new CModelStatus(IStatus.ERROR, "PathEntry: unknown kind (" + kindAttr + ")"); //$NON-NLS-1$ //$NON-NLS-2$
			throw new CModelException(status);
		}
		}
	}

	private static CConfigurationData getCfgData(ICConfigurationDescription cfgDescription) {
		return cfgDescription instanceof CConfigurationDescriptionCache ? (CConfigurationData) cfgDescription
				: ((IInternalCCfgInfo) cfgDescription).getConfigurationData(false);
	}

	private static void addOutputEntries(PathEntryCollector cr, CConfigurationData cfgData) {
		CBuildData bData = cfgData.getBuildData();
		if (bData != null) {
			ICOutputEntry oEntries[] = bData.getOutputDirectories();
			if (oEntries != null && oEntries.length != 0) {
				cr.setSourceOutputEntries(ICSettingEntry.OUTPUT_PATH, oEntries);
			}
		}
	}

	public static PathEntryCollector collectEntries(IProject project, final ICConfigurationDescription cfgDescription) {
		CConfigurationData cfgData = getCfgData(cfgDescription);

		ReferenceSettingsInfo refInfo = new ReferenceSettingsInfo(cfgDescription);
		ICConfigurationDescription[] allCfgDescriptions = cfgDescription.isPreferenceConfiguration()
				? new ICConfigurationDescription[] { cfgDescription }
				: cfgDescription.getProjectDescription().getConfigurations();

		CConfigurationData[] allDatas = new CConfigurationData[allCfgDescriptions.length];
		for (int i = 0; i < allCfgDescriptions.length; i++) {
			allDatas[i] = getCfgData(allCfgDescriptions[i]);
		}

		final PathEntryCollector collector = new PathEntryCollector(project/*, cfgDescription*/);
		PathSettingsContainer rcDatas = createRcDataHolder(cfgData);
		ICSourceEntry sEntries[] = cfgData.getSourceEntries();
		if (sEntries != null && sEntries.length != 0) {
			collector.setSourceOutputEntries(ICSettingEntry.SOURCE_PATH, sEntries);
		}
		for (CConfigurationData allData : allDatas) {
			addOutputEntries(collector, allData);
		}
		final HashSet<ICSettingEntry> exportedSettings = new HashSet<>();
		collector.setRefProjects(refInfo.getReferencedProjectsPaths());
		ICExternalSetting[] settings = refInfo.getExternalSettings();
		for (ICExternalSetting setting : settings) {
			exportedSettings.addAll(Arrays.asList(setting.getEntries()));
		}

		final int kinds[] = KindBasedStore.getLanguageEntryKinds();
		rcDatas.accept(new IPathSettingsContainerVisitor() {
			@Override
			public boolean visit(PathSettingsContainer container) {
				CResourceData rcData = (CResourceData) container.getValue();
				if (rcData != null) {
					PathEntryCollector child = collector.createChild(container.getPath());
					for (int kind : kinds) {
						Set<ICLanguageSettingEntry> set = new LinkedHashSet<>();
						if (collectResourceDataEntries(cfgDescription, kind, rcData, set)) {
							ICLanguageSettingEntry[] entries = set.toArray(new ICLanguageSettingEntry[set.size()]);
							child.setEntries(kind, entries, exportedSettings);
						}
					}
				}
				return true;
			}
		});
		return collector;
	}

	private static boolean collectResourceDataEntries(ICConfigurationDescription cfgDescription, int kind,
			CResourceData rcData, Set<ICLanguageSettingEntry> list) {
		CLanguageData[] lDatas = null;
		if (rcData instanceof CFolderData) {
			lDatas = ((CFolderData) rcData).getLanguageDatas();
		} else if (rcData instanceof CFileData) {
			CLanguageData lData = ((CFileData) rcData).getLanguageData();
			if (lData != null)
				lDatas = new CLanguageData[] { lData };
		} else {
			Exception e = new Exception(UtilMessages.getString("PathEntryTranslator.1") + rcData.getClass().getName()); //$NON-NLS-1$
			IStatus status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, e.getMessage(), e);
			CCorePlugin.log(status);
		}
		if (lDatas == null || lDatas.length == 0) {
			return false;
		}

		IProject project = cfgDescription.getProjectDescription().getProject();
		if (ScannerDiscoveryLegacySupport.isLanguageSettingsProvidersFunctionalityEnabled(project)) {
			IResource rc = findResourceInWorkspace(project, rcData.getPath());
			if (rc == null) {
				// If resource does not exist make a handle to be able to supply the path.
				// This does not create actual resource.
				// Gotta be a folder so language settings provider won't filter out languages.
				rc = project.getFolder(rcData.getPath());
			}
			for (CLanguageData lData : lDatas) {
				list.addAll(LanguageSettingsProvidersSerializer.getSettingEntriesByKind(cfgDescription, rc,
						lData.getLanguageId(), kind));
			}
			return list.size() > 0;

		}
		// Legacy logic (before Language Settings Providers)
		boolean supported = false;
		for (CLanguageData lData : lDatas) {
			if (collectLanguageDataEntries(kind, lData, list))
				supported = true;
		}
		return supported;
	}

	private static boolean collectLanguageDataEntries(int kind, CLanguageData lData, Set<ICLanguageSettingEntry> list) {
		if ((kind & lData.getSupportedEntryKinds()) != 0) {
			ICLanguageSettingEntry[] entries = lData.getEntries(kind);
			if (entries != null && entries.length != 0) {
				list.addAll(Arrays.asList(entries));
			}
			return true;
		}

		return false;
	}

	public static IPathEntry[] getPathEntries(IProject project, ICConfigurationDescription cfgDescription, int flags) {
		PathEntryCollector cr = collectEntries(project, cfgDescription);
		return cr.getEntries(flags, cfgDescription);
	}

	private static IResource findResourceInWorkspace(IProject project, IPath workspacePath) {
		IResource rc;
		if (project != null) {
			rc = project.findMember(workspacePath);
		} else {
			rc = ResourcesPlugin.getWorkspace().getRoot().findMember(workspacePath);
		}
		return rc;
	}
}
