--- /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);
+ }
+
+}