diff -r 14dc2103a631 -r ed1c9f64298a trace/tracebuilder/com.nokia.tracebuilder/src/com/nokia/tracebuilder/engine/source/SourceEngine.java --- /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 { + + /** + * 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 listeners = new ArrayList(); + + /** + * Running flag + */ + private boolean running; + + /** + * Trace model + */ + private TraceModel model; + + /** + * Source list + */ + private ArrayList tempList = new ArrayList(); + + /** + * Read-only files + */ + private String[] READ_ONLY = { ".h" //$NON-NLS-1$ + }; + + /** + * Non-source file list + */ + private ArrayList nonSourceFiles = new ArrayList(); + + /** + * 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 getSources() { + tempList.clear(); + for (SourceDocumentInterface doc : documentMonitor) { + tempList.add((SourceProperties) doc.getOwner()); + } + return tempList.iterator(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Iterable#iterator() + */ + public Iterator 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: + *
    + *
  • Source file is not open
  • + *
  • Outside function implementation
  • + *
  • Within commented code
  • + *
  • Within quoted strings
  • + *
+ * + * @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 getNonSourceFiles() { + return nonSourceFiles; + } + +}