/*******************************************************************************
* Copyright (c) 2005 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
* QNX Software System
*******************************************************************************/
package org.erlide.ui.internal.compare;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.compare.CompareUI;
import org.eclipse.compare.IEditableContent;
import org.eclipse.compare.IEditableContentExtension;
import org.eclipse.compare.ISharedDocumentAdapter;
import org.eclipse.compare.IStreamContentAccessor;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.ResourceNode;
import org.eclipse.compare.structuremergeviewer.DocumentRangeNode;
import org.eclipse.compare.structuremergeviewer.IStructureComparator;
import org.eclipse.compare.structuremergeviewer.IStructureCreator;
import org.eclipse.compare.structuremergeviewer.StructureCreator;
import org.eclipse.compare.structuremergeviewer.StructureRootNode;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.services.IDisposable;
import org.erlide.engine.ErlangEngine;
import org.erlide.engine.model.ErlModelException;
import org.erlide.engine.model.IErlElement;
import org.erlide.engine.model.IParent;
import org.erlide.engine.model.root.IErlModel;
import org.erlide.engine.model.root.IErlModule;
import org.erlide.engine.model.root.IOpenable;
import org.erlide.ui.editors.erl.ErlangDocumentSetupParticipant;
import org.erlide.ui.editors.erl.scanner.IErlangPartitions;
import org.erlide.ui.internal.ErlideUIPlugin;
import org.erlide.util.ErlLogger;
public class ErlStructureCreator extends StructureCreator {
private static final String NAME = "Erlang Structure Compare";
// private final IErlProject fProject;
private IDocumentPartitioner documentPartitioner = null;
public ErlStructureCreator() {
}
/**
* @see IStructureCreator#getTitle
*/
@Override
public String getName() {
return ErlideUIPlugin.getResourceString(NAME);
}
/**
* A root node for the structure. It is similar to {@link StructureRootNode}
* but needed to be a subclass of {@link ErlNode} because of the code used
* to build the structure.
*/
private final class RootErlNode extends ErlNode implements IDisposable {
private Object fInput;
RootErlNode(final IDocument document, final Object input) {
super(document);
fInput = input;
}
@Override
protected void nodeChanged(final DocumentRangeNode node) {
save(this, fInput);
}
@Override
public boolean isReadOnly() {
if (fInput instanceof IEditableContentExtension) {
final IEditableContentExtension ext = (IEditableContentExtension) fInput;
return ext.isReadOnly();
}
return super.isReadOnly();
}
@Override
public IStatus validateEdit(final Shell shell) {
if (fInput instanceof IEditableContentExtension) {
final IEditableContentExtension ext = (IEditableContentExtension) fInput;
return ext.validateEdit(shell);
}
return super.validateEdit(shell);
}
@Override
public void dispose() {
fInput = null;
}
}
/**
* @see IStructureCreator#getStructure
*/
@Override
public IStructureComparator getStructure(final Object input) {
String contents = null;
char[] buffer = null;
IDocument doc = CompareUI.getDocument(input);
if (doc == null) {
if (input instanceof IStreamContentAccessor) {
final IStreamContentAccessor sca = (IStreamContentAccessor) input;
try {
contents = ErlangCompareUtilities.readString(sca);
} catch (final CoreException ex) {
// return null indicates the error.
return null;
}
}
if (contents != null) {
final int n = contents.length();
buffer = new char[n];
contents.getChars(0, n, buffer, 0);
doc = new Document(contents);
setupDocument(doc);
}
}
try {
return createStructureComparator(input, doc, null, null);
} catch (final CoreException e) {
ErlLogger.error(e); // TODO report error
}
return null;
}
private ErlNode recursiveMakeErlNodes(final IErlElement element, final ErlNode parent,
final IDocument doc) throws ErlModelException {
final ErlNode n = ErlNode.createErlNode(parent, element, doc);
if (element instanceof IOpenable) {
final IOpenable o = (IOpenable) element;
o.open(null);
}
if (element instanceof IParent) {
final IParent p = (IParent) element;
final Collection<IErlElement> children = p.getChildren();
for (final IErlElement child : children) {
recursiveMakeErlNodes(child, n, doc);
}
}
return n;
}
/**
* @see IStructureCreator#save
*/
@Override
public void save(final IStructureComparator structure, final Object input) {
if (input instanceof IEditableContent && structure instanceof ErlNode) {
final IDocument doc = ((ErlNode) structure).getDocument();
final IEditableContent bca = (IEditableContent) input;
final String c = doc.get();
bca.setContent(c.getBytes());
}
}
/**
* @see IStructureCreator#getContents
*/
@Override
public String getContents(final Object node, final boolean ignoreWhitespace) {
if (node instanceof IStreamContentAccessor) {
final IStreamContentAccessor sca = (IStreamContentAccessor) node;
try {
return readString(sca.getContents());
} catch (final CoreException ex) {
}
}
return null;
}
/**
* Returns null if an error occurred.
*/
private static String readString(final InputStream is) {
if (is == null) {
return null;
}
BufferedReader reader = null;
try {
final StringBuilder buffer = new StringBuilder();
final char[] part = new char[2048];
int read = 0;
reader = new BufferedReader(new InputStreamReader(is));
while ((read = reader.read(part)) != -1) {
buffer.append(part, 0, read);
}
return buffer.toString();
} catch (final IOException ex) {
} finally {
if (reader != null) {
try {
reader.close();
} catch (final IOException ex) {
}
}
}
return null;
}
@Override
protected IStructureComparator createStructureComparator(final Object element,
final IDocument document0, final ISharedDocumentAdapter sharedDocumentAdapter,
final IProgressMonitor monitor) throws CoreException {
IErlModule module = null;
final IErlModel model = ErlangEngine.getInstance().getModel();
String s = "";
IDocument document = document0;
if (element instanceof ResourceNode) {
final ResourceNode rn = (ResourceNode) element;
final IResource r = rn.getResource();
if (r instanceof IFile) {
final IFile f = (IFile) r;
final IErlElement e = model.findElement(r);
if (e instanceof IErlModule) {
module = (IErlModule) e;
}
if (document == null) {
try {
s = readString(f.getContents());
document = new Document(s);
} catch (final CoreException e1) {
}
}
}
} else if (document == null && element instanceof IStreamContentAccessor) {
try {
try (final InputStream contents = ((IStreamContentAccessor) element)
.getContents()) {
s = readString(contents);
} catch (final IOException e) {
}
document = new Document(s);
} catch (final CoreException ex) {
}
} else if (document != null) {
s = document.get();
}
if (module == null) {
String name = "comptemp";
if (element instanceof ITypedElement) {
final ITypedElement typedElement = (ITypedElement) element;
name = typedElement.getName();
}
module = model.getModuleFromText(model, name, s, s);
}
ErlNode root = null;
if (element != null && document != null) {
try {
module.open(null);
root = new RootErlNode(document, element);
recursiveMakeErlNodes(module, root, document);
} catch (final ErlModelException e) {
ErlLogger.warn(e);
}
}
return root;
}
@Override
protected String[] getPath(final Object element, final Object input) {
if (element instanceof IErlElement) {
IErlElement e = (IErlElement) element;
// build a path starting at the given element and walk
// up the parent chain until we reach a module
final List<String> args = new ArrayList<>();
while (e != null) {
// each path component has a name that uses the same
// conventions as a ErlNode name
final String name = ErlangCompareUtilities.getErlElementID(e);
if (name == null) {
return null;
}
args.add(name);
if (e instanceof IErlModule) {
break;
}
e = (IErlElement) e.getParent();
}
Collections.reverse(args);
return args.toArray(new String[args.size()]);
}
return null;
}
@Override
protected String getDocumentPartitioning() {
return IErlangPartitions.ERLANG_PARTITIONING;
}
@Override
protected IDocumentPartitioner getDocumentPartitioner() {
if (documentPartitioner == null) {
documentPartitioner = ErlangDocumentSetupParticipant
.createDocumentPartitioner();
}
return documentPartitioner;
}
}