/*******************************************************************************
 * Copyright (c) 2003, 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corp. - Rational Software - initial implementation
 *     Markus Schorn (Wind River Systems)
 *     Gerhard Schaber (Wind River Systems)
 *     Patrick Hofer [bug 325799]
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.viewsupport;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.StyledString.Styler;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.IBinary;
import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.IEnumerator;
import org.eclipse.cdt.core.model.IField;
import org.eclipse.cdt.core.model.IFunctionDeclaration;
import org.eclipse.cdt.core.model.IInheritance;
import org.eclipse.cdt.core.model.IMacro;
import org.eclipse.cdt.core.model.IMethodDeclaration;
import org.eclipse.cdt.core.model.ISourceRoot;
import org.eclipse.cdt.core.model.ITemplate;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.ITypeDef;
import org.eclipse.cdt.core.model.IVariableDeclaration;
import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility;
import org.eclipse.cdt.ui.CUIPlugin;

import org.eclipse.cdt.internal.core.model.CoreModelMessages;


// Most parts of this file were previously located in CElementLabels.
// FlexibleBuffer and sub-types are taken from JDTs JavaElementLabelComposer.

/**
 * Creates labels for ICElement objects.
 */
public class CElementLabelComposer {

	/**
	 * An adapter for buffer supported by the label composer.
	 */
	public static abstract class FlexibleBuffer {

		/**
		 * Appends the string representation of the given character to the buffer.
		 *
		 * @param ch the character to append
		 * @return a reference to this object
		 */
		public abstract FlexibleBuffer append(char ch);

		/**
		 * Appends the given string to the buffer.
		 *
		 * @param string the string to append
		 * @return a reference to this object
		 */
		public abstract FlexibleBuffer append(String string);

		/**
		 * Returns the length of the the buffer.
		 *
		 * @return the length of the current string
		 */
		public abstract int length();

		/**
		 * Sets a styler to use for the given source range. The range must be subrange of actual
		 * string of this buffer. Stylers previously set for that range will be overwritten.
		 *
		 * @param offset the start offset of the range
		 * @param length the length of the range
		 * @param styler the styler to set
		 *
		 * @throws StringIndexOutOfBoundsException if <code>start</code> is less than zero, or if
		 *             offset plus length is greater than the length of this object.
		 */
		public abstract void setStyle(int offset, int length, Styler styler);
	}

	public static class FlexibleStringBuffer extends FlexibleBuffer {
		private final StringBuffer fStringBuffer;

		public FlexibleStringBuffer(StringBuffer stringBuffer) {
			fStringBuffer= stringBuffer;
		}

		@Override
		public FlexibleBuffer append(char ch) {
			fStringBuffer.append(ch);
			return this;
		}

		@Override
		public FlexibleBuffer append(String string) {
			fStringBuffer.append(string);
			return this;
		}

		@Override
		public int length() {
			return fStringBuffer.length();
		}

		@Override
		public void setStyle(int offset, int length, Styler styler) {
			// no style
		}

		@Override
		public String toString() {
			return fStringBuffer.toString();
		}
	}

	public static class FlexibleStyledString extends FlexibleBuffer {
		private final StyledString fStyledString;

		public FlexibleStyledString(StyledString stringBuffer) {
			fStyledString= stringBuffer;
		}

		@Override
		public FlexibleBuffer append(char ch) {
			fStyledString.append(ch);
			return this;
		}

		@Override
		public FlexibleBuffer append(String string) {
			fStyledString.append(string);
			return this;
		}

		@Override
		public int length() {
			return fStyledString.length();
		}

		@Override
		public void setStyle(int offset, int length, Styler styler) {
			fStyledString.setStyle(offset, length, styler);
		}

		@Override
		public String toString() {
			return fStyledString.toString();
		}
	}
	
	
	private static final Styler QUALIFIER_STYLE= StyledString.QUALIFIER_STYLER;
	//private static final Styler COUNTER_STYLE= StyledString.COUNTER_STYLER;
	private static final Styler DECORATIONS_STYLE= StyledString.DECORATIONS_STYLER;

	
	private final FlexibleBuffer fBuffer;

	/**
	 * Creates a new java element composer based on the given buffer.
	 *
	 * @param buffer the buffer
	 */
	public CElementLabelComposer(FlexibleBuffer buffer) {
		fBuffer= buffer;
	}

	/**
	 * Creates a new java element composer based on the given buffer.
	 *
	 * @param buffer the buffer
	 */
	public CElementLabelComposer(StyledString buffer) {
		this(new FlexibleStyledString(buffer));
	}

	/**
	 * Creates a new java element composer based on the given buffer.
	 *
	 * @param buffer the buffer
	 */
	public CElementLabelComposer(StringBuffer buffer) {
		this(new FlexibleStringBuffer(buffer));
	}
	
	
	/**
	 * Appends the label for an element to a StringBuffer.
	 * @param element any element (IMethodDeclaration, IField, ITypeDef, IVariableDeclaration, etc.)
	 * @param flags any of the flags (M_*, F_*, ROOT_*, etc.) defined in this class
	 */
	public void appendElementLabel(ICElement element, long flags) {
		int type= element.getElementType();
		ISourceRoot root= null;
		
		if (type != ICElement.C_MODEL && type != ICElement.C_PROJECT && !(type == ICElement.C_CCONTAINER && element instanceof ISourceRoot))
			root= getSourceRoot(element);
		if (root != null && getFlag(flags, CElementLabels.PREPEND_ROOT_PATH)) {
			getSourceRootLabel(root, CElementLabels.ROOT_QUALIFIED);
			fBuffer.append(CElementLabels.CONCAT_STRING);
		}		
		switch (type) {
			case ICElement.C_MACRO:
				appendMacroLabel((IMacro) element, flags);
				break;
			case ICElement.C_METHOD : 
			case ICElement.C_METHOD_DECLARATION:
			case ICElement.C_TEMPLATE_METHOD:
			case ICElement.C_TEMPLATE_METHOD_DECLARATION:
				appendMethodLabel( (IMethodDeclaration) element, flags);
				break;
			case ICElement.C_FUNCTION:
			case ICElement.C_FUNCTION_DECLARATION:
			case ICElement.C_TEMPLATE_FUNCTION:
			case ICElement.C_TEMPLATE_FUNCTION_DECLARATION:
				appendFunctionLabel( (IFunctionDeclaration) element, flags);
				break;
			case ICElement.C_FIELD : 
				appendFieldLabel( (IField) element, flags);
				break;
			case ICElement.C_VARIABLE:
			case ICElement.C_VARIABLE_DECLARATION:
				appendVariableLabel( (IVariableDeclaration) element, flags);
				break;
			case ICElement.C_ENUMERATOR:
				appendEnumeratorLabel((IEnumerator) element, flags);
				break;
			case ICElement.C_CLASS:
			case ICElement.C_STRUCT:
			case ICElement.C_UNION:
			case ICElement.C_ENUMERATION:
			case ICElement.C_TEMPLATE_CLASS:
			case ICElement.C_TEMPLATE_STRUCT:
			case ICElement.C_TEMPLATE_UNION:
			case ICElement.C_TEMPLATE_CLASS_DECLARATION:
			case ICElement.C_TEMPLATE_STRUCT_DECLARATION:
			case ICElement.C_TEMPLATE_UNION_DECLARATION:
			case ICElement.C_NAMESPACE:
				appendTypeLabel( element, flags);
				break;
			case ICElement.C_TYPEDEF:
				appendTypeDefLabel((ITypeDef)element, flags);
				break;
			case ICElement.C_UNIT: 
				appendTranslationUnitLabel((ITranslationUnit) element, flags);
				break;	
			case ICElement.C_CCONTAINER:
				ICContainer container = (ICContainer) element;
				if (container instanceof ISourceRoot)
					getSourceRootLabel((ISourceRoot) container, flags);
				else
					appendContainerLabel(container, flags);
				break;
			case ICElement.C_PROJECT:
			case ICElement.C_MODEL:
				fBuffer.append(element.getElementName());
				break;
			default:
				fBuffer.append(element.getElementName());
		}
		
		if (root != null && getFlag(flags, CElementLabels.APPEND_ROOT_PATH)) {
			int offset= fBuffer.length();
			fBuffer.append(CElementLabels.CONCAT_STRING);
			getSourceRootLabel(root, CElementLabels.ROOT_QUALIFIED);
			if (getFlag(flags, CElementLabels.COLORIZE)) {
				fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
			}
		}
		
		if (element instanceof IBinary) {
			IBinary bin = (IBinary)element;
			fBuffer.append(" - [" + bin.getCPU() + "/" + (bin.isLittleEndian() ? "le" : "be") + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
		}
	}
	
	
	/**
	 * Appends the label for a macro definition to a StringBuffer.
	 * @param macro a macro definition
	 * @param flags {@link CElementLabels#MF_POST_FILE_QUALIFIED}, or 0.
	 */
	public void appendMacroLabel(IMacro macro, long flags) {
		fBuffer.append(macro.getElementName());
		if( getFlag( flags, CElementLabels.M_PARAMETER_TYPES ) ) {
			if (macro.isFunctionStyle()) {
				fBuffer.append("()"); //$NON-NLS-1$
			}
		}
		
		if( getFlag(flags, CElementLabels.MF_POST_FILE_QUALIFIED)) {
			IPath path= macro.getPath();
			if (path != null) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.CONCAT_STRING );
				fBuffer.append(path.toString());
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
				}
			}
		}
	}

	/**
	 * Appends the label for a method declaration to a StringBuffer.
	 * @param method a method declaration
	 * @param flags any of the M_* flags, and {@link CElementLabels#MF_POST_FILE_QUALIFIED}
	 */
	public void appendMethodLabel( IMethodDeclaration method, long flags ) {
		try {
		//return type
		if( getFlag( flags, CElementLabels.M_PRE_RETURNTYPE ) && method.exists() && !method.isConstructor() ) {
			fBuffer.append( method.getReturnType() );
			fBuffer.append( ' ' );
		}
		
		//qualification
		if( getFlag( flags, CElementLabels.M_FULLY_QUALIFIED ) ){
			ICElement parent = method.getParent();
			if (parent != null && parent.exists() && !(parent instanceof ITranslationUnit)) {
				appendTypeLabel( parent, CElementLabels.T_FULLY_QUALIFIED | (flags & CElementLabels.TEMPLATE_ARGUMENTS));
				fBuffer.append( "::" ); //$NON-NLS-1$
			}
		}
		
		if (getFlag(flags, CElementLabels.M_SIMPLE_NAME)) {
			fBuffer.append(getSimpleName(method.getElementName()));
		} else {
			fBuffer.append(method.getElementName());
		}
		
		//template parameters
		if (method instanceof ITemplate) {
			appendTemplateParameters((ITemplate)method, flags);
		}

		//parameters
		if( getFlag( flags, CElementLabels.M_PARAMETER_TYPES ) ) {
			fBuffer.append('(');

			String[] types = method.getParameterTypes();
			
			if (types != null) {
				for (int i= 0; i < types.length; i++) {
					if (i > 0) {
						fBuffer.append( CElementLabels.COMMA_STRING );
					}
					fBuffer.append( types[i] );
				}
			}
			fBuffer.append(')');
		}
		
		//exceptions
		if( getFlag( flags, CElementLabels.M_EXCEPTIONS ) && method.exists() ){
			String [] types = method.getExceptions();
			if (types.length > 0) {
				fBuffer.append(" throw( "); //$NON-NLS-1$
				for (int i= 0; i < types.length; i++) {
					if (i > 0) {
						fBuffer.append(CElementLabels.COMMA_STRING);
					}
					fBuffer.append( types[i] );
				}
				fBuffer.append( " )" ); //$NON-NLS-1$
			}
		}
		
		if( getFlag( flags, CElementLabels.M_APP_RETURNTYPE ) && method.exists() && !method.isConstructor() && !method.isDestructor()) {
			final String typeName= method.getReturnType();
			if (typeName != null && typeName.length() > 0) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.DECL_STRING );
				fBuffer.append(typeName);
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, DECORATIONS_STYLE);
				}
			}
		}			
		
		// post qualification
		if( getFlag(flags, CElementLabels.M_POST_QUALIFIED)) {
			int offset= fBuffer.length();
			fBuffer.append( CElementLabels.CONCAT_STRING );
			appendTypeLabel( method.getParent(), CElementLabels.T_FULLY_QUALIFIED | (flags & CElementLabels.TEMPLATE_ARGUMENTS));
			if (getFlag(flags, CElementLabels.COLORIZE)) {
				fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
			}
		}
		if( getFlag(flags, CElementLabels.MF_POST_FILE_QUALIFIED)) {
			
			IPath path= method.getPath();
			if (path != null) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.CONCAT_STRING );
				fBuffer.append(path.toString());
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
				}
			}
			
		}
		} catch (CModelException e) {
			CUIPlugin.log(e);
		}
	}

	/**
	 * Strip any qualifier from the given name.
	 * 
	 * @param elementName
	 * @return a "simple" name
	 */
	private static String getSimpleName(String elementName) {
		int idx = elementName.lastIndexOf("::"); //$NON-NLS-1$
		if (idx >= 0) {
			return elementName.substring(idx+2);
		}
		return elementName;
	}

	private void appendTemplateParameters(ITemplate template, long flags) {
		String[] args= null;
		if (getFlag(flags, CElementLabels.TEMPLATE_ARGUMENTS)) {
			args = template.getTemplateArguments();
		} else if (getFlag(flags, CElementLabels.TEMPLATE_PARAMETERS)) {
			args= template.getTemplateParameterTypes();
		} else {
			return;
		}
		
		fBuffer.append('<');
		if (args != null) {
			for (int i= 0; i < args.length; i++) {
				if (i > 0) {
					fBuffer.append( ',' );
				}
				fBuffer.append( args[i] );
			}
		}
		fBuffer.append('>');
	}

	/**
	 * Appends the label for a field to a StringBuffer.
	 * @param field a field
	 * @param flags any of the F_* flags, and {@link CElementLabels#MF_POST_FILE_QUALIFIED}
	 */
	public void appendFieldLabel(IField field, long flags ) {
		try {
		//return type
		if( getFlag( flags, CElementLabels.F_PRE_TYPE_SIGNATURE ) && field.exists()) {
			fBuffer.append( field.getTypeName() );
			fBuffer.append( ' ' );
		}
		
		//qualification
		if( getFlag( flags, CElementLabels.F_FULLY_QUALIFIED ) ){
			ICElement parent = field.getParent();
			if (parent != null && parent.exists()) {
				appendTypeLabel( parent, CElementLabels.T_FULLY_QUALIFIED | (flags & CElementLabels.TEMPLATE_PARAMETERS));
				fBuffer.append( "::" ); //$NON-NLS-1$
			}
		}
		
		if (getFlag(flags, CElementLabels.F_SIMPLE_NAME)) {
			fBuffer.append(getSimpleName(field.getElementName()));
		} else {
			fBuffer.append(field.getElementName());
		}
			
		if( getFlag( flags, CElementLabels.F_APP_TYPE_SIGNATURE ) && field.exists()) {
			int offset= fBuffer.length();
			fBuffer.append( CElementLabels.DECL_STRING );
			fBuffer.append( field.getTypeName() );	
			if (getFlag(flags, CElementLabels.COLORIZE)) {
				fBuffer.setStyle(offset, fBuffer.length() - offset, DECORATIONS_STYLE);
			}
		}	
		
		// post qualification
		if( getFlag(flags, CElementLabels.F_POST_QUALIFIED)) {
			int offset= fBuffer.length();
			fBuffer.append( CElementLabels.CONCAT_STRING );
			appendTypeLabel( field.getParent(), CElementLabels.T_FULLY_QUALIFIED | (flags & CElementLabels.TEMPLATE_PARAMETERS));
			if (getFlag(flags, CElementLabels.COLORIZE)) {
				fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
			}
		}
		if( getFlag(flags, CElementLabels.MF_POST_FILE_QUALIFIED)) {
			IPath path= field.getPath();
			if (path != null) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.CONCAT_STRING );
				fBuffer.append(path.toString());
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
				}
			}

		}
		} catch (CModelException e) {
			CUIPlugin.log(e);
		}
	}

	/**
	 * Appends the label for a variable declaration to a StringBuffer.
	 * @param var a variable declaration
	 * @param flags any of the F_* flags, and {@link CElementLabels#MF_POST_FILE_QUALIFIED}
	 */
	public void appendVariableLabel(IVariableDeclaration var, long flags ) {
		try {
		//return type
		if( getFlag( flags, CElementLabels.F_PRE_TYPE_SIGNATURE ) && var.exists()) {
			fBuffer.append( var.getTypeName() );
			fBuffer.append( ' ' );
		}
		
		//qualification
		if( getFlag( flags, CElementLabels.F_FULLY_QUALIFIED ) ){
			ICElement parent = var.getParent();
			if (parent != null && parent.exists() && parent.getElementType() == ICElement.C_NAMESPACE) {
				appendTypeLabel( parent, CElementLabels.T_FULLY_QUALIFIED);
				fBuffer.append( "::" ); //$NON-NLS-1$
			}
		}
		
		fBuffer.append( var.getElementName() );
		
		if( getFlag( flags, CElementLabels.F_APP_TYPE_SIGNATURE ) && var.exists()) {
			int offset= fBuffer.length();
			fBuffer.append( CElementLabels.DECL_STRING );
			fBuffer.append( var.getTypeName() );	
			if (getFlag(flags, CElementLabels.COLORIZE)) {
				fBuffer.setStyle(offset, fBuffer.length() - offset, DECORATIONS_STYLE);
			}
		}			
		
		// post qualification
		if( getFlag(flags, CElementLabels.F_POST_QUALIFIED)) {
			ICElement parent = var.getParent();
			if (parent != null && parent.exists() && parent.getElementType() == ICElement.C_NAMESPACE) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.CONCAT_STRING );
				appendTypeLabel( var.getParent(), CElementLabels.T_FULLY_QUALIFIED);
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, DECORATIONS_STYLE);
				}
			}
		}
		if( getFlag(flags, CElementLabels.MF_POST_FILE_QUALIFIED)) {
			int offset= fBuffer.length();
			IPath path= var.getPath();
			if (path != null) {
				fBuffer.append( CElementLabels.CONCAT_STRING );
				fBuffer.append(path.toString());
			}
			if (getFlag(flags, CElementLabels.COLORIZE)) {
				fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
			}
		}
		} catch (CModelException e) {
			CUIPlugin.log(e);
		}
	}

	/**
	 * Appends the label for an enumerator to a StringBuffer.
	 * @param var an enumerator
	 * @param flags any of the F_* flags, and {@link CElementLabels#MF_POST_FILE_QUALIFIED}
	 */
	public void appendEnumeratorLabel(IEnumerator var, long flags ) {
		//qualification
		if( getFlag( flags, CElementLabels.F_FULLY_QUALIFIED ) ){
			ICElement parent = var.getParent();
			if (parent != null && parent.exists() && parent.getElementType() == ICElement.C_NAMESPACE) {
				appendTypeLabel( parent, CElementLabels.T_FULLY_QUALIFIED);
				fBuffer.append( "::" ); //$NON-NLS-1$
			}
		}
		
		fBuffer.append( var.getElementName() );
						
		// post qualification
		if( getFlag(flags, CElementLabels.F_POST_QUALIFIED)) {
			ICElement parent = var.getParent();
			if (parent != null && parent.exists() && parent.getElementType() == ICElement.C_NAMESPACE) {
				fBuffer.append( CElementLabels.CONCAT_STRING );
				appendTypeLabel( var.getParent(), CElementLabels.T_FULLY_QUALIFIED);
			}
		}
		if( getFlag(flags, CElementLabels.MF_POST_FILE_QUALIFIED)) {
			int offset= fBuffer.length();
			IPath path= var.getPath();
			if (path != null) {
				fBuffer.append( CElementLabels.CONCAT_STRING );
				fBuffer.append(path.toString());
			}
			if (getFlag(flags, CElementLabels.COLORIZE)) {
				fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
			}
		}
	}

	/**
	 * Appends the label for a function declaration to a StringBuffer.
	 * @param func a function declaration
	 * @param flags any of the M_* flags, and {@link CElementLabels#MF_POST_FILE_QUALIFIED}
	 */
	public void appendFunctionLabel(IFunctionDeclaration func, long flags) {
		//return type
		if( getFlag( flags, CElementLabels.M_PRE_RETURNTYPE ) && func.exists()) {
			fBuffer.append( func.getReturnType() );
			fBuffer.append( ' ' );
		}
		
		//qualification
		if( getFlag( flags, CElementLabels.M_FULLY_QUALIFIED ) ){
			ICElement parent = func.getParent();
			if (parent != null && parent.exists() && parent.getElementType() == ICElement.C_NAMESPACE) {
				appendTypeLabel( parent, CElementLabels.T_FULLY_QUALIFIED);
				fBuffer.append( "::" ); //$NON-NLS-1$
			}
		}
		
		fBuffer.append( func.getElementName() );

		//template parameters
		if (func instanceof ITemplate) {
			appendTemplateParameters((ITemplate)func, flags);
		}

		//parameters
		if( getFlag( flags, CElementLabels.M_PARAMETER_TYPES ) ) {
			fBuffer.append('(');

			String[] types = func.getParameterTypes();
			
			if (types != null) {
				for (int i= 0; i < types.length; i++) {
					if (i > 0) {
						fBuffer.append( CElementLabels.COMMA_STRING );
					}
					fBuffer.append( types[i] );
				}
			}
			fBuffer.append(')');
		}
		
		//exceptions
		if( getFlag( flags, CElementLabels.M_EXCEPTIONS ) && func.exists() ){
			String [] types = func.getExceptions();
			if (types.length > 0) {
				fBuffer.append(" throw( "); //$NON-NLS-1$
				for (int i= 0; i < types.length; i++) {
					if (i > 0) {
						fBuffer.append(CElementLabels.COMMA_STRING);
					}
					fBuffer.append( types[i] );
				}
				fBuffer.append( " )" ); //$NON-NLS-1$
			}
		}
		
		if( getFlag( flags, CElementLabels.M_APP_RETURNTYPE ) && func.exists()) {
			final String typeName= func.getReturnType();
			if (typeName != null && typeName.length() > 0) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.DECL_STRING );
				fBuffer.append(typeName);
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, DECORATIONS_STYLE);
				}
			}
		}			
		
		// post qualification
		if( getFlag(flags, CElementLabels.M_POST_QUALIFIED)) {
			ICElement parent = func.getParent();
			if (parent != null && parent.exists() && parent.getElementType() == ICElement.C_NAMESPACE) {
				fBuffer.append( CElementLabels.CONCAT_STRING );
				appendTypeLabel( func.getParent(), CElementLabels.T_FULLY_QUALIFIED);
			}
		}
		if( getFlag(flags, CElementLabels.MF_POST_FILE_QUALIFIED)) {
			IPath path= func.getPath();
			if (path != null) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.CONCAT_STRING );
				fBuffer.append(path.toString());
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
				}
			}
		}
	}

	/**
	 * Appends the label for a type definition to a StringBuffer.
	 * @param typedef a type definition
	 * @param flags any of the F_* flags, and CElementLabels.MF_POST_FILE_QUALIFIED
	 */
	public void appendTypeDefLabel(ITypeDef typedef, long flags ) {
		// type
		if( getFlag( flags, CElementLabels.F_PRE_TYPE_SIGNATURE ) && typedef.exists()) {
			fBuffer.append( typedef.getTypeName() );
			fBuffer.append( ' ' );
		}
		
		//qualification
		if( getFlag( flags, CElementLabels.F_FULLY_QUALIFIED ) ){
			ICElement parent = typedef.getParent();
			if (parent != null && parent.exists() && parent.getElementType() == ICElement.C_NAMESPACE) {
				appendTypeLabel( parent, CElementLabels.T_FULLY_QUALIFIED);
				fBuffer.append( "::" ); //$NON-NLS-1$
			}
		}
		
		fBuffer.append( typedef.getElementName() );
				
		if( getFlag( flags, CElementLabels.F_APP_TYPE_SIGNATURE ) && typedef.exists()) {
			String typeName= typedef.getTypeName();
			if (typeName != null && typeName.length() > 0) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.DECL_STRING );
				fBuffer.append(typeName);
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, DECORATIONS_STYLE);
				}
			}
		}			
		
		// post qualification
		if( getFlag(flags, CElementLabels.F_POST_QUALIFIED)) {
			ICElement parent = typedef.getParent();
			if (parent != null && parent.exists() && parent.getElementType() == ICElement.C_NAMESPACE) {
				fBuffer.append( CElementLabels.CONCAT_STRING );
				appendTypeLabel( typedef.getParent(), CElementLabels.T_FULLY_QUALIFIED);
			}
		}
		if( getFlag(flags, CElementLabels.MF_POST_FILE_QUALIFIED)) {
			IPath path= typedef.getPath();
			if (path != null) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.CONCAT_STRING );
				fBuffer.append(path.toString());
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
				}
			}
		}
	}

	/**
	 * Appends the label for a source root to a StringBuffer.
	 * @param root a source root
	 * @param flags any of the ROOT_* flags, and PROJECT_POST_QUALIFIED
	 */
	public void getSourceRootLabel(ISourceRoot root, long flags) {
		appendFolderLabel(root, flags);
	}
	
	/**
	 * Appends the label for a container to a StringBuffer.
	 * @param container a container
	 * @param flags any of the ROOT_* flags, and PROJECT_POST_QUALIFIED
	 */
	public void appendContainerLabel(ICContainer container, long flags) {
		appendFolderLabel(container, flags);
	}

	private void appendFolderLabel(ICContainer container, long flags) {
		IResource resource= container.getResource();
		boolean rootQualified= getFlag(flags, CElementLabels.ROOT_QUALIFIED);
		if (rootQualified) {
			fBuffer.append(container.getPath().makeRelative().toString());
		} else {
			if (CCorePlugin.showSourceRootsAtTopOfProject()) {
				fBuffer.append(container.getElementName());
			}
			else {
				String elementName = container.getElementName();
				IPath path = new Path(elementName);
				fBuffer.append(path.lastSegment());
			}
			if (getFlag(flags, CElementLabels.ROOT_QUALIFIED)) {
				if (resource != null && container instanceof ISourceRoot && isReferenced((ISourceRoot)container)) {
					fBuffer.append(CElementLabels.CONCAT_STRING);
					fBuffer.append(resource.getProject().getName());
				} else {
					fBuffer.append(CElementLabels.CONCAT_STRING);
					fBuffer.append(container.getParent().getElementName());
				}
			}
		}
	}

	/**
	 * Appends the label for a translation unit to a StringBuffer.
	 * @param tu a translation unit
	 * @param flags any of the TU_* flags
	 */
	public  void appendTranslationUnitLabel(ITranslationUnit tu, long flags) {
		IResource r= tu.getResource();
		IPath path;
		if (r != null) {
			path= r.getFullPath().makeRelative();
		}
		else {
			path= tu.getPath();
		}
		
		if (path == null) {
			fBuffer.append(tu.getElementName());
		}
		else {
			if (getFlag(flags, CElementLabels.TU_QUALIFIED)) {
				fBuffer.append(path.toString());
			}
			else if (getFlag(flags, CElementLabels.TU_POST_QUALIFIED)) {
				fBuffer.append(path.lastSegment());
				fBuffer.append(CElementLabels.CONCAT_STRING);
				fBuffer.append(path.removeLastSegments(1).toString());
			}
			else {
				fBuffer.append(path.lastSegment());
			}
		}
	}

	/**
	 * Appends the label for a type to a StringBuffer.
	 * @param elem a type
	 * @param flags any of the T_* flags, and {@link CElementLabels#MF_POST_FILE_QUALIFIED}
	 */
	private void appendTypeLabel(ICElement elem, long flags) {
		if (getFlag(flags, CElementLabels.T_FULLY_QUALIFIED)) {
			ICElement parent= elem.getParent();
			boolean isQualifier= true;
			if (parent != null) {
				switch (parent.getElementType()) {
				case ICElement.C_ARCHIVE:
				case ICElement.C_BINARY:
				case ICElement.C_CCONTAINER:
				case ICElement.C_MODEL:
				case ICElement.C_PROJECT:
				case ICElement.C_UNIT:
				case ICElement.C_VCONTAINER:
					isQualifier= false;
					break;
				}
			}
			// types cannot be qualified in plain c
			if (isQualifier && !isCLanguage(parent)) {
				long qflags= flags & ~CElementLabels.MF_POST_FILE_QUALIFIED;
				appendTypeLabel(parent, qflags);
				fBuffer.append("::"); //$NON-NLS-1$
			}
		}
		
		String typeName= elem.getElementName();
		if (typeName.length() == 0) { // anonymous
		    typeName = CoreModelMessages.getString("CElementLabels.anonymous");	//$NON-NLS-1$
		}
		fBuffer.append(typeName);

		if (getFlag(flags, CElementLabels.T_INHERITANCE) && elem instanceof IInheritance) {
			IInheritance inheritance= (IInheritance)elem;
			String[] superclassNames= inheritance.getSuperClassesNames();
			if (superclassNames != null && superclassNames.length > 0) {
				fBuffer.append(CElementLabels.DECL_STRING);
				for (int i = 0; i < superclassNames.length; i++) {
					if (i> 0) {
						fBuffer.append(CElementLabels.COMMA_STRING);
					}
					String superclass = superclassNames[i];
					String visibility = getVisibility(inheritance.getSuperClassAccess(superclass));
					fBuffer.append(visibility).append(' ').append(superclass);
				}
			}
		}

		//template parameters
		if (elem instanceof ITemplate) {
			appendTemplateParameters((ITemplate)elem, flags);
		}
		
		if( getFlag(flags, CElementLabels.MF_POST_FILE_QUALIFIED)) {
			IPath path= elem.getPath();
			if (path != null) {
				int offset= fBuffer.length();
				fBuffer.append( CElementLabels.CONCAT_STRING );
				fBuffer.append(path.toString());
				if (getFlag(flags, CElementLabels.COLORIZE)) {
					fBuffer.setStyle(offset, fBuffer.length() - offset, QUALIFIER_STYLE);
				}
			}
		}
	}
	
	private static boolean isCLanguage(ICElement elem) {
		while (elem != null) {
			elem= elem.getParent();
			if (elem instanceof ITranslationUnit) {
				 return ((ITranslationUnit) elem).isCLanguage();
			}
		}
		return false;
	}

	/**
	 * Convert an <code>ASTAccessVisibility</code> into its string representation.
	 * 
	 * @param access
	 * @return "public", "protected" or "private"
	 */
	private static String getVisibility(ASTAccessVisibility access) {
		if (access == ASTAccessVisibility.PUBLIC) {
			return "public"; //$NON-NLS-1$
		}
		if (access == ASTAccessVisibility.PROTECTED) {
			return "protected"; //$NON-NLS-1$
		}
		return "private"; //$NON-NLS-1$
	}

	private static boolean getFlag(long flags, long flag) {
		return (flags & flag) != 0;
	}


	/**
	 * Returns the source root of <code>ICElement</code>. If the given
	 * element is already a source root, the element itself is returned.
	 */
	public static ISourceRoot getSourceRoot(ICElement element) {
		ICElement root = element;
		while (root != null) {
			if (root instanceof ISourceRoot)
				return (ISourceRoot)root;
			ICElement parent = root.getAncestor(ICElement.C_CCONTAINER);
			if (parent == root)
				return null;
			root = parent;
		}
		return null;
	}
	
	/**
	 * Returns <code>true</code> if the given source root is
	 * referenced. This means it is own by a different project but is referenced
	 * by the root's parent. Returns <code>false</code> if the given root
	 * doesn't have an underlying resource.
	 */
	public static boolean isReferenced(ISourceRoot root) {
		IResource resource= root.getResource();
		if (resource != null) {
			IProject project= resource.getProject();
			IProject container= root.getCProject().getProject();
			return !container.equals(project);
		}
		return false;
	}
}
