/******************************************************************************* * Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Thomas Holland - initial API and implementation *******************************************************************************/ package de.innot.avreclipse.ui.editors; import java.util.HashMap; import java.util.Map; import org.eclipse.core.filebuffers.IDocumentSetupParticipant; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.text.IDocument; import org.eclipse.ui.editors.text.ForwardingDocumentProvider; import org.eclipse.ui.editors.text.TextFileDocumentProvider; import org.eclipse.ui.texteditor.IDocumentProvider; import de.innot.avreclipse.core.toolinfo.fuses.ByteValues; /** * Special Document Provider for fuses and locks files. * <p> * This Provider is based on a <code>TextFileDocumentProvider</code>. For every connected Input * this provider maintains a {@link DocumentByteValuesConnector} object, which connects an * <code>IDocument</code> to a <code>ByteValues</code> object. This connector is responsible for * keeping the Document and the ByteValues in sync. Changes to either of them are immediatly * reflected in the other. * </p> * <p> * In addition to the <code>IDocumentProvider</code> interface this provider has two public * methods to get and set the ByteValues for a given input object. * </p> * <p> * This provider uses the singleton pattern. There is only one provider per Eclipse instance and it * get be retrieved with the static {@link #getDefault()} method. * </p> * * @author Thomas Holland * @since 2.3 */ public class FuseFileDocumentProvider extends ForwardingDocumentProvider { /** Singleton provider instance. */ private static FuseFileDocumentProvider fInstance; /** Map of ByteValues connectors for all connected inputs. */ private static Map<Object, DocumentByteValuesConnector> fConnectorsMap = new HashMap<Object, DocumentByteValuesConnector>(); /** Usage counter for all connected inputs. */ private static Map<Object, Integer> fConnectorsCount = new HashMap<Object, Integer>(); /** * Dummy document setup participant. Currently only used because one is required for the * <code>ForwardingDocumentProvider</code> superclass. */ private static class InternalDocumentSetupParticipant implements IDocumentSetupParticipant { public void setup(IDocument document) { // nothing to setup yet // In the future we could set up a partitioner here, as well as a content assist. } } /** * Get the Fuse file document provider. * * @return The instance FuseFileDocumentProvider. */ public static FuseFileDocumentProvider getDefault() { if (fInstance == null) { fInstance = createProvider(); } return fInstance; } /** * Constructs a new fuse file document provider. * <p> * This constructor is implemented to support the "org.eclipse.ui.editors.documentProviders" * extension point defined in the plugin.xml. It should not be called directly. * </p> * */ public FuseFileDocumentProvider() { // TODO: Check if this constructor and the extension point is actually needed. // The Fuse file editor uses the singleton provider from the getDefault() method. super("__fuses", new InternalDocumentSetupParticipant(), new TextFileDocumentProvider()); } /** * Private constructor for the provider. All parameters are passed on to the superclass. * * @param partitioning * the partitioning * @param documentSetupParticipant * the document setup participant * @param parentProvider * the parent document provider */ private FuseFileDocumentProvider(String partitioning, IDocumentSetupParticipant documentSetupParticipant, IDocumentProvider parentProvider) { super(partitioning, documentSetupParticipant, parentProvider); } /** * Create a new fuse file document provider. * <p> * The new provider uses a <code>TextFileDocumentProvider</code> as a parent provider for the * chain-of-command pattern and an empty <code>DocumentSetupParticipant</code>. * </p> * <p> * This method is only called once from the static {@link #getDefault()} method to create the * single instance. * * @return A new document provider. */ private static FuseFileDocumentProvider createProvider() { String partitioning = "__fuses"; // don't yet know what this is for IDocumentSetupParticipant setupparticipant = new InternalDocumentSetupParticipant(); IDocumentProvider parentprovider = new TextFileDocumentProvider(); FuseFileDocumentProvider provider = new FuseFileDocumentProvider(partitioning, setupparticipant, parentprovider); return provider; } /* * (non-Javadoc) * * @see org.eclipse.ui.editors.text.ForwardingDocumentProvider#connect(java.lang.Object) */ @Override public void connect(Object element) throws CoreException { super.connect(element); DocumentByteValuesConnector connector = fConnectorsMap.get(element); if (connector == null) { connector = new DocumentByteValuesConnector(this, getDocument(element), element); fConnectorsMap.put(element, connector); fConnectorsCount.put(element, 1); } else { // someone already connected to this element // increase use counter int usecount = fConnectorsCount.get(element); fConnectorsCount.put(element, usecount + 1); } } /* * (non-Javadoc) * * @see org.eclipse.ui.editors.text.ForwardingDocumentProvider#disconnect(java.lang.Object) */ @Override public void disconnect(Object element) { // TODO Auto-generated method stub super.disconnect(element); Integer usecount = fConnectorsCount.get(element); if (usecount == null) { // the element was never connected return; } else { --usecount; if (usecount == 0) { // no active connections anymore DocumentByteValuesConnector dbvp = fConnectorsMap.get(element); dbvp.dispose(); fConnectorsMap.remove(element); fConnectorsCount.remove(element); } else { // there are still some active connections fConnectorsCount.put(element, usecount); } } } /** * Get the <code>ByteValues</code> for the given element. * <p> * The returned values object is connected to the <code>IDocument</code> for the same element. * All changes to the values are immediately passed on to the source document as long as the * element remains connected. Once the element is disconnected, the <code>ByteValues</code> * can still be used, but the connection to the source document and source file is removed. * </p> * * @param element * the input element * @return A <code>ByteValues</code> object. Can be <code>null</code> if the object could * not be created. */ public ByteValues getByteValues(Object element) { DocumentByteValuesConnector connector = fConnectorsMap.get(element); if (connector == null) { // probably not connected yet or the element was null return null; } return connector.getByteValues(); } /** * Copies the given <code>ByteValues</code> to an element. * <p> * The element must be connected before this method can be used. * </p> * <p> * This method is used to initialize new fuses / locks files. The given byte values object is * not modified. Only its values for MCU id and the current byte values are copied.<br> * The <code>IDocument</code> associated with the input element is updated immediately. But it * is up to the caller to save the document with the * {@link #saveDocument(org.eclipse.core.runtime.IProgressMonitor, Object, IDocument, boolean)} * method afterwards. * </p> * * @param element * the input element * @param newvalues * Source <code>ByteValues</code> */ public void setByteValues(Object element, ByteValues newvalues) { DocumentByteValuesConnector connector = fConnectorsMap.get(element); if (connector == null) { // probably not connected yet return; } connector.setByteValues(newvalues); } }