tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/engine/source/SourceProperties.java
changeset 56 aa2539c91954
equal deleted inserted replaced
54:a151135b0cf9 56:aa2539c91954
       
     1 /*
       
     2  * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). 
       
     3  * All rights reserved.
       
     4  * This component and the accompanying materials are made available
       
     5  * under the terms of "Eclipse Public License v1.0"
       
     6  * which accompanies this distribution, and is available
       
     7  * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8  *
       
     9  * Initial Contributors:
       
    10  * Nokia Corporation - initial contribution.
       
    11  *
       
    12  * Contributors:
       
    13  *
       
    14  * Description:
       
    15  *
       
    16  * Properties of a source document opened to Eclipse editor
       
    17  *
       
    18  */
       
    19 package com.nokia.tracecompiler.engine.source;
       
    20 
       
    21 import java.util.ArrayList;
       
    22 import java.util.Iterator;
       
    23 
       
    24 import com.nokia.tracecompiler.engine.TraceCompilerEngineConfiguration;
       
    25 import com.nokia.tracecompiler.engine.TraceCompilerEngineGlobals;
       
    26 import com.nokia.tracecompiler.engine.TraceLocation;
       
    27 import com.nokia.tracecompiler.model.TraceModel;
       
    28 import com.nokia.tracecompiler.source.SourceDocumentFactory;
       
    29 import com.nokia.tracecompiler.source.SourceDocumentInterface;
       
    30 import com.nokia.tracecompiler.source.SourceIterator;
       
    31 import com.nokia.tracecompiler.source.SourceParser;
       
    32 import com.nokia.tracecompiler.source.SourceParserException;
       
    33 import com.nokia.tracecompiler.source.SourcePropertyProvider;
       
    34 import com.nokia.tracecompiler.source.SourceStringSearch;
       
    35 
       
    36 /**
       
    37  * Properties of a source document which contains trace locations
       
    38  * 
       
    39  */
       
    40 public class SourceProperties implements Iterable<TraceLocation> {
       
    41 
       
    42 	/**
       
    43 	 * Trace locations within the source
       
    44 	 */
       
    45 	private ArrayList<TraceLocation> locations = new ArrayList<TraceLocation>();
       
    46 
       
    47 	/**
       
    48 	 * Source parser
       
    49 	 */
       
    50 	private SourceParser sourceParser;
       
    51 
       
    52 	/**
       
    53 	 * Offset is stored in preProcess and reset in postProcess.
       
    54 	 */
       
    55 	private int firstChangedLocation = -1;
       
    56 
       
    57 	/**
       
    58 	 * Offset is stored in preProcess and reset in postProcess.
       
    59 	 */
       
    60 	private int firstUnchangedLocation = -1;
       
    61 
       
    62 	/**
       
    63 	 * The searchers for trace identifiers
       
    64 	 */
       
    65 	private ArrayList<SourceStringSearch> searchers = new ArrayList<SourceStringSearch>();
       
    66 
       
    67 	/**
       
    68 	 * Start index for calls to parseTrace
       
    69 	 */
       
    70 	private int searchStartIndex;
       
    71 
       
    72 	/**
       
    73 	 * Read-only flag
       
    74 	 */
       
    75 	private boolean readOnly;
       
    76 
       
    77 	/**
       
    78 	 * Creates source properties for given source document
       
    79 	 * 
       
    80 	 * @param model
       
    81 	 *            the trace model
       
    82 	 * @param framework
       
    83 	 *            the document framework
       
    84 	 * @param document
       
    85 	 *            the document
       
    86 	 */
       
    87 	SourceProperties(TraceModel model, SourceDocumentFactory framework,
       
    88 			SourceDocumentInterface document) {
       
    89 		sourceParser = new SourceParser(framework, document);
       
    90 		Iterator<SourceParserRule> parsers = model
       
    91 				.getExtensions(SourceParserRule.class);
       
    92 		while (parsers.hasNext()) {
       
    93 			// The rule defines what to search and how to interpret the
       
    94 			// parameters. It is stored into the searcher as search data
       
    95 			addParserRule(parsers.next());
       
    96 		}
       
    97 	}
       
    98 
       
    99 	/**
       
   100 	 * Gets the source parser
       
   101 	 * 
       
   102 	 * @return the parser
       
   103 	 */
       
   104 	public SourceParser getSourceParser() {
       
   105 		return sourceParser;
       
   106 	}
       
   107 
       
   108 	/*
       
   109 	 * (non-Javadoc)
       
   110 	 * 
       
   111 	 * @see java.lang.Iterable#iterator()
       
   112 	 */
       
   113 	public Iterator<TraceLocation> iterator() {
       
   114 		return locations.iterator();
       
   115 	}
       
   116 
       
   117 	/**
       
   118 	 * Gets the file name of this source
       
   119 	 * 
       
   120 	 * @return the name
       
   121 	 */
       
   122 	public String getFileName() {
       
   123 		String retval = null;
       
   124 		if (sourceParser != null) {
       
   125 			SourceDocumentInterface source = sourceParser.getSource();
       
   126 			if (source != null) {
       
   127 				SourcePropertyProvider provider = source.getPropertyProvider();
       
   128 				if (provider != null) {
       
   129 					retval = provider.getFileName();
       
   130 				}
       
   131 			}
       
   132 		}
       
   133 		return retval;
       
   134 	}
       
   135 
       
   136 	/**
       
   137 	 * Sets the read-only flag for this source. Traces cannot be added to
       
   138 	 * read-only sources, but they can be parsed for data
       
   139 	 * 
       
   140 	 * @param readOnly
       
   141 	 *            the read-only flag
       
   142 	 */
       
   143 	void setReadOnly(boolean readOnly) {
       
   144 		this.readOnly = readOnly;
       
   145 	}
       
   146 
       
   147 	/**
       
   148 	 * Gets the read-only flag
       
   149 	 * 
       
   150 	 * @return read-only flag
       
   151 	 */
       
   152 	public boolean isReadOnly() {
       
   153 		return readOnly;
       
   154 	}
       
   155 
       
   156 	/**
       
   157 	 * Source opened notification
       
   158 	 */
       
   159 	void sourceOpened() {
       
   160 		updateTraces(0, sourceParser.getDataLength());
       
   161 	}
       
   162 
       
   163 	/**
       
   164 	 * Parses the document starting from given offset and locates the trace
       
   165 	 * entries from it. The first unchanged trace entry stops the search
       
   166 	 * 
       
   167 	 * @param startOffset
       
   168 	 *            the offset where to start the search
       
   169 	 * @param endOffset
       
   170 	 *            the offset where to end the search
       
   171 	 */
       
   172 	private void updateTraces(int startOffset, int endOffset) {
       
   173 		Iterator<SourceStringSearch> itr = searchers.iterator();
       
   174 		while (itr.hasNext()) {
       
   175 			SourceStringSearch searcher = itr.next();
       
   176 			searcher.resetSearch(startOffset, endOffset);
       
   177 			updateTraces(endOffset, searcher);
       
   178 		}
       
   179 	}
       
   180 
       
   181 	/**
       
   182 	 * Uses the given SourceSearch to parse traces
       
   183 	 * 
       
   184 	 * @param end
       
   185 	 *            the offset where parser should stop
       
   186 	 * @param searcher
       
   187 	 *            the searcher
       
   188 	 */
       
   189 	private void updateTraces(int end, SourceStringSearch searcher) {
       
   190 		int offset;
       
   191 		searchStartIndex = 0;
       
   192 		// If not updating, the entries contents are processed
       
   193 		do {
       
   194 			offset = searcher.findNext();
       
   195 			try {
       
   196 				if (offset != -1 && offset < end) {
       
   197 					String tag = isValidTrace(offset, searcher
       
   198 							.getSearchString().length(), searcher, false);
       
   199 					if (tag != null) {
       
   200 						parseTrace(offset, (SourceParserRule) searcher
       
   201 								.getSearchData(), tag);
       
   202 					}
       
   203 				}
       
   204 			} catch (SourceParserException e) {
       
   205 				TraceLocation location = new TraceLocation(this, offset,
       
   206 						offset + 80);
       
   207 				TraceCompilerEngineGlobals
       
   208 						.getEvents()
       
   209 						.postErrorMessage(
       
   210 								Messages
       
   211 										.getString("SourceProperties.parsingArrowAtBeginText") + location.getFilePath() + location.getFileName() + Messages.getString("SourceProperties.parsingArrownAtMiddleText") + location.getLineNumber(), null, true); //$NON-NLS-1$ //$NON-NLS-2$
       
   212 				// If the parameters cannot be parsed, the trace is
       
   213 				// not added to the array
       
   214 			}
       
   215 		} while (offset != -1 && offset < end);
       
   216 	}
       
   217 
       
   218 	/**
       
   219 	 * Parses a trace found from the document and adds it to the document's list
       
   220 	 * of positions. The position updater keeps the trace location up-to-date.
       
   221 	 * 
       
   222 	 * @param offset
       
   223 	 *            the offset to the trace
       
   224 	 * @param parserRule
       
   225 	 *            the parser to be attached to the location
       
   226 	 * @param locationTag
       
   227 	 *            the tag of the location
       
   228 	 * @throws SourceParserException
       
   229 	 *             if trace cannot be parsed
       
   230 	 */
       
   231 	private void parseTrace(int offset, SourceParserRule parserRule,
       
   232 			String locationTag) throws SourceParserException {
       
   233 		int arrayIndex = -1;
       
   234 		// Checks the changed locations. If a matching offset if found, the
       
   235 		// location is an existing one. In that case the location is not
       
   236 		// added to the array. If an offset larger than the new offset is
       
   237 		// found from the array, the location is inserted into that slot. If
       
   238 		// all locations within the array are smaller than the new offset,
       
   239 		// the new location is inserted before the first unchanged location.
       
   240 		// Since the locations in the array are ordered, the checking can
       
   241 		// always start from the latest location that has been found from
       
   242 		// the array. The caller of this function must set
       
   243 		// parseTraceStartIndex to 0 before starting a loop where this
       
   244 		// function is called. If firstUnchangedLocation is -1, this is the
       
   245 		// first time the file is being parsed and thus all locations are
       
   246 		// checked
       
   247 		boolean found = false;
       
   248 		int searchEndIndex;
       
   249 		int newSearchStartIndex = -1;
       
   250 		if (firstUnchangedLocation >= 0) {
       
   251 			searchEndIndex = firstUnchangedLocation;
       
   252 		} else {
       
   253 			searchEndIndex = locations.size();
       
   254 		}
       
   255 		for (int i = searchStartIndex; i < searchEndIndex && !found; i++) {
       
   256 			TraceLocation location = locations.get(i);
       
   257 			// Deleted locations are ignored. If a trace was replaced, the
       
   258 			// new offset will match the offset of the deleted one.
       
   259 			if (!location.isDeleted()) {
       
   260 				// If the offset of the trace matches an existing offset,
       
   261 				// the trace is old one. If the offset within the array is
       
   262 				// larger than the source offset, the trace found from
       
   263 				// source is new.
       
   264 				if (location.getOffset() == offset) {
       
   265 					found = true;
       
   266 					// Starts the next search from the value following the
       
   267 					// trace that was found
       
   268 					searchStartIndex = i + 1;
       
   269 					arrayIndex = -1;
       
   270 				} else if (location.getOffset() > offset) {
       
   271 					found = true;
       
   272 					// A new trace will be added into the current index, so
       
   273 					// the next search will start from the same location as
       
   274 					// was checked now. The index is updated after the trace has
       
   275 					// succesfully been created
       
   276 					newSearchStartIndex = i + 1;
       
   277 					arrayIndex = i;
       
   278 				}
       
   279 			}
       
   280 		}
       
   281 		// If trace was not found from the list, the trace is new and all
       
   282 		// traces following it are also new. The start index is set to point
       
   283 		// past the first unchanged location and thus the next search will
       
   284 		// ignore the above loop.
       
   285 		if (!found) {
       
   286 			arrayIndex = searchEndIndex;
       
   287 			searchStartIndex = firstUnchangedLocation + 1;
       
   288 		}
       
   289 		if (arrayIndex >= 0) {
       
   290 			// Creates a new location if it was not found
       
   291 			ArrayList<String> list = new ArrayList<String>();
       
   292 			int endOfTrace = sourceParser
       
   293 					.tokenizeParameters(offset, list, true);
       
   294 
       
   295 			TraceLocation location = new TraceLocation(this, offset, endOfTrace
       
   296 					- offset);
       
   297 
       
   298 			// The parser rules have been associated with the searchers. The
       
   299 			// parser rule that found the location is associated with the
       
   300 			// location and used to process its parameters
       
   301 			location.setTag(locationTag);
       
   302 			location.setParserRule(parserRule);
       
   303 			location.setData(list);
       
   304 
       
   305 			TraceCompilerEngineGlobals
       
   306 					.getEvents()
       
   307 					.postInfoMessage(
       
   308 							Messages
       
   309 									.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$
       
   310 
       
   311 			locations.add(arrayIndex, location);
       
   312 			// The changed flag is set to newly added traces. If a location
       
   313 			// is added prior to the first changed location, the index of first
       
   314 			// changed location needs to be adjusted so that the flag gets
       
   315 			// cleared in postprocessing. Also the index of first unchanged
       
   316 			// location needs to be updated to reflect the changed array
       
   317 			if (firstUnchangedLocation >= 0) {
       
   318 				location.setContentChanged(true);
       
   319 				if (arrayIndex < firstChangedLocation) {
       
   320 					firstChangedLocation = arrayIndex;
       
   321 				}
       
   322 				firstUnchangedLocation++;
       
   323 			}
       
   324 			// Updates the search start index if trace creation was succesful
       
   325 			if (newSearchStartIndex >= 0) {
       
   326 				searchStartIndex = newSearchStartIndex;
       
   327 			}
       
   328 		}
       
   329 	}
       
   330 
       
   331 	/**
       
   332 	 * Checks that a trace is valid
       
   333 	 * 
       
   334 	 * @param offset
       
   335 	 *            offset to trace identifier
       
   336 	 * @param length
       
   337 	 *            length of trace
       
   338 	 * @param searcher
       
   339 	 *            the source searcher
       
   340 	 * @param checkMainTag
       
   341 	 *            true if the main search tag needs to be checked, false if only
       
   342 	 *            the tag suffix is checked
       
   343 	 * @return the trace tag or null if trace is not valid
       
   344 	 */
       
   345 	private String isValidTrace(int offset, int length,
       
   346 			SourceStringSearch searcher, boolean checkMainTag) {
       
   347 		String retval = null;
       
   348 		try {
       
   349 			int idlen = searcher.getSearchString().length();
       
   350 			int idend = offset + idlen;
       
   351 			if (checkMainTag) {
       
   352 				if (length >= idlen
       
   353 						&& searcher.isSearchStringMatch(sourceParser.getData(
       
   354 								offset, idlen))) {
       
   355 					// The previous character must be a separator or white space
       
   356 					if (offset == 0
       
   357 							|| !Character.isJavaIdentifierPart(sourceParser
       
   358 									.getData(offset - 1))) {
       
   359 						retval = getSearchTag(offset, idend);
       
   360 					}
       
   361 				}
       
   362 			} else {
       
   363 				// If main tag is not checked
       
   364 				retval = getSearchTag(offset, idend);
       
   365 			}
       
   366 			retval = verifyTag(searcher, retval, idlen);
       
   367 		} catch (Exception e) {
       
   368 			if (TraceCompilerEngineConfiguration.ASSERTIONS_ENABLED) {
       
   369 				TraceCompilerEngineGlobals.getEvents().postAssertionFailed(
       
   370 						"Trace validity check failed", e); //$NON-NLS-1$
       
   371 			}
       
   372 		}
       
   373 		return retval;
       
   374 	}
       
   375 
       
   376 	/**
       
   377 	 * Verifies the tag against tag suffixes from parser
       
   378 	 * 
       
   379 	 * @param searcher
       
   380 	 *            the searcher
       
   381 	 * @param tag
       
   382 	 *            the tag include main tag and suffix
       
   383 	 * @param idlen
       
   384 	 *            the length of the main tag
       
   385 	 * @return the tag if it is valid, null if not
       
   386 	 */
       
   387 	private String verifyTag(SourceStringSearch searcher, String tag, int idlen) {
       
   388 		if (tag != null) {
       
   389 			// The trace suffix is verified by the parser. For example, if
       
   390 			// search data is "SymbianTrace" and the tag found from source
       
   391 			// is "SymbianTraceData1", the parser checks if "Data1" is a
       
   392 			// valid trace tag suffix.
       
   393 			if (!((SourceParserRule) searcher.getSearchData())
       
   394 					.isAllowedTagSuffix(tag.substring(idlen))) {
       
   395 				tag = null;
       
   396 			}
       
   397 		}
       
   398 		return tag;
       
   399 	}
       
   400 
       
   401 	/**
       
   402 	 * Gets the search tag between offset and next '(' character
       
   403 	 * 
       
   404 	 * @param offset
       
   405 	 *            the start of tag
       
   406 	 * @param idend
       
   407 	 *            the end of tag
       
   408 	 * @return the tag
       
   409 	 * @throws SourceParserException
       
   410 	 *             if parser fails
       
   411 	 */
       
   412 	private String getSearchTag(int offset, int idend)
       
   413 			throws SourceParserException {
       
   414 		// Locates the parameters starting from trace identifier
       
   415 		String retval = null;
       
   416 		SourceIterator srcitr = sourceParser.createIterator(idend - 1,
       
   417 				SourceParser.SKIP_ALL);
       
   418 		boolean found = false;
       
   419 		while (srcitr.hasNext() && !found) {
       
   420 			char c = srcitr.next();
       
   421 			if (c == ';') {
       
   422 				// Trace must have parameters
       
   423 				found = true;
       
   424 			} else if (c == '(') {
       
   425 				found = true;
       
   426 				// Stores the tag into location
       
   427 				retval = sourceParser.getData(offset, srcitr.previousIndex()
       
   428 						- offset + 1);
       
   429 			} else if (srcitr.hasSkipped()) {
       
   430 				// White spaces are not allowed within trace tag
       
   431 				found = true;
       
   432 			}
       
   433 		}
       
   434 		return retval;
       
   435 	}
       
   436 
       
   437 	/**
       
   438 	 * Checks if a trace can be inserted into given location
       
   439 	 * 
       
   440 	 * @param offset
       
   441 	 *            the offset to the location
       
   442 	 * @return true if location is valid
       
   443 	 */
       
   444 	boolean checkInsertLocation(int offset) {
       
   445 		boolean retval = true;
       
   446 		try {
       
   447 			offset = sourceParser.findStartOfLine(offset, false, true);
       
   448 			if (sourceParser.isInExcludedArea(offset)) {
       
   449 				retval = false;
       
   450 			}
       
   451 		} catch (SourceParserException e) {
       
   452 			retval = false;
       
   453 		}
       
   454 		return retval;
       
   455 	}
       
   456 
       
   457 	/**
       
   458 	 * Adds a new parser
       
   459 	 * 
       
   460 	 * @param rule
       
   461 	 *            the new parser rule
       
   462 	 */
       
   463 	void addParserRule(SourceParserRule rule) {
       
   464 		SourceStringSearch searcher = sourceParser.startStringSearch(rule
       
   465 				.getSearchTag(), 0, -1, SourceParser.MATCH_WORD_BEGINNING
       
   466 				| SourceParser.SKIP_ALL);
       
   467 		searcher.setSearchData(rule);
       
   468 		searchers.add(searcher);
       
   469 	}
       
   470 
       
   471 }