tracefw/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/source/ContextAreaParser.java
branchRCL_3
changeset 20 ca8a1b6995f6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tracefw/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/source/ContextAreaParser.java	Tue Aug 31 16:45:49 2010 +0300
@@ -0,0 +1,521 @@
+/*
+* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). 
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+* Parser for source contexts
+*
+*/
+package com.nokia.tracecompiler.source;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Parser for source contexts
+ * 
+ */
+class ContextAreaParser {
+
+	/**
+	 * Source parser
+	 */
+	private SourceParser parser;
+
+	/**
+	 * List of source file contexts
+	 */
+	protected ArrayList<SourceContext> contextAreas = new ArrayList<SourceContext>();
+
+	/**
+	 * Comparator for array sorting and searching
+	 */
+	private PositionArrayComparator arrayComparator = new PositionArrayComparator();
+
+	/**
+	 * "usingnamespace" text
+	 */
+	private static final String USINGNAMESPACE = "usingnamespace"; //$NON-NLS-1$
+
+	/**
+	 * Start index of "using" substring in "usingnamespace" string
+	 */
+	private static final int START_INDEX_OF_USING_SUBSTRING = 0; // CodForChk_Dis_Magic
+
+	/**
+	 * End index of "using" substring in "usingnamespace" string
+	 */
+	private static final int END_INDEX_OF_USING_SUBSTRING = 5; // CodForChk_Dis_Magic
+
+	/**
+	 * Start index of "namespace" substring in "usingnamespace" string
+	 */
+	private static final int START_INDEX_OF_NAMESPACE_SUBSTRING = 5; // CodForChk_Dis_Magic
+
+	/**
+	 * End index of "namespace" substring in "usingnamespace" string
+	 */
+	private static final int END_INDEX_OF_NAMESPACE_SUBSTRING = 14; // CodForChk_Dis_Magic
+
+	/**
+	 * Constructor
+	 * 
+	 * @param parser
+	 *            the source parser
+	 */
+	ContextAreaParser(SourceParser parser) {
+		this.parser = parser;
+	}
+
+	/**
+	 * Resets the context areas
+	 */
+	void reset() {
+		contextAreas.clear();
+	}
+
+	/**
+	 * Returns the context at given offset
+	 * 
+	 * @param offset
+	 *            the offset to the source data
+	 * @return the context at the offset or null if no context exists
+	 * @throws SourceParserException
+	 *             if parser fails
+	 */
+	SourceContext parseAndGet(int offset) throws SourceParserException {
+		if (contextAreas.isEmpty()) {
+			parseAll();
+		}
+		int index = find(offset);
+		SourceContext context = null;
+		if (index >= 0) {
+			context = contextAreas.get(index);
+		}
+		return context;
+	}
+
+	/**
+	 * Gets the context areas. If the areas have not been parsed, this parses
+	 * them
+	 * 
+	 * @return the areas
+	 * @throws SourceParserException
+	 *             if parser fails
+	 */
+	Iterator<SourceContext> parseAndGetAll() throws SourceParserException {
+		if (contextAreas.isEmpty()) {
+			parseAll();
+		}
+		return contextAreas.iterator();
+	}
+
+	/**
+	 * Gets the context area list. This does not parse the areas
+	 * 
+	 * @return the list of context areas
+	 */
+	List<SourceContext> getContextList() {
+		return contextAreas;
+	}
+
+	/**
+	 * Finds the array index of the context area which contains the offset. If
+	 * none of the areas contain the offset, returns negative integer indicating
+	 * the index of the context area following the offset
+	 * 
+	 * @param offset
+	 *            the offset to the data
+	 * @return the context area index
+	 */
+	int find(int offset) {
+		return Collections.binarySearch(contextAreas, new SourceLocationBase(
+				parser, offset), arrayComparator);
+	}
+
+	/**
+	 * Builds the context array
+	 * 
+	 * @throws SourceParserException
+	 *             if parser fails
+	 */
+	void parseAll() throws SourceParserException { // CodForChk_Dis_ComplexFunc
+		contextAreas.clear();
+		char value;
+		
+		int inBrackets = 0;
+		int inContext = 0;
+		int inNamespace = 0;
+
+		int usingIndex = START_INDEX_OF_USING_SUBSTRING;
+		int usingKeywordEnd = 0;
+		int namespaceIndex = START_INDEX_OF_NAMESPACE_SUBSTRING;
+		int nameSpaceKeywordEnd = 0;
+		int previousIndexBeforeNamespace = 0;
+		boolean checkNextCharacter = false;
+
+		SourceContext context = null;
+		SourceIterator itr = parser.createIterator(0, SourceParser.SKIP_ALL);
+
+		while (itr.hasNext()) {
+			value = itr.next();
+
+			// Next character check is need only if we have found "namespace"
+			// text
+			if (checkNextCharacter) {
+
+				// Next character after "namespace" text should be space.
+				// Because we have skipped spaces, current index should be
+				// bigger than nameSpaceKeywordEnd + 1. If it is not space then
+				// we are not inside namespace
+				if (itr.currentIndex() - nameSpaceKeywordEnd < 2) { // CodForChk_Dis_Magic
+					inNamespace--;
+				}
+				checkNextCharacter = false;
+			}
+
+			// Check is character part of "using" text
+			if (value == USINGNAMESPACE.charAt(usingIndex)) {
+				usingIndex++;
+			} else {
+
+				// Character not part of "using" text -> reset usingIndex
+				usingIndex = START_INDEX_OF_USING_SUBSTRING;
+			}
+
+			// Check that did we found "using" text
+			if (usingIndex == END_INDEX_OF_USING_SUBSTRING) {
+				usingKeywordEnd = itr.currentIndex();
+				usingIndex = START_INDEX_OF_USING_SUBSTRING;
+			}
+
+			// Check is character part of "namespace" text
+			if (value == USINGNAMESPACE.charAt(namespaceIndex)) {
+				if (previousIndexBeforeNamespace == 0) {
+					previousIndexBeforeNamespace = itr.previousIndex();
+				}
+				namespaceIndex++;
+			} else {
+
+				// Character not part of "namespace" text -> reset
+				// previousIndexBeforeNamespace and namespaceIndex
+				previousIndexBeforeNamespace = 0;
+				namespaceIndex = START_INDEX_OF_NAMESPACE_SUBSTRING;
+			}
+
+			// Check that did we found "namespace" text
+			if (namespaceIndex == END_INDEX_OF_NAMESPACE_SUBSTRING) {
+				nameSpaceKeywordEnd = itr.currentIndex();
+
+				// If there was "using" text just before "namespace" text, then
+				// namespace is defined like: "using namespace foo;" and we are
+				// not going inside namespace brackets
+				if (usingKeywordEnd != previousIndexBeforeNamespace) {
+					inNamespace++;
+					checkNextCharacter = true;
+				}
+				namespaceIndex = START_INDEX_OF_NAMESPACE_SUBSTRING;
+			}
+
+			if (value == '{') {
+				inBrackets++;
+
+				// Check that are we inside namespace or context
+				if (inBrackets > inNamespace) {
+					inContext++;
+					if (inContext == 1) {
+						int start = itr.currentIndex() + 1;
+						context = new SourceContext(parser, start);
+
+						// Includes the '{' character into the context
+						if (!createContext(context, start - 2)) { // CodForChk_Dis_Magic
+							context = null;
+						}
+					}
+				}
+			} else if (value == '}') {
+				// Check that are we exiting from context or namespace
+				if (inBrackets == inNamespace) {
+					inNamespace--;
+				} else {
+					inContext--;
+					if (inContext == 0 && context != null) {
+						context.setLength(itr.currentIndex() + 1
+								- context.getOffset());
+						contextAreas.add(context);
+					}
+				}
+
+				inBrackets--;
+			}
+		}
+	}
+
+	/**
+	 * Sets the data to the source context
+	 * 
+	 * @param context
+	 *            the source context to be updated
+	 * @param offset
+	 *            the index preceeding the '{' character
+	 * @return true if valid, false otherwise
+	 * @throws SourceParserException
+	 *             if processing fails
+	 */
+	private boolean createContext(SourceContext context, int offset)
+			throws SourceParserException {
+		ContextSearchData data = new ContextSearchData();
+		data.itr = parser.createIterator(offset, SourceParser.BACKWARD_SEARCH
+				| SourceParser.SKIP_ALL);
+		data.context = context;
+		while (data.itr.hasNext() && !data.finished) {
+			char c = data.itr.next();
+			// Function start or stop character or statement separator breaks
+			// the search in normal case. In case of nested class separator
+			// character breaks the search.
+			if (c == ';' || c == '}' || c == '{'
+					|| (c == ':' && data.itr.peek() == ':')
+					&& data.classStartIndex != -1) {
+				processContextTerminator(context, data, false);
+			} else if (!data.parametersFound) {
+				processParametersNotFoundCharacter(data, c);
+			} else if (c == ')' || c == '(' || c == ','
+					|| (c == ':' && data.itr.peek() != ':')) {
+				// Constructor member initializer list may contain brackets, ','
+				// and ':'. When one of the characters from member initializer
+				// list is encountered, this assumes that the previous
+				// one was not the actual function parameter list yet. All
+				// variables are reset in that case
+				data.parametersFound = false;
+				data.functionEndIndex = -1;
+				data.functionStartIndex = -1;
+				data.classEndIndex = -1;
+				processParametersNotFoundCharacter(data, c);
+			} else if (data.functionEndIndex == -1) {
+				processFunctionNameNotFoundCharacter(data, c);
+			} else if (data.functionStartIndex == -1) {
+				processFunctionNameCharacter(context, data, c);
+			} else if (data.classEndIndex == -1) {
+				processClassNameNotFoundCharacter(data);
+			} else if (data.classStartIndex == -1) {
+				processClassNameCharacter(context, data, c);
+			} else {
+				processReturnTypeCharacter(context, data);
+			}
+		}
+		if (!data.finished) {
+			processContextTerminator(context, data, true);
+		}
+		return data.valid;
+	}
+
+	/**
+	 * Processes a character after class and function names have been found
+	 * 
+	 * @param context
+	 *            the context
+	 * @param data
+	 *            the search data
+	 * @throws SourceParserException
+	 *             if processing fails
+	 */
+	private void processReturnTypeCharacter(SourceContext context,
+			ContextSearchData data) throws SourceParserException {
+		if (data.itr.hasSkipped()) {
+			// Collects all return type candidates to the context
+			addReturnType(context, data.itr.previousIndex(),
+					data.returnEndIndex);
+			data.returnEndIndex = data.itr.currentIndex();
+		}
+	}
+
+	/**
+	 * Processes a character after function name has been found, but class name
+	 * has not yet been found
+	 * 
+	 * @param data
+	 *            the search flags
+	 */
+	private void processClassNameNotFoundCharacter(ContextSearchData data) {
+		// After start of function and the separator has been found, the
+		// next character marks the end of class name
+		data.classEndIndex = data.itr.currentIndex() + 1;
+	}
+
+	/**
+	 * Parses a character which belongs to the class name
+	 * 
+	 * @param context
+	 *            the source context to be parsed
+	 * @param data
+	 *            the context search parameters
+	 * @param c
+	 *            the character
+	 * @throws SourceParserException
+	 *             if processing fails
+	 */
+	private void processClassNameCharacter(SourceContext context,
+			ContextSearchData data, char c) throws SourceParserException {
+		if (data.itr.hasSkipped() || (c == ':' && data.itr.peek() == ':')) {
+			// Start of class name is found when iterator skips over
+			// white space or comment characters or in case of nested class
+			// separator character has been found
+			context.setFunctionName(parser.getSource().get(
+					data.functionStartIndex,
+					data.functionEndIndex - data.functionStartIndex));
+			data.classStartIndex = data.itr.previousIndex();
+			data.returnEndIndex = data.itr.currentIndex();
+			context.setClassName(parser.getSource().get(data.classStartIndex,
+					data.classEndIndex - data.classStartIndex));
+
+			// In case of nested class skips over the second ':'
+			if (c == ':' && data.itr.peek() == ':') {
+				data.itr.next();
+			}
+		}
+	}
+
+	/**
+	 * Processes a character while within function name
+	 * 
+	 * @param context
+	 *            the source context under processing
+	 * @param data
+	 *            the context search flags
+	 * @param c
+	 *            the character
+	 * @throws SourceParserException
+	 *             if processing fails
+	 */
+	private void processFunctionNameCharacter(SourceContext context,
+			ContextSearchData data, char c) throws SourceParserException {
+		// After end of function has been found the separator character
+		// marks the start of function
+		if (c == ':') {
+			if (data.itr.hasNext() && data.itr.peek() == ':') {
+				data.functionStartIndex = data.itr.previousIndex();
+				context.setFunctionName(parser.getSource().get(
+						data.functionStartIndex,
+						data.functionEndIndex - data.functionStartIndex));
+				// Skips over the second ':'
+				data.itr.next();
+			} else {
+				// Only one ':' character -> Invalid
+				data.finished = true;
+			}
+		} else if (data.itr.hasSkipped()) {
+			// If the iterator skipped over some characters and the next
+			// character is not ':' the function is a non-member
+			data.functionStartIndex = data.itr.previousIndex();
+			context.setFunctionName(parser.getSource().get(
+					data.functionStartIndex,
+					data.functionEndIndex - data.functionStartIndex));
+			// Class name indices are set so parser does not search for them
+			data.classStartIndex = data.itr.previousIndex();
+			data.classEndIndex = data.itr.previousIndex();
+			data.returnEndIndex = data.itr.currentIndex();
+		}
+	}
+
+	/**
+	 * Processes a character when function name has not yet been found
+	 * 
+	 * @param data
+	 *            the search flags
+	 * @param c
+	 *            the character to be processed
+	 */
+	private void processFunctionNameNotFoundCharacter(ContextSearchData data,
+			char c) {
+		// The next character after parameters is the end of function
+		if (c == ':') {
+			data.finished = true;
+		}
+		data.functionEndIndex = data.itr.currentIndex() + 1;
+	}
+
+	/**
+	 * Checks if the character is '(' or ')' and updates the parametersFound
+	 * flag accordingly
+	 * 
+	 * @param data
+	 *            the search data
+	 * @param c
+	 *            the current character
+	 */
+	private void processParametersNotFoundCharacter(ContextSearchData data,
+			char c) {
+		if (c == ')') {
+			data.inParameters++;
+		} else if (c == '(') {
+			data.inParameters--;
+			if (data.inParameters == 0) {
+				data.context.setParametersStartIndex(data.itr.currentIndex());
+				data.parametersFound = true;
+			}
+		}
+	}
+
+	/**
+	 * Processes a context terminating character
+	 * 
+	 * @param context
+	 *            the context under processing
+	 * @param data
+	 *            the search data
+	 * @param startOfFile
+	 *            context was terminated due to start of file
+	 * @throws SourceParserException
+	 *             if processing fails
+	 */
+	private void processContextTerminator(SourceContext context,
+			ContextSearchData data, boolean startOfFile)
+			throws SourceParserException {
+		int offset = startOfFile ? data.itr.currentIndex() : data.itr
+				.previousIndex();
+		if (data.classStartIndex != -1) {
+			addReturnType(context, offset, data.returnEndIndex);
+			data.valid = true;
+		} else if (data.classEndIndex != -1) {
+			context.setClassName(parser.getSource().get(offset,
+					data.classEndIndex - offset));
+			data.valid = true;
+		} else if (data.functionEndIndex != -1) {
+			context.setFunctionName(parser.getSource().get(offset,
+					data.functionEndIndex - offset));
+			data.valid = true;
+		}
+		// Finished flag is set. If function name was not found, the valid flag
+		// remains false
+		data.finished = true;
+	}
+
+	/**
+	 * Adds a return type to the context
+	 * 
+	 * @param context
+	 *            the context to be searched
+	 * @param start
+	 *            the start index
+	 * @param end
+	 *            the end index
+	 * @throws SourceParserException
+	 *             if return type cannot be added
+	 */
+	private void addReturnType(SourceContext context, int start, int end)
+			throws SourceParserException {
+		context.addReturnType(parser.getSource().get(start, end - start + 1));
+	}
+}