// ============================================================================ // // Copyright (C) 2006-2016 Talend Inc. - www.talend.com // // This source code is available under agreement available at // %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt // // You should have received a copy of the agreement // along with this program; if not, write to Talend SA // 9 rue Pages 92150 Suresnes, France // // ============================================================================ /******************************************************************************* * Copyright (c) 2000, 2007 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 Matt McCutchen (hashproduct+eclipse@gmail.com) - Bug * 35390 Three-way compare cannot select (mis-selects) )ancestor resource *******************************************************************************/ package com.amalto.workbench.compare; import java.lang.reflect.InvocationTargetException; import java.util.HashSet; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.CompareEditorInput; import org.eclipse.compare.ITypedElement; import org.eclipse.compare.ZipFileStructureCreator; import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.compare.structuremergeviewer.DiffTreeViewer; import org.eclipse.compare.structuremergeviewer.Differencer; import org.eclipse.compare.structuremergeviewer.IDiffContainer; import org.eclipse.compare.structuremergeviewer.IDiffElement; import org.eclipse.compare.structuremergeviewer.IStructureComparator; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; import com.amalto.workbench.i18n.Messages; import com.amalto.workbench.utils.Util; import com.amalto.workbench.utils.XmlUtil; import com.amalto.workbench.webservices.WSDataClusterPK; import com.amalto.workbench.webservices.WSDataModelPK; import com.amalto.workbench.webservices.WSPutItem; /** * A two-way or three-way compare for arbitrary IResources. */ public class ResourceCompareInput extends CompareEditorInput { private static Log log = LogFactory.getLog(ResourceCompareInput.class); private static final boolean NORMALIZE_CASE = true; private boolean fThreeWay = false; private Object fRoot; private IStructureComparator fAncestor; private IStructureComparator fLeft; private IStructureComparator fRight; private IResource fAncestorResource; private IResource fLeftResource; private IResource fRightResource; private DiffTreeViewer fDiffViewer; private IAction fOpenAction; private CompareHeadInfo compareHeadInfo;// if head info not equal null, then we need save changes class MyDiffNode extends DiffNode { private boolean fDirty = false; private ITypedElement fLastId; private String fLastName; public MyDiffNode(IDiffContainer parent, int description, ITypedElement ancestor, ITypedElement left, ITypedElement right) { super(parent, description, ancestor, left, right); } @Override public void fireChange() { super.fireChange(); setDirty(true); fDirty = true; if (fDiffViewer != null) { fDiffViewer.refresh(this); } } void clearDirty() { fDirty = false; } @Override public String getName() { if (fLastName == null) { fLastName = super.getName(); } if (fDirty) { return '<' + fLastName + '>'; } return fLastName; } @Override public ITypedElement getId() { ITypedElement id = super.getId(); if (id == null) { return fLastId; } fLastId = id; return id; } } static class FilteredBufferedResourceNode extends BufferedResourceNode { FilteredBufferedResourceNode(IResource resource) { super(resource); } @Override protected IStructureComparator createChild(IResource child) { String name = child.getName(); // if (CompareUIPlugin.getDefault().filter(name, child instanceof IContainer, false)) // return null; return new FilteredBufferedResourceNode(child); } } /* * Creates an compare editor input for the given selection. */ ResourceCompareInput(CompareConfiguration config) { super(config); } @Override public Viewer createDiffViewer(Composite parent) { fDiffViewer = new DiffTreeViewer(parent, getCompareConfiguration()) { @Override protected void fillContextMenu(IMenuManager manager) { if (fOpenAction == null) { fOpenAction = new Action() { @Override public void run() { handleOpen(null); } }; Utilities.initAction(fOpenAction, getBundle(), "action.CompareContents."); //$NON-NLS-1$ } boolean enable = false; ISelection selection = getSelection(); if (selection instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection) selection; if (ss.size() == 1) { Object element = ss.getFirstElement(); if (element instanceof MyDiffNode) { ITypedElement te = ((MyDiffNode) element).getId(); if (te != null) { enable = !ITypedElement.FOLDER_TYPE.equals(te.getType()); } } else { enable = true; } } } fOpenAction.setEnabled(enable); manager.add(fOpenAction); super.fillContextMenu(manager); } }; fDiffViewer.getControl(); return fDiffViewer; } class SelectAncestorDialog extends MessageDialog { private IResource[] theResources; IResource ancestorResource; IResource leftResource; IResource rightResource; private Button[] buttons; public SelectAncestorDialog(Shell parentShell, IResource[] theResources) { super(parentShell, "", null, "", MessageDialog.QUESTION, new String[] { IDialogConstants.OK_LABEL,//$NON-NLS-1$//$NON-NLS-2$ IDialogConstants.CANCEL_LABEL }, 0); this.theResources = theResources; } @Override protected Control createCustomArea(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout()); buttons = new Button[3]; for (int i = 0; i < 3; i++) { buttons[i] = new Button(composite, SWT.RADIO); buttons[i].addSelectionListener(selectionListener); buttons[i].setText("custom");//$NON-NLS-1$ buttons[i].setFont(parent.getFont()); // set initial state buttons[i].setSelection(i == 0); } pickAncestor(0); return composite; } private void pickAncestor(int i) { ancestorResource = theResources[i]; leftResource = theResources[i == 0 ? 1 : 0]; rightResource = theResources[i == 2 ? 1 : 2]; } private SelectionListener selectionListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Button selectedButton = (Button) e.widget; if (!selectedButton.getSelection()) { return; } for (int i = 0; i < 3; i++) { if (selectedButton == buttons[i]) { pickAncestor(i); } } } }; } public CompareHeadInfo getCompareHeadInfo() { return compareHeadInfo; } public void setCompareHeadInfo(CompareHeadInfo compareHeadInfo) { this.compareHeadInfo = compareHeadInfo; } // If the compare is three-way, this method asks the user which resource // to use as the ancestor. Returns false if the user cancels the prompt, // true otherwise. public boolean setSelection(ISelection s, Shell shell) { IResource[] selection = Utilities.getResources(s); fThreeWay = selection.length == 3; if (fThreeWay) { SelectAncestorDialog dialog = new SelectAncestorDialog(shell, selection); int code = dialog.open(); if (code == Window.CANCEL) { return false; } fAncestorResource = dialog.ancestorResource; fAncestor = getStructure(fAncestorResource); fLeftResource = dialog.leftResource; fRightResource = dialog.rightResource; } else { fAncestorResource = null; fAncestor = null; fLeftResource = selection[0]; fRightResource = selection[1]; } fLeft = getStructure(fLeftResource); fRight = getStructure(fRightResource); return true; } /* * Returns true if compare can be executed for the given selection. */ public boolean isEnabled(ISelection s) { IResource[] selection = Utilities.getResources(s); if (selection.length < 2 || selection.length > 3) { return false; } boolean threeWay = selection.length == 3; if (threeWay) { // It only makes sense if they're all mutually comparable. // If not, the user should compare two of them. return comparable(selection[0], selection[1]) && comparable(selection[0], selection[2]) && comparable(selection[1], selection[2]); } return comparable(selection[0], selection[1]); } /** * Initializes the images in the compare configuration. */ void initializeCompareConfiguration() { CompareConfiguration cc = getCompareConfiguration(); if (fLeftResource != null) { cc.setLeftLabel(buildLabel(fLeftResource)); // cc.setLeftImage(CompareUIPlugin.getImage(fLeftResource)); } if (fRightResource != null) { cc.setRightLabel(buildLabel(fRightResource)); // cc.setRightImage(CompareUIPlugin.getImage(fRightResource)); } if (fThreeWay && fAncestorResource != null) { cc.setAncestorLabel(buildLabel(fAncestorResource)); // cc.setAncestorImage(CompareUIPlugin.getImage(fAncestorResource)); } } /* * Returns true if both resources are either structured or unstructured. */ private boolean comparable(IResource c1, IResource c2) { return hasStructure(c1) == hasStructure(c2); } /* * Returns true if the given argument has a structure. */ private boolean hasStructure(IResource input) { if (input instanceof IContainer) { return true; } if (input instanceof IFile) { IFile file = (IFile) input; String type = file.getFileExtension(); if (type != null) { type = normalizeCase(type); return "JAR".equals(type) || "ZIP".equals(type); //$NON-NLS-2$ //$NON-NLS-1$ } } return false; } /* * Creates a <code>IStructureComparator</code> for the given input. Returns <code>null</code> if no * <code>IStructureComparator</code> can be found for the <code>IResource</code>. */ private IStructureComparator getStructure(IResource input) { if (input instanceof IContainer) { return new FilteredBufferedResourceNode(input); } if (input instanceof IFile) { IStructureComparator rn = new FilteredBufferedResourceNode(input); IFile file = (IFile) input; String type = normalizeCase(file.getFileExtension()); if ("JAR".equals(type) || "ZIP".equals(type)) { return new ZipFileStructureCreator().getStructure(rn); } return rn; } return null; } /* * Performs a two-way or three-way diff on the current selection. */ @Override public Object prepareInput(IProgressMonitor pm) throws InvocationTargetException { try { // fix for PR 1GFMLFB: ITPUI:WIN2000 - files that are out of sync with the file system appear as empty fLeftResource.refreshLocal(IResource.DEPTH_INFINITE, pm); fRightResource.refreshLocal(IResource.DEPTH_INFINITE, pm); if (fThreeWay && fAncestorResource != null) { fAncestorResource.refreshLocal(IResource.DEPTH_INFINITE, pm); // end fix } //pm.beginTask(Utilities.getString("ResourceCompare.taskName"), IProgressMonitor.UNKNOWN); //$NON-NLS-1$ String leftLabel = fLeftResource.getName(); String rightLabel = fRightResource.getName(); String title; // if (fThreeWay) { // String format= Utilities.getString("ResourceCompare.threeWay.title"); //$NON-NLS-1$ // String ancestorLabel= fAncestorResource.getName(); // title= MessageFormat.format(format, new String[] {ancestorLabel, leftLabel, rightLabel}); // } else { // String format= Utilities.getString("ResourceCompare.twoWay.title"); //$NON-NLS-1$ // title= MessageFormat.format(format, new String[] {leftLabel, rightLabel}); // } // setTitle(title); Differencer d = new Differencer() { @Override protected Object visit(Object parent, int description, Object ancestor, Object left, Object right) { return new MyDiffNode((IDiffContainer) parent, description, (ITypedElement) ancestor, (ITypedElement) left, (ITypedElement) right); } }; fRoot = d.findDifferences(fThreeWay, pm, null, fAncestor, fLeft, fRight); return fRoot; } catch (CoreException ex) { throw new InvocationTargetException(ex); } finally { pm.done(); } } @Override public String getToolTipText() { // if (fLeftResource != null && fRightResource != null) { // String leftLabel= fLeftResource.getFullPath().makeRelative().toString(); // String rightLabel= fRightResource.getFullPath().makeRelative().toString(); // if (fThreeWay) { // String format= Utilities.getString("ResourceCompare.threeWay.tooltip"); //$NON-NLS-1$ // String ancestorLabel= fAncestorResource.getFullPath().makeRelative().toString(); // return MessageFormat.format(format, new String[] {ancestorLabel, leftLabel, rightLabel}); // } // String format= Utilities.getString("ResourceCompare.twoWay.tooltip"); //$NON-NLS-1$ // return MessageFormat.format(format, new String[] {leftLabel, rightLabel}); // } // fall back return super.getToolTipText(); } private String buildLabel(IResource r) { String n = r.getFullPath().toString(); if (n.charAt(0) == IPath.SEPARATOR) { return n.substring(1); } return n; } @Override public boolean isSaveNeeded() { if (compareHeadInfo == null) { return false; } if (fRoot instanceof MyDiffNode) { return ((MyDiffNode) fRoot).fDirty; } return false; } @Override public void saveChanges(IProgressMonitor pm) throws CoreException { super.saveChanges(pm); if (fRoot instanceof DiffNode) { try { commit(pm, (DiffNode) fRoot); } finally { if (fDiffViewer != null) { fDiffViewer.refresh(); } setDirty(false); if (fRoot instanceof MyDiffNode) { ((MyDiffNode) fRoot).fDirty = false; } } } } /* * Recursively walks the diff tree and commits all changes. */ private void commit(IProgressMonitor pm, DiffNode node) throws CoreException { if (node instanceof MyDiffNode) { ((MyDiffNode) node).clearDirty(); } ITypedElement left = node.getLeft(); if (left instanceof BufferedResourceNode) { ((BufferedResourceNode) left).commit(pm); } ITypedElement right = node.getRight(); if (right instanceof BufferedResourceNode) { ((BufferedResourceNode) right).commit(pm); } IDiffElement[] children = node.getChildren(); if (children != null) { for (IDiffElement element : children) { if (element instanceof DiffNode) { commit(pm, (DiffNode) element); } } } if (this.compareHeadInfo != null) { commitToDB(); } } private void commitToDB() { try { String toCommitContent = CompareManager.getInstance().getLeftContent(); toCommitContent = XmlUtil.formatCompact(toCommitContent, "UTF-8");//$NON-NLS-1$ if (this.compareHeadInfo.isItem()) { Util.getMDMService(compareHeadInfo.getXobject()).putItem( new WSPutItem(false, (WSDataClusterPK) compareHeadInfo.getXobject().getWsKey(), ""//$NON-NLS-1$ .equals(compareHeadInfo.getDataModelName()) ? null : new WSDataModelPK(compareHeadInfo .getDataModelName()), toCommitContent)); } else { // TODO add support for Object(s) } } catch (Exception e) { log.error(e.getMessage(), e); if (!Util.handleConnectionException((Shell) null, e, null)) { MessageDialog.openError(null, Messages._Error, Messages.bind(Messages.ResourceCompareInput_ErrorMsg, e.getLocalizedMessage())); } } } /* * (non Javadoc) see IAdaptable.getAdapter */ @Override public Object getAdapter(Class adapter) { if (IFile.class.equals(adapter)) { IProgressMonitor pm = new NullProgressMonitor(); // flush changes in any dirty viewer flushViewers(pm); IFile[] files = (IFile[]) getAdapter(IFile[].class); if (files != null && files.length > 0) { return files[0]; // can only return one: limitation on IDE.saveAllEditors; see #64617 } return null; } if (IFile[].class.equals(adapter)) { HashSet collector = new HashSet(); collectDirtyResources(fRoot, collector); return collector.toArray(new IFile[collector.size()]); } return super.getAdapter(adapter); } private void collectDirtyResources(Object o, Set collector) { if (o instanceof DiffNode) { DiffNode node = (DiffNode) o; ITypedElement left = node.getLeft(); if (left instanceof BufferedResourceNode) { BufferedResourceNode bn = (BufferedResourceNode) left; if (bn.isDirty()) { IResource resource = bn.getResource(); if (resource instanceof IFile) { collector.add(resource); } } } ITypedElement right = node.getRight(); if (right instanceof BufferedResourceNode) { BufferedResourceNode bn = (BufferedResourceNode) right; if (bn.isDirty()) { IResource resource = bn.getResource(); if (resource instanceof IFile) { collector.add(resource); } } } IDiffElement[] children = node.getChildren(); if (children != null) { for (IDiffElement element : children) { if (element instanceof DiffNode) { collectDirtyResources(element, collector); } } } } } private static String normalizeCase(String s) { if (NORMALIZE_CASE && s != null) { return s.toUpperCase(); } return s; } @Override public boolean canRunAsJob() { return true; } }