blob: 2abea56433b2ce3bb6938354b0622836e57b2c5d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.team.internal.ui.synchronize;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.structuremergeviewer.*;
import org.eclipse.core.resources.IEncodedStorage;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.*;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.internal.ui.Policy;
import org.eclipse.team.internal.ui.TeamUIPlugin;
/**
* A diff node used to display the synchronization state for resources described by
* existing {@link SyncInfo} objects. The synchronization state for a node can
* change after it has been created. Since it implements the <code>ITypedElement</code>
* and <code>ICompareInput</code> interfaces it can be used directly to
* display the compare result in a <code>DiffTreeViewer</code> and as the
* input to any other compare/merge viewer.
* <p>
* Clients typically use this class as is, but may subclass if required.
* </p>
* @see DiffTreeViewer
* @see Differencer
*/
public class SyncInfoModelElement extends SynchronizeModelElement {
private ITypedElement ancestor;
private SyncInfo info;
/**
* Construct a <code>SyncInfoModelElement</code> for the given resource.
*
* @param parent
* @param info
*/
public SyncInfoModelElement(IDiffContainer parent, SyncInfo info) {
super(parent);
Assert.isNotNull(info);
this.info = info;
// update state
setKind(info.getKind());
// local
setLeft(createLocalTypeElement(info));
// remote
setRight(createRemoteTypeElement(info));
// base
setAncestor(createBaseTypeElement(info));
fireChange();
}
/**
* Update this element with a changed sync info. The remote and base handles have to be updated
* with the new handles in the sync info.
*
* @param info the new sync info
*/
public void update(SyncInfo info) {
this.info = info;
// update state
setKind(info.getKind());
// Remote
RemoteResourceTypedElement rightEl = (RemoteResourceTypedElement)getRight();
IResourceVariant remote = info.getRemote();
if(rightEl == null && remote != null) {
setRight(createRemoteTypeElement(info));
} else if(rightEl != null) {
if(remote == null) {
setRight(null);
} else {
setRight(createRemoteTypeElement(info));
}
}
// Base
RemoteResourceTypedElement ancestorEl = (RemoteResourceTypedElement)getAncestor();
IResourceVariant base = info.getBase();
if(ancestorEl == null && base != null) {
setAncestor(createBaseTypeElement(info));
} else if(ancestorEl != null) {
if(base == null) {
setAncestor(null);
} else {
setAncestor(createBaseTypeElement(info));
}
}
fireChange();
}
/* (non-Javadoc)
* @see org.eclipse.compare.structuremergeviewer.DiffElement#getKind()
*/
public int getKind() {
SyncInfo info = getSyncInfo();
if (info != null) {
return info.getKind();
} else {
return SyncInfo.IN_SYNC;
}
}
/**
* We have to track the base because <code>DiffNode</code> doesn't provide a
* setter. See:
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=52261
*/
public void setAncestor(ITypedElement ancestor) {
this.ancestor = ancestor;
}
/* (non-Javadoc)
* @see org.eclipse.compare.structuremergeviewer.DiffNode#getAncestor()
*/
public ITypedElement getAncestor() {
return this.ancestor;
}
/* (non-Javadoc)
* @see org.eclipse.compare.structuremergeviewer.DiffNode#getName()
*/
public String getName() {
IResource resource = getResource();
if(resource != null) {
return resource.getName();
} else {
return super.getName();
}
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class adapter) {
if(adapter == SyncInfo.class) {
return getSyncInfo();
}
return super.getAdapter(adapter);
}
/**
* Helper method that returns the resource associated with this node. A node is not
* required to have an associated local resource.
* @return the resource associated with this node or <code>null</code> if the local
* contributor is not a resource.
*/
public IResource getResource() {
return info.getLocal();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
return getResource().getFullPath().toString();
}
/**
* Cache the contents for the base and remote.
* @param monitor
*/
public void cacheContents(IProgressMonitor monitor) throws TeamException {
ITypedElement base = getAncestor();
ITypedElement remote = getRight();
int work = Math.min((remote== null ? 0 : 50) + (base == null ? 0 : 50), 10);
monitor.beginTask(null, work);
try {
if (base != null && base instanceof RemoteResourceTypedElement) {
((RemoteResourceTypedElement)base).cacheContents(Policy.subMonitorFor(monitor, 50));
}
if (remote != null && remote instanceof RemoteResourceTypedElement) {
((RemoteResourceTypedElement)remote).cacheContents(Policy.subMonitorFor(monitor, 50));
}
} catch (CoreException e) {
throw TeamException.asTeamException(e);
} finally {
monitor.done();
}
}
public SyncInfo getSyncInfo() {
return info;
}
/**
* Create an ITypedElement for the given local resource. The returned ITypedElement
* will prevent editing of outgoing deletions.
*/
private static ITypedElement createTypeElement(final IResource resource, final int kind) {
if(resource != null) {
return new LocalResourceTypedElement(resource) {
public boolean isEditable() {
if(! resource.exists() && SyncInfo.getDirection(kind) == SyncInfo.OUTGOING && SyncInfo.getChange(kind) == SyncInfo.DELETION) {
return false;
}
return super.isEditable();
}
};
}
return null;
}
/**
* Create an ITypedElement for the given remote resource. The contents for the remote resource
* will be retrieved from the given IStorage which is a local cache used to buffer the remote contents
*/
protected static ITypedElement createTypeElement(IResourceVariant remoteResource, String encoding) {
return new RemoteResourceTypedElement(remoteResource,encoding);
}
protected static ITypedElement createRemoteTypeElement(SyncInfo info) {
if(info != null && info.getRemote() != null) {
return createTypeElement(info.getRemote(), getEncoding(info.getLocal()));
}
return null;
}
private static String getEncoding(IResource local) {
if (local instanceof IEncodedStorage) {
IEncodedStorage es = (IEncodedStorage) local;
try {
return es.getCharset();
} catch (CoreException e) {
TeamUIPlugin.log(e);
}
}
return null;
}
protected static ITypedElement createLocalTypeElement(SyncInfo info) {
if(info != null && info.getLocal() != null) {
return createTypeElement(info.getLocal(), info.getKind());
}
return null;
}
protected static ITypedElement createBaseTypeElement(SyncInfo info) {
if(info != null && info.getBase() != null) {
return createTypeElement(info.getBase(), getEncoding(info.getLocal()));
}
return null;
}
}