/******************************************************************************* * Copyright (c) 2000, 2010 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.internal; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.StringTokenizer; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.CompareEditorInput; import org.eclipse.compare.IStreamContentAccessor; import org.eclipse.compare.IStreamMerger; import org.eclipse.compare.ITypedElement; import org.eclipse.compare.internal.core.ComparePlugin; import org.eclipse.compare.structuremergeviewer.ICompareInput; import org.eclipse.compare.structuremergeviewer.IStructureCreator; import org.eclipse.compare.structuremergeviewer.StructureDiffViewer; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorRegistry; import org.eclipse.ui.IReusableEditor; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; /** * The Compare UI plug-in defines the entry point to initiate a configurable * compare operation on arbitrary resources. The result of the compare * is opened into a compare editor where the details can be browsed and * edited in dynamically selected structure and content viewers. * <p> * The Compare UI provides a registry for content and structure compare viewers, * which is initialized from extensions contributed to extension points * declared by this plug-in. * <p> * This class is the plug-in runtime class for the * <code>"org.eclipse.compare"</code> plug-in. * </p> */ public final class CompareUIPlugin extends AbstractUIPlugin { static class CompareRegistry { private final static String ID_ATTRIBUTE= "id"; //$NON-NLS-1$ private final static String EXTENSIONS_ATTRIBUTE= "extensions"; //$NON-NLS-1$ private final static String CONTENT_TYPE_ID_ATTRIBUTE= "contentTypeId"; //$NON-NLS-1$ private HashMap fIdMap; // maps ids to data private HashMap fExtensionMap; // multimap: maps extensions to list of data private HashMap fContentTypeBindings; // multimap: maps content type bindings to list of data void register(IConfigurationElement element, Object data) { String id= element.getAttribute(ID_ATTRIBUTE); if (id != null) { if (fIdMap == null) fIdMap= new HashMap(); fIdMap.put(id, data); } String types= element.getAttribute(EXTENSIONS_ATTRIBUTE); if (types != null) { if (fExtensionMap == null) fExtensionMap= new HashMap(); StringTokenizer tokenizer= new StringTokenizer(types, ","); //$NON-NLS-1$ while (tokenizer.hasMoreElements()) { String extension= tokenizer.nextToken().trim(); List l = (List) fExtensionMap.get(normalizeCase(extension)); if (l == null) fExtensionMap.put(normalizeCase(extension), l = new ArrayList()); l.add(data); } } } void createBinding(IConfigurationElement element, String idAttributeName) { String type= element.getAttribute(CONTENT_TYPE_ID_ATTRIBUTE); String id= element.getAttribute(idAttributeName); if (id == null) logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.targetIdAttributeMissing", idAttributeName)); //$NON-NLS-1$ if (type != null && id != null && fIdMap != null) { Object o= fIdMap.get(id); if (o != null) { IContentType ct= fgContentTypeManager.getContentType(type); if (ct != null) { if (fContentTypeBindings == null) fContentTypeBindings= new HashMap(); List l = (List) fContentTypeBindings.get(ct); if (l == null) fContentTypeBindings.put(ct, l = new ArrayList()); l.add(o); } else { logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.contentTypeNotFound", type)); //$NON-NLS-1$ } } else { logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.targetNotFound", id)); //$NON-NLS-1$ } } } Object search(IContentType type) { List list = searchAll(type); return list != null ? list.get(0) : null; } List searchAll(IContentType type) { if (fContentTypeBindings != null) { for (; type != null; type= type.getBaseType()) { List data= (List) fContentTypeBindings.get(type); if (data != null) return data; } } return null; } Object search(String extension) { List list = searchAll(extension); return list != null ? list.get(0) : null; } List searchAll(String extension) { if (fExtensionMap != null) return (List) fExtensionMap.get(normalizeCase(extension)); return null; } } /** Status code describing an internal error */ public static final int INTERNAL_ERROR= 1; private static boolean NORMALIZE_CASE= true; public static final String PLUGIN_ID= "org.eclipse.compare"; //$NON-NLS-1$ private static final String BINARY_TYPE= "binary"; //$NON-NLS-1$ private static final String STREAM_MERGER_EXTENSION_POINT= "streamMergers"; //$NON-NLS-1$ private static final String STREAM_MERGER= "streamMerger"; //$NON-NLS-1$ private static final String STREAM_MERGER_ID_ATTRIBUTE= "streamMergerId"; //$NON-NLS-1$ private static final String STRUCTURE_CREATOR_EXTENSION_POINT= "structureCreators"; //$NON-NLS-1$ private static final String STRUCTURE_CREATOR= "structureCreator"; //$NON-NLS-1$ private static final String STRUCTURE_CREATOR_ID_ATTRIBUTE= "structureCreatorId"; //$NON-NLS-1$ private static final String VIEWER_TAG= "viewer"; //$NON-NLS-1$ private static final String STRUCTURE_MERGE_VIEWER_EXTENSION_POINT= "structureMergeViewers"; //$NON-NLS-1$ private static final String STRUCTURE_MERGE_VIEWER_ID_ATTRIBUTE= "structureMergeViewerId"; //$NON-NLS-1$ private static final String CONTENT_MERGE_VIEWER_EXTENSION_POINT= "contentMergeViewers"; //$NON-NLS-1$ private static final String CONTENT_MERGE_VIEWER_ID_ATTRIBUTE= "contentMergeViewerId"; //$NON-NLS-1$ private static final String CONTENT_VIEWER_EXTENSION_POINT= "contentViewers"; //$NON-NLS-1$ private static final String CONTENT_VIEWER_ID_ATTRIBUTE= "contentViewerId"; //$NON-NLS-1$ private static final String CONTENT_TYPE_BINDING= "contentTypeBinding"; //$NON-NLS-1$ private static final String COMPARE_EDITOR= PLUGIN_ID + ".CompareEditor"; //$NON-NLS-1$ private static final String STRUCTUREVIEWER_ALIASES_PREFERENCE_NAME= "StructureViewerAliases"; //$NON-NLS-1$ // content type private static final IContentTypeManager fgContentTypeManager= Platform.getContentTypeManager(); public static final int NO_DIFFERENCE = 10000; /** * The plugin singleton. */ private static CompareUIPlugin fgComparePlugin; /** Maps type to icons */ private static Map fgImages= new Hashtable(10); /** Maps type to ImageDescriptors */ private static Map fgImageDescriptors= new Hashtable(10); /** Maps ImageDescriptors to Images */ private static Map fgImages2= new Hashtable(10); private static List fgDisposeOnShutdownImages= new ArrayList(); private ResourceBundle fResourceBundle; private boolean fRegistriesInitialized; private CompareRegistry fStreamMergers= new CompareRegistry(); private CompareRegistry fStructureCreators= new CompareRegistry(); private CompareRegistry fStructureMergeViewers= new CompareRegistry(); private CompareRegistry fContentViewers= new CompareRegistry(); private CompareRegistry fContentMergeViewers= new CompareRegistry(); private Map fStructureViewerAliases; private CompareFilter fFilter; private IPropertyChangeListener fPropertyChangeListener; /** * Creates the <code>CompareUIPlugin</code> object and registers all * structure creators, content merge viewers, and structure merge viewers * contributed to this plug-in's extension points. * <p> * Note that instances of plug-in runtime classes are automatically created * by the platform in the course of plug-in activation. */ public CompareUIPlugin() { super(); Assert.isTrue(fgComparePlugin == null); fgComparePlugin= this; } public void start(BundleContext context) throws Exception { super.start(context); ComparePlugin.getDefault().setCappingDisabled( getPreferenceStore().getBoolean( ComparePreferencePage.CAPPING_DISABLED)); } public void stop(BundleContext context) throws Exception { IPreferenceStore ps= getPreferenceStore(); rememberAliases(ps); if (fPropertyChangeListener != null) { ps.removePropertyChangeListener(fPropertyChangeListener); fPropertyChangeListener= null; } super.stop(context); if (fgDisposeOnShutdownImages != null) { Iterator i= fgDisposeOnShutdownImages.iterator(); while (i.hasNext()) { Image img= (Image) i.next(); if (!img.isDisposed()) img.dispose(); } fgImages= null; } } /** * Returns the singleton instance of this plug-in runtime class. * * @return the compare plug-in instance */ public static CompareUIPlugin getDefault() { return fgComparePlugin; } /** * Returns this plug-in's resource bundle. * * @return the plugin's resource bundle */ public ResourceBundle getResourceBundle() { if (fResourceBundle == null) fResourceBundle= Platform.getResourceBundle(getBundle()); return fResourceBundle; } /** * Returns this plug-in's unique identifier. * * @return the plugin's unique identifier */ public static String getPluginId() { return getDefault().getBundle().getSymbolicName(); } private void initializeRegistries() { if (!fRegistriesInitialized) { registerExtensions(); fRegistriesInitialized= true; } } /** * Registers all stream mergers, structure creators, content merge viewers, and structure merge viewers * that are found in the XML plugin files. */ private void registerExtensions() { IExtensionRegistry registry= Platform.getExtensionRegistry(); // collect all IStreamMergers IConfigurationElement[] elements= registry.getConfigurationElementsFor(PLUGIN_ID, STREAM_MERGER_EXTENSION_POINT); for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; if (STREAM_MERGER.equals(element.getName())) fStreamMergers.register(element, new StreamMergerDescriptor(element)); } for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; if (CONTENT_TYPE_BINDING.equals(element.getName())) fStreamMergers.createBinding(element, STREAM_MERGER_ID_ATTRIBUTE); } // collect all IStructureCreators elements= registry.getConfigurationElementsFor(PLUGIN_ID, STRUCTURE_CREATOR_EXTENSION_POINT); for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; String name= element.getName(); if (!CONTENT_TYPE_BINDING.equals(name)) { if (!STRUCTURE_CREATOR.equals(name)) logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.unexpectedTag", name, STRUCTURE_CREATOR)); //$NON-NLS-1$ fStructureCreators.register(element, new StructureCreatorDescriptor(element)); } } for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; if (CONTENT_TYPE_BINDING.equals(element.getName())) fStructureCreators.createBinding(element, STRUCTURE_CREATOR_ID_ATTRIBUTE); } // collect all viewers which define the structure merge viewer extension point elements= registry.getConfigurationElementsFor(PLUGIN_ID, STRUCTURE_MERGE_VIEWER_EXTENSION_POINT); for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; String name= element.getName(); if (!CONTENT_TYPE_BINDING.equals(name)) { if (!VIEWER_TAG.equals(name)) logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.unexpectedTag", name, VIEWER_TAG)); //$NON-NLS-1$ fStructureMergeViewers.register(element, new ViewerDescriptor(element)); } } for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; if (CONTENT_TYPE_BINDING.equals(element.getName())) fStructureMergeViewers.createBinding(element, STRUCTURE_MERGE_VIEWER_ID_ATTRIBUTE); } // collect all viewers which define the content merge viewer extension point elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_MERGE_VIEWER_EXTENSION_POINT); for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; String name= element.getName(); if (!CONTENT_TYPE_BINDING.equals(name)) { if (!VIEWER_TAG.equals(name)) logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.unexpectedTag", name, VIEWER_TAG)); //$NON-NLS-1$ fContentMergeViewers.register(element, new ViewerDescriptor(element)); } } for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; if (CONTENT_TYPE_BINDING.equals(element.getName())) fContentMergeViewers.createBinding(element, CONTENT_MERGE_VIEWER_ID_ATTRIBUTE); } // collect all viewers which define the content viewer extension point elements= registry.getConfigurationElementsFor(PLUGIN_ID, CONTENT_VIEWER_EXTENSION_POINT); for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; String name= element.getName(); if (!CONTENT_TYPE_BINDING.equals(name)) { if (!VIEWER_TAG.equals(name)) logErrorMessage(Utilities.getFormattedString("CompareUIPlugin.unexpectedTag", name, VIEWER_TAG)); //$NON-NLS-1$ fContentViewers.register(element, new ViewerDescriptor(element)); } } for (int i= 0; i < elements.length; i++) { IConfigurationElement element= elements[i]; if (CONTENT_TYPE_BINDING.equals(element.getName())) fContentViewers.createBinding(element, CONTENT_VIEWER_ID_ATTRIBUTE); } } public static IWorkbench getActiveWorkbench() { CompareUIPlugin plugin= getDefault(); if (plugin == null) return null; return plugin.getWorkbench(); } public static IWorkbenchWindow getActiveWorkbenchWindow() { IWorkbench workbench= getActiveWorkbench(); if (workbench == null) return null; return workbench.getActiveWorkbenchWindow(); } /** * Returns the active workbench page or <code>null</code> if * no active workbench page can be determined. * * @return the active workbench page or <code>null</code> if * no active workbench page can be determined */ private static IWorkbenchPage getActivePage() { IWorkbenchWindow window= getActiveWorkbenchWindow(); if (window == null) return null; return window.getActivePage(); } /** * Returns the SWT Shell of the active workbench window or <code>null</code> if * no workbench window is active. * * @return the SWT Shell of the active workbench window, or <code>null</code> if * no workbench window is active */ public static Shell getShell() { IWorkbenchWindow window= getActiveWorkbenchWindow(); if (window == null) return null; return window.getShell(); } /** * Registers the given image for being disposed when this plug-in is shutdown. * * @param image the image to register for disposal */ public static void disposeOnShutdown(Image image) { if (image != null) fgDisposeOnShutdownImages.add(image); } /** * Performs the comparison described by the given input and opens a compare * editor on the result. * * @param input * the input on which to open the compare editor * @param page * the workbench page on which to create a new compare editor * @param editor * if not null the input is opened in this editor * @param activate * if <code>true</code> the editor will be activated * @see IWorkbenchPage#openEditor(org.eclipse.ui.IEditorInput, String, * boolean) * @see CompareEditorInput */ public void openCompareEditor(final CompareEditorInput input, final IWorkbenchPage page, final IReusableEditor editor, final boolean activate) { CompareConfiguration configuration = input.getCompareConfiguration(); if (configuration != null) { IPreferenceStore ps= configuration.getPreferenceStore(); if (ps != null) configuration.setProperty( CompareConfiguration.USE_OUTLINE_VIEW, Boolean.valueOf(ps.getBoolean(ComparePreferencePage.USE_OUTLINE_VIEW))); } if (input.canRunAsJob()) { openEditorInBackground(input, page, editor, activate); } else { if (compareResultOK(input, null)) { internalOpenEditor(input, page, editor, activate); } } } private void openEditorInBackground(final CompareEditorInput input, final IWorkbenchPage page, final IReusableEditor editor, final boolean activate) { internalOpenEditor(input, page, editor, activate); } private void internalOpenEditor(final CompareEditorInput input, final IWorkbenchPage wp, final IReusableEditor editor, final boolean activate) { Runnable runnable = new Runnable() { public void run() { if (editor != null && !editor.getSite().getShell().isDisposed()) { // reuse the given editor editor.setInput(input); return; } IWorkbenchPage page = wp; if (page == null) page= getActivePage(); if (page != null) { // open new CompareEditor on page try { page.openEditor(input, COMPARE_EDITOR, activate); } catch (PartInitException e) { MessageDialog.openError(getShell(), Utilities.getString("CompareUIPlugin.openEditorError"), e.getMessage()); //$NON-NLS-1$ } } else { MessageDialog.openError(getShell(), Utilities.getString("CompareUIPlugin.openEditorError"), //$NON-NLS-1$ Utilities.getString("CompareUIPlugin.noActiveWorkbenchPage")); //$NON-NLS-1$ } } }; syncExec(runnable); } /** * Performs the comparison described by the given input and opens a * compare dialog on the result. * * @param input the input on which to open the compare editor * @see CompareEditorInput */ public void openCompareDialog(final CompareEditorInput input) { // We don't ever open dialogs in the background if (compareResultOK(input, null)) { internalOpenDialog(input); } } public IStatus prepareInput(CompareEditorInput input, IProgressMonitor monitor) { try { input.run(monitor); String message= input.getMessage(); if (message != null) { return new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, message, null); } if (input.getCompareResult() == null) { return new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, NO_DIFFERENCE, Utilities.getString("CompareUIPlugin.noDifferences"), null); //$NON-NLS-1$ } return Status.OK_STATUS; } catch (InterruptedException e) { throw new OperationCanceledException(); } catch (InvocationTargetException e) { return new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0, Utilities.getString("CompareUIPlugin.compareFailed"), e.getTargetException()); //$NON-NLS-1$ } } /* * @return <code>true</code> if compare result is OK to show, <code>false</code> otherwise */ public boolean compareResultOK(CompareEditorInput input, IRunnableContext context) { final Shell shell= getShell(); try { // run operation in separate thread and make it cancelable if (context == null) context = PlatformUI.getWorkbench().getProgressService(); context.run(true, true, input); String message= input.getMessage(); if (message != null) { MessageDialog.openError(shell, Utilities.getString("CompareUIPlugin.compareFailed"), message); //$NON-NLS-1$ return false; } if (input.getCompareResult() == null) { MessageDialog.openInformation(shell, Utilities.getString("CompareUIPlugin.dialogTitle"), Utilities.getString("CompareUIPlugin.noDifferences")); //$NON-NLS-2$ //$NON-NLS-1$ return false; } return true; } catch (InterruptedException x) { // canceled by user } catch (InvocationTargetException x) { MessageDialog.openError(shell, Utilities.getString("CompareUIPlugin.compareFailed"), x.getTargetException().getMessage()); //$NON-NLS-1$ } return false; } /* * Registers an image for the given type. */ private static void registerImage(String type, Image image, boolean dispose) { fgImages.put(normalizeCase(type), image); if (image != null && dispose) { fgDisposeOnShutdownImages.add(image); } } /** * Registers an image descriptor for the given type. * * @param type the type * @param descriptor the image descriptor */ public static void registerImageDescriptor(String type, ImageDescriptor descriptor) { fgImageDescriptors.put(normalizeCase(type), descriptor); } public static ImageDescriptor getImageDescriptor(String relativePath) { if (fgComparePlugin == null) return null; IPath path= Utilities.getIconPath(null).append(relativePath); URL url= FileLocator.find(fgComparePlugin.getBundle(), path, null); if (url == null) return null; return ImageDescriptor.createFromURL(url); } /** * Returns a shared image for the given type, or a generic image if none * has been registered for the given type. * <p> * Note: Images returned from this method will be automatically disposed * of when this plug-in shuts down. Callers must not dispose of these * images themselves. * </p> * * @param type the type * @return the image */ public static Image getImage(String type) { type= normalizeCase(type); boolean dispose= false; Image image= null; if (type != null) image= (Image) fgImages.get(type); if (image == null) { ImageDescriptor id= (ImageDescriptor) fgImageDescriptors.get(type); if (id != null) { image= id.createImage(); dispose= true; } if (image == null) { if (fgComparePlugin != null) { if (ITypedElement.FOLDER_TYPE.equals(type)) { image= getDefault().getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER); //image= SharedImages.getImage(ISharedImages.IMG_OBJ_FOLDER); } else { image= createWorkbenchImage(type); dispose= true; } } else { id= (ImageDescriptor) fgImageDescriptors.get(normalizeCase("file")); //$NON-NLS-1$ image= id.createImage(); dispose= true; } } if (image != null) registerImage(type, image, dispose); } return image; } /** * Returns a shared image for the given adaptable. * This convenience method queries the given adaptable * for its <code>IWorkbenchAdapter.getImageDescriptor</code>, which it * uses to create an image if it does not already have one. * <p> * Note: Images returned from this method will be automatically disposed * of when this plug-in shuts down. Callers must not dispose of these * images themselves. * </p> * * @param adaptable the adaptable for which to find an image * @return an image */ public static Image getImage(IAdaptable adaptable) { if (adaptable != null) { Object o= adaptable.getAdapter(IWorkbenchAdapter.class); if (o instanceof IWorkbenchAdapter) { ImageDescriptor id= ((IWorkbenchAdapter) o).getImageDescriptor(adaptable); if (id != null) { Image image= (Image)fgImages2.get(id); if (image == null) { image= id.createImage(); try { fgImages2.put(id, image); } catch (NullPointerException ex) { // NeedWork } fgDisposeOnShutdownImages.add(image); } return image; } } } return null; } private static Image createWorkbenchImage(String type) { IEditorRegistry er= getDefault().getWorkbench().getEditorRegistry(); ImageDescriptor id= er.getImageDescriptor("foo." + type); //$NON-NLS-1$ return id.createImage(); } /** * Returns an structure creator descriptor for the given type. * * @param type the type for which to find a descriptor * @return a descriptor for the given type, or <code>null</code> if no * descriptor has been registered */ public StructureCreatorDescriptor getStructureCreator(String type) { initializeRegistries(); return (StructureCreatorDescriptor) fStructureCreators.search(type); } /** * Returns a stream merger for the given type. * * @param type the type for which to find a stream merger * @return a stream merger for the given type, or <code>null</code> if no * stream merger has been registered */ public IStreamMerger createStreamMerger(String type) { initializeRegistries(); StreamMergerDescriptor descriptor= (StreamMergerDescriptor) fStreamMergers.search(type); if (descriptor != null) return descriptor.createStreamMerger(); return null; } /** * Returns a stream merger for the given content type. * * @param type the type for which to find a stream merger * @return a stream merger for the given type, or <code>null</code> if no * stream merger has been registered */ public IStreamMerger createStreamMerger(IContentType type) { initializeRegistries(); StreamMergerDescriptor descriptor= (StreamMergerDescriptor) fStreamMergers.search(type); if (descriptor != null) return descriptor.createStreamMerger(); return null; } public ViewerDescriptor[] findStructureViewerDescriptor(Viewer oldViewer, ICompareInput input, CompareConfiguration configuration) { if (input == null) return null; // we don't show the structure of additions or deletions if (input == null || input.getLeft() == null || input.getRight() == null) return null; Set result = new LinkedHashSet(); // content type search IContentType ctype= getCommonType(input); if (ctype != null) { initializeRegistries(); List list = fStructureMergeViewers.searchAll(ctype); if (list != null) result.addAll(list); } // old style search String[] types= getTypes(input); String type= null; if (isHomogenous(types)) { type= normalizeCase(types[0]); initializeRegistries(); List list = fStructureMergeViewers.searchAll(type); if (list != null) result.addAll(list); String alias= getStructureViewerAlias(type); if (alias != null) { list = fStructureMergeViewers.searchAll(alias); if (list != null) result.addAll(list); } } return result.size() > 0 ? (ViewerDescriptor[]) result .toArray(new ViewerDescriptor[0]) : null; } /** * Returns a structure compare viewer based on an old viewer and an input object. * If the old viewer is suitable for showing the input, the old viewer * is returned. Otherwise, the input's type is used to find a viewer descriptor in the registry * which in turn is used to create a structure compare viewer under the given parent composite. * If no viewer descriptor can be found <code>null</code> is returned. * * @param oldViewer a new viewer is only created if this old viewer cannot show the given input * @param input the input object for which to find a structure viewer * @param parent the SWT parent composite under which the new viewer is created * @param configuration a configuration which is passed to a newly created viewer * @return the compare viewer which is suitable for the given input object or <code>null</code> */ public Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent, CompareConfiguration configuration) { ViewerDescriptor[] descriptors = findStructureViewerDescriptor(oldViewer, input, configuration); if (descriptors == null || descriptors.length == 0) { // we didn't found any viewer so far. // now we try to find a structure creator for the generic StructureDiffViewer IContentType ctype= getCommonType(input); String[] types= getTypes(input); String type= null; if (isHomogenous(types)) { type= normalizeCase(types[0]); } StructureCreatorDescriptor scc= null; initializeRegistries(); Object desc= fStructureCreators.search(ctype); // search for content type if (desc instanceof StructureCreatorDescriptor) scc= (StructureCreatorDescriptor) desc; if (scc == null && type != null) scc= getStructureCreator(type); // search for old-style type scheme if (scc != null) { IStructureCreator sc= scc.createStructureCreator(); if (sc != null) { StructureDiffViewer sdv= new StructureDiffViewer(parent, configuration); sdv.setStructureCreator(sc); return sdv; } } return null; } return getViewer(descriptors[0], oldViewer, parent, configuration); } public ViewerDescriptor[] findContentViewerDescriptor(Viewer oldViewer, Object in, CompareConfiguration cc) { Set result = new LinkedHashSet(); if (in instanceof IStreamContentAccessor) { String type= ITypedElement.TEXT_TYPE; if (in instanceof ITypedElement) { ITypedElement tin= (ITypedElement) in; IContentType ct= getContentType(tin); if (ct != null) { initializeRegistries(); List list = fContentViewers.searchAll(ct); if (list != null) result.addAll(list); } String ty= tin.getType(); if (ty != null) type= ty; } initializeRegistries(); List list = fContentViewers.searchAll(type); if (list != null) result.addAll(list); // fallback result.add(fContentViewers.search(Platform.getContentTypeManager().getContentType(IContentTypeManager.CT_TEXT))); return (ViewerDescriptor[]) result.toArray(new ViewerDescriptor[0]); } if (!(in instanceof ICompareInput)) return null; ICompareInput input= (ICompareInput) in; IContentType ctype = getCommonType(input); if (ctype != null) { initializeRegistries(); List list = fContentMergeViewers.searchAll(ctype); if (list != null) result.addAll(list); } String[] types= getTypes(input); String type= null; if (isHomogenous(types)) type= types[0]; if (ITypedElement.FOLDER_TYPE.equals(type)) return null; if (type == null) { int n= 0; for (int i= 0; i < types.length; i++) if (!ITypedElement.UNKNOWN_TYPE.equals(types[i])) { n++; if (type == null) type= types[i]; // remember the first known type } if (n > 1) // don't use the type if there were more than one type= null; } if (type != null) { initializeRegistries(); List list = fContentMergeViewers.searchAll(type); if (list != null) result.addAll(list); } // fallback String leftType= guessType(input.getLeft()); String rightType= guessType(input.getRight()); if (leftType != null || rightType != null) { boolean right_text = rightType != null && ITypedElement.TEXT_TYPE.equals(rightType); boolean left_text = leftType != null && ITypedElement.TEXT_TYPE.equals(leftType); initializeRegistries(); if ((rightType != null && !right_text) || (leftType != null && !left_text)) { List list = fContentMergeViewers.searchAll(BINARY_TYPE); if (list != null) result.addAll(list); } List list = fContentMergeViewers.searchAll(ITypedElement.TEXT_TYPE); if (list != null) result.addAll(list); return (ViewerDescriptor[]) result.toArray(new ViewerDescriptor[0]); } return null; } /** * Returns a content compare viewer based on an old viewer and an input object. * If the old viewer is suitable for showing the input the old viewer * is returned. Otherwise the input's type is used to find a viewer descriptor in the registry * which in turn is used to create a content compare viewer under the given parent composite. * If no viewer descriptor can be found <code>null</code> is returned. * * @param oldViewer a new viewer is only created if this old viewer cannot show the given input * @param in the input object for which to find a content viewer * @param parent the SWT parent composite under which the new viewer is created * @param cc a configuration which is passed to a newly created viewer * @return the compare viewer which is suitable for the given input object or <code>null</code> */ public Viewer findContentViewer(Viewer oldViewer, Object in, Composite parent, CompareConfiguration cc) { ViewerDescriptor[] descriptors = findContentViewerDescriptor(oldViewer, in, cc); return getViewer(descriptors != null ? descriptors[0] : null, oldViewer, parent, cc); } private static Viewer getViewer(Object descriptor, Viewer oldViewer, Composite parent, CompareConfiguration cc) { if (descriptor instanceof IViewerDescriptor) return ((IViewerDescriptor)descriptor).createViewer(oldViewer, parent, cc); return null; } private static String[] getTypes(ICompareInput input) { ITypedElement ancestor= input.getAncestor(); ITypedElement left= input.getLeft(); ITypedElement right= input.getRight(); ArrayList tmp= new ArrayList(); if (ancestor != null) { String type= ancestor.getType(); if (type != null) tmp.add(normalizeCase(type)); } if (left != null) { String type= left.getType(); if (type != null) tmp.add(normalizeCase(type)); } if (right != null) { String type= right.getType(); if (type != null) tmp.add(normalizeCase(type)); } return (String[]) tmp.toArray(new String[tmp.size()]); } private static IContentType getContentType(ITypedElement element) { if (element == null) return null; String name= element.getName(); IContentType ct= null; if (element instanceof IStreamContentAccessor) { IStreamContentAccessor isa= (IStreamContentAccessor) element; try { InputStream is= isa.getContents(); if (is != null) { InputStream bis= new BufferedInputStream(is); try { ct= fgContentTypeManager.findContentTypeFor(is, name); } catch (IOException e) { // silently ignored } finally { try { bis.close(); } catch (IOException e2) { // silently ignored } } } } catch (CoreException e1) { // silently ignored } } if (ct == null) ct= fgContentTypeManager.findContentTypeFor(name); return ct; } /* * Returns true if the given types are homogeneous. */ private static boolean isHomogenous(String[] types) { switch (types.length) { case 1: return true; case 2: return types[0].equals(types[1]); case 3: return types[0].equals(types[1]) && types[1].equals(types[2]); } return false; } /* * Returns the most specific content type that is common to the given inputs or null. */ private static IContentType getCommonType(ICompareInput input) { ITypedElement ancestor= input.getAncestor(); ITypedElement left= input.getLeft(); ITypedElement right= input.getRight(); int n= 0; IContentType[] types= new IContentType[3]; IContentType type= null; if (ancestor != null) { type= getContentType(ancestor); if (type != null) types[n++]= type; } type= getContentType(left); if (type != null) types[n++]= type; else return null; type= getContentType(right); if (type != null) types[n++]= type; else return null; IContentType result= null; IContentType[] s0, s1, s2; switch (n) { case 0: return null; case 1: return types[0]; case 2: if (types[0].equals(types[1])) return types[0]; s0= toFullPath(types[0]); s1= toFullPath(types[1]); for (int i= 0; i < Math.min(s0.length, s1.length); i++) { if (!s0[i].equals(s1[i])) break; result= s0[i]; } return result; case 3: if (types[0].equals(types[1]) && types[1].equals(types[2])) return types[0]; s0= toFullPath(types[0]); s1= toFullPath(types[1]); s2= toFullPath(types[2]); for (int i= 0; i < Math.min(Math.min(s0.length, s1.length), s2.length); i++) { if (!s0[i].equals(s1[i]) || !s1[i].equals(s2[i])) break; result= s0[i]; } return result; } return null; } private static IContentType[] toFullPath(IContentType ct) { List l= new ArrayList(); for (; ct != null; ct= ct.getBaseType()) l.add(0, ct); return (IContentType[]) l.toArray(new IContentType[l.size()]); } /* * Guesses the file type of the given input. * Returns ITypedElement.TEXT_TYPE if none of the first 10 lines is longer than 1000 bytes. * Returns ITypedElement.UNKNOWN_TYPE otherwise. * Returns <code>null</code> if the input isn't an <code>IStreamContentAccessor</code>. */ private static String guessType(ITypedElement input) { if (input instanceof IStreamContentAccessor) { IStreamContentAccessor sca= (IStreamContentAccessor) input; InputStream is= null; try { is= sca.getContents(); if (is == null) return null; int lineLength= 0; int lines= 0; while (lines < 10) { int c= is.read(); if (c == -1) // EOF break; if (c == '\n' || c == '\r') { // reset line length lineLength= 0; lines++; } else lineLength++; if (lineLength > 1000) return ITypedElement.UNKNOWN_TYPE; } return ITypedElement.TEXT_TYPE; } catch (CoreException ex) { // be silent and return UNKNOWN_TYPE } catch (IOException ex) { // be silent and return UNKNOWN_TYPE } finally { if (is != null) { try { is.close(); } catch (IOException ex) { // silently ignored } } } return ITypedElement.UNKNOWN_TYPE; } return null; } private static String normalizeCase(String s) { if (NORMALIZE_CASE && s != null) return s.toUpperCase(); return s; } //---- alias management private String getStructureViewerAlias(String type) { return (String) getStructureViewerAliases().get(type); } public void addStructureViewerAlias(String type, String alias) { getStructureViewerAliases().put(normalizeCase(alias), normalizeCase(type)); } private Map getStructureViewerAliases() { if (fStructureViewerAliases == null) { fStructureViewerAliases= new Hashtable(10); String aliases= getPreferenceStore().getString(STRUCTUREVIEWER_ALIASES_PREFERENCE_NAME); if (aliases != null && aliases.length() > 0) { StringTokenizer st= new StringTokenizer(aliases, " "); //$NON-NLS-1$ while (st.hasMoreTokens()) { String pair= st.nextToken(); int pos= pair.indexOf('.'); if (pos > 0) { String key= pair.substring(0, pos); String alias= pair.substring(pos+1); fStructureViewerAliases.put(key, alias); } } } } return fStructureViewerAliases; } public void removeAllStructureViewerAliases(String type) { if (fStructureViewerAliases == null) return; String t= normalizeCase(type); Set entrySet= fStructureViewerAliases.entrySet(); for (Iterator iter= entrySet.iterator(); iter.hasNext(); ) { Map.Entry entry= (Map.Entry)iter.next(); if (entry.getValue().equals(t)) iter.remove(); } } /* * Converts the aliases into a single string before they are stored * in the preference store. * The format is: * <key> '.' <alias> ' ' <key> '.' <alias> ... */ private void rememberAliases(IPreferenceStore ps) { if (fStructureViewerAliases == null) return; StringBuffer buffer= new StringBuffer(); Iterator iter= fStructureViewerAliases.keySet().iterator(); while (iter.hasNext()) { String key= (String) iter.next(); String alias= (String) fStructureViewerAliases.get(key); buffer.append(key); buffer.append('.'); buffer.append(alias); buffer.append(' '); } ps.setValue(STRUCTUREVIEWER_ALIASES_PREFERENCE_NAME, buffer.toString()); } //---- filters public boolean filter(String name, boolean isFolder, boolean isArchive) { if (fFilter == null) { fFilter= new CompareFilter(); final IPreferenceStore ps= getPreferenceStore(); fFilter.setFilters(ps.getString(ComparePreferencePage.PATH_FILTER)); fPropertyChangeListener= new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (ComparePreferencePage.PATH_FILTER.equals(event.getProperty())) fFilter.setFilters(ps.getString(ComparePreferencePage.PATH_FILTER)); } }; ps.addPropertyChangeListener(fPropertyChangeListener); } return fFilter.filter(name, isFolder, isArchive); } private void internalOpenDialog(final CompareEditorInput input) { Runnable runnable = new Runnable() { public void run() { CompareDialog dialog= new CompareDialog(getShell(), input); dialog.open(); } }; syncExec(runnable); } private void syncExec(Runnable runnable) { if (Display.getCurrent() == null) { Display.getDefault().syncExec(runnable); } else { runnable.run(); } } //---- more utilities protected void handleNoDifference() { Runnable runnable = new Runnable() { public void run() { MessageDialog.openInformation(getShell(), Utilities.getString("CompareUIPlugin.dialogTitle"), Utilities.getString("CompareUIPlugin.noDifferences")); //$NON-NLS-1$//$NON-NLS-2$ } }; syncExec(runnable); } /** * Returns an array of all editors that have an unsaved content. If the identical content is * presented in more than one editor, only one of those editor parts is part of the result. * * @return an array of all dirty editor parts. */ public static IEditorPart[] getDirtyEditors() { Set inputs= new HashSet(); List result= new ArrayList(0); IWorkbench workbench= getDefault().getWorkbench(); IWorkbenchWindow[] windows= workbench.getWorkbenchWindows(); for (int i= 0; i < windows.length; i++) { IWorkbenchPage[] pages= windows[i].getPages(); for (int x= 0; x < pages.length; x++) { IEditorPart[] editors= pages[x].getDirtyEditors(); for (int z= 0; z < editors.length; z++) { IEditorPart ep= editors[z]; IEditorInput input= ep.getEditorInput(); if (!inputs.contains(input)) { inputs.add(input); result.add(ep); } } } } return (IEditorPart[])result.toArray(new IEditorPart[result.size()]); } public static void logErrorMessage(String message) { if (message == null) message= ""; //$NON-NLS-1$ log(new Status(IStatus.ERROR, getPluginId(), INTERNAL_ERROR, message, null)); } public static void log(Throwable e) { log(new Status(IStatus.ERROR, getPluginId(), INTERNAL_ERROR, CompareMessages.ComparePlugin_internal_error, e)); } public static void log(IStatus status) { getDefault().getLog().log(status); } String findContentTypeNameOrType(ICompareInput input, ViewerDescriptor vd, CompareConfiguration cc) { IContentType ctype= getCommonType(input); if (ctype != null) { initializeRegistries(); List list = fContentMergeViewers.searchAll(ctype); if (list != null) if (list.contains(vd)) return ctype.getName(); } String[] types= getTypes(input); String type= null; if (isHomogenous(types)) type= types[0]; if (ITypedElement.FOLDER_TYPE.equals(type)) return null; if (type == null) { int n= 0; for (int i= 0; i < types.length; i++) if (!ITypedElement.UNKNOWN_TYPE.equals(types[i])) { n++; if (type == null) type= types[i]; // remember the first known type } if (n > 1) // don't use the type if there were more than one type= null; } if (type != null) { initializeRegistries(); List list = fContentMergeViewers.searchAll(type); if (list != null) if (list.contains(vd)) return type; } // fallback String leftType= guessType(input.getLeft()); String rightType= guessType(input.getRight()); if (leftType != null || rightType != null) { boolean right_text = rightType != null && ITypedElement.TEXT_TYPE.equals(rightType); boolean left_text = leftType != null && ITypedElement.TEXT_TYPE.equals(leftType); initializeRegistries(); if ((rightType != null && !right_text) || (leftType != null && !left_text)) { List list = fContentMergeViewers.searchAll(BINARY_TYPE); if (list != null) if (list.contains(vd)) return type; } List list = fContentMergeViewers.searchAll(ITypedElement.TEXT_TYPE); if (list != null) if (list.contains(vd)) return type; } return null; } String findStructureTypeNameOrType(ICompareInput input, ViewerDescriptor vd, CompareConfiguration cc) { if (input == null) return null; // we don't show the structure of additions or deletions if (input == null || input.getLeft() == null || input.getRight() == null) return null; // content type search IContentType ctype= getCommonType(input); if (ctype != null) { initializeRegistries(); List list = fStructureMergeViewers.searchAll(ctype); if (list != null) if (list.contains(vd)) return ctype.getName(); } // old style search String[] types= getTypes(input); String type= null; if (isHomogenous(types)) { type= normalizeCase(types[0]); initializeRegistries(); List list = fStructureMergeViewers.searchAll(type); if (list != null) if (list.contains(vd)) return type; String alias= getStructureViewerAlias(type); if (alias != null) { list = fStructureMergeViewers.searchAll(alias); if (list != null) if (list.contains(vd)) return alias; } } return null; } }