/*******************************************************************************
 * Copyright (c) 2006, 2014 Wind River Systems, Inc. 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:
 *     Anton Leherbauer (Wind River Systems) - initial API and implementation
 *     Markus Schorn (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.model;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFieldDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorFunctionStyleMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTProblemDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTAliasDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLinkageSpecification;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceAlias;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTStaticAssertDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTypeTransformationSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.IContributedModelBuilder;
import org.eclipse.cdt.core.model.INamespace;
import org.eclipse.cdt.core.model.IProblemRequestor;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.IStructure;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.core.parser.ParseError;
import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;

/**
 * Build TranslationUnit structure from an {@code IASTTranslationUnit}.
 *
 * @since 4.0
 */
public class CModelBuilder2 implements IContributedModelBuilder {
	private final static boolean DEBUG = Util.isActive(DebugLogConstants.MODEL);

	private final TranslationUnit fTranslationUnit;
	private final Map<ICElement, CElementInfo> fNewElements;
	private final IProgressMonitor fProgressMonitor;

	private ASTAccessVisibility fCurrentVisibility;
	private Stack<ASTAccessVisibility> fVisibilityStack;
	private HashMap<ISourceReference, int[]> fEqualElements;

	/**
	 * Create a model builder for the given translation unit.
	 *
	 * @param tu  the translation unit (must be a {@link TranslationUnit}
	 * @param newElements  element cache
	 * @param monitor the progress monitor
	 */
	public CModelBuilder2(ITranslationUnit tu, Map<ICElement, CElementInfo> newElements, IProgressMonitor monitor) {
		fTranslationUnit = (TranslationUnit) tu;
		fNewElements = newElements;
		fProgressMonitor = monitor;
	}

	@Override
	public void parse(boolean quickParseMode) throws Exception {
		final IIndexManager indexManager = CCorePlugin.getIndexManager();
		IIndex index = indexManager.getIndex(fTranslationUnit.getCProject(),
				IIndexManager.ADD_EXTENSION_FRAGMENTS_EDITOR);

		try {
			if (index != null) {
				try {
					index.acquireReadLock();
				} catch (InterruptedException e) {
					index = null;
				}
			}
			checkCanceled();
			long startTime = System.currentTimeMillis();
			final CElementInfo elementInfo = getElementInfo(fTranslationUnit);
			int parseFlags = quickParseMode ? ITranslationUnit.AST_SKIP_ALL_HEADERS
					: ITranslationUnit.AST_SKIP_INDEXED_HEADERS;
			if (!(elementInfo instanceof ASTHolderTUInfo)) {
				parseFlags |= ITranslationUnit.AST_SKIP_FUNCTION_BODIES;
			} else {
				parseFlags |= ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT;
			}
			parseFlags |= ITranslationUnit.AST_SKIP_TRIVIAL_EXPRESSIONS_IN_AGGREGATE_INITIALIZERS;
			parseFlags |= ITranslationUnit.AST_PARSE_INACTIVE_CODE;
			final IASTTranslationUnit ast;
			try {
				ast = fTranslationUnit.getAST(index, parseFlags, fProgressMonitor);
				if (DEBUG) {
					Util.debugLog("CModelBuilder2: parsing " //$NON-NLS-1$
							+ fTranslationUnit.getElementName() + " mode=" //$NON-NLS-1$
							+ (quickParseMode ? "skip all " : "skip indexed ") //$NON-NLS-1$ //$NON-NLS-2$
							+ " time=" + (System.currentTimeMillis() - startTime) + "ms", //$NON-NLS-1$ //$NON-NLS-2$
							DebugLogConstants.MODEL, false);
				}
			} catch (ParseError e) {
				checkCanceled();
				throw e;
			}
			if (ast == null) {
				return;
			}

			checkCanceled();
			startTime = System.currentTimeMillis();
			buildModel(ast);
			elementInfo.setIsStructureKnown(true);
			if (DEBUG) {
				Util.debugLog("CModelBuilder2: building " //$NON-NLS-1$
						+ "children=" + elementInfo.internalGetChildren().size() //$NON-NLS-1$
						+ " time=" + (System.currentTimeMillis() - startTime) + "ms", //$NON-NLS-1$ //$NON-NLS-2$
						DebugLogConstants.MODEL, false);
			}

			if (elementInfo instanceof ASTHolderTUInfo) {
				((ASTHolderTUInfo) elementInfo).fAST = ast;
				// preserve index lock for AST receiver
				index = null;
			}
		} finally {
			if (index != null) {
				index.releaseReadLock();
			}
		}
	}

	private boolean isCanceled() {
		return fProgressMonitor != null && fProgressMonitor.isCanceled();
	}

	private void checkCanceled() {
		if (fProgressMonitor != null && fProgressMonitor.isCanceled()) {
			if (DEBUG)
				Util.debugLog("CModelBuilder2: cancelled ", DebugLogConstants.MODEL, false); //$NON-NLS-1$
			throw new OperationCanceledException();
		}
	}

	/**
	 * Build the model from the given AST.
	 * @param ast
	 * @throws CModelException
	 * @throws DOMException
	 */
	private void buildModel(IASTTranslationUnit ast) throws CModelException, DOMException {
		fVisibilityStack = new Stack<>();
		fEqualElements = new HashMap<>();

		// includes
		final IASTPreprocessorIncludeStatement[] includeDirectives = ast.getIncludeDirectives();
		for (IASTPreprocessorIncludeStatement includeDirective : includeDirectives) {
			if (isLocalToFile(includeDirective)) {
				createInclusion(fTranslationUnit, includeDirective);
			}
		}
		// Macros
		final IASTPreprocessorMacroDefinition[] macroDefinitions = ast.getMacroDefinitions();
		for (IASTPreprocessorMacroDefinition macroDefinition : macroDefinitions) {
			if (isLocalToFile(macroDefinition)) {
				createMacro(fTranslationUnit, macroDefinition);
			}
		}
		// Declarations
		final IASTDeclaration[] declarations = ast.getDeclarations(true);
		for (IASTDeclaration declaration : declarations) {
			if (isLocalToFile(declaration)) {
				createDeclaration(fTranslationUnit, declaration);
			}
		}
		fEqualElements.clear();

		// Sort by offset
		final List<ICElement> children = getElementInfo(fTranslationUnit).internalGetChildren();
		Collections.sort(children, new Comparator<ICElement>() {
			@Override
			public int compare(ICElement o1, ICElement o2) {
				final SourceManipulationInfo info1 = getSourceManipulationInfo((SourceManipulation) o1);
				final SourceManipulationInfo info2 = getSourceManipulationInfo((SourceManipulation) o2);
				int delta = info1.getStartPos() - info2.getStartPos();
				if (delta == 0) {
					delta = info1.getIdStartPos() - info2.getIdStartPos();
				}
				return delta;
			}
		});

		if (isCanceled()) {
			return;
		}

		// Report problems
		IProblemRequestor problemRequestor = fTranslationUnit.getProblemRequestor();
		if (problemRequestor != null && problemRequestor.isActive()) {
			problemRequestor.beginReporting();
			final IASTProblem[] ppProblems = ast.getPreprocessorProblems();
			IASTProblem[] problems = ppProblems;
			for (IASTProblem problem : problems) {
				if (isLocalToFile(problem)) {
					problemRequestor.acceptProblem(problem);
				}
			}
			problems = CPPVisitor.getProblems(ast);
			for (IASTProblem problem : problems) {
				if (isLocalToFile(problem)) {
					problemRequestor.acceptProblem(problem);
				}
			}
			problemRequestor.endReporting();
		}
	}

	private boolean isLocalToFile(IASTNode node) {
		return node.isPartOfTranslationUnitFile();
	}

	private Include createInclusion(Parent parent, IASTPreprocessorIncludeStatement inclusion) throws CModelException {
		// Create element
		final IASTName name = inclusion.getName();
		Include element = new Include(parent, ASTStringUtil.getSimpleName(name), inclusion.isSystemInclude());
		element.setFullPathName(inclusion.getPath());
		setIndex(element);

		element.setActive(inclusion.isActive());
		element.setResolved(inclusion.isResolved());
		// Add to parent
		parent.addChild(element);
		// Set positions
		setIdentifierPosition(element, name);
		setBodyPosition(element, inclusion);
		return element;
	}

	private void setIndex(SourceManipulation element) {
		int[] idx = fEqualElements.get(element);
		if (idx == null) {
			idx = new int[] { 0 };
			fEqualElements.put(element, idx);
		} else {
			element.setIndex(++idx[0]);
		}
	}

	private Macro createMacro(Parent parent, IASTPreprocessorMacroDefinition macro) throws CModelException {
		// Create element
		final IASTName name = macro.getName();
		Macro element = new Macro(parent, ASTStringUtil.getSimpleName(name));
		setIndex(element);
		element.setActive(macro.isActive());
		// Add to parent
		parent.addChild(element);
		// Set positions
		setIdentifierPosition(element, name);
		setBodyPosition(element, macro);
		if (macro instanceof IASTPreprocessorFunctionStyleMacroDefinition) {
			element.setFunctionStyle(true);
		}
		return element;
	}

	private void createDeclaration(Parent parent, IASTDeclaration declaration) throws CModelException, DOMException {
		if (declaration instanceof IASTFunctionDefinition) {
			createFunctionDefinition(parent, (IASTFunctionDefinition) declaration, false);
		} else if (declaration instanceof IASTSimpleDeclaration) {
			createSimpleDeclarations(parent, (IASTSimpleDeclaration) declaration, false);
		} else if (declaration instanceof ICPPASTVisibilityLabel) {
			handleVisibilityLabel((ICPPASTVisibilityLabel) declaration);
		} else if (declaration instanceof ICPPASTNamespaceDefinition) {
			createNamespace(parent, (ICPPASTNamespaceDefinition) declaration);
		} else if (declaration instanceof ICPPASTNamespaceAlias) {
			// TODO [cmodel] namespace alias?
		} else if (declaration instanceof ICPPASTTemplateDeclaration) {
			createTemplateDeclaration(parent, (ICPPASTTemplateDeclaration) declaration);
		} else if (declaration instanceof ICPPASTTemplateSpecialization) {
			// TODO [cmodel] template specialization?
		} else if (declaration instanceof ICPPASTExplicitTemplateInstantiation) {
			// TODO [cmodel] explicit template instantiation?
		} else if (declaration instanceof ICPPASTAliasDeclaration) {
			// TODO [cmodel] alias declaration?
		} else if (declaration instanceof ICPPASTUsingDeclaration) {
			createUsingDeclaration(parent, (ICPPASTUsingDeclaration) declaration);
		} else if (declaration instanceof ICPPASTUsingDirective) {
			createUsingDirective(parent, (ICPPASTUsingDirective) declaration);
		} else if (declaration instanceof ICPPASTLinkageSpecification) {
			createLinkageSpecification(parent, (ICPPASTLinkageSpecification) declaration);
		} else if (declaration instanceof IASTASMDeclaration) {
			// TODO [cmodel] asm declaration?
		} else if (declaration instanceof IASTProblemDeclaration) {
			// TODO [cmodel] problem declaration?
		} else if (declaration instanceof ICPPASTStaticAssertDeclaration) {
			// ignore
		} else {
			assert false : "TODO: " + declaration.getClass().getName(); //$NON-NLS-1$
		}
	}

	private void createTemplateDeclaration(Parent parent, ICPPASTTemplateDeclaration templateDeclaration)
			throws CModelException, DOMException {
		IASTDeclaration declaration = templateDeclaration.getDeclaration();
		if (declaration instanceof IASTFunctionDefinition) {
			CElement element = createFunctionDefinition(parent, (IASTFunctionDefinition) declaration, true);
			String[] parameterTypes = ASTStringUtil
					.getTemplateParameterArray(templateDeclaration.getTemplateParameters());
			// set the template parameters
			if (element instanceof FunctionTemplate) {
				FunctionTemplate functionTemplate = (FunctionTemplate) element;
				functionTemplate.setTemplateParameterTypes(parameterTypes);
			} else if (element instanceof MethodTemplate) {
				MethodTemplate methodTemplate = (MethodTemplate) element;
				methodTemplate.setTemplateParameterTypes(parameterTypes);
			}
			// set the body position
			if (element instanceof SourceManipulation) {
				setBodyPosition((SourceManipulation) element, templateDeclaration);
			}
		} else if (declaration instanceof IASTSimpleDeclaration) {
			CElement[] elements = createSimpleDeclarations(parent, (IASTSimpleDeclaration) declaration, true);
			String[] parameterTypes = ASTStringUtil
					.getTemplateParameterArray(templateDeclaration.getTemplateParameters());
			for (CElement element : elements) {
				// set the template parameters
				if (element instanceof StructureTemplate) {
					StructureTemplate classTemplate = (StructureTemplate) element;
					classTemplate.setTemplateParameterTypes(parameterTypes);
				} else if (element instanceof StructureTemplateDeclaration) {
					StructureTemplateDeclaration classTemplate = (StructureTemplateDeclaration) element;
					classTemplate.setTemplateParameterTypes(parameterTypes);
				} else if (element instanceof VariableTemplate) {
					VariableTemplate varTemplate = (VariableTemplate) element;
					varTemplate.setTemplateParameterTypes(parameterTypes);
				} else if (element instanceof FunctionTemplateDeclaration) {
					FunctionTemplateDeclaration functionTemplate = (FunctionTemplateDeclaration) element;
					functionTemplate.setTemplateParameterTypes(parameterTypes);
				} else if (element instanceof MethodTemplateDeclaration) {
					MethodTemplateDeclaration methodTemplate = (MethodTemplateDeclaration) element;
					methodTemplate.setTemplateParameterTypes(parameterTypes);
				} else if (element instanceof FunctionTemplate) {
					FunctionTemplate functionTemplate = (FunctionTemplate) element;
					functionTemplate.setTemplateParameterTypes(parameterTypes);
				} else if (element instanceof MethodTemplate) {
					MethodTemplate methodTemplate = (MethodTemplate) element;
					methodTemplate.setTemplateParameterTypes(parameterTypes);
				}
				// set the body position
				if (element instanceof SourceManipulation) {
					setBodyPosition((SourceManipulation) element, templateDeclaration);
				}
			}
		} else if (declaration instanceof ICPPASTAliasDeclaration) {
			createDeclaration(parent, declaration);
		} else if (declaration instanceof ICPPASTTemplateDeclaration) {
			// strange: template decl inside template decl
			createTemplateDeclaration(parent, (ICPPASTTemplateDeclaration) declaration);
		} else if (declaration instanceof IASTProblemDeclaration) {
			// ignore problem declarations (or create special elements for debugging?)
		} else {
			assert false : "TODO: " + declaration.getClass().getName(); //$NON-NLS-1$
		}
	}

	/**
	 * Handle extern "C" and related kinds.
	 *
	 * @param parent
	 * @param declaration
	 * @throws CModelException
	 * @throws DOMException
	 */
	private void createLinkageSpecification(Parent parent, ICPPASTLinkageSpecification linkageDeclaration)
			throws CModelException, DOMException {
		IASTDeclaration[] declarations = linkageDeclaration.getDeclarations(true);
		for (IASTDeclaration declaration : declarations) {
			if (linkageDeclaration.getFileLocation() != null || isLocalToFile(declaration)) {
				createDeclaration(parent, declaration);
			}
		}
	}

	private CElement[] createSimpleDeclarations(Parent parent, IASTSimpleDeclaration declaration, boolean isTemplate)
			throws CModelException, DOMException {
		final IASTDeclSpecifier declSpecifier = declaration.getDeclSpecifier();
		final IASTDeclarator[] declarators = declaration.getDeclarators();
		final CElement[] elements;
		boolean isCompositeType = declSpecifier instanceof IASTCompositeTypeSpecifier
				|| declSpecifier instanceof IASTEnumerationSpecifier;
		if (declarators.length == 0) {
			elements = new CElement[1];
			final CElement element = createSimpleDeclaration(parent, declSpecifier, null, isTemplate);
			elements[0] = element;
		} else {
			if (isCompositeType) {
				createSimpleDeclaration(parent, declSpecifier, null, isTemplate);
			}
			elements = new CElement[declarators.length];
			for (int i = 0; i < declarators.length; i++) {
				final IASTDeclarator declarator = declarators[i];
				final CElement element = createSimpleDeclaration(parent, declSpecifier, declarator, isTemplate);
				if (!isTemplate && element instanceof SourceManipulation && declarators.length > 1) {
					setBodyPosition((SourceManipulation) element, declarator);
				}
				elements[i] = element;
			}
		}
		return elements;
	}

	private CElement createSimpleDeclaration(Parent parent, IASTDeclSpecifier declSpecifier, IASTDeclarator declarator,
			boolean isTemplate) throws CModelException, DOMException {
		if (declSpecifier instanceof IASTCompositeTypeSpecifier) {
			if (declarator != null) {
				return createTypedefOrFunctionOrVariable(parent, declSpecifier, declarator, isTemplate);
			}
			return createCompositeType(parent, (IASTCompositeTypeSpecifier) declSpecifier, isTemplate);
		} else if (declSpecifier instanceof IASTElaboratedTypeSpecifier) {
			if (declarator != null) {
				return createTypedefOrFunctionOrVariable(parent, declSpecifier, declarator, isTemplate);
			}
			return createElaboratedTypeDeclaration(parent, (IASTElaboratedTypeSpecifier) declSpecifier, isTemplate);
		} else if (declSpecifier instanceof IASTEnumerationSpecifier) {
			if (declarator != null) {
				return createTypedefOrFunctionOrVariable(parent, declSpecifier, declarator, isTemplate);
			}
			return createEnumeration(parent, (IASTEnumerationSpecifier) declSpecifier);
		} else if (declSpecifier instanceof IASTNamedTypeSpecifier) {
			if (declarator != null) {
				return createTypedefOrFunctionOrVariable(parent, declSpecifier, declarator, isTemplate);
			}
		} else if (declSpecifier instanceof IASTSimpleDeclSpecifier) {
			if (declarator != null) {
				return createTypedefOrFunctionOrVariable(parent, declSpecifier, declarator, isTemplate);
			}
		} else if (declSpecifier instanceof ICPPASTTypeTransformationSpecifier) {
			if (declarator != null) {
				return createTypedefOrFunctionOrVariable(parent, declSpecifier, declarator, isTemplate);
			}
		} else {
			assert false : "TODO: " + declSpecifier.getClass().getName(); //$NON-NLS-1$
		}
		return null;
	}

	private CElement createTypedefOrFunctionOrVariable(Parent parent, IASTDeclSpecifier declSpecifier,
			IASTDeclarator declarator, boolean isTemplate) throws CModelException {
		assert declarator != null;
		if (declSpecifier.getStorageClass() == IASTDeclSpecifier.sc_typedef) {
			return createTypeDef(parent, declSpecifier, declarator);
		}
		IASTDeclarator typeRelevant = ASTQueries.findTypeRelevantDeclarator(declarator);
		if (typeRelevant instanceof IASTFunctionDeclarator) {
			return createFunctionDeclaration(parent, declSpecifier, (IASTFunctionDeclarator) typeRelevant, isTemplate);
		}
		return createVariable(parent, declSpecifier, declarator, isTemplate);
	}

	private void createNamespace(Parent parent, ICPPASTNamespaceDefinition declaration)
			throws CModelException, DOMException {
		// create element
		final String type = Keywords.NAMESPACE;
		final IASTName name = declaration.getName();
		final String nsName = ASTStringUtil.getQualifiedName(name);
		final Namespace element = new Namespace(parent, nsName);
		setIndex(element);
		element.setActive(declaration.isActive());

		// add to parent
		parent.addChild(element);
		// set positions
		if (name != null && nsName.length() > 0) {
			setIdentifierPosition(element, name);
		} else {
			final IASTFileLocation nsLocation = declaration.getFileLocation();
			if (nsLocation != null) {
				// Note: don't want to call element.setIdPos(), because we want the SourceManipulationInfo
				//       lookup to go through the local 'fNewElements' cache.
				getSourceManipulationInfo(element).setIdPos(nsLocation.getNodeOffset(), type.length());
			}
		}
		setBodyPosition(element, declaration);

		element.setTypeName(type);

		IASTDeclaration[] nsDeclarations = declaration.getDeclarations(true);
		for (IASTDeclaration nsDeclaration : nsDeclarations) {
			if (declaration.getFileLocation() != null || isLocalToFile(nsDeclaration)) {
				createDeclaration(element, nsDeclaration);
			}
		}
	}

	private StructureDeclaration createElaboratedTypeDeclaration(Parent parent,
			IASTElaboratedTypeSpecifier elaboratedTypeSpecifier, boolean isTemplate) throws CModelException {
		// create element
		final String type;
		final int kind;
		switch (elaboratedTypeSpecifier.getKind()) {
		case IASTElaboratedTypeSpecifier.k_struct:
			kind = (isTemplate) ? ICElement.C_TEMPLATE_STRUCT_DECLARATION : ICElement.C_STRUCT_DECLARATION;
			type = Keywords.STRUCT;
			break;
		case IASTElaboratedTypeSpecifier.k_union:
			kind = (isTemplate) ? ICElement.C_TEMPLATE_UNION_DECLARATION : ICElement.C_UNION_DECLARATION;
			type = Keywords.UNION;
			break;
		case ICPPASTElaboratedTypeSpecifier.k_class:
			kind = (isTemplate) ? ICElement.C_TEMPLATE_CLASS_DECLARATION : ICElement.C_CLASS_DECLARATION;
			type = Keywords.CLASS;
			break;
		case IASTElaboratedTypeSpecifier.k_enum:
			// do we need a C_ENUM_DECLARATION?
			kind = ICElement.C_CLASS_DECLARATION;
			type = Keywords.ENUM;
			break;
		default:
			kind = ICElement.C_CLASS_DECLARATION;
			type = ""; //$NON-NLS-1$
			break;
		}

		final IASTName astClassName = elaboratedTypeSpecifier.getName();
		final String className = ASTStringUtil.getSimpleName(astClassName);

		StructureDeclaration element;
		if (isTemplate) {
			element = new StructureTemplateDeclaration(parent, kind, className);
		} else {
			element = new StructureDeclaration(parent, className, kind);
		}
		setIndex(element);
		element.setActive(elaboratedTypeSpecifier.isActive());
		StructureInfo info = (StructureInfo) getElementInfo(element);
		info.setTypeName(type);

		// add to parent
		parent.addChild(element);

		// set positions
		if (className.length() > 0) {
			setIdentifierPosition(info, astClassName);
		} else {
			final IASTFileLocation classLocation = getMinFileLocation(elaboratedTypeSpecifier.getNodeLocations());
			if (classLocation != null) {
				info.setIdPos(classLocation.getNodeOffset(), type.length());
			}
		}
		setBodyPosition(info, elaboratedTypeSpecifier);
		return element;
	}

	private Enumeration createEnumeration(Parent parent, IASTEnumerationSpecifier enumSpecifier)
			throws CModelException {
		// create element
		final String type = Keywords.ENUM;
		final IASTName astEnumName = enumSpecifier.getName();
		final String enumName = ASTStringUtil.getSimpleName(astEnumName);
		final Enumeration element = new Enumeration(parent, enumName);
		setIndex(element);
		element.setActive(enumSpecifier.isActive());

		// add to parent
		parent.addChild(element);
		EnumerationInfo info = (EnumerationInfo) getElementInfo(element);
		// set enumeration position
		if (astEnumName != null && enumName.length() > 0) {
			setIdentifierPosition(info, astEnumName);
		} else {
			final IASTFileLocation enumLocation = enumSpecifier.getFileLocation();
			info.setIdPos(enumLocation.getNodeOffset(), type.length());
		}
		// add enumerators
		final IASTEnumerator[] enumerators = enumSpecifier.getEnumerators();
		for (final IASTEnumerator enumerator : enumerators) {
			createEnumerator(element, enumerator);
		}
		setBodyPosition(info, enumSpecifier);
		info.setTypeName(type);
		return element;
	}

	private Enumerator createEnumerator(Parent enumarator, IASTEnumerator enumDef) throws CModelException {
		final IASTName astEnumName = enumDef.getName();
		final Enumerator element = new Enumerator(enumarator, ASTStringUtil.getSimpleName(astEnumName));
		setIndex(element);
		element.setActive(enumDef.isActive());

		IASTExpression initialValue = enumDef.getValue();
		if (initialValue != null) {
			element.setConstantExpression(ASTStringUtil.getExpressionString(initialValue));
		}
		// add to parent
		enumarator.addChild(element);
		// set positions
		setIdentifierPosition(element, astEnumName);
		setBodyPosition(element, enumDef);
		return element;
	}

	private Structure createCompositeType(Parent parent, IASTCompositeTypeSpecifier compositeTypeSpecifier,
			boolean isTemplate) throws CModelException, DOMException {
		// create element
		final String type;
		final int kind;
		final ASTAccessVisibility defaultVisibility;
		switch (compositeTypeSpecifier.getKey()) {
		case IASTCompositeTypeSpecifier.k_struct:
			kind = (isTemplate) ? ICElement.C_TEMPLATE_STRUCT : ICElement.C_STRUCT;
			type = Keywords.STRUCT;
			defaultVisibility = ASTAccessVisibility.PUBLIC;
			break;
		case IASTCompositeTypeSpecifier.k_union:
			kind = (isTemplate) ? ICElement.C_TEMPLATE_UNION : ICElement.C_UNION;
			type = Keywords.UNION;
			defaultVisibility = ASTAccessVisibility.PUBLIC;
			break;
		case ICPPASTCompositeTypeSpecifier.k_class:
			kind = (isTemplate) ? ICElement.C_TEMPLATE_CLASS : ICElement.C_CLASS;
			type = Keywords.CLASS;
			defaultVisibility = ASTAccessVisibility.PRIVATE;
			break;
		default:
			kind = ICElement.C_CLASS;
			type = ""; //$NON-NLS-1$
			defaultVisibility = ASTAccessVisibility.PUBLIC;
			break;
		}

		final IASTName astClassName = compositeTypeSpecifier.getName();
		final String className = ASTStringUtil.getSimpleName(astClassName);

		final Structure element;
		if (!isTemplate) {
			Structure classElement = new Structure(parent, kind, className);
			element = classElement;
		} else {
			StructureTemplate classTemplate = new StructureTemplate(parent, kind, className);
			element = classTemplate;
		}
		setIndex(element);
		element.setActive(compositeTypeSpecifier.isActive());

		if (compositeTypeSpecifier instanceof ICPPASTCompositeTypeSpecifier) {
			// store super classes names
			final ICPPASTCompositeTypeSpecifier cppCompositeTypeSpecifier = (ICPPASTCompositeTypeSpecifier) compositeTypeSpecifier;
			ICPPASTBaseSpecifier[] baseSpecifiers = cppCompositeTypeSpecifier.getBaseSpecifiers();
			for (final ICPPASTBaseSpecifier baseSpecifier : baseSpecifiers) {
				final ICPPASTNameSpecifier nameSpec = baseSpecifier.getNameSpecifier();
				final ASTAccessVisibility visibility;
				switch (baseSpecifier.getVisibility()) {
				case ICPPASTBaseSpecifier.v_public:
					visibility = ASTAccessVisibility.PUBLIC;
					break;
				case ICPPASTBaseSpecifier.v_protected:
					visibility = ASTAccessVisibility.PROTECTED;
					break;
				case ICPPASTBaseSpecifier.v_private:
					visibility = ASTAccessVisibility.PRIVATE;
					break;
				default:
					visibility = ASTAccessVisibility.PUBLIC;
				}
				if (nameSpec instanceof IASTName) {
					element.addSuperClass(ASTStringUtil.getSimpleName((IASTName) nameSpec), visibility);
				} else {
					element.addSuperClass(new String(nameSpec.toCharArray()), visibility);
				}
			}
		}

		StructureInfo info = (StructureInfo) getElementInfo(element);
		info.setTypeName(type);

		// add to parent
		parent.addChild(element);
		// set positions
		if (!isTemplate) {
			setBodyPosition(info, compositeTypeSpecifier);
		}
		if (className.length() > 0) {
			setIdentifierPosition(info, astClassName);
		} else {
			final IASTFileLocation classLocation = getMinFileLocation(compositeTypeSpecifier.getNodeLocations());
			if (classLocation != null) {
				info.setIdPos(classLocation.getNodeOffset(), type.length());
			}
		}
		// add members
		pushDefaultVisibility(defaultVisibility);
		try {
			final IASTDeclaration[] memberDeclarations = compositeTypeSpecifier.getDeclarations(true);
			for (IASTDeclaration member : memberDeclarations) {
				if (compositeTypeSpecifier.getFileLocation() != null || isLocalToFile(member)) {
					createDeclaration(element, member);
				}
			}
		} finally {
			popDefaultVisibility();
		}
		return element;
	}

	private TypeDef createTypeDef(Parent parent, IASTDeclSpecifier declSpecifier, IASTDeclarator declarator)
			throws CModelException {
		IASTDeclarator nestedDeclarator = declarator;
		while (nestedDeclarator.getNestedDeclarator() != null) {
			nestedDeclarator = nestedDeclarator.getNestedDeclarator();
		}
		final IASTName astTypedefName = nestedDeclarator.getName();
		if (astTypedefName == null) {
			return null;
		}
		// create the element
		String name = ASTStringUtil.getSimpleName(astTypedefName);

		final TypeDef element = new TypeDef(parent, name);
		setIndex(element);
		element.setActive(declarator.isActive());

		String typeName = ASTStringUtil.getSignatureString(declSpecifier, declarator);
		element.setTypeName(typeName);

		// add to parent
		parent.addChild(element);

		// set positions
		final SourceManipulationInfo info = getSourceManipulationInfo(element);
		if (name.length() > 0) {
			setIdentifierPosition(info, astTypedefName);
		} else {
			setIdentifierPosition(info, declSpecifier);
		}
		if (declSpecifier instanceof IASTCompositeTypeSpecifier) {
			setBodyPosition(info, astTypedefName);
		} else {
			setBodyPosition(info, declSpecifier.getParent());
		}
		return element;
	}

	private VariableDeclaration createVariable(Parent parent, IASTDeclSpecifier specifier, IASTDeclarator declarator,
			boolean isTemplate) throws CModelException {
		IASTDeclarator nestedDeclarator = declarator;
		while (nestedDeclarator.getNestedDeclarator() != null) {
			nestedDeclarator = nestedDeclarator.getNestedDeclarator();
		}
		final IASTName astVariableName = nestedDeclarator.getName();
		if (astVariableName == null) {
			return null;
		}
		final String variableName = ASTStringUtil.getQualifiedName(astVariableName);

		final VariableDeclaration element;
		final SourceManipulationInfo info;

		if (declarator instanceof IASTFieldDeclarator || parent instanceof IStructure
				|| CModelBuilder2.getScope(astVariableName) instanceof ICPPClassScope) {
			// field
			Field newElement = new Field(parent, variableName);
			setIndex(newElement);
			final FieldInfo fieldInfo = (FieldInfo) getElementInfo(newElement);
			if (specifier instanceof ICPPASTDeclSpecifier) {
				final ICPPASTDeclSpecifier cppSpecifier = (ICPPASTDeclSpecifier) specifier;
				fieldInfo.setMutable(cppSpecifier.getStorageClass() == IASTDeclSpecifier.sc_mutable);
			}
			fieldInfo.setTypeName(ASTStringUtil.getSignatureString(specifier, declarator));
			fieldInfo.setVisibility(getCurrentVisibility());
			fieldInfo.setConst(specifier.isConst());
			fieldInfo.setVolatile(specifier.isVolatile());
			element = newElement;
			info = fieldInfo;
		} else {
			if (isTemplate) {
				// template variable
				VariableTemplate newElement = new VariableTemplate(parent, variableName);
				element = newElement;
			} else {
				if (specifier.getStorageClass() == IASTDeclSpecifier.sc_extern) {
					// variable declaration
					VariableDeclaration newElement = new VariableDeclaration(parent, variableName);
					element = newElement;
				} else {
					// variable
					Variable newElement = new Variable(parent, variableName);
					element = newElement;
				}
			}
			setIndex(element);
			VariableInfo varInfo = (VariableInfo) getElementInfo(element);
			varInfo.setTypeName(ASTStringUtil.getSignatureString(specifier, declarator));
			varInfo.setConst(specifier.isConst());
			varInfo.setVolatile(specifier.isVolatile());
			info = varInfo;
		}
		element.setActive(declarator.isActive());
		// TODO [cmodel] correctly resolve isStatic
		element.setStatic(specifier.getStorageClass() == IASTDeclSpecifier.sc_static);
		// add to parent
		parent.addChild(element);

		// set positions
		setIdentifierPosition(info, astVariableName);
		if (!isTemplate) {
			if (specifier instanceof IASTCompositeTypeSpecifier) {
				setBodyPosition(info, astVariableName);
			} else {
				setBodyPosition(info, specifier.getParent());
			}
		}
		return element;
	}

	private FunctionDeclaration createFunctionDefinition(Parent parent, IASTFunctionDefinition functionDeclaration,
			boolean isTemplate) throws CModelException, DOMException {
		final IASTFunctionDeclarator declarator = functionDeclaration.getDeclarator();
		IASTDeclarator nestedDeclarator = declarator;
		while (nestedDeclarator.getNestedDeclarator() != null) {
			nestedDeclarator = nestedDeclarator.getNestedDeclarator();
		}
		final IASTName name = nestedDeclarator.getName();
		if (name == null) {
			// Something is wrong, skip this element
			return null;
		}

		final IASTDeclSpecifier declSpecifier = functionDeclaration.getDeclSpecifier();

		final String simpleName = ASTStringUtil.getSimpleName(name);
		final String[] parameterTypes = ASTStringUtil.getParameterSignatureArray(declarator);
		final String returnType = ASTStringUtil.getReturnTypeString(declSpecifier, declarator);

		final FunctionDeclaration element;
		final FunctionInfo info;

		if (declarator instanceof ICPPASTFunctionDeclarator) {
			final ICPPASTFunctionDeclarator cppFunctionDeclarator = (ICPPASTFunctionDeclarator) declarator;
			final IASTName simpleAstName;
			if (name instanceof ICPPASTQualifiedName) {
				final ICPPASTQualifiedName quName = (ICPPASTQualifiedName) name;
				simpleAstName = quName.getLastName();
			} else {
				simpleAstName = name;
			}
			IScope scope = null;
			// try to avoid expensive resolution of scope and binding
			boolean isMethod = parent instanceof IStructure;
			if (!isMethod && name instanceof ICPPASTQualifiedName) {
				if (isTemplate) {
					final ICPPASTNameSpecifier[] segments = ((ICPPASTQualifiedName) name).getAllSegments();
					for (ICPPASTNameSpecifier segment : segments) {
						if (segment instanceof ICPPASTTemplateId) {
							isMethod = true;
							break;
						}
					}
				}
				if (!isMethod) {
					scope = CPPVisitor.getContainingScope(simpleAstName);
					isMethod = scope instanceof ICPPClassScope || simpleAstName.resolveBinding() instanceof ICPPMethod;
				}
			}
			if (isMethod) {
				// method
				final MethodDeclaration methodElement;
				final String methodName;
				if (parent instanceof IStructure) {
					methodName = ASTStringUtil.getSimpleName(name);
				} else {
					methodName = ASTStringUtil.getQualifiedName(name);
				}
				if (isTemplate) {
					methodElement = new MethodTemplate(parent, methodName);
				} else {
					methodElement = new Method(parent, methodName);
				}
				element = methodElement;
				// establish identity attributes before getElementInfo()
				methodElement.setParameterTypes(parameterTypes);
				methodElement.setReturnType(returnType);
				methodElement.setConst(cppFunctionDeclarator.isConst());
				setIndex(element);

				final MethodInfo methodInfo = (MethodInfo) getElementInfo(methodElement);
				info = methodInfo;
				ICPPMethod methodBinding = null;
				if (scope != null) {
					final IBinding binding = simpleAstName.resolveBinding();
					if (binding instanceof ICPPMethod) {
						methodBinding = (ICPPMethod) binding;
					}
				}
				if (methodBinding != null) {
					methodInfo.setVirtual(methodBinding.isVirtual());
					methodInfo.setInline(methodBinding.isInline());
					methodInfo.setFriend(((ICPPASTDeclSpecifier) declSpecifier).isFriend());
					methodInfo.setVolatile(cppFunctionDeclarator.isVolatile());
					methodInfo.setVisibility(adaptVisibilityConstant(methodBinding.getVisibility()));
					methodInfo.setPureVirtual(false);
					methodElement.setConstructor(methodBinding instanceof ICPPConstructor);
					methodElement.setDestructor(methodBinding.isDestructor());
				} else {
					if (declSpecifier instanceof ICPPASTDeclSpecifier) {
						final ICPPASTDeclSpecifier cppDeclSpecifier = (ICPPASTDeclSpecifier) declSpecifier;
						methodInfo.setVirtual(cppDeclSpecifier.isVirtual());
						methodInfo.setInline(cppDeclSpecifier.isInline());
						methodInfo.setFriend(cppDeclSpecifier.isFriend());
					}
					methodInfo.setVolatile(cppFunctionDeclarator.isVolatile());
					methodInfo.setVisibility(getCurrentVisibility());
					methodInfo.setPureVirtual(false);
					final boolean isConstructor;
					if (scope != null) {
						isConstructor = CPPVisitor.isConstructor(scope, declarator);
					} else if (parent instanceof IStructure) {
						isConstructor = parent.getElementName().equals(simpleName);
					} else if (name instanceof ICPPASTQualifiedName) {
						final ICPPASTQualifiedName quName = (ICPPASTQualifiedName) name;
						final ICPPASTNameSpecifier[] qualifier = quName.getQualifier();
						if (qualifier.length == 0)
							isConstructor = false;
						else {
							ICPPASTNameSpecifier nameSpec = qualifier[qualifier.length - 1];
							isConstructor = nameSpec instanceof IASTName
									&& simpleName.equals(ASTStringUtil.getSimpleName((IASTName) nameSpec));
						}
					} else {
						isConstructor = false;
					}
					methodElement.setConstructor(isConstructor);
					methodElement.setDestructor(simpleName.charAt(0) == '~');
				}
			} else {
				String functionName = ASTStringUtil.getQualifiedName(name);
				// strip namespace qualifier if parent is same namespace
				if (name instanceof ICPPASTQualifiedName && parent instanceof INamespace) {
					final ICPPASTQualifiedName quName = (ICPPASTQualifiedName) name;
					final ICPPASTNameSpecifier[] qualifier = quName.getQualifier();
					if (qualifier.length >= 1) {
						ICPPASTNameSpecifier nameSpec = qualifier[qualifier.length - 1];
						if (nameSpec instanceof IASTName
								&& parent.getElementName().equals(ASTStringUtil.getSimpleName((IASTName) nameSpec))) {
							functionName = simpleName;
						}
					}
				}
				if (isTemplate) {
					// template function
					element = new FunctionTemplate(parent, functionName);
				} else {
					// function
					element = new Function(parent, functionName);
				}
				element.setParameterTypes(parameterTypes);
				element.setReturnType(returnType);
				setIndex(element);

				info = (FunctionInfo) getElementInfo(element);
				info.setConst(cppFunctionDeclarator.isConst());
			}

		} else {
			final String functionName = ASTStringUtil.getQualifiedName(name);
			element = new Function(parent, functionName);
			element.setParameterTypes(parameterTypes);
			element.setReturnType(returnType);
			setIndex(element);

			info = (FunctionInfo) getElementInfo(element);
		}
		element.setActive(functionDeclaration.isActive());

		// TODO [cmodel] correctly resolve isStatic
		info.setStatic(declSpecifier.getStorageClass() == IASTDeclSpecifier.sc_static);

		// add to parent
		parent.addChild(element);

		// set positions
		setIdentifierPosition(info, name);
		if (!isTemplate) {
			setBodyPosition(info, functionDeclaration);
		}
		return element;
	}

	private FunctionDeclaration createFunctionDeclaration(Parent parent, IASTDeclSpecifier declSpecifier,
			IASTFunctionDeclarator declarator, boolean isTemplate) throws CModelException {
		IASTDeclarator nestedDeclarator = declarator;
		while (nestedDeclarator.getNestedDeclarator() != null) {
			nestedDeclarator = nestedDeclarator.getNestedDeclarator();
		}
		final IASTName name = nestedDeclarator.getName();
		if (name == null) {
			// Something is wrong, skip this element
			return null;
		}

		final String functionName = ASTStringUtil.getSimpleName(name);
		final String[] parameterTypes = ASTStringUtil.getParameterSignatureArray(declarator);
		final String returnType = ASTStringUtil.getReturnTypeString(declSpecifier, declarator);

		final FunctionDeclaration element;
		final FunctionInfo info;

		if (declarator instanceof ICPPASTFunctionDeclarator) {
			final ICPPASTFunctionDeclarator cppFunctionDeclarator = (ICPPASTFunctionDeclarator) declarator;
			if (parent instanceof IStructure) {
				// method
				final MethodDeclaration methodElement;
				if (isTemplate) {
					methodElement = new MethodTemplateDeclaration(parent, functionName);
				} else {
					methodElement = new MethodDeclaration(parent, functionName);
				}
				element = methodElement;
				// establish identity attributes before getElementInfo()
				methodElement.setParameterTypes(parameterTypes);
				methodElement.setReturnType(returnType);
				methodElement.setConst(cppFunctionDeclarator.isConst());
				setIndex(element);
				final MethodInfo methodInfo = (MethodInfo) getElementInfo(methodElement);
				info = methodInfo;
				if (declSpecifier instanceof ICPPASTDeclSpecifier) {
					final ICPPASTDeclSpecifier cppDeclSpecifier = (ICPPASTDeclSpecifier) declSpecifier;
					methodInfo.setVirtual(cppDeclSpecifier.isVirtual());
					methodInfo.setInline(cppDeclSpecifier.isInline());
					methodInfo.setFriend(cppDeclSpecifier.isFriend());
				}
				methodInfo.setVolatile(cppFunctionDeclarator.isVolatile());
				methodInfo.setVisibility(getCurrentVisibility());
				methodInfo.setPureVirtual(cppFunctionDeclarator.isPureVirtual());
				methodElement.setConstructor(functionName.equals(parent.getElementName()));
				methodElement.setDestructor(functionName.charAt(0) == '~');
			} else {
				if (isTemplate) {
					element = new FunctionTemplateDeclaration(parent, functionName);
				} else {
					element = new FunctionDeclaration(parent, functionName);
				}
				element.setParameterTypes(parameterTypes);
				element.setReturnType(returnType);
				setIndex(element);

				info = (FunctionInfo) getElementInfo(element);
				info.setConst(cppFunctionDeclarator.isConst());
			}
		} else if (declarator instanceof IASTStandardFunctionDeclarator) {
			if (isTemplate) {
				element = new FunctionTemplateDeclaration(parent, functionName);
			} else {
				element = new FunctionDeclaration(parent, functionName);
			}
			element.setParameterTypes(parameterTypes);
			element.setReturnType(returnType);
			setIndex(element);

			info = (FunctionInfo) getElementInfo(element);
		} else {
			assert false;
			return null;
		}
		element.setActive(declarator.isActive());

		// TODO [cmodel] correctly resolve isStatic
		info.setStatic(declSpecifier.getStorageClass() == IASTDeclSpecifier.sc_static);

		// add to parent
		parent.addChild(element);

		// hook up the offsets
		setIdentifierPosition(info, name);
		if (!isTemplate) {
			setBodyPosition(info, declarator.getParent());
		}
		return element;
	}

	private Using createUsingDirective(Parent parent, ICPPASTUsingDirective usingDirDeclaration)
			throws CModelException {
		// create the element
		IASTName name = usingDirDeclaration.getQualifiedName();
		Using element = new Using(parent, ASTStringUtil.getQualifiedName(name), true);
		setIndex(element);
		element.setActive(usingDirDeclaration.isActive());

		// add to parent
		parent.addChild(element);

		// set positions
		setIdentifierPosition(element, name);
		setBodyPosition(element, usingDirDeclaration);
		return element;
	}

	private Using createUsingDeclaration(Parent parent, ICPPASTUsingDeclaration usingDeclaration)
			throws CModelException {
		// create the element
		IASTName name = usingDeclaration.getName();
		Using element = new Using(parent, ASTStringUtil.getSimpleName(name), false);
		setIndex(element);
		element.setActive(usingDeclaration.isActive());
		// add to parent
		parent.addChild(element);

		// set positions
		setIdentifierPosition(element, name);
		setBodyPosition(element, usingDeclaration);
		return element;
	}

	private CElementInfo getElementInfo(CElement cElement) {
		CElementInfo info = fNewElements.get(cElement);
		if (info == null) {
			info = cElement.createElementInfo();
			fNewElements.put(cElement, info);
		}
		return info;
	}

	private SourceManipulationInfo getSourceManipulationInfo(SourceManipulation cElement) {
		return (SourceManipulationInfo) getElementInfo(cElement);
	}

	/**
	 * Utility method to set the body position of an element from an AST node.
	 *
	 * @param element
	 * @param astNode
	 * @throws CModelException
	 */
	private void setBodyPosition(SourceManipulation element, IASTNode astNode) throws CModelException {
		setBodyPosition(getSourceManipulationInfo(element), astNode);
	}

	/**
	 * Utility method to set the body position of an element from an AST node.
	 *
	 * @param info
	 * @param astNode
	 */
	private void setBodyPosition(SourceManipulationInfo info, IASTNode astNode) {
		final IASTFileLocation location = astNode.getFileLocation();
		if (location != null) {
			info.setPos(location.getNodeOffset(), location.getNodeLength());
			info.setLines(location.getStartingLineNumber(), location.getEndingLineNumber());
		} else {
			final IASTNodeLocation[] locations = astNode.getNodeLocations();
			final IASTFileLocation minLocation = getMinFileLocation(locations);
			if (minLocation != null) {
				final IASTFileLocation maxLocation = getMaxFileLocation(locations);
				if (maxLocation != null) {
					final int startOffset = minLocation.getNodeOffset();
					final int endOffset = maxLocation.getNodeOffset() + maxLocation.getNodeLength();
					info.setPos(startOffset, endOffset - startOffset);
					final int startLine = minLocation.getStartingLineNumber();
					final int endLine = maxLocation.getEndingLineNumber();
					info.setLines(startLine, endLine);
				}
			}
		}
	}

	/**
	 * Utility method to set the identifier position of an element from an AST name.
	 *
	 * @param element
	 * @param astName
	 */
	private void setIdentifierPosition(SourceManipulation element, IASTName astName) {
		setIdentifierPosition(getSourceManipulationInfo(element), astName);
	}

	/**
	 * Utility method to set the identifier position of an element from an AST name.
	 *
	 * @param info
	 * @param astName
	 */
	private void setIdentifierPosition(SourceManipulationInfo info, IASTNode astName) {
		final IASTFileLocation location = astName.getFileLocation();
		if (location != null) {
			info.setIdPos(location.getNodeOffset(), location.getNodeLength());
		} else {
			final IASTNodeLocation[] locations = astName.getNodeLocations();
			final IASTFileLocation minLocation = getMinFileLocation(locations);
			if (minLocation != null) {
				final IASTFileLocation maxLocation = getMaxFileLocation(locations);
				if (maxLocation != null) {
					final int startOffset = minLocation.getNodeOffset();
					final int endOffset = maxLocation.getNodeOffset() + maxLocation.getNodeLength();
					info.setIdPos(startOffset, endOffset - startOffset);
				}
			}
		}
	}

	private static IASTFileLocation getMaxFileLocation(IASTNodeLocation[] locations) {
		if (locations == null || locations.length == 0) {
			return null;
		}
		final IASTNodeLocation nodeLocation = locations[locations.length - 1];
		return nodeLocation.asFileLocation();
	}

	private static IASTFileLocation getMinFileLocation(IASTNodeLocation[] locations) {
		if (locations == null || locations.length == 0) {
			return null;
		}
		final IASTNodeLocation nodeLocation = locations[0];
		return nodeLocation.asFileLocation();
	}

	/**
	 * Handle the special "declaration" visibility label
	 * @param visibilityLabel
	 */
	private void handleVisibilityLabel(ICPPASTVisibilityLabel visibilityLabel) {
		setCurrentVisibility(adaptVisibilityConstant(visibilityLabel.getVisibility()));
	}

	/**
	 * Convert the given <code>ICPPASTVisiblityLabel</code> visibility constant
	 * into an <code>ASTAccessVisibility</code>.
	 *
	 * @param visibility
	 * @return the corresponding <code>ASTAccessVisibility</code>
	 */
	private ASTAccessVisibility adaptVisibilityConstant(int visibility) {
		switch (visibility) {
		case ICPPASTVisibilityLabel.v_public:
			return ASTAccessVisibility.PUBLIC;
		case ICPPASTVisibilityLabel.v_protected:
			return ASTAccessVisibility.PROTECTED;
		case ICPPASTVisibilityLabel.v_private:
			return ASTAccessVisibility.PRIVATE;
		}
		assert false : "Unknown visibility"; //$NON-NLS-1$
		return ASTAccessVisibility.PUBLIC;
	}

	private void setCurrentVisibility(ASTAccessVisibility visibility) {
		fCurrentVisibility = visibility;
	}

	private ASTAccessVisibility getCurrentVisibility() {
		if (fCurrentVisibility == null) {
			return ASTAccessVisibility.PUBLIC;
		}
		return fCurrentVisibility;
	}

	/**
	 * Pop the default visibility from the outer scope.
	 */
	private void popDefaultVisibility() {
		if (!fVisibilityStack.isEmpty()) {
			setCurrentVisibility(fVisibilityStack.pop());
		}
	}

	/**
	 * Push given visibility as default class/struct/union visibility.
	 *
	 * @param visibility
	 */
	private void pushDefaultVisibility(ASTAccessVisibility visibility) {
		fVisibilityStack.push(getCurrentVisibility());
		setCurrentVisibility(visibility);
	}

	/**
	 * Determine the scope for given name.
	 *
	 * @param astName
	 * @return the scope or <code>null</code>
	 */
	private static IScope getScope(IASTName astName) {
		IBinding binding = astName.resolveBinding();
		if (binding != null) {
			try {
				return binding.getScope();
			} catch (DOMException e) {
				return null;
			}
		}
		return null;
	}
}
