package org.etsi.t3q.visitor;

import java.io.BufferedReader;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.etsi.common.MiscTools;
import org.etsi.common.logging.LoggingInterface;
import org.etsi.common.logging.LoggingInterface.MessageClass;
import org.etsi.t3q.T3Q;
import org.etsi.t3q.config.QualityCheckProfile;

import antlr.collections.AST;


import de.ugoe.cs.swe.trex.core.analyzer.astutil.ReferenceFinder;
import de.ugoe.cs.swe.trex.core.analyzer.astutil.ReferenceFinderWithCache;
import de.ugoe.cs.swe.trex.core.analyzer.astutil.ReferenceWithContext;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.ASTUtil;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.LocationAST;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.TTCN3ParserTokenTypes;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.symboltable.AltstepSymbol;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.symboltable.FunctionSymbol;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.symboltable.Symbol;


public class QualityChecker {
	
	private LoggingInterface logger = null;
	
	protected static final int LEVEL0 = 0;
	protected T3QVisitor visitor = null;
	ModuleContentsChecker moduleContentsChecker = new ModuleContentsChecker();

	//TODO: consider unified approach to statistics
	//TODO: consider extracting the whole identifier management concept
	private int totalIdentifierCount = 0;
	private int repeatedIdentifierCount = 0;
	private HashMap<String, ArrayList<LocationAST>> moduleLevelIdentifiersMap = new HashMap<String, ArrayList<LocationAST>>();
	private ArrayList<LocationAST> subsequentLogStatementNodes = new ArrayList<LocationAST>();
	private Stack<LocationAST> cyclicFunctionReferences = new Stack<LocationAST>();

	private ReferenceFinder referenceFinder;
	
	
	public QualityChecker(T3QVisitor visitor) {
		this.visitor = visitor;
		this.logger = new LoggingInterface(T3Q.activeProfile.getLoggingConfiguration());
		this.logger.setMaximumLogLevel(T3Q.getLogLevel());
		this.setReferenceFinder(new ReferenceFinderWithCache());
	}
	
	public LoggingInterface getLoggingInterface(){
		return this.logger;
	}
	
	// -------------------------------------------------------------------------------------------
	// This should serve as a base template - simply copy it with the
	// appropriate name and change its contents
	public void qualityCheckTemplate(LocationAST node) {
		// Not explicitly necessary, for consistency only
		boolean problemOccured = false;
		problemOccured = true;
		if (problemOccured) {
			String message = "<message>";
			this.getLoggingInterface().logWarning(node.getLine(), node.getEndLine(), MessageClass.UNIVERSAL, message);

		}
	}

	// -------------------------------------------------------------------------------------------

	private void addModuleLevelIdentifier(LocationAST identifierNode){
		String identifierText = identifierNode.getFirstChild().getText();
		if (!moduleLevelIdentifiersMap.containsKey(identifierText)){
			moduleLevelIdentifiersMap.put(identifierText, new ArrayList<LocationAST>());
		}
		moduleLevelIdentifiersMap.get(identifierText).add(identifierNode);
	}
		
	// -------------------------------------------------------------------------------------------

	// TODO: move to LocationAST?
	// currently not used, it was supposed to replace the default equals method
	// due to an error, but the error has been since fixed
	private boolean isNodeEqualToNode(LocationAST node1, LocationAST node2) {
		boolean nodesEqual = false;
//		if ((node1.getType() == node2.getType())
//				&& (node1.getOffset() == node2.getOffset())) {
		if (node1 == node2){
			nodesEqual = true;
		}

		return nodesEqual;
	}

	// -------------------------------------------------------------------------------------------

	public void stringLevelChecks(String filename) {
		// TODO: document features
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
			String line = null;
			int lineNumber = 0;
			while ((line = br.readLine()) != null) {
				lineNumber++;
				if (T3Q.activeProfile.isCheckNoTabs()) {
					if (line.contains("\t")) {
						this.getLoggingInterface().logWarning(lineNumber,
								lineNumber,
								MessageClass.STYLE,
								"Line contains a tab character!",
								"at column " + line.indexOf("\t") + "");
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// -------------------------------------------------------------------------------------------
	
	private boolean isNodeWithinGroup(LocationAST startNode,
			LocationAST targetGroupNode) {
		boolean isNodeWithinGroup = false;
		LocationAST groupDef = LocationAST.resolveParentsUntilType(startNode
				.getParent(), TTCN3ParserTokenTypes.GroupDef);
		if (groupDef != null) {
			if (groupDef.equals(targetGroupNode)) {
				isNodeWithinGroup = true;
			} else {
				isNodeWithinGroup = isNodeWithinGroup(groupDef, targetGroupNode);
			}
		}

		return isNodeWithinGroup;
	}

	// -------------------------------------------------------------------------------------------

	// Return value is boolean - true if function is used to start a behavior,
	// false otherwise
	private boolean isFunctionStartsBehavior(
			LocationAST functionDefIdentifierNode) {
		boolean startsBehavior = true;
		Symbol functionDefSymbol = functionDefIdentifierNode.getSymbol();

		Map<String, List<ReferenceWithContext>> referenceMap = getReferenceFinder()
				.findReferences(functionDefSymbol);
		if (referenceMap.isEmpty()) {
			startsBehavior = false;
		} else {
			Iterator<Entry<String, List<ReferenceWithContext>>> referenceMapIterator = referenceMap
					.entrySet().iterator();

			while (referenceMapIterator.hasNext()) {
				Entry<String, List<ReferenceWithContext>> referenceEntry = referenceMapIterator
						.next();
				Iterator<ReferenceWithContext> referenceIterator = referenceEntry
						.getValue().iterator();
				while (referenceIterator.hasNext()) {
					ReferenceWithContext referenceWithContext = referenceIterator
							.next();
					LocationAST referenceNode = referenceWithContext
							.getReferenceNode();
					//TODO: review getNthParent calls below!!!
					if (referenceNode.getNthParent(4).getType() != TTCN3ParserTokenTypes.StartTCStatement) {
						startsBehavior = false;
					} else {
						LocationAST startingEntityIdentifierNode = (LocationAST) ASTUtil
								.findChild(referenceNode.getNthParent(4),
										TTCN3ParserTokenTypes.Identifier)
								.getFirstChild();

						Symbol startingEntitySymbol = startingEntityIdentifierNode.getSymbol();
						if (startingEntitySymbol.getDeclarationNode()
								.getNthParent(3).getType() == TTCN3ParserTokenTypes.TimerInstance) {
							startsBehavior = false;
						}
					}
				}
			}
		}
		return startsBehavior;
	}

	// -------------------------------------------------------------------------------------------

	private boolean isReferenceWithinImport(LocationAST referenceNode){
		boolean isReferenceWithinImport = false;
		if (LocationAST.resolveParentsUntilType(referenceNode, TTCN3ParserTokenTypes.ImportDef) != null){
			isReferenceWithinImport = true;
		}
		return isReferenceWithinImport;
	}

	// -------------------------------------------------------------------------------------------

	private boolean isReferenceWithinModule(
			LocationAST referenceNode, LocationAST moduleNode) {
		boolean isReferenceWithinModule = false;
		
		LocationAST containingModuleNode = ASTUtil.getModuleNode(referenceNode);
		if (isNodeEqualToNode(moduleNode, containingModuleNode)){
			isReferenceWithinModule = true;
		}
		return isReferenceWithinModule;
	}

	
	// -------------------------------------------------------------------------------------------

	private boolean isReferenceWithinModuleDefinition(
			LocationAST referenceNode, LocationAST moduleDefinitionNode) {
		boolean isReferenceWithinModuleDefinition = false;
		LocationAST containingModuleDefinitionNode = LocationAST.resolveParentsUntilType(referenceNode, TTCN3ParserTokenTypes.ModuleDefinition);
		if (isNodeEqualToNode(moduleDefinitionNode, containingModuleDefinitionNode)){
			isReferenceWithinModuleDefinition = true;
		}
		return isReferenceWithinModuleDefinition;
	}

	// -------------------------------------------------------------------------------------------

	// TODO: move to Symbol or LocationAST
	private int getReferenceCountWithinModule(LocationAST identifierNode,
			LocationAST moduleNode) {
		int referencesWithinCurrentModule = 0;
		Symbol definitionSymbol = identifierNode
		.getFirstChild().getSymbol();

		if (definitionSymbol != null) {
			ArrayList<Symbol> relevantSymbols = new ArrayList<Symbol>();
			relevantSymbols.add(definitionSymbol);
			LocationAST moduleDefinitionNode = LocationAST.resolveParentsUntilType(definitionSymbol.getDeclarationNode(), TTCN3ParserTokenTypes.ModuleDefinition);
			LocationAST moduleDefinitionTypeNode = LocationAST.getModuleDefinitionTypeNode(moduleDefinitionNode);
			if (moduleDefinitionTypeNode.getType()==TTCN3ParserTokenTypes.TypeDef && ASTUtil.findChild(moduleDefinitionTypeNode, TTCN3ParserTokenTypes.EnumDef)!=null) {
				relevantSymbols.addAll(definitionSymbol.getScope().getSymbols().values());
			}
			
			Map<String, List<ReferenceWithContext>> referenceMap = getReferenceFinder()
					.findReferences(relevantSymbols);
			Iterator<List<ReferenceWithContext>> referenceMapIterator = referenceMap
					.values().iterator();
			while (referenceMapIterator.hasNext()) {
				Iterator<ReferenceWithContext> referenceIterator = referenceMapIterator
						.next().iterator();
				while (referenceIterator.hasNext()) {

					ReferenceWithContext referenceWithContext = referenceIterator
							.next();
					LocationAST referenceNode = referenceWithContext
							.getReferenceNode();

					if (!isReferenceWithinImport(referenceNode)) {
						if (isReferenceWithinModule(referenceNode, moduleNode)) {
							referencesWithinCurrentModule++;
						}
					}
				}
			}
		} else {
			//TODO: need handling
		}

		return referencesWithinCurrentModule;
	}

	// -------------------------------------------------------------------------------------------

	//TODO: move to Symbol or LocationAST
	private int getReferenceCountWithinModuleOrGroup(ArrayList<LocationAST> moduleOrGroupLevelIdentifiers, LocationAST moduleNode) {
		int referencesWithinCurrentModuleOrGroup = 0;

		for (LocationAST importedModuleDefinitionIdentifier : moduleOrGroupLevelIdentifiers){
			referencesWithinCurrentModuleOrGroup += getReferenceCountWithinModule(
						importedModuleDefinitionIdentifier, moduleNode);
		}
		return referencesWithinCurrentModuleOrGroup;
	}
	
	// -------------------------------------------------------------------------------------------
	//TODO: may be deprecated
	private boolean isIdentifierWithinAllWithExcept(LocationAST identifierNode) {
		boolean isIdentifierWithinAllWithExcept = false;
		int[] exceptTypeNodes = {
				TTCN3ParserTokenTypes.AllAltstepsWithExcept,
				TTCN3ParserTokenTypes.AllTestcasesWithExcept,
				TTCN3ParserTokenTypes.AllConstsWithExcept,
				TTCN3ParserTokenTypes.AllFunctionsWithExcept,
				TTCN3ParserTokenTypes.AllModuleParWithExcept,
				TTCN3ParserTokenTypes.AllSignaturesWithExcept,
				TTCN3ParserTokenTypes.AllTemplsWithExcept,
				TTCN3ParserTokenTypes.AllTypesWithExcept,
				TTCN3ParserTokenTypes.AllGroupsWithExcept}; 
		if (LocationAST.resolveParentsUntilType(identifierNode, exceptTypeNodes) != null){
			isIdentifierWithinAllWithExcept = true;
		} 
		return isIdentifierWithinAllWithExcept;
	}
	
	// -------------------------------------------------------------------------------------------

	/**
	 * @param importElementNode - import element node
	 * @return true if the import element is non-specific (has an all keyword), false otherwise
	 */
	
	private boolean isImportElementNonSpecific(LocationAST importElementNode) {
		boolean isImportElementNonSpecific = false;
		List<Integer> nodeTypes =  Arrays.asList(
				TTCN3ParserTokenTypes.AllAltstepsWithExcept,
				TTCN3ParserTokenTypes.AllTestcasesWithExcept,
				TTCN3ParserTokenTypes.AllConstsWithExcept,
				TTCN3ParserTokenTypes.AllFunctionsWithExcept,
				TTCN3ParserTokenTypes.AllModuleParWithExcept,
				TTCN3ParserTokenTypes.AllSignaturesWithExcept,
				TTCN3ParserTokenTypes.AllTemplsWithExcept,
				TTCN3ParserTokenTypes.AllTypesWithExcept,
				TTCN3ParserTokenTypes.AllGroupsWithExcept); 
		
		if (nodeTypes.contains(importElementNode.getNthChild(2).getType())){
			isImportElementNonSpecific = true;
		} 
		return isImportElementNonSpecific;
	}
	
	// -------------------------------------------------------------------------------------------

	/**
	 * @param importElementNode - import element node
	 * @return type of the corresponding module definition
	 */
	
	private int getCorrespondingModuleDefinitionType(LocationAST importElementNode) {
		//TODO: duplicated, sort out
		int correspondingType = 0;
		switch (importElementNode.getNthChild(2).getType()) {
		case TTCN3ParserTokenTypes.AllAltstepsWithExcept:
		case TTCN3ParserTokenTypes.AltstepRefList:
			correspondingType = TTCN3ParserTokenTypes.AltstepDef;
			break;
		case TTCN3ParserTokenTypes.AllTestcasesWithExcept:
		case TTCN3ParserTokenTypes.TestcaseRefList:
			correspondingType = TTCN3ParserTokenTypes.TestcaseDef;
			break;
		case TTCN3ParserTokenTypes.AllConstsWithExcept:
		case TTCN3ParserTokenTypes.ConstRefList:
			correspondingType = TTCN3ParserTokenTypes.ConstDef;
			break;
		case TTCN3ParserTokenTypes.AllFunctionsWithExcept:
		case TTCN3ParserTokenTypes.FunctionRefList:
			correspondingType = TTCN3ParserTokenTypes.FunctionDef;
			break;
		case TTCN3ParserTokenTypes.AllModuleParWithExcept:
		case TTCN3ParserTokenTypes.ModuleParRefList:
			correspondingType = TTCN3ParserTokenTypes.ModuleParDef;
			break;
		case TTCN3ParserTokenTypes.AllSignaturesWithExcept:
		case TTCN3ParserTokenTypes.SignatureRefList:
			correspondingType = TTCN3ParserTokenTypes.SignatureDef;
			break;
		case TTCN3ParserTokenTypes.AllTemplsWithExcept:
		case TTCN3ParserTokenTypes.TemplateRefList:
			correspondingType = TTCN3ParserTokenTypes.TemplateDef;
			break;
		case TTCN3ParserTokenTypes.AllTypesWithExcept:
		case TTCN3ParserTokenTypes.TypeRefList:
			correspondingType = TTCN3ParserTokenTypes.TypeDef;
			break;
		case TTCN3ParserTokenTypes.AllGroupsWithExcept:
		case TTCN3ParserTokenTypes.GroupRefListWithExcept:
			correspondingType = TTCN3ParserTokenTypes.GroupDef;
			break;
		default:
			break;
		}
		return correspondingType;
	}

	// -------------------------------------------------------------------------------------------

	public void checkNoUnusedLocalDefinitions(LocationAST node) {
		ArrayList<LocationAST> localDefinitionsIdentifiersList = LocationAST.getLocalDefinitionsIdentifiersList(node);
		if (localDefinitionsIdentifiersList == null) {
			return; //irrelevant type of module definition!
		}
		
		String moduleDefinitionIdentifierText;
		if (node.getType()!=TTCN3ParserTokenTypes.ModuleDefinition) {
			moduleDefinitionIdentifierText = "Module Control Part";
		} else {
			moduleDefinitionIdentifierText = LocationAST.getModuleDefinitionIdentifiersList(node).get(0).getFirstChild().getText();
		}
		for (LocationAST localDefinitionIdentifier : localDefinitionsIdentifiersList) {
			Symbol symbol = localDefinitionIdentifier.getFirstChild().getSymbol();
			if (getReferenceFinder().countReferences(symbol)==0) {
				this.getLoggingInterface().logWarning(localDefinitionIdentifier.getLine(),
						localDefinitionIdentifier.getEndLine(),
						MessageClass.STYLE,
						"Local definition for \""+localDefinitionIdentifier.getFirstChild().getText()+"\" in definition of \""+moduleDefinitionIdentifierText+"\" is never used!",
						"6.11, " + MiscTools.getMethodName());
			}
		}
	}
	
	// -------------------------------------------------------------------------------------------

	public void checkNoLiterals(LocationAST node) {
		// TODO: matching symbols too? boolean values too? verdicts, omits,
		// enums and addresses too?
		boolean problemOccured = false;
		switch (node.getFirstChild().getType()) {
		case TTCN3ParserTokenTypes.BitStringValue:
		case TTCN3ParserTokenTypes.CharStringValue:
		case TTCN3ParserTokenTypes.IntegerValue:
		case TTCN3ParserTokenTypes.OctetStringValue:
		case TTCN3ParserTokenTypes.HexStringValue:
		case TTCN3ParserTokenTypes.FloatValue:
			// TODO: what about inline templates?
			LocationAST containingModuleDefinition = LocationAST.resolveParentsUntilType(node,
					new int[] { TTCN3ParserTokenTypes.ModuleParDef,
							TTCN3ParserTokenTypes.TemplateDef,
							TTCN3ParserTokenTypes.ConstDef });
			if (containingModuleDefinition == null) {
				problemOccured = true;
			}
		}
		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(),
					node.getEndLine(),
					MessageClass.STYLE,
					"Literal value is used!",
					"6.12, " + MiscTools.getMethodName());
		}

	}
	// -------------------------------------------------------------------------------------------

	public void checkNoUnusedFormalParameters(LocationAST node) {
		if (LocationAST.resolveParentsUntilType(node, TTCN3ParserTokenTypes.ExtFunctionDef)!=null) {
			return;
		}

		LocationAST identifierNode = (LocationAST) ASTUtil.findSibling(node.getFirstChild(), TTCN3ParserTokenTypes.Identifier);

		//TODO: extract to a separate method
		LocationAST TemplateDefNode = LocationAST.resolveParentsUntilType(node, TTCN3ParserTokenTypes.TemplateDef);
		if (TemplateDefNode != null) {
			LocationAST derivedDefNode = ASTUtil.findDerivedDef(TemplateDefNode);
			if (derivedDefNode!=null) {
				Symbol symbol = derivedDefNode.getFirstChild().getFirstChild().getSymbol();
				if (symbol == null) {
					this.getLoggingInterface().logInformation(node.getLine(),
							node.getEndLine(),
							MessageClass.LOGGING,
							"Template definition for \""
									+ derivedDefNode.getFirstChild().getFirstChild().getText()
									+ "\" cannot be resolved! It cannot be determined whether formal parameter \""+identifierNode.getFirstChild().getText()+"\" is used or not!",
							"6.11, " + MiscTools.getMethodName());
					//SKIP
					return;
				} else {
					LocationAST declarationNode = symbol.getDeclarationNode();
					List<AST> inheritedFormalParameters = ASTUtil.getFormalParameters(LocationAST.resolveParentsUntilType(declarationNode, TTCN3ParserTokenTypes.TemplateDef));
					if (inheritedFormalParameters != null) {
						for (AST inheritedFormalParameter : inheritedFormalParameters) {
							if (inheritedFormalParameter.getText().equals(identifierNode.getFirstChild().getText())) {
								//SKIP
								return;
							}
						}
					}
				}
			}
		}
		
		Symbol formalParSymbol = identifierNode.getFirstChild().getSymbol();
		int referenceCount = 0;
		referenceCount = getReferenceFinder().countReferences(formalParSymbol);
		if (referenceCount == 0) {
			String containingModuleDefinitionName = node.getContainingModuleDefinitionIdentifiersList().get(0).getFirstChild().getText();
			this.getLoggingInterface().logWarning(identifierNode.getLine(),
					identifierNode.getEndLine(),
					MessageClass.STYLE,
					"Formal parameter \""
							+ identifierNode.getFirstChild().getText()
							+ "\" in definition for \""
							+ containingModuleDefinitionName
							+ "\" is never used!",
					"6.11, " + MiscTools.getMethodName());
		}

	}

	// -------------------------------------------------------------------------------------------
	//TODO duplicated
	public void listImportingModuleNames(LocationAST node) {
		LocationAST moduleIdentifier = node.getNthChild(3);
		Symbol moduleSymbol = moduleIdentifier.getFirstChild().getSymbol();

		Map<String, List<ReferenceWithContext>> referenceMap = getReferenceFinder().findReferences(moduleSymbol);
		Iterator<List<ReferenceWithContext>> referenceMapIterator = referenceMap.values().iterator();
		while (referenceMapIterator.hasNext()) {
			Iterator<ReferenceWithContext> referenceIterator = referenceMapIterator.next().iterator();
			while (referenceIterator.hasNext()) {
				LocationAST referenceNode = referenceIterator.next().getReferenceNode();
				if (isReferenceWithinImport(referenceNode)) {
					this.getLoggingInterface().logInformation(moduleIdentifier.getLine(),
							moduleIdentifier.getEndLine(),
							MessageClass.GENERAL,
							"Module \""
									+ moduleIdentifier.getFirstChild().getText()
									+ "\" is imported in module \""
									+ ASTUtil.getEnclosingModuleName(referenceNode)
									+ "\"!",
							"1.19, " + MiscTools.getMethodName());
				}
			}
		}
	}

	// -------------------------------------------------------------------------------------------
	//TODO: duplicated
	public void listImportingModuleFileNames(LocationAST node) {
		LocationAST moduleIdentifier = node.getNthChild(3);
		Symbol moduleSymbol = moduleIdentifier.getFirstChild().getSymbol();

		Map<String, List<ReferenceWithContext>> referenceMap = getReferenceFinder().findReferences(moduleSymbol);
		Iterator<List<ReferenceWithContext>> referenceMapIterator = referenceMap.values().iterator();
		while (referenceMapIterator.hasNext()) {
			Iterator<ReferenceWithContext> referenceIterator = referenceMapIterator.next().iterator();
			while (referenceIterator.hasNext()) {
				LocationAST referenceNode = referenceIterator.next().getReferenceNode();
				if (isReferenceWithinImport(referenceNode)) {
					this.getLoggingInterface().logInformation(moduleIdentifier.getLine(),
							moduleIdentifier.getEndLine(),
							MessageClass.GENERAL,
							"Module \""
									+ moduleIdentifier.getFirstChild().getText()
									+ "\" is imported in module \""
									+ ASTUtil.getEnclosingModuleName(referenceNode)
									+ "\" located in \""
									+ referenceNode.getScope().getAssociatedFilename()
									+ "\"!",
							"1.19, " + MiscTools.getMethodName());
				}
			}
		}
	}

	
	// -------------------------------------------------------------------------------------------

	public void listImportedModuleNames(LocationAST node) {
		LocationAST importedModuleIdentifier = node.getNthChild(4);
		this.getLoggingInterface().logInformation(importedModuleIdentifier.getLine(),
				importedModuleIdentifier.getEndLine(),
				MessageClass.GENERAL,
				"Importing from module \""
						+ importedModuleIdentifier.getFirstChild().getText()
						+ "\"...",
				"1.18, " + MiscTools.getMethodName());
	}

	// -------------------------------------------------------------------------------------------

	public void listImportedModuleFileNames(LocationAST node) {
		LocationAST importedModuleIdentifier = node.getNthChild(4);
		Symbol importedModuleSymbol = importedModuleIdentifier.getFirstChild().getSymbol();
		if (importedModuleSymbol == null) {
			// resolvable?
			this.getLoggingInterface().logInformation(importedModuleIdentifier.getLine(),
					importedModuleIdentifier.getEndLine(),
					MessageClass.GENERAL,
					"Imported module \""
							+ importedModuleIdentifier.getFirstChild().getText()
							+ "\" cannot be resolved!",
					"1.18, " + MiscTools.getMethodName());
			return;
		}

		this.getLoggingInterface().logInformation(importedModuleIdentifier.getLine(),
				importedModuleIdentifier.getEndLine(),
				MessageClass.GENERAL,
				"Importing from module \""
						+ importedModuleIdentifier.getFirstChild().getText()
						+ "\" located in \""
						+ importedModuleSymbol.getScope().getAssociatedFilename()
						+ "\"...",
				"1.18, " + MiscTools.getMethodName());
	}

	
	// -------------------------------------------------------------------------------------------
	
	public void checkNoUnusedImports(LocationAST node) {
		// TODO: Consider splitting further
		LocationAST importedModuleIdentifier = (LocationAST) ASTUtil.findChild(node,
				TTCN3ParserTokenTypes.Identifier);
		Symbol importedModuleSymbol = importedModuleIdentifier.getFirstChild().getSymbol();
		if (importedModuleSymbol == null) {
			// resolvable?
			this.getLoggingInterface().logInformation(importedModuleIdentifier.getLine(),
					importedModuleIdentifier.getEndLine(),
					MessageClass.STYLE,
					"Imported module \""
							+ importedModuleIdentifier.getFirstChild().getText()
							+ "\" cannot be resolved!",
					"6.18, " + MiscTools.getMethodName());
			return;
		}

		LocationAST currentModuleNode = ASTUtil.getModuleNode(node);
		LocationAST importedModuleNode = ASTUtil.getModuleNode(importedModuleSymbol.getDeclarationNode());

		LocationAST importSpecNode = node.getFirstChild().getNextSibling();
		if (importSpecNode.getType() == TTCN3ParserTokenTypes.AllWithExcepts) {
			// any reference?
			ArrayList<LocationAST> moduleLevelIdentifiers = LocationAST.getModuleOrGroupLevelIdentifiers(importedModuleNode);

			int referencesWithinCurrentModule = getReferenceCountWithinModuleOrGroup(moduleLevelIdentifiers,
					currentModuleNode);

			if (referencesWithinCurrentModule == 0) {
				this.getLoggingInterface().logWarning(importedModuleIdentifier.getLine(),
						importedModuleIdentifier.getEndLine(),
						MessageClass.STYLE,
						"No definitions from imported module \""
								+ importedModuleIdentifier.getFirstChild().getText()
								+ "\" are ever used!",
						"6.18, " + MiscTools.getMethodName());
			}

		} else if (importSpecNode.getType() == TTCN3ParserTokenTypes.ImportSpec) {
			LinkedList<LocationAST> importElementNodes = ASTUtil.findTypeNodes(importSpecNode,
					TTCN3ParserTokenTypes.ImportElement);
			for (LocationAST importElementNode : importElementNodes) {

				if (isImportElementNonSpecific(importElementNode)) {
					// generic
					// any of type referenced?
					ArrayList<LocationAST> moduleLevelIdentifiers = LocationAST.getModuleLevelIdentifiersOfType(importedModuleNode,
							getCorrespondingModuleDefinitionType(importElementNode));
					int referencesWithinCurrentModule = 0;
					if (importElementNode.getFirstChild().getType() != TTCN3ParserTokenTypes.ImportGroupSpec) {
						referencesWithinCurrentModule = getReferenceCountWithinModuleOrGroup(moduleLevelIdentifiers,
								currentModuleNode);

					} else {
						// group handling
						for (LocationAST importedGroupIdentifer : moduleLevelIdentifiers) {
							ArrayList<LocationAST> groupLevelIdentifiers = LocationAST.getModuleOrGroupLevelIdentifiers(LocationAST.resolveParentsUntilType(importedGroupIdentifer,
									TTCN3ParserTokenTypes.GroupDef));
							referencesWithinCurrentModule += getReferenceCountWithinModuleOrGroup(groupLevelIdentifiers,
									currentModuleNode);
						}
					}
					if (referencesWithinCurrentModule == 0) {
						this.getLoggingInterface().logWarning(importedModuleIdentifier.getLine(),
								importedModuleIdentifier.getEndLine(),
								MessageClass.STYLE,
								"No definitions of type \""
										+ LocationAST.getTTCN3ParserTokenTypeTypePrettyName(getCorrespondingModuleDefinitionType(importElementNode))
										+ "\" from imported module \""
										+ importedModuleIdentifier.getFirstChild().getText()
										+ "\" are ever used!",
								"6.18, " + MiscTools.getMethodName());
					}

				} else {
					// specific
					LinkedList<LocationAST> identifierNodes = ASTUtil.findTypeNodes(importElementNode,
							TTCN3ParserTokenTypes.Identifier);
					for (LocationAST identifierNode : identifierNodes) {
						Symbol s = identifierNode.getFirstChild().getSymbol();
						if (s == null) {
							// identifier not resolvable?
							this.getLoggingInterface().logInformation(identifierNode.getLine(),
									identifierNode.getEndLine(),
									MessageClass.STYLE,
									"Definition for \""
											+ identifierNode.getFirstChild().getText()
											+ "\" of type \""
											+ LocationAST.getTTCN3ParserTokenTypeTypePrettyName(getCorrespondingModuleDefinitionType(importElementNode))
											+ "\" from imported module \""
											+ importedModuleIdentifier.getFirstChild().getText()
											+ "\" cannot be resolved!",
									"6.18, " + MiscTools.getMethodName());
						} else {
							if (!isNodeEqualToNode(ASTUtil.getModuleNode(importedModuleSymbol.getDeclarationNode()),
									ASTUtil.getModuleNode(s.getDeclarationNode()))) {
								// identifier already imported?
								// (declarationNode.module != importedModule)
								// this should happen only if the declaration
								// actually exists in the imported module
								ArrayList<LocationAST> moduleLevelIdentifiers = LocationAST.getModuleLevelIdentifiersOfType(importedModuleNode,
										getCorrespondingModuleDefinitionType(importElementNode));
								boolean importedModuleContainsIdentifierInQuestion = false;
								for (LocationAST moduleLevelIdentifier : moduleLevelIdentifiers) {
									if (moduleLevelIdentifier.getFirstChild().getText().equals(identifierNode.getFirstChild().getText())) {
										importedModuleContainsIdentifierInQuestion = true;
									}
								}
								if (!importedModuleContainsIdentifierInQuestion) {
									this.getLoggingInterface().logInformation(identifierNode.getLine(),
											identifierNode.getEndLine(),
											MessageClass.STYLE,
											"Imported definition for "
													+ identifierNode.getFirstChild().getText()
													+ " is not found in corresponding imported module \""
													+ ASTUtil.getEnclosingModuleName(importedModuleNode)
													+ "\", but it is already imported from module \""
													+ ASTUtil.getEnclosingModuleName(s.getDeclarationNode())
													+ "\"!",
											"6.18, "
													+ MiscTools.getMethodName());
								} else {
									this.getLoggingInterface().logInformation(identifierNode.getLine(),
											identifierNode.getEndLine(),
											MessageClass.STYLE,
											"Imported definition for "
													+ identifierNode.getFirstChild().getText()
													+ " is ambiguous (already imported from module \""
													+ ASTUtil.getEnclosingModuleName(s.getDeclarationNode())
													+ "\")!",
											"6.18, "
													+ MiscTools.getMethodName());
								}
							} else {
								// identifier referenced?
								if (identifierNode.getParent().getType() != TTCN3ParserTokenTypes.FullGroupIdentifier) {
									int referencesWithinCurrentModule = getReferenceCountWithinModule(identifierNode,
											currentModuleNode);
									if (referencesWithinCurrentModule == 0) {
										this.getLoggingInterface().logWarning(identifierNode.getLine(),
												identifierNode.getEndLine(),
												MessageClass.STYLE,
												"Imported definition for "
														+ identifierNode.getFirstChild().getText()
														+ " is never used!",
												"6.18, "
														+ MiscTools.getMethodName());
									}
								} else {
									// any reference of grouped elements
									// check if it is a nested group import
									if ((identifierNode.getNextSibling() == null)) {
										LocationAST importedGroupDeclarationNode = s.getDeclarationNode();
										// System.out.println();

										ArrayList<LocationAST> identifiersWithinGroup = LocationAST.getModuleOrGroupLevelIdentifiers(LocationAST.resolveParentsUntilType(importedGroupDeclarationNode,
												TTCN3ParserTokenTypes.GroupDef));
										// importedGroupDeclarationNode.getNthParent(2));
										int referencesWithinCurrentModule = getReferenceCountWithinModuleOrGroup(identifiersWithinGroup,
												currentModuleNode);

										if (referencesWithinCurrentModule == 0) {
											this.getLoggingInterface().logWarning(importedModuleIdentifier.getLine(),
													importedModuleIdentifier.getEndLine(),
													MessageClass.STYLE,
													"No definitions from imported group \""
															+ identifierNode.getFirstChild().getText()
															+ "\" from module \""
															+ importedModuleIdentifier.getFirstChild().getText()
															+ "\" are ever used!",
													"6.18, "
															+ MiscTools.getMethodName());
										}
									}

								}
							}
						}
					}
				}
			}
		}

	}

	// -------------------------------------------------------------------------------------------

	public void checkNoOverSpecificRunsOnClauses(LocationAST node) {
		if (node.getParent().getType()==TTCN3ParserTokenTypes.ConfigSpec){
			//skip testcases from check
			return;
		}
		
		//TODO: adapt to new paradigm
		int componentElementReferencesWithinScope = 0;
		LocationAST componentIdentifier = node.getNthChild(3);
		Symbol componentSymbol = componentIdentifier.getSymbol();
		if (componentSymbol == null) {
			this.getLoggingInterface().logInformation(componentIdentifier.getLine(),
					componentIdentifier.getEndLine(),
					MessageClass.STYLE,
					"Definition for \""
							+ componentIdentifier.getText()
							+ "\" cannot be resolved, therefore it cannot be determined whether an over specific runs on clause is used in the definition of \""
							+ node.getContainingModuleDefinitionIdentifiersList().get(0).getFirstChild().getText()
							+ "\"!",
					"6.13, " + MiscTools.getMethodName());
			return;
		}
		LocationAST componentDeclarationNode = componentSymbol.getDeclarationNode();
		LocationAST componentModuleDefinitionNode = LocationAST.resolveParentsUntilType(componentDeclarationNode,
				TTCN3ParserTokenTypes.ModuleDefinition);
		if (componentModuleDefinitionNode == null) {
			//TODO: need to output info? likely to happen at all?
			return;
		}

		ArrayList<Symbol> localDefinitionsSymbolsList = new ArrayList<Symbol>();
		ArrayList<LocationAST> localDefinitionsIdentifiersList = LocationAST.getLocalDefinitionsIdentifiersList(componentModuleDefinitionNode);
		if (localDefinitionsIdentifiersList==null) {
			this.getLoggingInterface()
				.logInformation(componentIdentifier.getLine(),
						componentIdentifier.getEndLine(),
						MessageClass.STYLE,
						"Reference to \""
								+ componentIdentifier.getText()
								+ "\" in a runs on clause does not resolve to a component type definition! It could be an error!",
						"6.13, " + MiscTools.getMethodName());
			return; //componentIdentifierNode does not correspond to a component definition
		}
		for (LocationAST localDefinitionIdentifier : localDefinitionsIdentifiersList) {
			Symbol s = localDefinitionIdentifier.getFirstChild().getSymbol();
			if (s == null) {
				this.getLoggingInterface().logFix(localDefinitionIdentifier.getLine(),
						localDefinitionIdentifier.getLine(),
						MessageClass.STYLE,
						"Symbol resolution failed for "
								+ localDefinitionIdentifier.getFirstChild().getText(),
						"6.13, " + MiscTools.getMethodName());
			} else {
				localDefinitionsSymbolsList.add(s);
			}
		}
		
		
		LocationAST moduleDefinitionNode = LocationAST.resolveParentsUntilType(node,
				TTCN3ParserTokenTypes.ModuleDefinition);
		LinkedHashSet<LocationAST> relevantModuleDefinitions = new LinkedHashSet<LocationAST>();
		relevantModuleDefinitions.add(moduleDefinitionNode);

		if (T3Q.activeProfile.isRecursionInCheckNoOverSpecificRunsOnClauses()) {
			LinkedHashSet<LocationAST> collectedReferences = collectReferencedFunctionsAndAltsteps(moduleDefinitionNode);
			relevantModuleDefinitions.addAll(collectedReferences);
		}
		
		Map<String, List<ReferenceWithContext>> referenceMap = getReferenceFinder()
				.findReferences(localDefinitionsSymbolsList);
		
		for (List<ReferenceWithContext> refWithContextList : referenceMap.values()){
			for (ReferenceWithContext refWithContext : refWithContextList){
				LocationAST referenceNode = refWithContext.getReferenceNode();
				for (LocationAST moduleDefinition : relevantModuleDefinitions) {
					if (isReferenceWithinModuleDefinition(referenceNode,
							moduleDefinition)) {
						componentElementReferencesWithinScope++;
					}
				}
			}
		}
		
		if (componentElementReferencesWithinScope == 0) {
			this.getLoggingInterface().logWarning(moduleDefinitionNode.getLine(),
					moduleDefinitionNode.getLine(),
					MessageClass.STYLE,
					"Definition for \""
							+ LocationAST.getModuleDefinitionIdentifiersList(moduleDefinitionNode).get(0).getFirstChild().getText()
							+ "\" contains an over specific runs on clause!",
					"6.13, " + MiscTools.getMethodName());
		}
	}


	//TODO: consider splitting again
	//TODO: does not include defaults?!
	private LinkedHashSet<LocationAST> collectReferencedFunctionsAndAltsteps(
			LocationAST moduleDefinitionNode) {
		LinkedHashSet<LocationAST> relevantModuleDefinitions = new LinkedHashSet<LocationAST>();
		if (cyclicFunctionReferences.contains(moduleDefinitionNode)) {
			String cycle = ""; 
			for (LocationAST x : cyclicFunctionReferences){
				cycle+=LocationAST.getModuleDefinitionIdentifiersList(x).get(0).getFirstChild().getText()+", ";
			}
			cycle+=LocationAST.getModuleDefinitionIdentifiersList(moduleDefinitionNode).get(0).getFirstChild().getText();
			this
			.getLoggingInterface()
			.logInformation(
					moduleDefinitionNode.getLine(),
					moduleDefinitionNode.getEndLine(),
					MessageClass.STYLE,
					""
							+ "Possible cyclic call sequence: \""
							+ cycle
							+ "\". Skipping...",
					"6.13, " + MiscTools.getMethodName());
		} else {
			cyclicFunctionReferences.push(moduleDefinitionNode);
			LinkedList<LocationAST> referenceNodes = ASTUtil.findTypeNodes(
					moduleDefinitionNode, TTCN3ParserTokenTypes.FunctionRef);
			referenceNodes.addAll(ASTUtil.findTypeNodes(moduleDefinitionNode,
					TTCN3ParserTokenTypes.AltstepInstance));

			for (LocationAST referenceNode : referenceNodes) {
				LocationAST referenceIdentifier = referenceNode.getFirstChild()
						.getFirstChild();
				Symbol referenceSymbol = referenceIdentifier.getSymbol();

				if (referenceSymbol != null) {
					if (((referenceSymbol instanceof FunctionSymbol) && !((FunctionSymbol) referenceSymbol)
							.isExternal())
							|| referenceSymbol instanceof AltstepSymbol) {
						LocationAST declarationNode = referenceSymbol
								.getDeclarationNode();

						if (declarationNode != null) {
							LocationAST referenceModuleDefinitionNode = LocationAST
									.resolveParentsUntilType(
											declarationNode,
											TTCN3ParserTokenTypes.ModuleDefinition);
							relevantModuleDefinitions
									.add(referenceModuleDefinitionNode);
							relevantModuleDefinitions
									.addAll(collectReferencedFunctionsAndAltsteps(referenceModuleDefinitionNode));
						}
					}
				} else {
					this
							.getLoggingInterface()
							.logInformation(
									referenceIdentifier.getLine(),
									referenceIdentifier.getEndLine(),
									MessageClass.STYLE,
									""
											+ "Definition for reference \""
											+ referenceIdentifier.getText()
											+ "\" cannot be resolved. It may not be possible to determine whether the enclosing construct has an over-specific runs on clause!",
									"6.13, " + MiscTools.getMethodName());

				}
			}
			cyclicFunctionReferences.pop();
		}
		return relevantModuleDefinitions;
	}

	// -------------------------------------------------------------------------------------------

	public void checkNoInlineTemplates(LocationAST node){
		boolean problemOccured = false;
		if (node.getFirstChild().getType() == TTCN3ParserTokenTypes.Type || 
				node.getFirstChild().getType() == TTCN3ParserTokenTypes.Signature ){
			problemOccured = true;
		}
		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(),
					node.getEndLine(),
					MessageClass.STYLE,
					"Inline Template is used!",
					"6.13, " + MiscTools.getMethodName());
			
		}
	}
	
	// -------------------------------------------------------------------------------------------
	//TODO: consider group definitions as well? currently: yes
	//implementation of 6.17
	//the additional parts in 6.11 will be implemented separately as "checkZeroReferencedLocalDefinitions"
	public void checkZeroReferencedModuleDefinitions(LocationAST node) {

		Pattern zeroReferencedModuleDefinitionsExcludedPattern = Pattern.compile(T3Q.activeProfile.getZeroReferencedModuleDefinitionsExcludedRegExp());
		Matcher zeroReferencedModuleDefinitionsExcludedMatcher = zeroReferencedModuleDefinitionsExcludedPattern.matcher(ASTUtil.getEnclosingModuleName(node));
		;

		if (zeroReferencedModuleDefinitionsExcludedMatcher.matches()) {
			return;
		}
		//exclude groups
		if (LocationAST.getModuleDefinitionTypeNode(node).getType()==TTCN3ParserTokenTypes.GroupDef) {
			return;
		}
		
		ArrayList<LocationAST> identifiersList = LocationAST.getModuleDefinitionIdentifiersList(node);
		LocationAST identifierNode = null;
		String identifierText = null;
		for (int i = 0; i < identifiersList.size(); i++) {
			identifierNode = identifiersList.get(i);
			identifierText = identifierNode.getFirstChild().getText();
			Symbol s = identifierNode.getFirstChild().getSymbol();

			if (s == null) {
				this.getLoggingInterface().logFix(identifierNode.getLine(),
						identifierNode.getLine(),
						MessageClass.STYLE,
						"Symbol resolution failed for " + identifierText
								+ " in " + MiscTools.getMethodName(),
						"6.17, " + MiscTools.getMethodName());
				return;
			}
			if (getReferenceFinder().countReferences(s) == 0) {
				// TODO: add stats counters
				// TODO: add type information
				// TODO: groups shall probably be excluded
				this.getLoggingInterface().logWarning(identifierNode.getLine(),
						identifierNode.getEndLine(),
						MessageClass.STYLE,
						"Definition for \"" + identifierText
								+ "\" is never referenced!",
						"6.17, " + MiscTools.getMethodName());
			} else {
				int referencesOutsideImports = 0;
				Map<String, List<ReferenceWithContext>> referenceMap = getReferenceFinder().findReferences(s);
				Iterator<List<ReferenceWithContext>> referenceMapIterator = referenceMap.values().iterator();
				while (referenceMapIterator.hasNext()) {
					Iterator<ReferenceWithContext> referenceIterator = referenceMapIterator.next().iterator();
					while (referenceIterator.hasNext()) {
						LocationAST referenceNode = referenceIterator.next().getReferenceNode();
						// System.out.println("++++> "+referenceNode);
						if (!isReferenceWithinImport(referenceNode)) {
							referencesOutsideImports++;
						}
					}
				}
				if (referencesOutsideImports == 0) {
					this.getLoggingInterface().logWarning(identifierNode.getLine(),
							identifierNode.getEndLine(),
							MessageClass.STYLE,
							"Definition for \""
									+ identifierText
									+ "\" is only referenced within import statements!",
							"6.17, " + MiscTools.getMethodName());

				}
			}
		}

	}

	// -------------------------------------------------------------------------------------------
	
	//TODO:
	//Arguably, this should be migrated to the symbol table and support multiple declarations for the same entity and then resolve them based on type, but this is a long term future consideration
	//Alternatively, this shall be split and most of it shall be migrated to the visitor to accommodate reuse for 1.6.3
	public void checkNoDuplicatedModuleDefinitionIdentifiers(LocationAST node) {
		ArrayList<LocationAST> identifiersList = LocationAST.getModuleDefinitionIdentifiersList(node);
		LocationAST identifierNode = null;
		String identifierText = null;
		for (int i = 0; i < identifiersList.size(); i++){
			totalIdentifierCount++; //TODO: replace with statistics interface
			identifierNode = identifiersList.get(i);
			identifierText = identifierNode.getFirstChild().getText();
			if (moduleLevelIdentifiersMap.containsKey(identifierText)){
				repeatedIdentifierCount++; //TODO: replace with statistics interface
				ArrayList<LocationAST> locationNodes = moduleLevelIdentifiersMap.get(identifierText);
				String locationsList = "";
				String moduleName = "";
				for (int l = 0; l < locationNodes.size(); l++){
					moduleName = ASTUtil.getModuleSymbol(locationNodes.get(l)).getName();
					locationsList+= "<\""+moduleName+"\": "+locationNodes.get(l).getLine()+">, ";
				}
				locationsList = locationsList.substring(0, locationsList.length()-2);
			
				this.getLoggingInterface().logWarning(identifiersList.get(i).getLine(),
						identifiersList.get(i).getEndLine(),
						MessageClass.STYLE,
						"Identifier \""
								+ identifierText
								+ "\" on the module level has already been used in "
								+ locationsList + "!",
						"6.10, " + MiscTools.getMethodName());
			}
			addModuleLevelIdentifier(identifierNode);

		}

	}
	
	// -------------------------------------------------------------------------------------------

	public void checkModuleSize(LocationAST node) {
		int moduleSize = node.getEndOffset() - node.getOffset();
		int referenceSize = T3Q.activeProfile.getMaximumAllowedModuleSizeInBytes();
		if (moduleSize > referenceSize) {
			String moduleIdentifier = ASTUtil.findChild(node,
					TTCN3ParserTokenTypes.Identifier).getFirstChild().getText();

			this.getLoggingInterface().logWarning(node.getLine(),
					node.getEndLine(),
					MessageClass.MODULARIZATION,
					"Module \"" + moduleIdentifier + "\" is bigger ("
							+ moduleSize
							+ " bytes) that the maximum allowed module size ("
							+ referenceSize + " bytes)!",
					"7.21, " + MiscTools.getMethodName());

		}
	}

	// -------------------------------------------------------------------------------------------

	public void checkModuleContainment(LocationAST node,
			String moduleRestriction) {
		if (node.getType() == TTCN3ParserTokenTypes.TTCN3ModuleId) {
			LocationAST textNode = node.getNthChild(4);
			String moduleName = textNode.getText();
			if (!moduleName.contains(moduleRestriction)) {
				return;
			}

		}

		LocationAST moduleDefinitionsList = null;

		if (node.getType() == TTCN3ParserTokenTypes.GroupDef) {
			if (node.getFirstChild().getNextSibling() == null) {
				return;
			}
			moduleDefinitionsList = node.getFirstChild().getNextSibling().getFirstChild();
		} else {
			if ((node.getNextSibling() == null)
					|| (node.getNextSibling().getType() != TTCN3ParserTokenTypes.ModuleDefinitionsPart)) {
				return;
			}
			moduleDefinitionsList = node.getNextSibling().getFirstChild();
		}

		LocationAST moduleDefinition = moduleDefinitionsList.getFirstChild();
		do {
			if (moduleDefinition.getType() == TTCN3ParserTokenTypes.ModuleDefinition) {
				LocationAST definitionTypeNode = moduleDefinition.getFirstChild();
				if (definitionTypeNode.getType() == TTCN3ParserTokenTypes.Visibility){
					definitionTypeNode = definitionTypeNode.getNextSibling();
				}

				if (definitionTypeNode.getType() == TTCN3ParserTokenTypes.GroupDef) {
					checkModuleContainment(definitionTypeNode,
							moduleRestriction);
				} else {
					if (!moduleRestriction.equals("TypesAndValues")
							&& !moduleRestriction.equals("Interface")
							&& (definitionTypeNode.getType() == TTCN3ParserTokenTypes.TypeDef)
							&& (definitionTypeNode.getFirstChild().getType() == TTCN3ParserTokenTypes.StructuredTypeDef)) {
						// if definition is of the non-specific type TypeDef,
						// with sub-type StructuredDefType, descend two levels
						// in the tree to get the relevant nodes that are
						// specific enough for restrictions other than
						// TypesAndValues
						definitionTypeNode = definitionTypeNode.getFirstChild().getFirstChild();

					}
					if (!moduleContentsChecker.typePermittedInRestrictedModule(moduleRestriction,
							definitionTypeNode.getType())) {

						this.getLoggingInterface().logWarning(definitionTypeNode.getLine(),
								definitionTypeNode.getEndLine(),
								MessageClass.MODULARIZATION,
								"Definition in "
										+ moduleRestriction
										+ " module is not of the permissible definition type(s)!",
								"7.x, " + MiscTools.getMethodName());
					} else {
						// special case scenario
						if ((moduleRestriction.equals("Testcases"))
								&& (definitionTypeNode.getType() == TTCN3ParserTokenTypes.FunctionDef)) {

							if (!isFunctionStartsBehavior(definitionTypeNode.getFirstChild().getFirstChild())) {
								this.getLoggingInterface().logWarning(definitionTypeNode.getLine(),
										definitionTypeNode.getEndLine(),
										MessageClass.MODULARIZATION,
										"Definition in "
												+ moduleRestriction
												+ " module is not of the permissible definition type(s)! Function is never referenced in a start statement!",
										"7.x, " + MiscTools.getMethodName());
							}

						}
					}
				}
			}
		} while ((moduleDefinition = moduleDefinition.getNextSibling()) != null);
	}

	// -------------------------------------------------------------------------------------------

	public void checkPortMessageGrouping(LocationAST node) {

		String portModuleName = ASTUtil.getEnclosingModuleName(node);
		String portIdentifierText = ASTUtil.findChild(node, TTCN3ParserTokenTypes.IDENTIFIER).getText();

		LocationAST portGroupDef = LocationAST.resolveParentsUntilType(node,
				TTCN3ParserTokenTypes.GroupDef);

		if (portGroupDef == null) {

			this.getLoggingInterface().logWarning(node.getLine(),
					node.getEndLine(),
					MessageClass.STRUCTURE,
					"Port type definition for \""
					+ portIdentifierText
					+ "\" is found outside a group definition! Related messages can therefore never be in the same group as the port definition!",
					"4.2, " + MiscTools.getMethodName());

		} else {
			LocationAST portDefAttribsNode = node.getFirstChild().getNextSibling();

			LocationAST portDefAttribsList = portDefAttribsNode.getFirstChild();
			do {
				LinkedList<LocationAST> identifierList = ASTUtil.findTypeNodes(portDefAttribsList,
						TTCN3ParserTokenTypes.Identifier);
				for (int j = 0; j < identifierList.size(); j++) {
					LocationAST identifierNode = identifierList.get(j);

					Symbol symbol = null;
					String type = null;
					if (identifierNode.getParent().getType() == TTCN3ParserTokenTypes.Signature) {
						type = "Signature";

					} else {
						if (identifierNode.getParent().getType() == TTCN3ParserTokenTypes.TypeReference) {
							type = "Message type";
						}
					}
					if (type != null) {
						symbol = identifierNode.getFirstChild().getSymbol();

						if (symbol == null) {

							this.getLoggingInterface().logWarning(identifierNode.getLine(),
									identifierNode.getEndLine(),
									MessageClass.STRUCTURE,
									""
									+ type
									+ " definition for \""
									+ identifierNode.getFirstChild().getText()
									+ "\" related to port type definition for \""
									+ portIdentifierText
									+ "\" cannot be resolved! It is therefore not in the same group!",
									"4.2, " + MiscTools.getMethodName());

							
						} else {
							LocationAST declarationIdentifierNode = symbol.getDeclarationNode();

							String declarationModuleName = ASTUtil.getEnclosingModuleName(declarationIdentifierNode);

							LocationAST declarationGroupDef = LocationAST.resolveParentsUntilType(declarationIdentifierNode,
									TTCN3ParserTokenTypes.GroupDef);
							if (declarationGroupDef == null) {
								this.getLoggingInterface().logWarning(identifierNode.getLine(),
										identifierNode.getEndLine(),
										MessageClass.STRUCTURE,
										""
												+ type
												+ " definition for \""
												+ declarationIdentifierNode.getText()
												+ "\" <"
												+ declarationIdentifierNode.getLine()
												+ ","
												+ declarationModuleName
												+ ",- "
												+ "> "
												+ "related to port type definition for \""
												+ portIdentifierText
												+ "\" <"
												+ node.getLine()
												+ ","
												+ portModuleName
												+ ","
												+ portGroupDef.getNthChild(2).getText()
												+ "> "
												+ "is found outside a group definition! It can therefore never be in the same group as the port type definition!",
										"4.2, " + MiscTools.getMethodName());

							} else {
								if (declarationModuleName.equals(portModuleName)) {
									if (!portGroupDef.equals(declarationGroupDef)) {
										if (!isNodeWithinGroup(declarationGroupDef,
												portGroupDef)) {

											this.getLoggingInterface().logWarning(identifierNode.getLine(),
													identifierNode.getEndLine(),
													MessageClass.STRUCTURE,
													""
															+ type
															+ " definition for \""
															+ declarationIdentifierNode.getText()
															+ "\" <"
															+ declarationIdentifierNode.getLine()
															+ ","
															+ declarationModuleName
															+ ","
															+ declarationGroupDef.getNthChild(2).getText()
															+ "> "
															+ "related to port type definition for \""
															+ portIdentifierText
															+ "\" <"
															+ node.getLine()
															+ ","
															+ portModuleName
															+ ","
															+ portGroupDef.getNthChild(2).getText()
															+ "> "
															+ "is not within the same group as the port type definition!",
													"4.2, "
															+ MiscTools.getMethodName());
											
										} else {
											this.getLoggingInterface().logInformation(identifierNode.getLine(),
													identifierNode.getEndLine(),
													MessageClass.STRUCTURE,
													""
															+ type
															+ " definition for \""
															+ declarationIdentifierNode.getText()
															+ "\" <"
															+ declarationIdentifierNode.getLine()
															+ ","
															+ declarationModuleName
															+ ","
															+ declarationGroupDef.getNthChild(2).getText()
															+ "> "
															+ "related to port type definition for \""
															+ portIdentifierText
															+ "\" <"
															+ node.getLine()
															+ ","
															+ portModuleName
															+ ","
															+ portGroupDef.getNthChild(2).getText()
															+ "> "
															+ "is within a nested subgroup within the same group as the port type definition!",
													"4.2, "
															+ MiscTools.getMethodName());
										}
									}
								} else {
									this.getLoggingInterface().logWarning(identifierNode.getLine(),
											identifierNode.getEndLine(),
											MessageClass.STRUCTURE,
											""
													+ type
													+ " definition for \""
													+ declarationIdentifierNode.getText()
													+ "\" <"
													+ declarationIdentifierNode.getLine()
													+ ","
													+ declarationModuleName
													+ ","
													+ declarationGroupDef.getNthChild(2).getText()
													+ "> "
													+ "related to port type definition for \""
													+ portIdentifierText
													+ "\" <"
													+ node.getLine()
													+ ","
													+ portModuleName
													+ ","
													+ portGroupDef.getNthChild(2).getText()
													+ "> "
													+ "is not within the same module as the port type definition! It can therefore never be in the same group as the port definition!",

											"4.2, " + MiscTools.getMethodName());
								}
							}

						}
					}

				}

			} while ((portDefAttribsList = portDefAttribsList.getNextSibling()) != null);

		}

	}

	// -------------------------------------------------------------------------------------------

	public void checkTypeDefOrderInGroup(LocationAST node) {
		String previousDefinitionIdentifierText = null;
		String currentDefinitionIdentifierText = null;
		LocationAST groupIdentifierNode = node.getFirstChild();
		String groupIdentifierText = groupIdentifierNode.getFirstChild().getText();

		if (groupIdentifierNode.getNextSibling() != null) {
			LocationAST moduleDefinitionNode = groupIdentifierNode.getNextSibling()
					.getNthChild(2);
			do {
				if (moduleDefinitionNode.getType() != TTCN3ParserTokenTypes.SemiColon) {
					if (LocationAST.getModuleDefinitionTypeNode(moduleDefinitionNode).getType() == TTCN3ParserTokenTypes.TypeDef) {
						ArrayList<LocationAST> moduleDefinitionIdentifiersList = LocationAST.getModuleDefinitionIdentifiersList(moduleDefinitionNode);
						for (LocationAST moduleDefinitionIdentifier : moduleDefinitionIdentifiersList) {
							currentDefinitionIdentifierText = moduleDefinitionIdentifier
									.getFirstChild().getText();
							// switched to case insensitive ordering
							if (previousDefinitionIdentifierText != null) {
								if (currentDefinitionIdentifierText
										.compareToIgnoreCase(previousDefinitionIdentifierText) <= 0) {
									this.getLoggingInterface()
											.logWarning(
													node.getLine(),
													node.getEndLine(),
													MessageClass.STRUCTURE,
													"Type definitions <\""
															+ previousDefinitionIdentifierText
															+ "\",\""
															+ currentDefinitionIdentifierText
															+ "\"> within group \""
															+ groupIdentifierText
															+ "\" are not alphabetically ordered!"
															+ "4.1, "
															+ MiscTools
																	.getMethodName());
								}
							}
							previousDefinitionIdentifierText = currentDefinitionIdentifierText;
						}
					}
				}
			} while ((moduleDefinitionNode = moduleDefinitionNode.getNextSibling()) != null);
		}
	}

	// -------------------------------------------------------------------------------------------

	public void checkLocalDefinitionsComeFirst(LocationAST node) {
		boolean hasOtherDefinitions = false;
		int previousLocalDefinitionsOrderLevel = 0;
		//TODO: REFACTOR!!! and reorganize
		ArrayList<Integer> configuredLocalDefinitionTypes = getConfiguredLocalDefinitionTypes();
		if (configuredLocalDefinitionTypes.size() == 0){
			return;
		}

		String definedLocalDefinitionOrder = "";
		for (String localDef : T3Q.activeProfile.getLocalDefinitionTypes()){
			definedLocalDefinitionOrder += localDef+", ";
		}
		definedLocalDefinitionOrder = definedLocalDefinitionOrder.substring(0, definedLocalDefinitionOrder.length()-2);
		
		if (node.getFirstChild().getType() == TTCN3ParserTokenTypes.AltstepDef) {
			LocationAST altstepLocalDefList = (LocationAST) ASTUtil
					.findSibling(node.getNthChild(2),
							TTCN3ParserTokenTypes.AltstepLocalDefList);

			if (altstepLocalDefList != null) {

				LocationAST altstepLocalDef = altstepLocalDefList
						.getFirstChild();

				do {
					if (altstepLocalDef.getFirstChild() != null) {
						LocationAST definition = altstepLocalDef
								.getFirstChild();
						if (configuredLocalDefinitionTypes.contains(definition.getType())) {
							int currentLocalDefinitionTypeOrderLevel = configuredLocalDefinitionTypes.indexOf(definition.getType());
							if (hasOtherDefinitions){
								this.getLoggingInterface().logWarning(
										definition.getLine(),
										definition.getEndLine(),
										MessageClass.STYLE,
										"Local definition is not at the begining!",
										"6.8, " + MiscTools.getMethodName());
								
							} else {
								if ((currentLocalDefinitionTypeOrderLevel < previousLocalDefinitionsOrderLevel)) {
									this.getLoggingInterface().logWarning(
											definition.getLine(),
											definition.getEndLine(),
											MessageClass.STYLE,
											"Local definition order is not maintained as configured ("
											+ definedLocalDefinitionOrder
											+ ")!" +
											"6.8, " + MiscTools.getMethodName());
								} else {
									previousLocalDefinitionsOrderLevel = currentLocalDefinitionTypeOrderLevel;
								}
							}
						} else {
							hasOtherDefinitions = true;
						}
					}
				} while ((altstepLocalDef = altstepLocalDef.getNextSibling()) != null);

			}

			LocationAST altstepGuardList = (LocationAST) ASTUtil.findSibling(
					node.getNthChild(2), TTCN3ParserTokenTypes.AltGuardList);

			if (altstepGuardList != null) {

				LinkedList<LocationAST> localDefNodes = new LinkedList<LocationAST>();
				for (int configuredLocalDefinitionType : configuredLocalDefinitionTypes){
					localDefNodes.addAll(ASTUtil
						.findTypeNodes(altstepGuardList,
								configuredLocalDefinitionType));
				}
				if (!localDefNodes.isEmpty()) {
					LocationAST localDefNode;
					for (int i = 0; i < localDefNodes.size(); i++) {
						localDefNode = localDefNodes.get(i);
						this.getLoggingInterface().logWarning(
								localDefNode.getLine(),
								localDefNode.getEndLine(),
								MessageClass.STYLE,
								"Local definition is not at the begining!",
								"6.8, " + MiscTools.getMethodName());
					}

				}
			}

		}
		else if (node.getFirstChild().getType() == TTCN3ParserTokenTypes.FunctionDef
				|| node.getFirstChild().getType() == TTCN3ParserTokenTypes.TestcaseDef) {
			LocationAST functionStatementBlock = (LocationAST) ASTUtil
					.findSibling(node.getNthChild(2),
							TTCN3ParserTokenTypes.StatementBlock); // node.getFirstChild()

			if (functionStatementBlock != null) {

				LocationAST functionStatementOrDefNode = functionStatementBlock
						.getFirstChild();

				if (functionStatementOrDefNode == null) {
					return;
				}

				do {
					if (functionStatementOrDefNode.getFirstChild() != null) {
						if ((functionStatementOrDefNode
								.getType() == TTCN3ParserTokenTypes.FunctionLocalInst) || 
								(functionStatementOrDefNode
								.getType() == TTCN3ParserTokenTypes.FunctionLocalDef)) {
							//duplicated
							LocationAST definition = functionStatementOrDefNode
									.getFirstChild();
							if (configuredLocalDefinitionTypes.contains(definition.getType())) {
								int currentLocalDefinitionTypeOrderLevel = configuredLocalDefinitionTypes.indexOf(definition.getType());
								if (hasOtherDefinitions) {
									this.getLoggingInterface().logWarning(
											definition.getLine(),
											definition.getEndLine(),
											MessageClass.STYLE,
											"Local definition is not at the begining!",
											"6.8, " + MiscTools.getMethodName());

								} else {
									if ((currentLocalDefinitionTypeOrderLevel < previousLocalDefinitionsOrderLevel)) {
										this.getLoggingInterface().logWarning(
												definition.getLine(),
												definition.getEndLine(),
												MessageClass.STYLE,
												"Local definition order is not maintained as configured ("
												+ definedLocalDefinitionOrder
												+ ")!" +
												"6.8, " + MiscTools.getMethodName());
									} else {
										previousLocalDefinitionsOrderLevel = currentLocalDefinitionTypeOrderLevel;
									}

								}
							} else {
								// it could be a timer
								hasOtherDefinitions = true;
							}
						} else {
							// anything else
							hasOtherDefinitions = true;
							// search for local definition nodes
							//duplicated
							LinkedList<LocationAST> localDefNodes = new LinkedList<LocationAST>();
							for (int configuredLocalDefinitionType : configuredLocalDefinitionTypes){
								localDefNodes.addAll(ASTUtil
									.findTypeNodes(functionStatementOrDefNode,
											configuredLocalDefinitionType));
							}

							
							if (!localDefNodes.isEmpty()) {
								LocationAST localDefNode;
								for (int i = 0; i < localDefNodes.size(); i++) {
									localDefNode = localDefNodes.get(i);
									this.getLoggingInterface().logWarning(
											localDefNode.getLine(),
											localDefNode.getEndLine(),
											MessageClass.STYLE,
											"Local definition is not at the begining!",
											"6.8, " + MiscTools.getMethodName());
								}

							}

						}
					}
				} while ((functionStatementOrDefNode = functionStatementOrDefNode
						.getNextSibling()) != null);
			}
		}

		else if ((node.getFirstChild().getType() == TTCN3ParserTokenTypes.TypeDef) && (node.getNthChild(3).getType() == TTCN3ParserTokenTypes.ComponentDef)) {
			LinkedList<LocationAST> componentElementDefs = ASTUtil
					.findTypeNodes(node,
							TTCN3ParserTokenTypes.ComponentElementDef);
			for (LocationAST componentElementDef : componentElementDefs) {
				// cloned
				LocationAST definition = componentElementDef.getFirstChild();
				if (configuredLocalDefinitionTypes.contains(definition
						.getType())) {
					int currentLocalDefinitionTypeOrderLevel = configuredLocalDefinitionTypes
							.indexOf(definition.getType());
					if (hasOtherDefinitions) {
						this.getLoggingInterface().logWarning(
								definition.getLine(),
								definition.getEndLine(),
								MessageClass.STYLE,
								"Local definition is not at the begining!",
								"6.8, " + MiscTools.getMethodName());
					} else {
						if ((currentLocalDefinitionTypeOrderLevel < previousLocalDefinitionsOrderLevel)) {
							this.getLoggingInterface().logWarning(
									definition.getLine(),
									definition.getEndLine(),
									MessageClass.STYLE,
									"Local definition order is not maintained as configured ("
									+ definedLocalDefinitionOrder
									+ ")!" +
									"6.8, " + MiscTools.getMethodName());

						} else {
							previousLocalDefinitionsOrderLevel = currentLocalDefinitionTypeOrderLevel;
						}

					}
				} else {
					// it could be a timer
					hasOtherDefinitions = true;
				}

			}
		}
		
	}

	// -------------------------------------------------------------------------------------------

	//TODO: largely duplicated, factor out
	public void checkLocalDefinitionsComeFirstWithinControlPart(LocationAST node) {
		boolean hasOtherDefinitions = false;
		int previousLocalDefinitionsOrderLevel = 0;
		// TODO: refactor and reorganize
		ArrayList<Integer> configuredLocalDefinitionTypes = getConfiguredLocalDefinitionTypes();
		if (configuredLocalDefinitionTypes.size() == 0) {
			return;
		}

		String definedLocalDefinitionOrder = "";
		for (String localDef : T3Q.activeProfile
				.getLocalDefinitionTypes()) {
			definedLocalDefinitionOrder += localDef + ", ";
		}
		definedLocalDefinitionOrder = definedLocalDefinitionOrder.substring(0,
				definedLocalDefinitionOrder.length() - 2);
		
		LocationAST controlStatementOrDefNode = node
		.getFirstChild();
		
		if (controlStatementOrDefNode == null) {
			return;
		}

		do {
			if (controlStatementOrDefNode.getFirstChild() != null) {
				if ((controlStatementOrDefNode.getFirstChild()
						.getType() == TTCN3ParserTokenTypes.FunctionLocalInst) || 
						(controlStatementOrDefNode.getFirstChild()
						.getType() == TTCN3ParserTokenTypes.FunctionLocalDef)) {
					//duplicated
					LocationAST definition = controlStatementOrDefNode
							.getFirstChild().getFirstChild();
					if (configuredLocalDefinitionTypes.contains(definition.getType())) {
						int currentLocalDefinitionTypeOrderLevel = configuredLocalDefinitionTypes.indexOf(definition.getType());
						if (hasOtherDefinitions) {
							this.getLoggingInterface().logWarning(
									definition.getLine(),
									definition.getEndLine(),
									MessageClass.STYLE,
									"Local definition is not at the begining!",
									"6.8, " + MiscTools.getMethodName());
						} else {
							if ((currentLocalDefinitionTypeOrderLevel < previousLocalDefinitionsOrderLevel)) {
								this.getLoggingInterface().logWarning(
										definition.getLine(),
										definition.getEndLine(),
										MessageClass.STYLE,
										"Local definition order is not maintained as configured ("+definedLocalDefinitionOrder+")!",
										"6.8, " + MiscTools.getMethodName());
							} else {
								previousLocalDefinitionsOrderLevel = currentLocalDefinitionTypeOrderLevel;
							}
						}
					} else {
						// it could be a timer
						hasOtherDefinitions = true;
					}
				} else {
					// anything else
					hasOtherDefinitions = true;
					// search for local definition nodes
					//duplicated
					LinkedList<LocationAST> localDefNodes = new LinkedList<LocationAST>();
					for (int configuredLocalDefinitionType : configuredLocalDefinitionTypes){
						localDefNodes.addAll(ASTUtil
							.findTypeNodes(controlStatementOrDefNode,
									configuredLocalDefinitionType));
					}

					
					if (!localDefNodes.isEmpty()) {
						LocationAST localDefNode;
						for (int i = 0; i < localDefNodes.size(); i++) {
							localDefNode = localDefNodes.get(i);
							this.getLoggingInterface().logWarning(
									localDefNode.getLine(),
									localDefNode.getEndLine(),
									MessageClass.STYLE,
									"Local definition is not at the begining!",
									"6.8, " + MiscTools.getMethodName());
						}

					}

				}
			}
		} while ((controlStatementOrDefNode = controlStatementOrDefNode
				.getNextSibling()) != null);

	}

	// -------------------------------------------------------------------------------------------
	
	private ArrayList<Integer> getConfiguredLocalDefinitionTypes() {
		ArrayList<Integer> configuredLocalDefinitionTypes = new ArrayList<Integer>();;
		for (String fieldName : T3Q.activeProfile
				.getLocalDefinitionTypes()) {
			if (!Arrays.asList(
					new QualityCheckProfile().getLocalDefinitionTypes())
					.contains(fieldName)) {
				// TODO: error message
			} else {
				try {
					Class<TTCN3ParserTokenTypes> TTCN3TokenTypes = TTCN3ParserTokenTypes.class;
					Field field = TTCN3TokenTypes.getField(fieldName);
					configuredLocalDefinitionTypes.add(Integer.parseInt(field.get(TTCN3TokenTypes)
							.toString()));
				} catch (SecurityException e) {

				} catch (NoSuchFieldException e) {
					// TODO: error message
					// System.out.println("NO SUCH FIELD!");
				} catch (IllegalArgumentException e) {
				} catch (IllegalAccessException e) {
				}

			}
		}
		return configuredLocalDefinitionTypes;
	}


	// -------------------------------------------------------------------------------------------

	public void checkImportsComeFirst(LocationAST node) {
		boolean hasOtherDefinitions = false;

		if ((node.getNextSibling() == null)
				|| (node.getNextSibling().getType() != TTCN3ParserTokenTypes.ModuleDefinitionsPart)) {
			return;
		}

		LocationAST moduleDefinitionsList = node.getNextSibling()
				.getFirstChild();

		LocationAST moduleDefinition = moduleDefinitionsList.getFirstChild();

		do {
			if (moduleDefinition.getFirstChild() != null) {
				LocationAST definition = moduleDefinition.getFirstChild();
				if (definition.getType() == TTCN3ParserTokenTypes.ImportDef) {
					if (hasOtherDefinitions) {
						this.getLoggingInterface().logWarning(
								definition.getLine(),
								definition.getEndLine(),
								MessageClass.STRUCTURE,
								"Import statement is not at the begining!",
								"6.9, " + MiscTools.getMethodName());
					}
				} else {
					hasOtherDefinitions = true;
				}
			}
		} while ((moduleDefinition = moduleDefinition.getNextSibling()) != null);
	}

	// -------------------------------------------------------------------------------------------

	public void checkNoAllKeywordInPortDefinitions(LocationAST node) {
		LocationAST identifierNode = node.getFirstChild();
		String identifierText = identifierNode.getFirstChild().getText();
		LinkedList<LocationAST> allKeywordNodes = ASTUtil.findTypeNodes(node,
				TTCN3ParserTokenTypes.AllKeyword);
		if (!allKeywordNodes.isEmpty()) {
			LocationAST allKeywordNode;
			for (int i = 0; i < allKeywordNodes.size(); i++) {
				allKeywordNode = allKeywordNodes.get(i);
				this.getLoggingInterface().logWarning(
						allKeywordNode.getLine(),
						allKeywordNode.getEndLine(),
						MessageClass.STRUCTURE,
						"\"all\" keyword is used in the definition of port \""
								+ identifierText + "\"!",
						"4.2.1, " + MiscTools.getMethodName());
			}
		}

	}

	// -------------------------------------------------------------------------------------------

	public void checkLogStatementFormat(LocationAST node) {
		if (subsequentLogStatementNodes.contains(node)){
			//skipping subsequently checked nodes
			return;
		}
		int endLine = node.getEndLine();
		String logStatementText = getLogStatementText(node);
		if (T3Q.activeProfile.isProcessSubsequentLogStatementsAsOne()) {
			LocationAST subsequentLogStatementNode = getSubsequentLogStatement(node);
			while (subsequentLogStatementNode != null){
				logStatementText += getLogStatementText(subsequentLogStatementNode);
				endLine = subsequentLogStatementNode.getEndLine();
				subsequentLogStatementNodes.add(subsequentLogStatementNode);
				subsequentLogStatementNode = getSubsequentLogStatement(subsequentLogStatementNode);
			}
		}
		checkLogFormat(node, logStatementText, endLine);
	}

	// -------------------------------------------------------------------------------------------
	
	private LocationAST getSubsequentLogStatement(LocationAST logStatementNode) {
		LocationAST subsequentLogStatementNode = null;
		LocationAST nextStatement = LocationAST.resolveParentsUntilType(logStatementNode, new int[]{TTCN3ParserTokenTypes.FunctionStatement, TTCN3ParserTokenTypes.ControlStatementOrDef}).getNextSibling();

		if ((nextStatement != null) && (nextStatement.getType() == TTCN3ParserTokenTypes.SemiColon)) {
				nextStatement = nextStatement.getNextSibling();
		}
		if (nextStatement != null){
			if (nextStatement.getType() == TTCN3ParserTokenTypes.FunctionStatement || 
					nextStatement.getType() == TTCN3ParserTokenTypes.ControlStatementOrDef) {
				LocationAST logStatement = nextStatement
						.getNthChild(2);
				if (logStatement != null
						&& logStatement.getType() == TTCN3ParserTokenTypes.LogStatement) {
					subsequentLogStatementNode = logStatement;
				}
			}
		}

		return subsequentLogStatementNode;
	}

	// -------------------------------------------------------------------------------------------
	
	private String getLogStatementText(LocationAST node) {
		String logStatementText = "";
		
		LocationAST logItem = node.getFirstChild();
		
		do {
			if (logItem.getFirstChild().getType()!=TTCN3ParserTokenTypes.TemplateInstance){
				//get actual log item text
				logStatementText += logItem.getFirstChild().getText();
			} else {
				//substitute other elements with empty string
				logStatementText += "";
				//TODO: consider fixing the parser, this is an ugly workaround
				LinkedList<LocationAST> cStringNodes = ASTUtil.findTypeNodes(logItem, TTCN3ParserTokenTypes.CharStringValue);
				for (LocationAST cStringNode : cStringNodes){
					logStatementText+=cStringNode.getFirstChild().getText();
				}
			}
			logItem = logItem.getNextSibling();
		} while (logItem!=null && logItem.getType()==TTCN3ParserTokenTypes.LogItem);
		return logStatementText;
	}

	// -------------------------------------------------------------------------------------------

	public void checkLogItemFormat(LocationAST node) {
		String logItemText = node.getFirstChild().getText();
		checkLogFormat(node, logItemText, node.getEndLine());
	}

	// -------------------------------------------------------------------------------------------
	
	private void checkLogFormat(LocationAST node, String logItemText, int endLine) {
		boolean problemOccured = false;
		String warning = "";

		Pattern logPattern = Pattern.compile(T3Q.activeProfile
				.getLogFormatRegExp(), Pattern.DOTALL);
		Matcher logMatcher = logPattern.matcher(logItemText);

		if (!logMatcher.matches()) {
			problemOccured = true;
			warning = "Invalid log format (\"" + logItemText + "\")!";
		} else if (!logMatcher.group(1).equals("")) { // check if the first
			// group is empty
			// if not try to resolve the container identifier
			String logItemContainerIdentifier = logMatcher.group(1);

			// check if it is a module definition
			LocationAST moduleDefinitionNode = LocationAST
					.resolveParentsUntilType(node,
							TTCN3ParserTokenTypes.ModuleDefinition);

			if (moduleDefinitionNode != null) {

				// check if it is a function def or altstep def or testcase def
				int parentContainerType = moduleDefinitionNode.getFirstChild()
						.getType();
				if (parentContainerType == TTCN3ParserTokenTypes.Visibility){
					parentContainerType = moduleDefinitionNode.getFirstChild().getNextSibling()
					.getType(); 
				}
				if (parentContainerType == TTCN3ParserTokenTypes.TestcaseDef
						|| parentContainerType == TTCN3ParserTokenTypes.FunctionDef
						|| parentContainerType == TTCN3ParserTokenTypes.AltstepDef) {
					LocationAST parentContainerNodeIdentifier = LocationAST.getModuleDefinitionIdentifiersList(moduleDefinitionNode).get(0);
					Symbol parentContainerSymbol = parentContainerNodeIdentifier.getFirstChild().getSymbol();
					String parentContainerSymbolName = parentContainerSymbol
							.getName();

					if (!parentContainerSymbolName
							.equals(logItemContainerIdentifier)) {
						problemOccured = true;
						warning = "Log statement contains invalid container identifier (\""
								+ logItemContainerIdentifier
								+ "\", should be \""
								+ parentContainerSymbolName + "\")!";
					}

				} else {
					// not a testcase, a function or an altstep
					// is it possible at all? - probably not
				}

			} else {
				// it is a control part
				moduleDefinitionNode = LocationAST.resolveParentsUntilType(node,
						TTCN3ParserTokenTypes.ModuleControlPart);
				if (moduleDefinitionNode == null) {
					// else it is not a control part - is it possible? probably
					// not
				}
			}

		}

		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(), endLine, MessageClass.LOGGING, warning, "5.1. "+MiscTools.getMethodName());
		}
	}

	// -------------------------------------------------------------------------------------------

	public void checkTestcasesModuleImportsLibCommon_Sync(LocationAST node) {
		LocationAST identifierNode = (LocationAST) ASTUtil.findChild(node, TTCN3ParserTokenTypes.IDENTIFIER);
		String moduleName = identifierNode.getText();
		if ((!moduleName.contains("Testcases"))
				|| moduleName.contains("LibCommon_Sync")) {
			return;
		}

		boolean libCommonReferenceFound = false;
		
		//Identical part
		LinkedList<LocationAST> importDefNodes = ASTUtil
		.findTypeNodes(node.getParent(),
				TTCN3ParserTokenTypes.ImportDef);
		for (int i = 0; i < importDefNodes.size(); i++) {
			String importedModuleIdentifierText = ASTUtil.findChild(importDefNodes.get(i), TTCN3ParserTokenTypes.IDENTIFIER).getText();
			if (importedModuleIdentifierText.contains("LibCommon_Sync")) {
				libCommonReferenceFound = true;
			}
		}
		
//TODO: Superseded by above version, check for correctness and discard odler version	
//		LocationAST moduleDefinitionsList = node.getNextSibling()
//				.getFirstChild();
//		LocationAST moduleDefinition = moduleDefinitionsList.getFirstChild();
//		do {
//			if (moduleDefinition.getFirstChild() != null) {
//				LocationAST definition = moduleDefinition.getFirstChild();
//				if (definition.getType() == TTCN3ParserTokenTypes.ImportDef) {
//					LinkedList<LocationAST> importIdentifierNodes = ASTUtil
//							.findTypeNodes(definition,
//									TTCN3ParserTokenTypes.Identifier);
//					for (int i = 0; i < importIdentifierNodes.size(); i++) {
//						if (importIdentifierNodes.get(i).getFirstChild()
//								.getText().contains("LibCommon_Sync")) {
//							LocationAST importFromSpecNode = LocationAST
//									.resolveParentsUntilType(
//											importIdentifierNodes.get(i),
//											TTCN3ParserTokenTypes.ImportFromSpec);
//							if (importFromSpecNode != null)
//								libCommonReferenceFound = true;
//						}
//					}
//				}
//			}
//		} while ((moduleDefinition = moduleDefinition.getNextSibling()) != null);

		if (!libCommonReferenceFound) {
			this
			.getLoggingInterface()
			.logWarning(
					identifierNode.getLine(),
					identifierNode.getEndLine(),
					MessageClass.STYLE,
					"Required import from \"LibCommon_Sync\" not found!",
					"7.11, " + MiscTools.getMethodName());
		}
	}

	// -------------------------------------------------------------------------------------------

	public void checkTypesAndValuesModuleImportsLibCommon(LocationAST node) {
		LocationAST identifierNode = (LocationAST) ASTUtil.findChild(node, TTCN3ParserTokenTypes.IDENTIFIER);
		String moduleName = identifierNode.getText();

		Pattern typesAndValuesImportsLibNamesExcludedPattern = Pattern.compile(T3Q.activeProfile
				.getTypesAndValuesImportsLibNamesExcludedRegExp());
		Matcher typesAndValuesImportsLibNamesExcludedMatcher = typesAndValuesImportsLibNamesExcludedPattern.matcher(moduleName);;
		
		if (!moduleName.contains("TypesAndValues")
				|| typesAndValuesImportsLibNamesExcludedMatcher.matches())
			return;

		Pattern typesAndValuesImportsLibNamesPattern = Pattern.compile(T3Q.activeProfile
				.getTypesAndValuesImportsLibNamesRegExp());
		Matcher typesAndValuesImportsLibNamesMatcher = null;
		
		boolean libCommonReferenceFound = false;

		LinkedList<LocationAST> importDefNodes = ASTUtil
		.findTypeNodes(node.getParent(),
				TTCN3ParserTokenTypes.ImportDef);
		for (int i = 0; i < importDefNodes.size(); i++) {
			String importedModuleIdentifierText = ASTUtil.findChild(importDefNodes.get(i), TTCN3ParserTokenTypes.IDENTIFIER).getText();
			typesAndValuesImportsLibNamesMatcher = typesAndValuesImportsLibNamesPattern.matcher(importedModuleIdentifierText);
			if (typesAndValuesImportsLibNamesMatcher.matches()) {
				libCommonReferenceFound = true;
			}
		}

//TODO: Superseded by above version, check for correctness and discard odler version	
//		LocationAST moduleDefinitionsList = node.getNextSibling()
//				.getFirstChild();
//		LocationAST moduleDefinition = moduleDefinitionsList.getFirstChild();
//		do {
//			if (moduleDefinition.getFirstChild() != null) {
//				LocationAST definition = moduleDefinition.getFirstChild();
//				if (definition.getType() == TTCN3ParserTokenTypes.ImportDef) {
//					LocationAST importIdentifierNode = definition.getNthChild(4);
//					LinkedList<LocationAST> importIdentifierNodes = ASTUtil
//							.findTypeNodes(definition,
//									TTCN3ParserTokenTypes.Identifier);
//					for (int i = 0; i < importIdentifierNodes.size(); i++) {
//						String identifierText = importIdentifierNodes.get(i).getFirstChild()
//								.getText();
//						typesAndValuesImportsLibNamesMatcher = typesAndValuesImportsLibNamesPattern.matcher(identifierText);
//						if (typesAndValuesImportsLibNamesMatcher.matches()) {
//							LocationAST importFromSpecNode = LocationAST
//									.resolveParentsUntilType(
//											importIdentifierNodes.get(i),
//											TTCN3ParserTokenTypes.ImportFromSpec);
//							if (importFromSpecNode != null)
//								libCommonReferenceFound = true;
//						}
//					}
//				}
//			}
//		} while ((moduleDefinition = moduleDefinition.getNextSibling()) != null);

		if (!libCommonReferenceFound) {
			this
			.getLoggingInterface()
			.logWarning(
					identifierNode.getLine(),
					identifierNode.getEndLine(),
					MessageClass.STYLE,
					"Required import from \""+T3Q.activeProfile.getTypesAndValuesImportsLibNamesRegExp()+"\" not found!!",
					"7.3, " + MiscTools.getMethodName());
			
		}
	}

	// -------------------------------------------------------------------------------------------
	//TODO: address wolfgang's problem
	public void checkNoModifiedTemplateOfModifiedTemplate(LocationAST node) {
		// Not explicitly necessary, for consistency only
		boolean problemOccured = false;

		// resolution of the modified template scope
		LocationAST identifierNode = (LocationAST) ASTUtil.findChild(node,
				TTCN3ParserTokenTypes.IDENTIFIER);

		Symbol modifiedSymbol = identifierNode.getSymbol();

		if (modifiedSymbol != null) {

			// derives the identifier of the declaration node
			LocationAST modifiedIdentifierNode = modifiedSymbol
					.getDeclarationNode();

			// resolution of the declaration node
			LocationAST modifiedTemplateNode = LocationAST.resolveParentsUntilType(modifiedIdentifierNode, TTCN3ParserTokenTypes.TemplateDef);
			LocationAST modifiedTemplateDerivedDefNode = (LocationAST) ASTUtil
					.findDerivedDef(modifiedTemplateNode);

			if (modifiedTemplateDerivedDefNode != null) {
				problemOccured = true;

				// resolution of the modifying template scope
				//TODO: revise
				
				LocationAST modifyingIdentifierNode = node.getContainingModuleDefinitionIdentifiersList().get(0);
				System.out.println(modifyingIdentifierNode);
				Symbol modifyingSymbol = modifyingIdentifierNode.getFirstChild().getSymbol();

				if (problemOccured) {
					this
							.getLoggingInterface()
							.logWarning(
									node.getLine(),
									node.getEndLine(),
									MessageClass.STYLE,
									"Template \""
											+ modifyingSymbol.getName()
											+ "\" modifies another modified template (\""
											+ modifiedSymbol.getName() + "\")!",
									"6.7, " + MiscTools.getMethodName());
				}

			}
		} else {
			this
			.getLoggingInterface()
			.logInformation(
					identifierNode.getLine(),
					identifierNode.getEndLine(),
					MessageClass.STYLE,
					"The declaration of template \""
					+ identifierNode.getText()
					+ "\" cannot be resolved! It cannot be determined whether it is a modified template.",
					"6.7, " + MiscTools.getMethodName());
		}

	}

	// -------------------------------------------------------------------------------------------

	public void checkNoNestedAltStatements(LocationAST node) {


		LinkedList<LocationAST> nestedAltConstructNodes = ASTUtil.findTypeNodes(node
//				.getFirstChild()
				, TTCN3ParserTokenTypes.AltConstruct);

		if (nestedAltConstructNodes != null && nestedAltConstructNodes.size() > 0) {
			LocationAST nestedAltConstructNode;

			for (int n = 0; n < nestedAltConstructNodes.size(); n++) {
				nestedAltConstructNode = nestedAltConstructNodes.get(n);
				int nestingDepth = getAltConstructNesting(nestedAltConstructNode); 
				if (nestingDepth > T3Q.activeProfile.getMaximumAllowedNestingDepth()) {
					this.getLoggingInterface().logWarning(nestedAltConstructNode.getLine(), nestedAltConstructNode.getEndLine(), MessageClass.STYLE, "Alt statement nesting depth ("+nestingDepth+") exceeds maximum allowed nesting depth ("+T3Q.activeProfile.getMaximumAllowedNestingDepth()+")!", "6.3, "+MiscTools.getMethodName());
				}
			}
		}

	}

	// -------------------------------------------------------------------------------------------

	private int getAltConstructNesting(LocationAST node){
		int nestingDepth = 1;
		LocationAST containingAltConstructNode = LocationAST.resolveParentsUntilType(node.getParent(), new int[] {TTCN3ParserTokenTypes.AltConstruct, TTCN3ParserTokenTypes.AltstepDef});
		
		if (containingAltConstructNode != null){
			nestingDepth += getAltConstructNesting(containingAltConstructNode);
		} else {
			nestingDepth = 0;
		}
		return nestingDepth;
	}
	
	// -------------------------------------------------------------------------------------------

	public void checkNoAnyTypeKeyword(LocationAST node) {
		// Not explicitly necessary, for consistency only
		boolean problemOccured = false;
		problemOccured = true;
		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(), node.getEndLine(), MessageClass.STYLE, "Keyword \"anytype\" is used!", "6.5, "+MiscTools.getMethodName());
		}

	}

	// -------------------------------------------------------------------------------------------

	public void checkNoGotoStatements(LocationAST node) {
		// Not explicitly necessary, for consistency only
		boolean problemOccured = false;
		problemOccured = true;
		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(), node.getEndLine(), MessageClass.STYLE, "A \"goto\" statement is used!", "6.1, "+MiscTools.getMethodName());
		}

	}

	// -------------------------------------------------------------------------------------------

	public void checkNoLabelStatements(LocationAST node) {
		// Not explicitly necessary, for consistency only
		boolean problemOccured = false;
		problemOccured = true;
		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(), node.getEndLine(), MessageClass.STYLE, "A \"label\" statement is used!", "6.1, "+MiscTools.getMethodName());
		}

	}

	// -------------------------------------------------------------------------------------------

	public void checkExternalFunctionInvocationPrecededByLogStatement(
			LocationAST node) {

		LocationAST identifierNode = (LocationAST) ASTUtil.findChild(node,
				TTCN3ParserTokenTypes.Identifier);

		Symbol symbol = identifierNode.getFirstChild().getSymbol();

		// TODO: fix
		// ETSI SIP/ETSI TS 102 027-3
		// (SIP) -v4.2.5 (2008-01)/SIP_Steps.ttcn
		// : Function Call to Response_401_r_1 on Line:12830
		// PROBLEM: Symbol scope cannot be determined.

		if (symbol == null) {
			this.getLoggingInterface().logInformation(node.getLine(),
					node.getEndLine(),
					MessageClass.LOGGING,
					"Function definition for \""
							+ identifierNode.getFirstChild().getText()
							+ "\" cannot be resolved! It cannot be determined if it is an external function call!",
					"5.3, " + MiscTools.getMethodName());
			
		}
		
		if (symbol instanceof FunctionSymbol) {
			FunctionSymbol fs = (FunctionSymbol) symbol;

			if (fs.isExternal()) {
				boolean problemOccured = false;
				String symbolName = fs.getName();

				LocationAST parentNode = LocationAST.resolveParentsUntilType(node,
						TTCN3ParserTokenTypes.FunctionStatement);

				if (parentNode == null) {
					LocationAST moduleDefinitionNode = LocationAST.resolveParentsUntilType(node,
							TTCN3ParserTokenTypes.ModuleDefinition);
					if (moduleDefinitionNode != null) {

						this.getLoggingInterface().logInformation(node.getLine(),
								node.getEndLine(),
								MessageClass.LOGGING,
								"Call to external function \""
										+ symbolName
										+ "\" within a definition on the module level. It cannot be preceded by a log statement!",
								"5.3, " + MiscTools.getMethodName());

					} else {
						// TODO: produce a debugging message?
					}
					return;
				}

				int functionStatementOrDefsVisitedCount = visitor	.getFunctionStatementOrDefNodes()
																	.size();
				if (parentNode == visitor	.getFunctionStatementOrDefNodes()
											.get(functionStatementOrDefsVisitedCount - 1)) {

					int i = functionStatementOrDefsVisitedCount - 2;
					if (i >= 0) {
						LocationAST next = null;
						// skip semicolons
						next = visitor	.getFunctionStatementOrDefNodes()
										.get(i)
										.getNextSibling();

						while ((next != null)
								&& (next.getType() == TTCN3ParserTokenTypes.SemiColon)) {
							next = next.getNextSibling();
						}

						if (next == parentNode) {
							LocationAST resultNode = (LocationAST) ASTUtil.findChild(visitor.getFunctionStatementOrDefNodes()
																							.get(i),
									TTCN3ParserTokenTypes.LogStatement);
							if (resultNode == null) {
								problemOccured = true;
							}
						} else {
							problemOccured = true;
						}
					} else {
						problemOccured = true;
					}
				}				
				
//				LocationAST parentSibling = parentNode.getNextSibling();
//
//				// Check if there is a next sibling and if it is a SemiColon
//				if (parentSibling != null
//						&& parentSibling.getType() == TTCN3ParserTokenTypes.SemiColon) {
//					// Next sibling is a SemiColon, skip to the next one
//					parentSibling = parentSibling.getNextSibling();
//				}
//
//				if (parentSibling != null) {
//
//					if (parentSibling.getType() == TTCN3ParserTokenTypes.FunctionStatement) {
//						if (ASTUtil.findTypeNodes(parentSibling,
//								TTCN3ParserTokenTypes.LogStatement).isEmpty()) {
//							// fixed: consider using ASTUtil.findChild instead
//							// => not for now
//
//							// Sibling does not contain a log statement
//							problemOccured = true;
//
//						}
//					} else {
//						// Sibling not of type FunctionStateme -> no Log
//						// statement present
//						problemOccured = true;
//					}
//				} else {
//					// No sibling present -> no Log statement present
//					problemOccured = true;
//
//				}

				if (problemOccured) {
					this.getLoggingInterface().logWarning(node.getLine(),
							node.getEndLine(),
							MessageClass.LOGGING,
							"Call to external function \"" + symbolName
									+ "\" not preceded by a log statement!",
							"5.3, " + MiscTools.getMethodName());
				}

			} else {
				// Symbol Not a Function Symbol
			}
		}
	}
	
	// -------------------------------------------------------------------------------------------
	
	public void checkInconcOrFailSetVerdictPrecededByLog(LocationAST node) {
		//TODO: no more function statement or defs 
		//TODO: fixed issue with statement blocks
		
		// Fixed: currently based on SetLocalVerdict node,
		// consider starting at the fail or inconc node instead, checking
		// whether it is
		// within a set verdict context and proceed from there
		// fix: it is specific to the set verdict and not to inconc or fail
		// alone

		boolean problemOccured = false;
		if (ASTUtil.findChild(node, TTCN3ParserTokenTypes.Fail) != null
				|| ASTUtil.findChild(node, TTCN3ParserTokenTypes.Inconc) != null) {

			LocationAST parentNode = LocationAST.resolveParentsUntilType(node,
					new int[] {TTCN3ParserTokenTypes.FunctionStatement});
			if (parentNode == null){
				this.getLoggingInterface().logFix(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Cannot resolve the parent node", "5.4, "+MiscTools.getMethodName());		
				return;
			}

			int functionStatementOrDefsVisitedCount = visitor
					.getFunctionStatementOrDefNodes().size();
			if (parentNode == visitor.getFunctionStatementOrDefNodes().get(
					functionStatementOrDefsVisitedCount - 1)) {

				int i = functionStatementOrDefsVisitedCount - 2;
				if (i >= 0) {
					LocationAST next = null;
					// skip semicolons
					next = visitor.getFunctionStatementOrDefNodes().get(i)
							.getNextSibling();

					while ((next != null)
							&& (next.getType() == TTCN3ParserTokenTypes.SemiColon)) {
						next = next.getNextSibling();
					}

					if (next == parentNode) {
						LocationAST resultNode = (LocationAST) ASTUtil
								.findChild(visitor
										.getFunctionStatementOrDefNodes()
										.get(i),
										TTCN3ParserTokenTypes.LogStatement);
						if (resultNode == null) {
							problemOccured = true;
						}
					} else {
						problemOccured = true;
					}
				} else {
					problemOccured = true;
				}
			}
		}

		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "No log statement precedes a fail or inconc setverdict statement!", "5.4, "+MiscTools.getMethodName());
		}
	}

	// -------------------------------------------------------------------------------------------

	public void checkNoPermutationKeyword(LocationAST node) {
		// Not explicitly necessary, for consistency only
		boolean problemOccured = false;
		problemOccured = true;
		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(), node.getEndLine(), MessageClass.STYLE, "Keyword \"permutation\" is used!", "6.4, "+MiscTools.getMethodName());
		}

	}

	// ---------------BASE REFERENCES - TO BE DISCARDED---------------------

	// DEPRECATED: FOR REFERENCE ONLY
	public void checkExternalFunctionInvocationFollowedByLogStatementDEV(
			LocationAST node) {
		// DEBUG

		LocationAST identifierNode = (LocationAST) ASTUtil.findChild(node,
				TTCN3ParserTokenTypes.IDENTIFIER);

		Symbol symb = identifierNode.getScope().resolve(identifierNode);


		if (symb instanceof FunctionSymbol) {
			FunctionSymbol fs = (FunctionSymbol) symb;

			// DEBUG
			this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "external: " + fs.isExternal());

			
			if (fs.isExternal()) {
				boolean problemOccured = false;
				String symbolName = fs.getName();
				LocationAST parentNode = LocationAST.resolveParentsUntilType(
						node, TTCN3ParserTokenTypes.FunctionStatement);
				// DEBUG
				this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Parent Type:"
						+ parentNode.getType());

				this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Parent Text:"
						+ parentNode.getText());

				// 2x getNextSibling to skip the SemiColon
				LocationAST parentSibling = parentNode.getNextSibling()
						.getNextSibling();

				if (parentSibling != null) {
					// DEBUG
					this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Parent Sibling Type:"
							+ parentSibling.getType());
					this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Parent Sibling Text:"
							+ parentSibling.getText());

					if (parentSibling.getType() == TTCN3ParserTokenTypes.FunctionStatement) {
						if (ASTUtil.findTypeNodes(parentSibling,
								TTCN3ParserTokenTypes.LogStatement).isEmpty()) {
							// DEBUG
							this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Sibling does not containg a log statement!");
							problemOccured = true;

						}
					} else {
						// DEBUG
						this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Sibling not of Type FunctionStatement  => No Log Statement Present");
						problemOccured = true;
					}
				} else {
					// DEBUG
					this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "No Sibling Present! => No Log Statement Present");
					problemOccured = true;

				}

				if (problemOccured) {
					this.getLoggingInterface().logWarning(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Call to external function \""
							+ symbolName
							+ "\" not followed by a log statement!", "5.4, "+ MiscTools.getMethodName());
				}

			} else {
				// DEBUG
				this.getLoggingInterface().logDebug(node.getLine(), node.getEndLine(), MessageClass.LOGGING, "Symbol NOT a function symbol");
			}

		}
	}

	public void setReferenceFinder(ReferenceFinder referenceFinder) {
		this.referenceFinder = referenceFinder;
	}

	public ReferenceFinder getReferenceFinder() {
		return referenceFinder;
	}




}
