tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/engine/source/SourceProperties.java
changeset 56 aa2539c91954
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/engine/source/SourceProperties.java	Fri Oct 08 14:56:39 2010 +0300
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2010 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:
+ *
+ * Properties of a source document opened to Eclipse editor
+ *
+ */
+package com.nokia.tracecompiler.engine.source;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import com.nokia.tracecompiler.engine.TraceCompilerEngineConfiguration;
+import com.nokia.tracecompiler.engine.TraceCompilerEngineGlobals;
+import com.nokia.tracecompiler.engine.TraceLocation;
+import com.nokia.tracecompiler.model.TraceModel;
+import com.nokia.tracecompiler.source.SourceDocumentFactory;
+import com.nokia.tracecompiler.source.SourceDocumentInterface;
+import com.nokia.tracecompiler.source.SourceIterator;
+import com.nokia.tracecompiler.source.SourceParser;
+import com.nokia.tracecompiler.source.SourceParserException;
+import com.nokia.tracecompiler.source.SourcePropertyProvider;
+import com.nokia.tracecompiler.source.SourceStringSearch;
+
+/**
+ * Properties of a source document which contains trace locations
+ * 
+ */
+public class SourceProperties implements Iterable<TraceLocation> {
+
+	/**
+	 * Trace locations within the source
+	 */
+	private ArrayList<TraceLocation> locations = new ArrayList<TraceLocation>();
+
+	/**
+	 * Source parser
+	 */
+	private SourceParser sourceParser;
+
+	/**
+	 * Offset is stored in preProcess and reset in postProcess.
+	 */
+	private int firstChangedLocation = -1;
+
+	/**
+	 * Offset is stored in preProcess and reset in postProcess.
+	 */
+	private int firstUnchangedLocation = -1;
+
+	/**
+	 * The searchers for trace identifiers
+	 */
+	private ArrayList<SourceStringSearch> searchers = new ArrayList<SourceStringSearch>();
+
+	/**
+	 * Start index for calls to parseTrace
+	 */
+	private int searchStartIndex;
+
+	/**
+	 * Read-only flag
+	 */
+	private boolean readOnly;
+
+	/**
+	 * Creates source properties for given source document
+	 * 
+	 * @param model
+	 *            the trace model
+	 * @param framework
+	 *            the document framework
+	 * @param document
+	 *            the document
+	 */
+	SourceProperties(TraceModel model, SourceDocumentFactory framework,
+			SourceDocumentInterface document) {
+		sourceParser = new SourceParser(framework, document);
+		Iterator<SourceParserRule> parsers = model
+				.getExtensions(SourceParserRule.class);
+		while (parsers.hasNext()) {
+			// The rule defines what to search and how to interpret the
+			// parameters. It is stored into the searcher as search data
+			addParserRule(parsers.next());
+		}
+	}
+
+	/**
+	 * Gets the source parser
+	 * 
+	 * @return the parser
+	 */
+	public SourceParser getSourceParser() {
+		return sourceParser;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Iterable#iterator()
+	 */
+	public Iterator<TraceLocation> iterator() {
+		return locations.iterator();
+	}
+
+	/**
+	 * Gets the file name of this source
+	 * 
+	 * @return the name
+	 */
+	public String getFileName() {
+		String retval = null;
+		if (sourceParser != null) {
+			SourceDocumentInterface source = sourceParser.getSource();
+			if (source != null) {
+				SourcePropertyProvider provider = source.getPropertyProvider();
+				if (provider != null) {
+					retval = provider.getFileName();
+				}
+			}
+		}
+		return retval;
+	}
+
+	/**
+	 * Sets the read-only flag for this source. Traces cannot be added to
+	 * read-only sources, but they can be parsed for data
+	 * 
+	 * @param readOnly
+	 *            the read-only flag
+	 */
+	void setReadOnly(boolean readOnly) {
+		this.readOnly = readOnly;
+	}
+
+	/**
+	 * Gets the read-only flag
+	 * 
+	 * @return read-only flag
+	 */
+	public boolean isReadOnly() {
+		return readOnly;
+	}
+
+	/**
+	 * Source opened notification
+	 */
+	void sourceOpened() {
+		updateTraces(0, sourceParser.getDataLength());
+	}
+
+	/**
+	 * Parses the document starting from given offset and locates the trace
+	 * entries from it. The first unchanged trace entry stops the search
+	 * 
+	 * @param startOffset
+	 *            the offset where to start the search
+	 * @param endOffset
+	 *            the offset where to end the search
+	 */
+	private void updateTraces(int startOffset, int endOffset) {
+		Iterator<SourceStringSearch> itr = searchers.iterator();
+		while (itr.hasNext()) {
+			SourceStringSearch searcher = itr.next();
+			searcher.resetSearch(startOffset, endOffset);
+			updateTraces(endOffset, searcher);
+		}
+	}
+
+	/**
+	 * Uses the given SourceSearch to parse traces
+	 * 
+	 * @param end
+	 *            the offset where parser should stop
+	 * @param searcher
+	 *            the searcher
+	 */
+	private void updateTraces(int end, SourceStringSearch searcher) {
+		int offset;
+		searchStartIndex = 0;
+		// If not updating, the entries contents are processed
+		do {
+			offset = searcher.findNext();
+			try {
+				if (offset != -1 && offset < end) {
+					String tag = isValidTrace(offset, searcher
+							.getSearchString().length(), searcher, false);
+					if (tag != null) {
+						parseTrace(offset, (SourceParserRule) searcher
+								.getSearchData(), tag);
+					}
+				}
+			} catch (SourceParserException e) {
+				TraceLocation location = new TraceLocation(this, offset,
+						offset + 80);
+				TraceCompilerEngineGlobals
+						.getEvents()
+						.postErrorMessage(
+								Messages
+										.getString("SourceProperties.parsingArrowAtBeginText") + location.getFilePath() + location.getFileName() + Messages.getString("SourceProperties.parsingArrownAtMiddleText") + location.getLineNumber(), null, true); //$NON-NLS-1$ //$NON-NLS-2$
+				// If the parameters cannot be parsed, the trace is
+				// not added to the array
+			}
+		} while (offset != -1 && offset < end);
+	}
+
+	/**
+	 * Parses a trace found from the document and adds it to the document's list
+	 * of positions. The position updater keeps the trace location up-to-date.
+	 * 
+	 * @param offset
+	 *            the offset to the trace
+	 * @param parserRule
+	 *            the parser to be attached to the location
+	 * @param locationTag
+	 *            the tag of the location
+	 * @throws SourceParserException
+	 *             if trace cannot be parsed
+	 */
+	private void parseTrace(int offset, SourceParserRule parserRule,
+			String locationTag) throws SourceParserException {
+		int arrayIndex = -1;
+		// Checks the changed locations. If a matching offset if found, the
+		// location is an existing one. In that case the location is not
+		// added to the array. If an offset larger than the new offset is
+		// found from the array, the location is inserted into that slot. If
+		// all locations within the array are smaller than the new offset,
+		// the new location is inserted before the first unchanged location.
+		// Since the locations in the array are ordered, the checking can
+		// always start from the latest location that has been found from
+		// the array. The caller of this function must set
+		// parseTraceStartIndex to 0 before starting a loop where this
+		// function is called. If firstUnchangedLocation is -1, this is the
+		// first time the file is being parsed and thus all locations are
+		// checked
+		boolean found = false;
+		int searchEndIndex;
+		int newSearchStartIndex = -1;
+		if (firstUnchangedLocation >= 0) {
+			searchEndIndex = firstUnchangedLocation;
+		} else {
+			searchEndIndex = locations.size();
+		}
+		for (int i = searchStartIndex; i < searchEndIndex && !found; i++) {
+			TraceLocation location = locations.get(i);
+			// Deleted locations are ignored. If a trace was replaced, the
+			// new offset will match the offset of the deleted one.
+			if (!location.isDeleted()) {
+				// If the offset of the trace matches an existing offset,
+				// the trace is old one. If the offset within the array is
+				// larger than the source offset, the trace found from
+				// source is new.
+				if (location.getOffset() == offset) {
+					found = true;
+					// Starts the next search from the value following the
+					// trace that was found
+					searchStartIndex = i + 1;
+					arrayIndex = -1;
+				} else if (location.getOffset() > offset) {
+					found = true;
+					// A new trace will be added into the current index, so
+					// the next search will start from the same location as
+					// was checked now. The index is updated after the trace has
+					// succesfully been created
+					newSearchStartIndex = i + 1;
+					arrayIndex = i;
+				}
+			}
+		}
+		// If trace was not found from the list, the trace is new and all
+		// traces following it are also new. The start index is set to point
+		// past the first unchanged location and thus the next search will
+		// ignore the above loop.
+		if (!found) {
+			arrayIndex = searchEndIndex;
+			searchStartIndex = firstUnchangedLocation + 1;
+		}
+		if (arrayIndex >= 0) {
+			// Creates a new location if it was not found
+			ArrayList<String> list = new ArrayList<String>();
+			int endOfTrace = sourceParser
+					.tokenizeParameters(offset, list, true);
+
+			TraceLocation location = new TraceLocation(this, offset, endOfTrace
+					- offset);
+
+			// The parser rules have been associated with the searchers. The
+			// parser rule that found the location is associated with the
+			// location and used to process its parameters
+			location.setTag(locationTag);
+			location.setParserRule(parserRule);
+			location.setData(list);
+
+			TraceCompilerEngineGlobals
+					.getEvents()
+					.postInfoMessage(
+							Messages
+									.getString("SourceProperties.newTraceLocationFoundBeginText") + location.getFilePath() + location.getFileName() + Messages.getString("SourceProperties.newTraceLocationFoundMiddleText") + location.getLineNumber() + Messages.getString("SourceProperties.newTraceLocationFoundEndText") + location.getTraceText(), null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+			locations.add(arrayIndex, location);
+			// The changed flag is set to newly added traces. If a location
+			// is added prior to the first changed location, the index of first
+			// changed location needs to be adjusted so that the flag gets
+			// cleared in postprocessing. Also the index of first unchanged
+			// location needs to be updated to reflect the changed array
+			if (firstUnchangedLocation >= 0) {
+				location.setContentChanged(true);
+				if (arrayIndex < firstChangedLocation) {
+					firstChangedLocation = arrayIndex;
+				}
+				firstUnchangedLocation++;
+			}
+			// Updates the search start index if trace creation was succesful
+			if (newSearchStartIndex >= 0) {
+				searchStartIndex = newSearchStartIndex;
+			}
+		}
+	}
+
+	/**
+	 * Checks that a trace is valid
+	 * 
+	 * @param offset
+	 *            offset to trace identifier
+	 * @param length
+	 *            length of trace
+	 * @param searcher
+	 *            the source searcher
+	 * @param checkMainTag
+	 *            true if the main search tag needs to be checked, false if only
+	 *            the tag suffix is checked
+	 * @return the trace tag or null if trace is not valid
+	 */
+	private String isValidTrace(int offset, int length,
+			SourceStringSearch searcher, boolean checkMainTag) {
+		String retval = null;
+		try {
+			int idlen = searcher.getSearchString().length();
+			int idend = offset + idlen;
+			if (checkMainTag) {
+				if (length >= idlen
+						&& searcher.isSearchStringMatch(sourceParser.getData(
+								offset, idlen))) {
+					// The previous character must be a separator or white space
+					if (offset == 0
+							|| !Character.isJavaIdentifierPart(sourceParser
+									.getData(offset - 1))) {
+						retval = getSearchTag(offset, idend);
+					}
+				}
+			} else {
+				// If main tag is not checked
+				retval = getSearchTag(offset, idend);
+			}
+			retval = verifyTag(searcher, retval, idlen);
+		} catch (Exception e) {
+			if (TraceCompilerEngineConfiguration.ASSERTIONS_ENABLED) {
+				TraceCompilerEngineGlobals.getEvents().postAssertionFailed(
+						"Trace validity check failed", e); //$NON-NLS-1$
+			}
+		}
+		return retval;
+	}
+
+	/**
+	 * Verifies the tag against tag suffixes from parser
+	 * 
+	 * @param searcher
+	 *            the searcher
+	 * @param tag
+	 *            the tag include main tag and suffix
+	 * @param idlen
+	 *            the length of the main tag
+	 * @return the tag if it is valid, null if not
+	 */
+	private String verifyTag(SourceStringSearch searcher, String tag, int idlen) {
+		if (tag != null) {
+			// The trace suffix is verified by the parser. For example, if
+			// search data is "SymbianTrace" and the tag found from source
+			// is "SymbianTraceData1", the parser checks if "Data1" is a
+			// valid trace tag suffix.
+			if (!((SourceParserRule) searcher.getSearchData())
+					.isAllowedTagSuffix(tag.substring(idlen))) {
+				tag = null;
+			}
+		}
+		return tag;
+	}
+
+	/**
+	 * Gets the search tag between offset and next '(' character
+	 * 
+	 * @param offset
+	 *            the start of tag
+	 * @param idend
+	 *            the end of tag
+	 * @return the tag
+	 * @throws SourceParserException
+	 *             if parser fails
+	 */
+	private String getSearchTag(int offset, int idend)
+			throws SourceParserException {
+		// Locates the parameters starting from trace identifier
+		String retval = null;
+		SourceIterator srcitr = sourceParser.createIterator(idend - 1,
+				SourceParser.SKIP_ALL);
+		boolean found = false;
+		while (srcitr.hasNext() && !found) {
+			char c = srcitr.next();
+			if (c == ';') {
+				// Trace must have parameters
+				found = true;
+			} else if (c == '(') {
+				found = true;
+				// Stores the tag into location
+				retval = sourceParser.getData(offset, srcitr.previousIndex()
+						- offset + 1);
+			} else if (srcitr.hasSkipped()) {
+				// White spaces are not allowed within trace tag
+				found = true;
+			}
+		}
+		return retval;
+	}
+
+	/**
+	 * Checks if a trace can be inserted into given location
+	 * 
+	 * @param offset
+	 *            the offset to the location
+	 * @return true if location is valid
+	 */
+	boolean checkInsertLocation(int offset) {
+		boolean retval = true;
+		try {
+			offset = sourceParser.findStartOfLine(offset, false, true);
+			if (sourceParser.isInExcludedArea(offset)) {
+				retval = false;
+			}
+		} catch (SourceParserException e) {
+			retval = false;
+		}
+		return retval;
+	}
+
+	/**
+	 * Adds a new parser
+	 * 
+	 * @param rule
+	 *            the new parser rule
+	 */
+	void addParserRule(SourceParserRule rule) {
+		SourceStringSearch searcher = sourceParser.startStringSearch(rule
+				.getSearchTag(), 0, -1, SourceParser.MATCH_WORD_BEGINNING
+				| SourceParser.SKIP_ALL);
+		searcher.setSearchData(rule);
+		searchers.add(searcher);
+	}
+
+}