diff -r a151135b0cf9 -r aa2539c91954 tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/model/TraceModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/model/TraceModel.java Fri Oct 08 14:56:39 2010 +0300 @@ -0,0 +1,997 @@ +/* +* 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: +* +* Trace model is a collection of trace groups +* +*/ +package com.nokia.tracecompiler.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Properties; + +import com.nokia.tracecompiler.engine.TraceCompilerEngineGlobals; +import com.nokia.tracecompiler.engine.TraceCompilerEngineErrorCodes.TraceCompilerErrorCode; +import com.nokia.tracecompiler.engine.project.SortedProperties; +import com.nokia.tracecompiler.project.FormattingUtils; +import com.nokia.tracecompiler.source.SourceConstants; + +/** + * Trace model is a collection of trace groups. Listener interfaces can be + * attached to a trace model to receive change notifications when traces are + * added, removed or modified. + * + */ +public class TraceModel extends TraceObject implements Iterable { + + /** + * Group property prefix + */ + public final String GROUP_PROPERTY_PREFIX = "[GROUP]"; //$NON-NLS-1$ + + /** + * Trace property prefix + */ + public final String TRACE_PROPERTY_PREFIX = "[TRACE]"; //$NON-NLS-1$ + + /** + * Obsolete property prefix + */ + public final String OBSOLETE_PROPERTY_PREFIX = "[[OBSOLETE]]"; //$NON-NLS-1$ + + /** + * Group Id prefix + */ + public final String GROUP_ID_PREFIX = "["; //$NON-NLS-1$ + + /** + * Group Id suffix + */ + public final String GROUP_ID_SUFFIX = "]"; //$NON-NLS-1$ + + /** + * Factory object for creating other trace objects + */ + private TraceObjectFactory factory; + + /** + * Property verifier interface + */ + private TraceObjectPropertyVerifier verifier; + + /** + * List of trace groups + */ + private ArrayList groups = new ArrayList(); + + /** + * List of traces, sorted by name + */ + private ArrayList tracesByName = new ArrayList(); + + /** + * List of model listeners + */ + private ArrayList modelListeners = new ArrayList(); + + /** + * List of extension listeners + */ + private ArrayList extensionListeners = new ArrayList(); + + /** + * List of reset listeners + */ + private ArrayList resetListeners = new ArrayList(); + + /** + * Processing listeners + */ + private ArrayList processingListeners = new ArrayList(); + + /** + * List of constant tables + */ + private ArrayList constantTables = new ArrayList(); + + /** + * Validity flag + */ + private boolean valid; + + /** + * Number of nested calls to startProcessing + */ + private int isProcessing; + + /** + * Model was changed during processing + */ + private boolean modelChangedDuringProcessing; + + /** + * Fixed Ids from fixed Ids definition file + */ + private SortedProperties fixedIds; + + /** + * Hex radix + */ + public int HEX_RADIX = 16; // CodForChk_Dis_Magic + + /** + * Constructor + * + * @param factory + * the rule factory + * @param verifier + * the property verifier + * @throws TraceCompilerException + */ + public TraceModel(TraceObjectRuleFactory factory, + TraceObjectPropertyVerifier verifier) throws TraceCompilerException { + // Stored for callback purposes + setModel(this); + this.factory = new TraceObjectFactory(this, factory); + this.verifier = verifier; + } + + /** + * Adds a new trace model listener to this model + * + * @param listener + * the new listener + */ + public void addModelListener(TraceModelListener listener) { + modelListeners.add(listener); + } + + /** + * Removes a trace model listener. Does nothing if the listener is not found + * + * @param listener + * the listener to be removed + */ + public void removeModelListener(TraceModelListener listener) { + modelListeners.remove(listener); + } + + /** + * Adds a new trace model extension listener to this model + * + * @param listener + * the new listener + */ + public void addExtensionListener(TraceModelExtensionListener listener) { + extensionListeners.add(listener); + } + + /** + * Removes a trace model extension listener. Does nothing if the listener is + * not found + * + * @param listener + * the listener to be removed + */ + public void removeExtensionListener(TraceModelExtensionListener listener) { + extensionListeners.remove(listener); + } + + /** + * Adds a new trace model reset listener to this model + * + * @param listener + * the new listener + */ + public void addResetListener(TraceModelResetListener listener) { + resetListeners.add(listener); + } + + /** + * Removes a trace model reset listener. Does nothing if the listener is not + * found + * + * @param listener + * the listener to be removed + */ + public void removeResetListener(TraceModelResetListener listener) { + resetListeners.remove(listener); + } + + /** + * Adds a new trace model listener to this model + * + * @param listener + * the new listener + */ + public void addProcessingListener(TraceProcessingListener listener) { + processingListeners.add(listener); + } + + /** + * Removes a processing listener. Does nothing if the listener is not found + * + * @param listener + * the listener to be removed + */ + public void removeProcessingListener(TraceProcessingListener listener) { + processingListeners.remove(listener); + } + + /** + * Returns highest group ID + 1. Can be used to create an unique ID for a + * new trace group. + * + * @return the next trace group ID + * @throws TraceCompilerException + */ + public int getNextGroupID() throws TraceCompilerException { + int currentMaxGroupId = 0; + int nextGroupId = 0; + // Check if there are some fixed Ids + if (fixedIds != null) { + Enumeration keys = this.fixedIds.keys(); + // Go through fixed Ids and check if there are fixed group Ids + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + if (key.startsWith(GROUP_PROPERTY_PREFIX) + || key.startsWith(OBSOLETE_PROPERTY_PREFIX + + GROUP_PROPERTY_PREFIX)) { + // Fixed group Id found. Try to covert it to int value. + String value = fixedIds.getProperty(key); + int fixedId = 0; + try { + fixedId = Integer.decode(value).intValue(); + } catch (NumberFormatException e) { + // Corrupted. Get next group Id later on. + currentMaxGroupId = 0; + break; + } + // Check if found fixed Id is bigger than current max group + // Id + if (fixedId > currentMaxGroupId) { + currentMaxGroupId = fixedId; + } + } + } + } + + // If there were fixed group Ids. Set next group Id to be current max + // group Id + 1 + if (currentMaxGroupId != 0) { + nextGroupId = currentMaxGroupId + 1; + } + + // Get current max group id in model + int maxGroupIdInModel = 0; + for (TraceGroup group : groups) { + int groupIdInModel = group.getID(); + if (groupIdInModel > maxGroupIdInModel) { + maxGroupIdInModel = groupIdInModel; + } + } + + // If next group Id is zero or smaller than current max group id in + // model + 1. Set it be same as current max group id in model + 1. This + // is done in case that we have added more than one new group after last + // fixed Id update + if (nextGroupId == 0 || nextGroupId < maxGroupIdInModel + 1) { + nextGroupId = maxGroupIdInModel + 1; + } + + // Check that next group Id is not bigger than max group Id + if (nextGroupId > TraceCompilerEngineGlobals.MAX_GROUP_ID) { + throw new TraceCompilerException( + TraceCompilerErrorCode.RUN_OUT_OF_GROUP_IDS); + } + + return nextGroupId; + } + + /** + * Returns highest constant table ID + 1. Can be used to create an unique ID + * for a new constant table. + * + * @return the next constant table ID + */ + public int getNextConstantTableID() { + int max = 0; + for (TraceConstantTable table : constantTables) { + int id = table.getID(); + if (id > max) { + max = id; + } + } + return max + 1; + } + + /** + * Removes a trace group from this model. Create groupRemoved event to model + * listeners + * + * @param group + * the group to be removed + * @throws TraceCompilerException + */ + public void removeGroup(TraceGroup group) throws TraceCompilerException { + if (groups.remove(group)) { + notifyObjectRemoved(this, group); + group.reset(); + } + } + + /** + * Determines if this model contains any trace groups + * + * @return true if there are trace groups + */ + public boolean hasGroups() { + return !groups.isEmpty(); + } + + /** + * Gets the number of trace groups + * + * @return trace group count + */ + public int getGroupCount() { + return groups.size(); + } + + /** + * Returns the trace groups of this model + * + * @return the iterator over the groups + */ + public Iterator getGroups() { + return groups.iterator(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Iterable#iterator() + */ + public Iterator iterator() { + return groups.iterator(); + } + + /** + * Removes all trace groups and parameters from this model. Extensions are + * not removed. Notifies the reset listeners with modelResetting and + * modelReset + * + * @see TraceModelResetListener#modelResetting + * @see TraceModelResetListener#modelReset + */ + @Override + public void reset() { + notifyModelResetting(); + // Properties are removed, other extensions are left + removeExtensions(TraceObjectPropertyList.class); + groups.clear(); + tracesByName.clear(); + constantTables.clear(); + fixedIds = null; + super.reset(); + notifyModelReset(); + } + + /** + * Gets the group which has given ID + * + * @param id + * the id + * @return group or null + */ + public TraceGroup findGroupByID(int id) { + TraceGroup retval = null; + for (TraceGroup group : groups) { + if (group.getID() == id) { + retval = group; + break; + } + } + return retval; + } + + /** + * Locates a trace group which has the given name. + * + * @param name + * the name of the trace group + * @return the group or null if not found + * @see TraceObject#getName + */ + public TraceGroup findGroupByName(String name) { + TraceGroup retval = null; + for (TraceGroup group : groups) { + if (group.getName().equals(name)) { + retval = group; + break; + } + } + return retval; + } + + /** + * Locates a trace which has the given name. + * + * @param name + * the name of the trace + * @return the trace or null if not found + * @see TraceObject#getName + */ + public Trace findTraceByName(String name) { + Trace retval; + int index = Collections.binarySearch(tracesByName, name, + TraceObjectUtils.traceToNameComparator); + if (index >= 0) { + retval = tracesByName.get(index); + } else { + retval = null; + } + return retval; + } + + /** + * Returns the group at given index + * + * @param index + * the group index + * @return the group + */ + public TraceGroup getGroupAt(int index) { + return groups.get(index); + } + + /** + * Removes a constant table from this model. Creates objectRemoved event to + * model listeners + * + * @see TraceModelListener#objectRemoved(TraceObject, TraceObject) + * @param table + * the table to be removed + * @throws TraceCompilerException + */ + public void removeConstantTable(TraceConstantTable table) throws TraceCompilerException { + if (constantTables.remove(table)) { + notifyObjectRemoved(this, table); + table.reset(); + } + } + + /** + * Gets the constant tables of this model + * + * @return the tables iterator + */ + public Iterator getConstantTables() { + return constantTables.iterator(); + } + + /** + * Gets a constant table by ID + * + * @param id + * the ID + * @return the table or null + */ + public TraceConstantTable findConstantTableByID(int id) { + TraceConstantTable retval = null; + for (TraceConstantTable table : constantTables) { + if (table.getID() == id) { + retval = table; + break; + } + } + return retval; + } + + /** + * Gets a constant table by name + * + * @param tableName + * the name + * @return the table or null + */ + public TraceConstantTable findConstantTableByName(String tableName) { + TraceConstantTable retval = null; + for (TraceConstantTable table : constantTables) { + if (table.getName().equals(tableName)) { + retval = table; + break; + } + } + return retval; + } + + /** + * Checks if this model contains constant tables + * + * @return true if there are constant tables + */ + public boolean hasConstantTables() { + return !constantTables.isEmpty(); + } + + /** + * Returns the constant table at given index + * + * @param index + * the group index + * @return the group + */ + public TraceConstantTable getConstantTableAt(int index) { + return constantTables.get(index); + } + + /** + * Adds a new trace group to this model. Creates objectAdded event to model + * listeners. This is only intended to be called from TraceGroup + * constructor, so this is not public. + * + * @see TraceModelListener#objectAdded(TraceObject, TraceObject) + * @param group + * the group to be added + */ + void addGroup(TraceGroup group) { + groups.add(group); + notifyObjectAdded(this, group); + } + + /** + * Adds a constant table to this model. Created objectAdded event to model + * listeners. This is only intended to be called from constant table + * constructor, so this is not public + * + * @see TraceModelListener#objectAdded(TraceObject, TraceObject) + * @param table + * the constant table + */ + void addConstantTable(TraceConstantTable table) { + constantTables.add(table); + notifyObjectAdded(this, table); + } + + /** + * Fires propertiesUpdated event. Called from trace objects when their + * properties change. + * + * @see TraceModelListener#propertyUpdated(TraceObject, int) + * @param source + * the object that changed + * @param property + * the property that changed + * @throws TraceCompilerException + */ + void notifyPropertyUpdated(TraceObject source, int property) throws TraceCompilerException { + if (source instanceof Trace) { + if (property == TraceModelListener.NAME) { + Collections.sort(tracesByName, + TraceObjectUtils.traceObjectNameComparator); + } + ((Trace) source).getGroup().tracePropertyUpdated(source, property); + } + for (TraceModelListener l : modelListeners) { + l.propertyUpdated(source, property); + } + modelChangedDuringProcessing = true; + } + + /** + * Fires objectAdded event to listeners + * + * @see TraceModelListener#objectAdded(TraceObject, TraceObject) + * @param owner + * the owner object + * @param object + * the object that was added + */ + void notifyObjectAdded(TraceObject owner, TraceObject object) { + // List is sorted when the name is set to the trace + if (object instanceof Trace) { + tracesByName.add((Trace) object); + } + for (TraceModelListener l : modelListeners) { + l.objectAdded(owner, object); + } + modelChangedDuringProcessing = true; + } + + /** + * Fires objectRemoved event to listeners + * + * @see TraceModelListener#objectRemoved(TraceObject, TraceObject) + * @param owner + * the owner object + * @param object + * the object that was removed + * @throws TraceCompilerException + */ + void notifyObjectRemoved(TraceObject owner, TraceObject object) throws TraceCompilerException { + if (object instanceof Trace) { + int index = Collections.binarySearch(tracesByName, (Trace) object, + TraceObjectUtils.traceObjectNameComparator); + tracesByName.remove(index); + } + notifyOnDelete(object); + for (TraceModelListener l : modelListeners) { + l.objectRemoved(owner, object); + } + modelChangedDuringProcessing = true; + } + + /** + * Notifies that an object creation is complete + * + * @see TraceModelListener#objectCreationComplete(TraceObject) + * @param object + * the object + * @throws TraceCompilerException + */ + void notifyObjectCreationComplete(TraceObject object) throws TraceCompilerException { + for (TraceModelListener l : modelListeners) { + l.objectCreationComplete(object); + } + } + + /** + * Fires modelResetting event to all listeners + * + * @see TraceModelResetListener#modelResetting() + */ + private void notifyModelResetting() { + for (TraceModelResetListener l : resetListeners) { + l.modelResetting(); + } + } + + /** + * Fires modelReset event to all listeners + * + * @see TraceModelResetListener#modelReset() + */ + private void notifyModelReset() { + for (TraceModelResetListener l : resetListeners) { + l.modelReset(); + } + } + + /** + * Fires extensionAdded event. Called from TraceObject when extension is + * added to it + * + * @see TraceModelExtensionListener#extensionAdded(TraceObject, + * TraceModelExtension) + * @param object + * the trace object + * @param extension + * the new extension + */ + void notifyExtensionAdded(TraceObject object, TraceModelExtension extension) { + for (TraceModelExtensionListener l : extensionListeners) { + l.extensionAdded(object, extension); + } + modelChangedDuringProcessing = true; + } + + /** + * Fires extensionRemoved event. Called from TraceObject when extension is + * removed from it + * + * @see TraceModelExtensionListener#extensionRemoved(TraceObject, + * TraceModelExtension) + * @param object + * the object + * @param extension + * the removed extension + */ + void notifyExtensionRemoved(TraceObject object, + TraceModelExtension extension) { + for (TraceModelExtensionListener l : extensionListeners) { + l.extensionRemoved(object, extension); + } + modelChangedDuringProcessing = true; + } + + /** + * Gets the validity flag of this model + * + * @return the validity flag + */ + public boolean isValid() { + return valid; + } + + /** + * Sets the validity flag + * + * @param valid + * new flag value + * @throws TraceCompilerException + */ + public void setValid(boolean valid) throws TraceCompilerException { + if (valid != this.valid) { + this.valid = valid; + for (TraceModelResetListener l : resetListeners) { + l.modelValid(valid); + } + } + } + + /** + * Gets the trace object factory + * + * @return the factory + */ + public TraceObjectFactory getFactory() { + return factory; + } + + /** + * Gets the object verifier interface. The verifier should be used before + * updating object properties + * + * @return the verifier + */ + public TraceObjectPropertyVerifier getVerifier() { + return verifier; + } + + /** + * Notifies the process listeners that a process the results in multiple + * listener updates is about to start + */ + public void startProcessing() { + isProcessing++; + if (isProcessing == 1) { + modelChangedDuringProcessing = false; + for (TraceProcessingListener l : processingListeners) { + l.processingStarted(); + } + } + } + + /** + * Notifies the process listeners that a process the results in multiple + * listener updates has finished + */ + public void processingComplete() { + isProcessing--; + if (isProcessing == 0) { + for (TraceProcessingListener l : processingListeners) { + l.processingComplete(modelChangedDuringProcessing); + } + modelChangedDuringProcessing = false; + } + } + + /** + * Checks the state of the processing flag + * + * @return the processing flag + */ + public boolean isProcessing() { + return isProcessing > 0; + } + + /** + * Checks if model has traces + * + * @return true if there's traces, false if not + */ + public boolean hasTraces() { + boolean retval = false; + for (TraceGroup group : groups) { + if (group.hasTraces()) { + retval = true; + break; + } + } + return retval; + } + + /** + * Gets group ID from properties + * + * @param properties + * the properties + * @param group + * the group + * @return the group ID + * @throws TraceCompilerException + */ + public int getGroupID(Properties properties, TraceGroup group) + throws TraceCompilerException { + String value = properties.getProperty(GROUP_PROPERTY_PREFIX + + group.getName()); + int id; + if (value == null) { + // Not found, assign a proper ID + id = getNewIdForGroup(group); + + } else { + try { + id = Integer.decode(value).intValue(); + TraceGroup traceGroup = findGroupByID(id); + if (traceGroup != null && !traceGroup.equals(group)) { + // Id already in use, assign a proper ID + id = getNewIdForGroup(group); + } + } catch (NumberFormatException e) { + // Corrupted, assign a proper ID + id = getNewIdForGroup(group); + } + } + group.internalSetID(id); + return id; + } + + /** + * Gets a new ID for this group + * + * @param group + * the group + * @return a new ID for this group + * @throws TraceCompilerException + */ + private int getNewIdForGroup(TraceGroup group) throws TraceCompilerException { + int id = FormattingUtils.getGroupID(group.getModel(), group.getName()); + return id; + } + + /** + * Saves trace and group identifiers to given properties. + * + * @param properties + * the properties to use + */ + public void saveIDs(Properties properties) { + properties.clear(); + StringBuffer sb = new StringBuffer(); + for (TraceGroup group : this) { + int groupId = group.getID(); + properties.setProperty(GROUP_PROPERTY_PREFIX + group.getName(), + SourceConstants.HEX_PREFIX + Integer.toHexString(groupId)); + for (Trace trace : group) { + int traceId = trace.getID(); + properties.setProperty(TRACE_PROPERTY_PREFIX + + createTraceName(sb, group, trace), + SourceConstants.HEX_PREFIX + + Integer.toHexString(traceId)); + } + } + } + + /** + * Merges the group and trace names together + * + * @param sb + * the buffer where name is stored + * @param group + * group + * @param trace + * trace + * @return the trace name + */ + public String createTraceName(StringBuffer sb, TraceGroup group, Trace trace) { + sb.setLength(0); + sb.append(group.getName()); + sb.append(GROUP_ID_PREFIX); + int groupId = group.getID(); + String groupIdString = SourceConstants.HEX_PREFIX + + Integer.toString(groupId, HEX_RADIX).toUpperCase(); + sb.append(groupIdString); + sb.append(GROUP_ID_SUFFIX); + sb.append(SourceConstants.UNDERSCORE); + sb.append(trace.getName()); + return sb.toString(); + } + + /** + * Gets fixed group and trace ids + * + * @return fixed Ids + */ + public SortedProperties getFixedIds() { + return fixedIds; + } + + /** + * Set fixed group and trace ids + * + * @param fixedIds + * fixed Ids + */ + public void setFixedIds(SortedProperties fixedIds) { + this.fixedIds = fixedIds; + } + + /** + * Gets next trace ID + * + * @param group + * the group + * @return the trace ID + * @throws TraceCompilerException + */ + public int getNextTraceId(TraceGroup group) throws TraceCompilerException { + int currentMaxTraceID = 0; + int nextTraceId = 0; + // Check if there are some fixed Ids + if (fixedIds != null) { + Enumeration keys = this.fixedIds.keys(); + String groupName = group.getName(); + int groupId = group.getID(); + String groupIdString = SourceConstants.HEX_PREFIX + + Integer.toString(groupId, HEX_RADIX).toUpperCase(); + // Go through fixed Ids and check if there are fixed trace Ids to + // this group + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + if (key.startsWith(TRACE_PROPERTY_PREFIX + groupName + + GROUP_ID_PREFIX + groupIdString + GROUP_ID_SUFFIX + + SourceConstants.UNDERSCORE) + || key.startsWith(OBSOLETE_PROPERTY_PREFIX + + TRACE_PROPERTY_PREFIX + groupName + + GROUP_ID_PREFIX + groupIdString + + GROUP_ID_SUFFIX + SourceConstants.UNDERSCORE)) { + // Fixed trace Id to in this group found. Try to covert it + // to int value. + String value = fixedIds.getProperty(key); + int fixedId = 0; + try { + fixedId = Integer.decode(value).intValue(); + } catch (NumberFormatException e) { + // Corrupted. Get next trace Id later on. + nextTraceId = 0; + break; + } + // Check if found fixed Id is bigger than current max trace + // Id in this group + if (fixedId > currentMaxTraceID) { + currentMaxTraceID = fixedId; + } + } + } + // If there were fixed trace Ids to this group. Set next trace Id to + // be current max trace Id + 1 + if (currentMaxTraceID != 0) { + nextTraceId = currentMaxTraceID + 1; + } + // If next trace Id is zero or smaller than trace Id that group + // suggest. Set it be same as group suggest. This is done in case + // that we have added more than one new trace to same group after + // last fixed Id update + if (nextTraceId == 0 || nextTraceId < group.getNextTraceID()) { + nextTraceId = group.getNextTraceID(); + } + } else { + // No fixed Ids. Get next trace Id from group + nextTraceId = group.getNextTraceID(); + } + // Check that next trace Id is not bigger than max trace Id + if (nextTraceId > TraceCompilerEngineGlobals.MAX_TRACE_ID) { + throw new TraceCompilerException( + TraceCompilerErrorCode.RUN_OUT_OF_TRACE_IDS); + } + + return nextTraceId; + } + +}