Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java')
-rw-r--r--bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java433
1 files changed, 433 insertions, 0 deletions
diff --git a/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java
new file mode 100644
index 000000000..799afba98
--- /dev/null
+++ b/bundles/org.eclipse.compare/compare/org/eclipse/compare/structuremergeviewer/StructureCreator.java
@@ -0,0 +1,433 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.compare.structuremergeviewer;
+
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.IEditableContent;
+import org.eclipse.compare.IEncodedStreamContentAccessor;
+import org.eclipse.compare.ISharedDocumentAdapter;
+import org.eclipse.compare.IStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.SharedDocumentAdapter;
+import org.eclipse.compare.contentmergeviewer.IDocumentRange;
+import org.eclipse.compare.internal.CompareUIPlugin;
+import org.eclipse.compare.internal.Utilities;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.services.IDisposable;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+
+/**
+ * An {@link IStructureCreator2} that attempts to use an {@link IDocumentProvider}
+ * to obtain a shared document for an {@link ITypedElement}.
+ * <p>
+ * Clients may subclass this class.
+ * </p>
+ *
+ * @since 3.3
+ */
+public abstract class StructureCreator implements IStructureCreator2 {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.IStructureCreator#getStructure(java.lang.Object)
+ */
+ public IStructureComparator getStructure(Object input) {
+ String contents= null;
+ IDocument doc= CompareUI.getDocument(input);
+ if (doc == null) {
+ if (input instanceof IStreamContentAccessor) {
+ IStreamContentAccessor sca= (IStreamContentAccessor) input;
+ try {
+ contents= Utilities.readString(sca);
+ } catch (CoreException e) {
+ // return null indicates the error.
+ CompareUIPlugin.log(e);
+ return null;
+ }
+ }
+
+ if (contents == null) {
+ // Node has no contents
+ return null;
+ }
+
+ doc= new Document(contents);
+ setupDocument(doc);
+ }
+
+ try {
+ return createStructureComparator(input, doc, null, null);
+ } catch (CoreException e) {
+ CompareUIPlugin.log(e);
+ return null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.IStructureCreator2#createStructure(java.lang.Object, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStructureComparator createStructure(final Object element, final IProgressMonitor monitor) throws CoreException {
+ final IStructureComparator[] result = new IStructureComparator[] { null };
+ Runnable runnable = new Runnable() {
+ public void run() {
+ try {
+ result[0]= internalCreateStructure(element, monitor);
+ } catch (OperationCanceledException ex) {
+ return;
+ }
+ }
+ };
+ Utilities.runInUIThread(runnable);
+ return result[0];
+ }
+
+ /*
+ * We need to create the structure in the UI thread since IDocument requires this
+ */
+ private IStructureComparator internalCreateStructure(Object element,
+ IProgressMonitor monitor) {
+ final ISharedDocumentAdapter sda = SharedDocumentAdapterWrapper.getAdapter(element);
+ if (sda != null) {
+ final IEditorInput input = sda.getDocumentKey(element);
+ if (input != null) {
+ final IDocumentProvider provider = SharedDocumentAdapter.getDocumentProvider(input);
+ if (provider != null) {
+ try {
+ sda.connect(provider, input);
+ IDocument document = provider.getDocument(input);
+ setupDocument(document);
+ return createStructureComparator(element, document, wrapSharedDocumentAdapter(sda, element, document), monitor);
+ } catch (CoreException e) {
+ // Connection to the document provider failed.
+ // Log and fall through to use simple structure
+ CompareUIPlugin.log(e);
+ }
+ }
+ }
+ }
+ return getStructure(element);
+ }
+
+ /**
+ * Create an {@link IStructureComparator} for the given element using the
+ * contents available in the given document. If the provided
+ * {@link ISharedDocumentAdapter} is not <code>null</code> then the
+ * {@link IStructureComparator} returned by this method must implement the
+ * {@link IDisposable} interface and disconnect from the adapter when the
+ * comparator is disposed. The {@link StructureDiffViewer} class will call
+ * dispose if the {@link IStructureComparator} also implements
+ * {@link IDisposable}. Other clients must do the same.
+ * <p>
+ * It should be noted that the provided {@link ISharedDocumentAdapter}
+ * will provide the key associated with the given element when
+ * {@link ISharedDocumentAdapter#getDocumentKey(Object)} is called
+ * for any {@link IDocumentRange} node whose document matches the
+ * provided document. Thus, this adapter should also be returned
+ * by the structure comparator and its children when they are adapted
+ * to an {@link ISharedDocumentAdapter}.
+ * @param element the element
+ * @param document the document that has the contents for the element
+ * @param sharedDocumentAdapter the shared document adapter from which the
+ * document was obtained or <code>null</code> if the document
+ * is not shared.
+ * @param monitor a progress monitor or <code>null</code> if progress is not required
+ *
+ * @return a structure comparator
+ * @throws CoreException
+ */
+ protected abstract IStructureComparator createStructureComparator(
+ final Object element, IDocument document,
+ final ISharedDocumentAdapter sharedDocumentAdapter,
+ IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Setup the newly created document as appropriate. Any document partitioners
+ * should be added to a custom slot using the {@link IDocumentExtension3} interface
+ * in case the document is shared via a file buffer.
+ * @param document a document
+ */
+ protected void setupDocument(IDocument document) {
+ String partitioning = getDocumentPartitioning();
+ if (partitioning == null || !(document instanceof IDocumentExtension3)) {
+ if (document.getDocumentPartitioner() == null) {
+ IDocumentPartitioner partitioner= getDocumentPartitioner();
+ if (partitioner != null) {
+ document.setDocumentPartitioner(partitioner);
+ partitioner.connect(document);
+ }
+ }
+ } else {
+ IDocumentExtension3 ex3 = (IDocumentExtension3) document;
+ if (ex3.getDocumentPartitioner(partitioning) == null) {
+ IDocumentPartitioner partitioner= getDocumentPartitioner();
+ if (partitioner != null) {
+ ex3.setDocumentPartitioner(partitioning, partitioner);
+ partitioner.connect(document);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return the partitioner to be associated with the document or
+ * <code>null</code> is partitioning is not needed or if the subclass
+ * overrode {@link #setupDocument(IDocument)} directly.
+ * @return a partitioner
+ */
+ protected IDocumentPartitioner getDocumentPartitioner() {
+ return null;
+ }
+
+ /**
+ * Return the partitioning to which the partitioner returned from
+ * {@link #getDocumentPartitioner()} is to be associated. Return <code>null</code>
+ * only if partitioning is not needed or if the subclass
+ * overrode {@link #setupDocument(IDocument)} directly.
+ * @see IDocumentExtension3
+ * @return a partitioning
+ */
+ protected String getDocumentPartitioning() {
+ return null;
+ }
+
+ /**
+ * Default implementation of save that extracts the contents from
+ * the document of an {@link IDocumentRange} and sets it on the
+ * input. If the input is an {@link IEncodedStreamContentAccessor},
+ * the charset of the input is used to extract the contents from the
+ * document. If the input adapts to {@link ISharedDocumentAdapter} and
+ * the document of the {@link IDocumentRange} matches that of the
+ * input, then the save is issued through the shared document adapter.
+ * @see org.eclipse.compare.structuremergeviewer.IStructureCreator#save(org.eclipse.compare.structuremergeviewer.IStructureComparator, java.lang.Object)
+ */
+ public void save(IStructureComparator node, Object input) {
+ if (node instanceof IDocumentRange && input instanceof IEditableContent) {
+ IDocument document= ((IDocumentRange)node).getDocument();
+ // First check to see if we have a shared document
+ final ISharedDocumentAdapter sda = SharedDocumentAdapterWrapper.getAdapter(input);
+ if (sda != null) {
+ IEditorInput key = sda.getDocumentKey(input);
+ if (key != null) {
+ IDocumentProvider provider = SharedDocumentAdapter.getDocumentProvider(key);
+ if (provider != null) {
+ IDocument providerDoc = provider.getDocument(key);
+ // We have to make sure that the document we are saving is the same as the shared document
+ if (providerDoc != null && providerDoc == document) {
+ if (save(provider, document, input, sda, key))
+ return;
+ }
+ }
+ }
+ }
+ IEditableContent bca= (IEditableContent) input;
+ String contents= document.get();
+ String encoding= null;
+ if (input instanceof IEncodedStreamContentAccessor) {
+ try {
+ encoding= ((IEncodedStreamContentAccessor)input).getCharset();
+ } catch (CoreException e1) {
+ // ignore
+ }
+ }
+ if (encoding == null)
+ encoding= ResourcesPlugin.getEncoding();
+ byte[] bytes;
+ try {
+ bytes= contents.getBytes(encoding);
+ } catch (UnsupportedEncodingException e) {
+ bytes= contents.getBytes();
+ }
+ bca.setContent(bytes);
+ }
+ }
+
+ private boolean save(final IDocumentProvider provider, final IDocument document,
+ final Object input, final ISharedDocumentAdapter sda, final IEditorInput key) {
+ try {
+ sda.flushDocument(provider, key, document, false);
+ return true;
+ } catch (CoreException e) {
+ CompareUIPlugin.log(e);
+ }
+ return false;
+ }
+
+ /**
+ * Create an {@link ISharedDocumentAdapter} that will provide the document key for the given input
+ * object for any {@link DocumentRangeNode} instances whose document is the same as the
+ * provided document.
+ * @param input the input element
+ * @param document the document associated with the input element
+ * @return a shared document adapter that provides the proper document key for document range nodes
+ */
+ private final ISharedDocumentAdapter wrapSharedDocumentAdapter(ISharedDocumentAdapter elementAdapter, final Object input, final IDocument document) {
+ // We need to wrap the adapter so that the proper document key gets returned
+ return new SharedDocumentAdapterWrapper(elementAdapter) {
+ public IEditorInput getDocumentKey(Object element) {
+ if (hasSameDocument(element)) {
+ return super.getDocumentKey(input);
+ }
+ return super.getDocumentKey(element);
+ }
+ private boolean hasSameDocument(Object element) {
+ if (element instanceof DocumentRangeNode) {
+ DocumentRangeNode drn = (DocumentRangeNode) element;
+ return drn.getDocument() == document;
+ }
+ return false;
+ }
+ };
+ }
+
+ /**
+ * Default implementation of {@link #createElement(Object, Object, IProgressMonitor)}
+ * that uses {@link #getPath(Object, Object)} to determine the
+ * path for the element, {@link #createStructure(Object, IProgressMonitor)} to create the structure
+ * and {@link #findElement(IStructureComparator, String[])} to find the
+ * element in the structure. Subclasses may override.
+ * @param element the element
+ * @param input the containing input
+ * @param monitor a progress monitor
+ * @return the sub-structure element in the input for the given element
+ * @throws CoreException if a parse error occurred
+ */
+ public ITypedElement createElement(Object element, Object input, IProgressMonitor monitor)
+ throws CoreException {
+ String[] path= getPath(element, input);
+ if (path == null) {
+ // TODO: Temporary code until subclasses are updated
+ IStructureComparator locate = locate(element, input);
+ if (locate instanceof ITypedElement) {
+ return (ITypedElement)locate;
+ }
+ return null;
+ }
+
+ // Build the structure
+ IStructureComparator structure= createStructure(input, monitor);
+ if (structure == null) // we couldn't parse the structure
+ return null; // so we can't find anything
+
+ // find the path in the tree
+ return findElement(structure, path);
+ }
+
+ /**
+ * Default implementation of {@link #locate(Object, Object)} that
+ * uses {@link #getPath(Object, Object)} to determine the
+ * path for the element, {@link #getStructure(Object)} to create the structure
+ * and {@link #findElement(IStructureComparator, String[])} to find the
+ * element in the structure. Subclasses may override.
+ * @param element the element
+ * @param input the containing input
+ * @return the sub-structure element in the input for the given element
+ */
+ public IStructureComparator locate(Object element, Object input) {
+ String[] path= getPath(element, input);
+ if (path == null)
+ return null;
+ // Build the structure
+ IStructureComparator structure= getStructure(input);
+ if (structure == null) // we couldn't parse the structure
+ return null; // so we can't find anything
+
+ // find the path in the tree
+ return (IStructureComparator)findElement(structure, path);
+ }
+
+ /**
+ * Find the element at the given path in the given structure.
+ * This method is invoked from the {@link #createElement(Object, Object, IProgressMonitor)}
+ * and {@link #locate(Object, Object)} methods to find the element for
+ * the given path.
+ * @param structure the structure
+ * @param path the path of an element in the structure
+ * @return the element at the given path in the structure or <code>null</code>
+ */
+ protected ITypedElement findElement(IStructureComparator structure, String[] path) {
+ return (ITypedElement)find(structure, path, 0);
+ }
+
+ /**
+ * Recursively extracts the given path from the tree.
+ */
+ private IStructureComparator find(IStructureComparator tree, String[] path, int index) {
+ if (tree != null) {
+ Object[] children= tree.getChildren();
+ if (children != null) {
+ for (int i= 0; i < children.length; i++) {
+ IStructureComparator child= (IStructureComparator) children[i];
+ if (child instanceof ITypedElement && child instanceof DocumentRangeNode) {
+ String n1= null;
+ if (child instanceof DocumentRangeNode)
+ n1= ((DocumentRangeNode)child).getId();
+ if (n1 == null)
+ n1= ((ITypedElement)child).getName();
+ String n2= path[index];
+ if (n1.equals(n2)) {
+ if (index == path.length-1)
+ return child;
+ IStructureComparator result= find(child, path, index+1);
+ if (result != null)
+ return result;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the path of the element in the structure of it's containing input
+ * or <code>null</code> if the element is not contained in the input. This method is
+ * invoked from {@link #createElement(Object, Object, IProgressMonitor)} and
+ * {@link #locate(Object, Object)} methods to determine
+ * the path to be passed to {@link #findElement(IStructureComparator, String[])}.
+ * By default, <code>null</code> is returned. Subclasses may override.
+ * @param element the element
+ * @param input the input
+ * @return the path of the element in the structure of it's containing input
+ * or <code>null</code>
+ */
+ protected String[] getPath(Object element, Object input) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.IStructureCreator2#destroy(java.lang.Object)
+ */
+ public void destroy(Object object) {
+ IDisposable disposable = getDisposable(object);
+ if (disposable != null)
+ disposable.dispose();
+ }
+
+ private IDisposable getDisposable(Object object) {
+ if (object instanceof IDisposable) {
+ return (IDisposable) object;
+ }
+ if (object instanceof DocumentRangeNode) {
+ DocumentRangeNode node = (DocumentRangeNode) object;
+ return getDisposable(node.getParentNode());
+ }
+ return null;
+ }
+}

Back to the top