diff -r a151135b0cf9 -r aa2539c91954 tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/engine/rules/SourceParserRuleBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/engine/rules/SourceParserRuleBase.java Fri Oct 08 14:56:39 2010 +0300 @@ -0,0 +1,565 @@ +/* +* Copyright (c) 2007 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: +* +* Implementation of parser rule +* +*/ +package com.nokia.tracecompiler.engine.rules; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.nokia.tracecompiler.engine.TraceLocation; +import com.nokia.tracecompiler.engine.TraceCompilerEngineErrorCodes.StringErrorParameters; +import com.nokia.tracecompiler.engine.TraceCompilerEngineErrorCodes.TraceCompilerErrorCode; +import com.nokia.tracecompiler.engine.source.SourceParserResult; +import com.nokia.tracecompiler.engine.source.SourceParserRule; +import com.nokia.tracecompiler.engine.utils.TraceUtils; +import com.nokia.tracecompiler.model.Trace; +import com.nokia.tracecompiler.model.TraceCompilerException; +import com.nokia.tracecompiler.model.TraceModelExtension; +import com.nokia.tracecompiler.project.TraceLocationParser; +import com.nokia.tracecompiler.project.TraceProjectAPI; +import com.nokia.tracecompiler.source.FormatMapping; +import com.nokia.tracecompiler.source.SourceConstants; +import com.nokia.tracecompiler.source.SourceExcludedArea; +import com.nokia.tracecompiler.source.SourceIterator; +import com.nokia.tracecompiler.source.SourceParser; +import com.nokia.tracecompiler.source.SourceParserException; +import com.nokia.tracecompiler.source.SourceUtils; + +/** + * Base class for parser rules. Instances of this class are added to the trace + * model. The source engine uses the parses it finds from the model to find + * trace locations from source files + * + */ +public abstract class SourceParserRuleBase extends RuleBase implements + SourceParserRule, TraceLocationParser { + + /** + * Tag to be searched from source + */ + private String tag; + + /** + * Allowed tag suffixes + */ + private ArrayList tagSuffixes = new ArrayList(); + + /** + * Default name for a parameter + */ + public static final String DEFAULT_PARAMETER_NAME = "arg"; //$NON-NLS-1$ + + /** + * Pattern for trimming c++ cast operator away from trace parameters + */ + private Pattern cppCastTrimPattern = Pattern.compile("\\s*(.+<.+>)\\s*"); //$NON-NLS-1$ + + /** + * Pattern for trimming c cast operator away from trace parameters + */ + private Pattern cCastTrimPattern = Pattern.compile("(\\([^(]+?\\))"); //$NON-NLS-1$ + + /** + * Pattern for getting a valid token from parameter data + */ + private Pattern parameterNamePattern = Pattern + .compile("[a-zA-Z][a-zA-Z\\d]*"); //$NON-NLS-1$ + + /** + * Symbian literal tags + */ + private String[] TRACE_TEXT_TAGS = { "_L8", //$NON-NLS-1$ + "_T8", //$NON-NLS-1$ + "_L", //$NON-NLS-1$ + "_T" //$NON-NLS-1$ + }; + + + /** + * Constructor + * + * @param tag + * the tag to be searched from source + * @param tagSuffixes + * the list of allowed suffixes to the tag + */ + protected SourceParserRuleBase(String tag, String[] tagSuffixes) { + this.tag = tag; + // Adds the sub-formats to the parsers + if (tagSuffixes != null) { + int len = tagSuffixes.length; + for (int i = 0; i < len; i++) { + this.tagSuffixes.add(tagSuffixes[i]); + } + } + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.engine.source.SourceParserRule#getSearchTag() + */ + public String getSearchTag() { + return tag; + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.engine.source.SourceParserRule# + * isAllowedTagSuffix(java.lang.String) + */ + public boolean isAllowedTagSuffix(String tag) { + boolean retval = false; + if (tag != null) { + if (tag.length() == 0 && tagSuffixes.isEmpty()) { + retval = true; + } else { + for (int i = 0; i < tagSuffixes.size() && !retval; i++) { + String s = tagSuffixes.get(i); + if (s.length() == tag.length()) { + retval = true; + for (int j = 0; j < s.length() && retval; j++) { + char c1 = s.charAt(j); + // '?' can be any character + if (c1 != '?') { + retval = tag.charAt(j) == c1; + } + } + } + } + } + } + return retval; + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.engine.source.SourceParserRule#getName() + */ + public String getName() { + return null; + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.engine.source.SourceParserRule# + * parseParameters(java.util.List) + */ + public SourceParserResult parseParameters(String tag, List list) + throws TraceCompilerException { + SourceParserResult result = new SourceParserResult(); + result.traceText = trimTraceText(list.get(0)); + result.originalName = result.traceText; + result.convertedName = TraceUtils.convertName( + SourceUtils.removePrintfFormatting(result.traceText)) + .toUpperCase(); + result.parameters = list.subList(1, list.size()); + return result; + } + + /** + * Converts a location to trace + * + * @param location + * the location to be converted + * @return the conversion result + * @throws TraceCompilerException + * if conversion fails + */ + public TraceConversionResult convertLocation(TraceLocation location) + throws TraceCompilerException { + // All parameter types are supported by default -> false is passed to + // buildParameterTypeList. + List types = buildParameterTypeList(location + .getTraceText(), false); + // Parameter count must match the number of format elements in trace + // text -> true is passed to convertLocation by default + return convertLocation(location, true, types); + } + + /** + * Converts a location to trace + * + * @param location + * the location to be converted + * @param useParametersFromLocation + * true if location parameters need to be checked + * @param types + * the parameter types list + * @return the conversion result + * @throws TraceCompilerException + * if conversion fails + */ + protected TraceConversionResult convertLocation(TraceLocation location, + boolean useParametersFromLocation, List types) + throws TraceCompilerException { + TraceConversionResult result = new TraceConversionResult(); + result.text = location.getTraceText(); + + // If trace text is in multiple lines, remove extra characters + int removableAreaBeging = -1; + for (int i = 0; i < result.text.length(); i++) { + char c = result.text.charAt(i); + if (c == SourceConstants.QUOTE_CHAR + && (i == 0 || result.text.charAt(i - 1) != SourceConstants.BACKSLASH_CHAR)) { + if (removableAreaBeging != -1) { + String startString = result.text.substring(0, + removableAreaBeging); + String endString = result.text.substring(i + 1, result.text + .length()); + result.text = startString + endString; + i = removableAreaBeging; + removableAreaBeging = -1; + } else { + removableAreaBeging = i; + } + } + } + + result.name = location.getConvertedName(); + + // If location parameters are used, the count must match the number of + // formats parsed from the trace string. + if (!useParametersFromLocation + || (types.size() == location.getParameterCount() && numberOfParametersInTagMatchSize( + location.getTag(), types.size()))) { + result.parameters = new ArrayList(types + .size()); + Iterator itr = location.getParameters(); + // The FormatMapping objects are converted to + // ParameterConversionResult objects: + // - Name is associated to the parameter + // - Extensions are created + for (int i = 0; i < types.size(); i++) { + FormatMapping mapping = types.get(i); + String name; + if (useParametersFromLocation) { + name = TraceUtils.convertName(trimParameter(itr.next(), i)); + } else { + name = DEFAULT_PARAMETER_NAME + (i + 1); + } + ParameterConversionResult param = mapFormatToConversionResult(mapping); + param.name = modifyDuplicateName(result.parameters, name); + result.parameters.add(param); + } + } else { + throw new TraceCompilerException( + TraceCompilerErrorCode.PARAMETER_FORMAT_MISMATCH, null, + location); + } + return result; + } + + + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.engine.source.SourceParserRule# + * processNewLocation(com.nokia.tracecompiler.engine.TraceLocation) + */ + public void processNewLocation(TraceLocation location) { + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.project.TraceLocationParser#getLocationGroup() + */ + public String getLocationGroup() { + return null; + } + + /** + * Maps a format to conversion result + * + * @param mapping + * the mapping + * @return the conversion result + */ + protected ParameterConversionResult mapFormatToConversionResult( + FormatMapping mapping) { + ParameterConversionResult param = new ParameterConversionResult(); + param.type = mapping.type; + if (mapping.isArray) { + param.extensions = new ArrayList(); + param.extensions.add(new ArrayParameterRuleImpl()); + } + return param; + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.engine.source.SourceParserRule# + * findLocationComment(com.nokia.tracecompiler.engine.TraceLocation) + */ + public SourceExcludedArea findLocationComment(TraceLocation location) { + SourceParser parser = location.getParser(); + SourceExcludedArea excludedArea = null; + if (parser != null) { + try { + int offset = location.getOffset() + location.getLength(); + SourceIterator itr = parser.createIterator(offset, + SourceParser.SKIP_WHITE_SPACES); + char c = itr.next(); + if (c == ';') { + offset = itr.currentIndex(); + c = itr.next(); + } + boolean skippedReturn = false; + int commentStart = itr.currentIndex(); + for (int i = offset; i < commentStart; i++) { + c = parser.getData(i); + if (c == '\n') { + skippedReturn = true; + } + } + // Comment must be on same line + if (!skippedReturn) { + excludedArea = parser.getExcludedArea(commentStart); + } + if (excludedArea == null) { + // If comment is not on same line, the previous line is + // checked + offset = parser.findStartOfLine(location.getOffset(), true, + true); + excludedArea = parser.getExcludedArea(offset - 1); + } + } catch (SourceParserException e) { + } + } + return excludedArea; + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.engine.source.SourceParserRule#getLocationParser() + */ + public TraceLocationParser getLocationParser() { + return this; + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.project.TraceLocationParser# + * isLocationConverted(com.nokia.tracecompiler.engine.TraceLocation) + */ + public boolean isLocationConverted(TraceLocation location) { + return false; + } + + /* + * (non-Javadoc) + * + * @see com.nokia.tracecompiler.project.TraceLocationParser# + * checkLocationValidity(com.nokia.tracecompiler.engine.TraceLocation) + */ + public TraceCompilerErrorCode checkLocationValidity(TraceLocation location) { + Trace trace = location.getTrace(); + TraceCompilerErrorCode retval = TraceCompilerErrorCode.TRACE_DOES_NOT_EXIST; + if (trace != null) { + retval = TraceCompilerErrorCode.OK; + } else { + // If the API does not match the parser, the needs conversion flag + // is set + TraceProjectAPI api = getOwner().getModel().getExtension( + TraceProjectAPI.class); + if (!api.getName().equals(location.getParserRule().getName())) { + retval = TraceCompilerErrorCode.TRACE_NEEDS_CONVERSION; + } + } + return retval; + } + + /** + * Checks if the number of the parameters in the trace tag matches the size + * of parameter list + * + * @param tag + * the trace tag + * @param size + * size of the parameter list + * @return true is parameter count matches, false otherwise + */ + private boolean numberOfParametersInTagMatchSize(String tag, int size) { + boolean matches = true; + // Parse the last character of the tag to a integer + if (tag != null) { + try { + + // Check the tag parameter count and compare it to the size of + // the parameter list + String tagParamStr = tag.substring(tag.length() - 1); + int tagParamCount = Integer.parseInt(tagParamStr); + + if (tagParamCount != size) { + matches = false; + } + } catch (NumberFormatException e) { + } + } + return matches; + } + + /** + * Builds the parameter type array which is passed to convertLocation + * + * @param text + * the trace text + * @param simpleParameters + * true if only simple types are supported + * @return the list of types + * @throws TraceCompilerException + * if parser fails + */ + protected List buildParameterTypeList(String text, + boolean simpleParameters) throws TraceCompilerException { + Matcher matcher = SourceUtils.traceTextPattern.matcher(text); + ArrayList types = new ArrayList(); + boolean found = true; + do { + found = matcher.find(); + if (found) { + String tag = matcher.group(); + FormatMapping mapping = SourceUtils + .mapFormatToParameterType(tag); + if (simpleParameters && !mapping.isSimple) { + StringErrorParameters param = new StringErrorParameters(); + param.string = tag; + throw new TraceCompilerException( + TraceCompilerErrorCode.PARAMETER_FORMAT_NEEDS_EXT_MACRO, + param, null); + } + types.add(mapping); + } + } while (found); + return types; + } + + /** + * Changes a duplicate parameter name to unique + * + * @param parameters + * the list of existing parameters + * @param name + * the name + * @return the modified name + */ + private String modifyDuplicateName( + List parameters, String name) { + String retval = name; + for (ParameterConversionResult result : parameters) { + if (result.name.equals(name)) { + retval = name + (parameters.size() + 1); + break; + } + } + return retval; + } + + /** + * Trims extra stuff away from trace text + * + * @param data + * the data to be trimmed + * @return trimmed text + */ + protected String trimTraceText(String data) { + // Removes literal macros + for (String element : TRACE_TEXT_TAGS) { + if (data.startsWith(element)) { + data = data.substring(element.length()); + } + } + data = data.trim(); + // Removes the opening bracket and quotes + data = removeBrackets(data); + if (data.startsWith("\"") //$NON-NLS-1$ + && data.length() >= 2) { // CodForChk_Dis_Magic + data = data.substring(1, data.length() - 1); + } + return data; + } + + /** + * Removes the brackets around the given data + * + * @param data + * the data + * @return the modified data + */ + protected String removeBrackets(String data) { + boolean canRemove = true; + while (data.startsWith("(") //$NON-NLS-1$ + && data.endsWith(")") //$NON-NLS-1$ + && canRemove) { + // If closing bracket is found first, the first bracket cannot be + // removed. + // TODO: Does not work with for example ((Cast)abc).Func() + // -> (Cast)abc).Func( + for (int i = 1; i < data.length() - 1 && canRemove; i++) { + if (data.charAt(i) == '(') { + i = data.length(); + } else if (data.charAt(i) == ')') { + canRemove = false; + } + } + if (canRemove) { + data = data.substring(1, data.length() - 1).trim(); + } + } + return data; + } + + /** + * Trims extra stuff away from a parameter to create a parameter label + * + * @param data + * the parameter data + * @param index + * the parameter index. The index will be used as label if the + * label cannot be parsed + * @return the parameter label + */ + protected String trimParameter(String data, int index) { + // Removes possible literal macros + data = trimTraceText(data); + // Removes casting operations. + Matcher matcher = cppCastTrimPattern.matcher(data); + data = removeBrackets(matcher.replaceFirst("")); //$NON-NLS-1$ + matcher = cCastTrimPattern.matcher(data); + data = matcher.replaceFirst(""); //$NON-NLS-1$ + // Finds the next valid token from the data + matcher = parameterNamePattern.matcher(data); + if (matcher.find()) { + data = matcher.group(); + } else { + data = DEFAULT_PARAMETER_NAME + (index + 1); + } + return data; + } +} \ No newline at end of file