--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/trace/tracebuilder/com.nokia.tracebuilder/src/com/nokia/tracebuilder/engine/source/SourceEngine.java Wed Jun 23 14:35:40 2010 +0300
@@ -0,0 +1,739 @@
+/*
+* 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:
+*
+* Source engine manages source documents that are opened to Eclipse UI
+*
+*/
+package com.nokia.tracebuilder.engine.source;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import com.nokia.tracebuilder.engine.LocationProperties;
+import com.nokia.tracebuilder.engine.TraceBuilderConfiguration;
+import com.nokia.tracebuilder.engine.TraceBuilderErrorMessages;
+import com.nokia.tracebuilder.engine.TraceBuilderGlobals;
+import com.nokia.tracebuilder.engine.TraceLocation;
+import com.nokia.tracebuilder.engine.TraceLocationList;
+import com.nokia.tracebuilder.engine.TraceBuilderErrorCodes.TraceBuilderErrorCode;
+import com.nokia.tracebuilder.model.Trace;
+import com.nokia.tracebuilder.model.TraceBuilderException;
+import com.nokia.tracebuilder.model.TraceModel;
+import com.nokia.tracebuilder.model.TraceObject;
+import com.nokia.tracebuilder.model.TraceParameter;
+import com.nokia.tracebuilder.rules.ReadOnlyObjectRule;
+import com.nokia.tracebuilder.source.OffsetLength;
+import com.nokia.tracebuilder.source.SourceContext;
+import com.nokia.tracebuilder.source.SourceDocumentInterface;
+import com.nokia.tracebuilder.source.SourceDocumentMonitor;
+import com.nokia.tracebuilder.source.SourceDocumentProcessor;
+import com.nokia.tracebuilder.source.SourceParserException;
+import com.nokia.tracebuilder.utils.DocumentFactory;
+
+/**
+ * Source engine manages source documents that are opened to Eclipse UI.
+ *
+ */
+public class SourceEngine implements SourceDocumentProcessor,
+ Iterable<SourceProperties> {
+
+ /**
+ * Document monitor
+ */
+ private SourceDocumentMonitor documentMonitor;
+
+ /**
+ * Trace model listener implementation
+ */
+ private SourceEngineModelListener modelListener = new SourceEngineModelListener(
+ this);
+
+ /**
+ * Trace model extension listener
+ */
+ private SourceEngineModelExtensionListener extensionListener = new SourceEngineModelExtensionListener(
+ this);
+
+ /**
+ * The callback interfaces are notified about source file changes
+ */
+ private ArrayList<SourceListener> listeners = new ArrayList<SourceListener>();
+
+ /**
+ * Running flag
+ */
+ private boolean running;
+
+ /**
+ * Trace model
+ */
+ private TraceModel model;
+
+ /**
+ * Source list
+ */
+ private ArrayList<SourceProperties> tempList = new ArrayList<SourceProperties>();
+
+ /**
+ * Read-only files
+ */
+ private String[] READ_ONLY = { ".h" //$NON-NLS-1$
+ };
+
+ /**
+ * Non-source file list
+ */
+ private ArrayList<String> nonSourceFiles = new ArrayList<String>();
+
+ /**
+ * Constructor
+ *
+ * @param model
+ * the trace model
+ */
+ public SourceEngine(TraceModel model) {
+ this.model = model;
+ }
+
+ /**
+ * Starts this engine. Does nothing if already running
+ */
+ public void start() {
+ if (!running) {
+ documentMonitor = DocumentFactory.getDocumentMonitor();
+ documentMonitor.startMonitor(this);
+ running = true;
+ model.addModelListener(modelListener);
+ model.addExtensionListener(extensionListener);
+ }
+ }
+
+ /**
+ * Shuts down the source engine. Does nothing if already stopped
+ */
+ public void shutdown() {
+ if (running) {
+ documentMonitor.stopMonitor();
+ documentMonitor = null;
+ running = false;
+ model.removeModelListener(modelListener);
+ model.removeExtensionListener(extensionListener);
+ }
+ }
+
+ /**
+ * Gets the running flag
+ *
+ * @return the flag
+ */
+ public boolean isRunning() {
+ return running;
+ }
+
+ /**
+ * Adds source listener callback interface
+ *
+ * @param listener
+ * the new listener
+ */
+ public void addSourceListener(SourceListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes a source listener
+ *
+ * @param listener
+ * the listener to be removed
+ */
+ public void removeSourceListener(SourceListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Gets the sources
+ *
+ * @return the sources
+ */
+ public Iterator<SourceProperties> getSources() {
+ tempList.clear();
+ for (SourceDocumentInterface doc : documentMonitor) {
+ tempList.add((SourceProperties) doc.getOwner());
+ }
+ return tempList.iterator();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Iterable#iterator()
+ */
+ public Iterator<SourceProperties> iterator() {
+ return getSources();
+ }
+
+ /**
+ * Gets the currently active source
+ *
+ * @return the source
+ */
+ public SourceProperties getSelectedSource() {
+ SourceProperties srcProperties = null;
+ SourceDocumentInterface srcDocInterface = documentMonitor.getSelectedSource();
+
+ if (srcDocInterface != null) {
+ srcProperties = (SourceProperties) srcDocInterface.getOwner();
+ }
+ return srcProperties;
+ }
+
+ /**
+ * Gets the current selection from the given source
+ *
+ * @param props
+ * the source
+ * @return the selection
+ */
+ public OffsetLength getSelection(SourceProperties props) {
+ return documentMonitor
+ .getSelection(props.getSourceEditor().getSource());
+ }
+
+ /**
+ * Sets the focus to the current source
+ */
+ public void setFocus() {
+ documentMonitor.setFocus();
+ }
+
+ /**
+ * Gets the source context based on current caret position. This throws an
+ * exception if the caret is not in valid position. Invalid positions are:
+ * <ul>
+ * <li>Source file is not open</li>
+ * <li>Outside function implementation</li>
+ * <li>Within commented code</li>
+ * <li>Within quoted strings</li>
+ * </ul>
+ *
+ * @return the context
+ */
+ public SourceContext getSelectedContext() {
+ SourceContext retval = null;
+ // checkInsertLocation verifies that source is editable
+ if (checkInsertLocation()) {
+ SourceDocumentInterface selectedSource = documentMonitor
+ .getSelectedSource();
+ SourceProperties properties = (SourceProperties) selectedSource
+ .getOwner();
+ OffsetLength offset = documentMonitor.getSelection(selectedSource);
+ retval = properties.getSourceEditor().getContext(offset.offset);
+ }
+ return retval;
+ }
+
+ /**
+ * Gets the source properties related to given context
+ *
+ * @param context
+ * the context
+ * @return the properties
+ */
+ public SourceProperties getSourceOfContext(SourceContext context) {
+ return (SourceProperties) context.getParser().getSource().getOwner();
+ }
+
+ /**
+ * Checks the validity of cursor location
+ *
+ * @throws TraceBuilderException
+ * if trace can be inserted to cursor location
+ */
+ public void checkCursorLocationValidity() throws TraceBuilderException {
+ SourceDocumentInterface document = documentMonitor.getSelectedSource();
+ if (document != null) {
+ SourceProperties properties = (SourceProperties) document
+ .getOwner();
+ if (!properties.isReadOnly()) {
+ OffsetLength selection = documentMonitor.getSelection(document);
+ if (selection == null) {
+ throw new TraceBuilderException(
+ TraceBuilderErrorCode.INVALID_SOURCE_LOCATION);
+ }
+ } else {
+ throw new TraceBuilderException(
+ TraceBuilderErrorCode.SOURCE_NOT_EDITABLE);
+ }
+ } else {
+ throw new TraceBuilderException(
+ TraceBuilderErrorCode.SOURCE_NOT_OPEN);
+ }
+ }
+
+ /**
+ * Inserts a trace to the start of the current selection. Creates a
+ * {@link SourceListener#sourceChanged(SourceProperties) sourceChanged}
+ * event to all listeners after each trace has been added.
+ *
+ * @param trace
+ * the trace to be added to the start of selection
+ * @return the insert location for the trace
+ * @throws TraceBuilderException
+ * if insertion fails
+ */
+ public int addTrace(Trace trace) throws TraceBuilderException {
+ // Checks the location validity before inserting
+ checkCursorLocationValidity();
+ ReadOnlyObjectRule readOnly = trace
+ .getExtension(ReadOnlyObjectRule.class);
+ if (readOnly != null) {
+ throw new TraceBuilderException(
+ TraceBuilderErrorCode.INSERT_TRACE_DOES_NOT_WORK);
+ }
+ SourceDocumentInterface document = documentMonitor.getSelectedSource();
+ int retval = documentMonitor.getSelection(document).offset;
+ insertTrace(trace, (SourceProperties) document.getOwner(), retval);
+ return retval;
+ }
+
+ /**
+ * Queues the addition of #include statement to source
+ *
+ * @param source
+ * the source to be updated
+ * @param name
+ * the include file name
+ */
+ public void addInclude(SourceProperties source, String name) {
+ if (!source.isReadOnly()) {
+ IncludeStatementAdder adder = new IncludeStatementAdder(source,
+ name);
+ adder.update();
+ }
+ }
+
+ /**
+ * Removes a trace location from source
+ *
+ * @param location
+ * the location to be removed
+ */
+ public void removeLocation(TraceLocation location) {
+ if (!location.getSource().isReadOnly()) {
+ TraceLocationWriter.removeLocation(location);
+ }
+ }
+
+ /**
+ * Inserts a trace into the given source file at given offset. Creates a
+ * {@link SourceListener#sourceChanged(SourceProperties) sourceChanged}
+ * event to all listeners after the trace has been added.
+ *
+ * @param trace
+ * the trace to be added
+ * @param source
+ * the source where trace is added
+ * @param offset
+ * the offset where trace is added
+ * @throws TraceBuilderException
+ * if insertion fails
+ */
+ public void insertTrace(Trace trace, SourceProperties source, int offset)
+ throws TraceBuilderException {
+ if (!source.isReadOnly()) {
+ try {
+ TraceLocationWriter.addLocation(source, trace, offset);
+ } catch (Exception e) {
+ throw new TraceBuilderException(
+ TraceBuilderErrorCode.INVALID_SOURCE_LOCATION, null,
+ trace);
+ }
+ }
+ }
+
+ /**
+ * Replaces a location with the given trace. The parameters from the
+ * existing location are not changed
+ *
+ * @param trace
+ * the trace to be inserted
+ * @param replaced
+ * the location to be replaced with the trace
+ * @throws TraceBuilderException
+ * if insertion fails
+ */
+ public void replaceLocationWithTrace(Trace trace, TraceLocation replaced)
+ throws TraceBuilderException {
+ removeLocation(replaced);
+ SourceProperties source = replaced.getSource();
+ if (source != null && !source.isReadOnly()) {
+ try {
+ TraceLocationWriter.replaceLocation(source, trace, replaced);
+ } catch (Exception e) {
+ throw new TraceBuilderException(
+ TraceBuilderErrorCode.INVALID_SOURCE_LOCATION, null,
+ trace);
+ }
+ }
+ }
+
+ /**
+ * Fires sourceChanged event to listeners
+ *
+ * @see SourceListener#sourceChanged(SourceProperties)
+ * @param source
+ * the source
+ */
+ private void fireSourceChanged(SourceProperties source) {
+ for (SourceListener l : listeners) {
+ l.sourceChanged(source);
+ }
+ }
+
+ /**
+ * Fires sourceProcessingStarted event to listeners
+ *
+ * @see SourceListener#sourceProcessingStarted(SourceProperties)
+ * @param source
+ * the source
+ */
+ private void fireSourceProcessingStarted(SourceProperties source) {
+ for (SourceListener l : listeners) {
+ l.sourceProcessingStarted(source);
+ }
+ }
+
+ /**
+ * Fires sourceProcessingComplete event to listeners
+ *
+ * @see SourceListener#sourceProcessingComplete(SourceProperties)
+ * @param source
+ * the source
+ */
+ private void fireSourceProcessingComplete(SourceProperties source) {
+ for (SourceListener l : listeners) {
+ l.sourceProcessingComplete(source);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.nokia.tracebuilder.source.SourceDocumentProcessor#
+ * sourceOpened(com.nokia.tracebuilder.source.SourceDocumentInterface)
+ */
+ public void sourceOpened(SourceDocumentInterface source) {
+ SourceProperties properties = new SourceProperties(model,
+ documentMonitor.getFactory(), source);
+ // Headers are marked read-only
+ for (String s : READ_ONLY) {
+ String fileName = properties.getFileName();
+ if (fileName != null && fileName.endsWith(s)) {
+ properties.setReadOnly(true);
+ break;
+ }
+ }
+ properties.sourceOpened();
+ source.setOwner(properties);
+ for (SourceListener l : listeners) {
+ l.sourceOpened(properties);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.nokia.tracebuilder.source.SourceDocumentProcessor#
+ * selectionChanged(com.nokia.tracebuilder.source.SourceDocumentInterface,
+ * int, int)
+ */
+ public void selectionChanged(SourceDocumentInterface source, int offset,
+ int length) {
+ SourceProperties properties = (SourceProperties) source.getOwner();
+ TraceLocation location = properties.getLocation(offset);
+ if (location != null && location.getLocationList() != null) {
+ for (SourceListener l : listeners) {
+ l.selectionChanged(location);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.nokia.tracebuilder.source.SourceDocumentProcessor#
+ * sourceAboutToBeChanged(com.nokia.tracebuilder.source.SourceDocumentInterface,
+ * int, int, java.lang.String)
+ */
+ public void sourceAboutToBeChanged(SourceDocumentInterface source,
+ int offset, int length, String newText) {
+ SourceProperties properties = (SourceProperties) source.getOwner();
+ try {
+ if (!properties.getUpdateQueue().hasQueuedUpdates()) {
+ properties.prepareChange(offset, length, newText);
+ }
+ } catch (Exception e) {
+ if (TraceBuilderConfiguration.ASSERTIONS_ENABLED) {
+ TraceBuilderGlobals.getEvents().postCriticalAssertionFailed(
+ "Document change preprocessor failure", e); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.nokia.tracebuilder.source.SourceDocumentProcessor#
+ * sourceChanged(com.nokia.tracebuilder.source.SourceDocumentInterface,
+ * int, int, java.lang.String)
+ */
+ public void sourceChanged(SourceDocumentInterface source, int offset,
+ int length, String newText) {
+ SourceProperties properties = (SourceProperties) source.getOwner();
+ try {
+ sourceChanged(properties, offset, length, newText);
+ } catch (Exception e) {
+ if (TraceBuilderConfiguration.ASSERTIONS_ENABLED) {
+ TraceBuilderGlobals.getEvents().postCriticalAssertionFailed(
+ "Document change processor failure", e); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.nokia.tracebuilder.source.SourceDocumentProcessor#
+ * sourceClosed(com.nokia.tracebuilder.source.SourceDocumentInterface)
+ */
+ public void sourceClosed(SourceDocumentInterface source) {
+ SourceProperties properties = (SourceProperties) source.getOwner();
+ for (SourceListener l : listeners) {
+ l.sourceClosed(properties);
+ }
+ properties.sourceClosed();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.nokia.tracebuilder.source.SourceDocumentProcessor#
+ * sourceSaved(com.nokia.tracebuilder.source.SourceDocumentInterface)
+ */
+ public void sourceSaved(SourceDocumentInterface source) {
+ SourceProperties properties = (SourceProperties) source.getOwner();
+ for (SourceListener l : listeners) {
+ l.sourceSaved(properties);
+ }
+ }
+
+ /**
+ * Checks that the current caret position is valid for trace insertion
+ *
+ * @return true if valid, false if not
+ */
+ private boolean checkInsertLocation() {
+ boolean retval = false;
+ SourceDocumentInterface selectedSource = documentMonitor
+ .getSelectedSource();
+ if (selectedSource != null) {
+ if (documentMonitor.isSourceEditable(selectedSource)) {
+ SourceProperties properties = (SourceProperties) selectedSource
+ .getOwner();
+ if (!properties.isReadOnly()) {
+ OffsetLength selection = documentMonitor
+ .getSelection(selectedSource);
+ if (selection != null) {
+ retval = properties
+ .checkInsertLocation(selection.offset);
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * Processes a source change notification
+ *
+ * @param source
+ * the changed source
+ * @param offset
+ * offset to the replaced text
+ * @param length
+ * the length of the replaced text
+ * @param newText
+ * the text that was added to the document
+ * @throws SourceParserException
+ * if processing fails
+ */
+ private void sourceChanged(SourceProperties source, int offset, int length,
+ String newText) throws SourceParserException {
+ // When the source update queue contains more than one element, it is
+ // faster to disable all updates and do a full sync afterwards
+ // The source update queue must ensure that the updates are run starting
+ // from the bottom of the file. This way they will not interfere with
+ // each other
+ if (source.getUpdateQueue().hasQueuedUpdates()) {
+ if (source.isActive()) {
+ fireSourceProcessingStarted(source);
+ source.setActive(false);
+ }
+ } else {
+ if (source.isActive()) {
+ source.preProcessChange(offset, length, newText);
+ fireSourceChanged(source);
+ source.postProcessChange();
+ } else {
+ source.setActive(true);
+ source.preProcessFullSync(offset, length, newText);
+ fireSourceProcessingComplete(source);
+ source.postProcessFullSync();
+ }
+ }
+ }
+
+ /**
+ * Updates a trace location
+ *
+ * @param trace
+ * the trace
+ */
+ public void updateTrace(Trace trace) {
+ if (trace.getModel().isValid()) {
+ // Read-only traces cannot be updated
+ if (trace.getExtension(ReadOnlyObjectRule.class) == null) {
+ TraceLocationList list = trace
+ .getExtension(TraceLocationList.class);
+ if (list != null) {
+ for (LocationProperties loc : list) {
+ TraceLocationWriter.updateLocation((TraceLocation) loc);
+ }
+ }
+ } else {
+ String msg = TraceBuilderErrorMessages.getErrorMessage(
+ TraceBuilderErrorCode.CANNOT_UPDATE_TRACE_INTO_SOURCE,
+ null);
+ TraceBuilderGlobals.getEvents().postWarningMessage(msg, trace);
+ }
+ }
+ }
+
+ /**
+ * Parser rule added notification
+ *
+ * @param rule
+ * the parser rule
+ */
+ void parserAdded(SourceParserRule rule) {
+ for (SourceProperties source : this) {
+ source.addParserRule(rule);
+ try {
+ source.preProcessFullSync(0, 0, ""); //$NON-NLS-1$
+ fireSourceChanged(source);
+ source.postProcessFullSync();
+ } catch (Exception e) {
+ if (TraceBuilderConfiguration.ASSERTIONS_ENABLED) {
+ TraceBuilderGlobals.getEvents()
+ .postCriticalAssertionFailed(
+ "Failed to add parser", e); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ /**
+ * Parser rule removed notification
+ *
+ * @param rule
+ * the parser rule
+ */
+ void parserRemoved(SourceParserRule rule) {
+ for (SourceProperties source : this) {
+ source.removeParserRule(rule);
+ try {
+ source.preProcessFullSync(0, 0, ""); //$NON-NLS-1$
+ fireSourceChanged(source);
+ source.postProcessFullSync();
+ } catch (Exception e) {
+ if (TraceBuilderConfiguration.ASSERTIONS_ENABLED) {
+ TraceBuilderGlobals.getEvents()
+ .postCriticalAssertionFailed(
+ "Failed to remove parser", e); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when a rule is added or removed
+ *
+ * @param object
+ * the object which was changed
+ */
+ void ruleUpdated(TraceObject object) {
+ // Formatting rule changes cause updates to source
+ if (object.isComplete()) {
+ if (object instanceof TraceParameter) {
+ Trace owner = ((TraceParameter) object).getTrace();
+ if (owner.isComplete()) {
+ updateTrace(owner);
+ }
+ } else if (object instanceof Trace) {
+ updateTrace((Trace) object);
+ }
+ }
+ }
+
+ /**
+ * Adds a non-source file to this list.
+ *
+ * @param filePath
+ * the non-source file path to added
+ */
+ public void addNonSourceFile(String filePath) {
+ nonSourceFiles.add(filePath);
+ }
+
+ /**
+ * Removes a non-source file from this list
+ *
+ * @param filePath
+ * the non-source file path to be removed
+ * @return true if removed
+ */
+ public boolean removeNonSourceFile(String filePath) {
+ boolean retVal = nonSourceFiles.remove(filePath);
+ return retVal;
+ }
+
+ /**
+ * Removes a non-source files
+ *
+ */
+ public void removeNonSourceFiles() {
+ nonSourceFiles.clear();
+ }
+
+ /**
+ * Gets lis of non-source files
+ *
+ * @return the list of non-source file paths
+ */
+ public ArrayList<String> getNonSourceFiles() {
+ return nonSourceFiles;
+ }
+
+}