/*******************************************************************************
 * Copyright (c) 2000, 2022 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.debug.ui;


import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.ISaveContext;
import org.eclipse.core.resources.ISaveParticipant;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.ILaunchConfigurationTab;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.debug.core.IJavaBreakpoint;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaHotCodeReplaceListener;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.core.IJavaThreadGroup;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.debug.core.IJavaVariable;
import org.eclipse.jdt.debug.core.JDIDebugModel;
import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
import org.eclipse.jdt.internal.debug.ui.breakpoints.ExceptionInspector;
import org.eclipse.jdt.internal.debug.ui.breakpoints.JavaBreakpointTypeAdapterFactory;
import org.eclipse.jdt.internal.debug.ui.classpath.ClasspathEntryAdapterFactory;
import org.eclipse.jdt.internal.debug.ui.display.JavaInspectExpression;
import org.eclipse.jdt.internal.debug.ui.monitors.JavaContendedMonitor;
import org.eclipse.jdt.internal.debug.ui.monitors.JavaOwnedMonitor;
import org.eclipse.jdt.internal.debug.ui.monitors.JavaOwningThread;
import org.eclipse.jdt.internal.debug.ui.monitors.JavaWaitingThread;
import org.eclipse.jdt.internal.debug.ui.monitors.MonitorsAdapterFactory;
import org.eclipse.jdt.internal.debug.ui.snippeteditor.SnippetFileDocumentProvider;
import org.eclipse.jdt.internal.debug.ui.sourcelookup.JavaDebugShowInAdapterFactory;
import org.eclipse.jdt.internal.debug.ui.threadgroups.TargetAdapterFactory;
import org.eclipse.jdt.internal.debug.ui.threadgroups.ThreadGroupAdapterFactory;
import org.eclipse.jdt.internal.debug.ui.variables.ColumnPresentationAdapterFactory;
import org.eclipse.jdt.internal.debug.ui.variables.JavaDebugElementAdapterFactory;
import org.eclipse.jdt.internal.launching.DefaultProjectClasspathEntry;
import org.eclipse.jdt.launching.sourcelookup.IJavaSourceLocation;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.text.JavaTextTools;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceNode;
import org.eclipse.jface.preference.IPreferencePage;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jface.preference.PreferenceNode;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.service.prefs.BackingStoreException;

/**
 * Plug-in class for the org.eclipse.jdt.debug.ui plug-in.
 */
@SuppressWarnings("deprecation")
public class JDIDebugUIPlugin extends AbstractUIPlugin {

	/**
	 * Unique identifier constant (value <code>"org.eclipse.jdt.debug.ui"</code>)
	 * for the JDI Debug plug-in.
	 */
	private static final String PI_JDI_DEBUG = "org.eclipse.jdt.debug.ui"; //$NON-NLS-1$

	/**
	 * Id for inspect command.
	 *
	 * @since 3.3
	 */
	public static final String COMMAND_INSPECT = "org.eclipse.jdt.debug.ui.commands.Inspect"; //$NON-NLS-1$

	/**
	 * Java Debug UI plug-in instance
	 */
	private static JDIDebugUIPlugin fgPlugin;

	private IDocumentProvider fSnippetDocumentProvider;

	private ImageDescriptorRegistry fImageDescriptorRegistry;

	private ActionFilterAdapterFactory fActionFilterAdapterFactory;
	private JavaSourceLocationWorkbenchAdapterFactory fSourceLocationAdapterFactory;
	private JavaBreakpointWorkbenchAdapterFactory fBreakpointAdapterFactory;

	private IDebugModelPresentation fUtilPresentation;

	/**
	 * Java Debug UI listeners
	 */
	private IJavaHotCodeReplaceListener fHCRListener;

	// Map of VMInstallTypeIDs to IConfigurationElements
	protected Map<String, IConfigurationElement> fVmInstallTypePageMap;

	/**
	 * Whether this plugin is in the process of shutting
	 * down.
	 */
	private boolean fShuttingDown= false;

	/**
	 * Singleton text tools for debug plug-in.
	 */
	private JavaTextTools fTextTools = null;

	/**
	 * @see Plugin()
	 */
	public JDIDebugUIPlugin() {
		super();
		setDefault(this);
	}

	/**
	 * Sets the Java Debug UI plug-in instance
	 *
	 * @param plugin the plugin instance
	 */
	private static void setDefault(JDIDebugUIPlugin plugin) {
		fgPlugin = plugin;
	}

	/**
	 * Returns the Java Debug UI plug-in instance
	 *
	 * @return the Java Debug UI plug-in instance
	 */
	public static JDIDebugUIPlugin getDefault() {
		return fgPlugin;
	}

	/**
	 * Convenience method which returns the unique identifier of this plugin.
	 */
	public static String getUniqueIdentifier() {
		return PI_JDI_DEBUG;
	}

	/**
	 * Logs the specified status with this plug-in's log.
	 *
	 * @param status status to log
	 */
	public static void log(IStatus status) {
		getDefault().getLog().log(status);
	}

	/**
	 * Logs an internal error with the specified message and throwable
	 *
	 * @param message the error message to log
	 * @param e
	 *            the exception to be logged
	 */
	public static void log(String message, Throwable e) {
		log(new Status(IStatus.ERROR, getUniqueIdentifier(), IJavaDebugUIConstants.INTERNAL_ERROR, message, e));
	}

	/**
	 * Logs an internal error with the specified throwable
	 *
	 * @param e the exception to be logged
	 */
	public static void log(Throwable e) {
		log(new Status(IStatus.ERROR, getUniqueIdentifier(), IJavaDebugUIConstants.INTERNAL_ERROR, "Internal Error", e)); //$NON-NLS-1$
	}

	/**
	 * Returns the active workbench window
	 *
	 * @return the active workbench window
	 */
	public static IWorkbenchWindow getActiveWorkbenchWindow() {
		return PlatformUI.getWorkbench().getActiveWorkbenchWindow();
	}

	public static IWorkbenchPage getActivePage() {
		IWorkbenchWindow w = getActiveWorkbenchWindow();
		if (w != null) {
			return w.getActivePage();
		}
		return null;
	}


	/**
	 * Returns the active workbench shell or <code>null</code> if none
	 *
	 * @return the active workbench shell or <code>null</code> if none
	 */
	public static Shell getActiveWorkbenchShell() {
		IWorkbenchWindow window = getActiveWorkbenchWindow();
		if (window != null) {
			return window.getShell();
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#createImageRegistry()
	 */
	@Override
	protected ImageRegistry createImageRegistry() {
		return JavaDebugImages.getImageRegistry();
	}

	public IDocumentProvider getSnippetDocumentProvider() {
		if (fSnippetDocumentProvider == null) {
			fSnippetDocumentProvider= new SnippetFileDocumentProvider();
		}
		return fSnippetDocumentProvider;
	}

	public static void statusDialog(IStatus status) {
		switch (status.getSeverity()) {
		case IStatus.ERROR:
			statusDialog(DebugUIMessages.JDIDebugUIPlugin_Error_1, status);
			break;
		case IStatus.WARNING:
			statusDialog(DebugUIMessages.JDIDebugUIPlugin_0, status);
			break;
		case IStatus.INFO:
			statusDialog(DebugUIMessages.JDIDebugUIPlugin_4, status);
			break;
		}
	}
	public static void statusDialog(String title, IStatus status) {
		Shell shell = getActiveWorkbenchShell();
		if (shell != null) {
			switch (status.getSeverity()) {
			case IStatus.ERROR:
				ErrorDialog.openError(shell, title, null, status);
				break;
			case IStatus.WARNING:
				MessageDialog.openWarning(shell, title, status.getMessage());
				break;
			case IStatus.INFO:
				MessageDialog.openInformation(shell, title, status.getMessage());
				break;
			}
		}
	}

	/**
	 * Creates a new internal error status with the specified message and throwable, then displays
	 * it in a status dialog.
	 *
	 * @param message error message
	 * @param t throwable cause or <code>null</code>
	 */
	public static void errorDialog(String message, Throwable t) {
		IStatus status= new Status(IStatus.ERROR, getUniqueIdentifier(), IJavaDebugUIConstants.INTERNAL_ERROR, message, t);
		statusDialog(status);
	}

	/**
	 * Opens an error dialog with the given title and message.
	 */
	public static void errorDialog(Shell shell, String title, String message, Throwable t) {
		IStatus status;
		if (t instanceof CoreException) {
			status= ((CoreException)t).getStatus();
			// if the 'message' resource string and the IStatus' message are the same,
			// don't show both in the dialog
			if (status != null && message.equals(status.getMessage())) {
				message= null;
			}
		} else {
			status= new Status(IStatus.ERROR, getUniqueIdentifier(), IDebugUIConstants.INTERNAL_ERROR, "Error within Debug UI: ", t); //$NON-NLS-1$
			log(status);
		}
		ErrorDialog.openError(shell, title, message, status);
	}

	/**
	 * Creates an extension.  If the extension plugin has not
	 * been loaded a busy cursor will be activated during the duration of
	 * the load.
	 *
	 * @param element the config element defining the extension
	 * @param classAttribute the name of the attribute carrying the class
	 * @return the extension object
	 */
	public static Object createExtension(final IConfigurationElement element, final String classAttribute) throws CoreException {
		// If plugin has been loaded create extension.
		// Otherwise, show busy cursor then create extension.
		Bundle bundle = Platform.getBundle(element.getContributor().getName());
		if (bundle.getState() == Bundle.ACTIVE) {
			return element.createExecutableExtension(classAttribute);
		}

		final Object [] ret = new Object[1];
		final CoreException [] exc = new CoreException[1];
		BusyIndicator.showWhile(null, new Runnable() {
			@Override
			public void run() {
				try {
					ret[0] = element.createExecutableExtension(classAttribute);
				} catch (CoreException e) {
					exc[0] = e;
				}
			}
		});
		if (exc[0] != null) {
			throw exc[0];
		}
		return ret[0];
	}

	/* (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
	 */
	@Override
	public void start(BundleContext context) throws Exception {
		super.start(context);
		IAdapterManager manager= Platform.getAdapterManager();
		fActionFilterAdapterFactory= new ActionFilterAdapterFactory();
		manager.registerAdapters(fActionFilterAdapterFactory, IMember.class);
		manager.registerAdapters(fActionFilterAdapterFactory, IJavaVariable.class);
		manager.registerAdapters(fActionFilterAdapterFactory, IJavaStackFrame.class);
		manager.registerAdapters(fActionFilterAdapterFactory, IJavaThread.class);
		manager.registerAdapters(fActionFilterAdapterFactory, JavaInspectExpression.class);
		fSourceLocationAdapterFactory = new JavaSourceLocationWorkbenchAdapterFactory();
		manager.registerAdapters(fSourceLocationAdapterFactory, IJavaSourceLocation.class);
		fBreakpointAdapterFactory= new JavaBreakpointWorkbenchAdapterFactory();
		manager.registerAdapters(fBreakpointAdapterFactory, IJavaBreakpoint.class);
        IAdapterFactory typeFactory = new JavaBreakpointTypeAdapterFactory();
        manager.registerAdapters(typeFactory, IJavaBreakpoint.class);

        IAdapterFactory monitorFactory = new MonitorsAdapterFactory();
        manager.registerAdapters(monitorFactory, IJavaThread.class);
        manager.registerAdapters(monitorFactory, JavaContendedMonitor.class);
        manager.registerAdapters(monitorFactory, JavaOwnedMonitor.class);
        manager.registerAdapters(monitorFactory, JavaOwningThread.class);
        manager.registerAdapters(monitorFactory, JavaWaitingThread.class);
        manager.registerAdapters(monitorFactory, IJavaStackFrame.class);

        IAdapterFactory targetFactory = new TargetAdapterFactory();
        manager.registerAdapters(targetFactory, IJavaDebugTarget.class);

        IAdapterFactory groupFactory = new ThreadGroupAdapterFactory();
        manager.registerAdapters(groupFactory, IJavaThreadGroup.class);

        IAdapterFactory showInFactory = new JavaDebugShowInAdapterFactory();
        manager.registerAdapters(showInFactory, IJavaStackFrame.class);

        IAdapterFactory columnFactory = new ColumnPresentationAdapterFactory();
        manager.registerAdapters(columnFactory, IJavaVariable.class);
        manager.registerAdapters(columnFactory, IJavaStackFrame.class);

        IAdapterFactory entryFactory = new ClasspathEntryAdapterFactory();
        manager.registerAdapters(entryFactory, DefaultProjectClasspathEntry.class);

        IAdapterFactory variableFactory = new JavaDebugElementAdapterFactory();
        manager.registerAdapters(variableFactory, IJavaVariable.class);
        manager.registerAdapters(variableFactory, IJavaValue.class);
        manager.registerAdapters(variableFactory, JavaInspectExpression.class);

		fHCRListener= new JavaHotCodeReplaceListener();
		JDIDebugModel.addHotCodeReplaceListener(fHCRListener);

		// initialize exception inspector handler
		new ExceptionInspector();

		ResourcesPlugin.getWorkspace().addSaveParticipant(getUniqueIdentifier(), new ISaveParticipant() {
			@Override
			public void doneSaving(ISaveContext sc) {}
			@Override
			public void prepareToSave(ISaveContext sc)	throws CoreException {}
			@Override
			public void rollback(ISaveContext sc) {}
			@Override
			public void saving(ISaveContext sc) throws CoreException {
				IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(getUniqueIdentifier());
				if(prefs != null) {
					try {
						prefs.flush();
					}
					catch (BackingStoreException e) {
						log(e);
					}
				}
			}
		});
		JavaDebugOptionsManager.getDefault().startup();
	}

	/* (non-Javadoc)
	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
	 */
	@Override
	public void stop(BundleContext context) throws Exception {
		try {
			setShuttingDown(true);
			JDIDebugModel.removeHotCodeReplaceListener(fHCRListener);
			JavaDebugOptionsManager.getDefault().shutdown();
			if (fImageDescriptorRegistry != null) {
				fImageDescriptorRegistry.dispose();
			}
			IAdapterManager manager= Platform.getAdapterManager();
			manager.unregisterAdapters(fActionFilterAdapterFactory);
			manager.unregisterAdapters(fSourceLocationAdapterFactory);
			manager.unregisterAdapters(fBreakpointAdapterFactory);
			if (fUtilPresentation != null) {
				fUtilPresentation.dispose();
			}
			if (fTextTools != null) {
				fTextTools.dispose();
			}
			ResourcesPlugin.getWorkspace().removeSaveParticipant(getUniqueIdentifier());
		} finally {
			super.stop(context);
		}
	}

	/**
	 * Returns whether this plug-in is in the process of
	 * being shutdown.
	 *
	 * @return whether this plug-in is in the process of
	 *  being shutdown
	 */
	protected boolean isShuttingDown() {
		return fShuttingDown;
	}

	/**
	 * Sets whether this plug-in is in the process of
	 * being shutdown.
	 *
	 * @param value whether this plug-in is in the process of
	 *  being shutdown
	 */
	private void setShuttingDown(boolean value) {
		fShuttingDown = value;
	}

	/**
	 * Returns the image descriptor registry used for this plugin.
	 */
	public static ImageDescriptorRegistry getImageDescriptorRegistry() {
		if (getDefault().fImageDescriptorRegistry == null) {
			getDefault().fImageDescriptorRegistry = new ImageDescriptorRegistry();
		}
		return getDefault().fImageDescriptorRegistry;
	}

	/**
	 * Returns the standard display to be used. The method first checks, if
	 * the thread calling this method has an associated display. If so, this
	 * display is returned. Otherwise the method returns the default display.
	 */
	public static Display getStandardDisplay() {
		Display display;
		display= Display.getCurrent();
		if (display == null) {
			display= Display.getDefault();
		}
		return display;
	}

	/**
	 * Returns the currently active workbench window shell or <code>null</code>
	 * if none.
	 *
	 * @return the currently active workbench window shell or <code>null</code>
	 */
	public static Shell getShell() {
		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (window == null) {
			IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
			if (windows.length > 0) {
				return windows[0].getShell();
			}
		}
		else {
			return window.getShell();
		}
		return null;
	}

	/**
	 * Utility method to create and return a selection dialog that allows
	 * selection of a specific Java package.  Empty packages are not returned.
	 * If Java Projects are provided, only packages found within those projects
	 * are included.  If no Java projects are provided, all Java projects in the
	 * workspace are considered.
	 */
	public static ElementListSelectionDialog createAllPackagesDialog(Shell shell, IJavaProject[] originals, final boolean includeDefaultPackage) throws JavaModelException{
		final List<IPackageFragment> packageList = new ArrayList<>();
		if (originals == null) {
			IWorkspaceRoot wsroot= ResourcesPlugin.getWorkspace().getRoot();
			IJavaModel model= JavaCore.create(wsroot);
			originals= model.getJavaProjects();
		}
		final IJavaProject[] projects= originals;
		final JavaModelException[] exception= new JavaModelException[1];
		final boolean[] monitorCanceled = new boolean[] {false};
		IRunnableWithProgress r= new IRunnableWithProgress() {
			@Override
			public void run(IProgressMonitor monitor) {
				try {
					Set<String> packageNameSet= new HashSet<>();
					monitor.beginTask(DebugUIMessages.JDIDebugUIPlugin_Searching_1, projects.length);
					for (int i = 0; i < projects.length; i++) {
						IPackageFragment[] pkgs= projects[i].getPackageFragments();
						for (int j = 0; j < pkgs.length; j++) {
							if (monitor.isCanceled()) {
								monitorCanceled[0] = true;
								return;
							}
							IPackageFragment pkg = pkgs[j];
							if (!pkg.hasChildren() && (pkg.getNonJavaResources().length > 0)) {
								continue;
							}
							String pkgName= pkg.getElementName();
							if (!includeDefaultPackage && pkgName.length() == 0) {
								continue;
							}
							if (packageNameSet.add(pkgName)) {
								packageList.add(pkg);
							}
						}
						monitor.worked(1);
					}
					monitor.done();
				} catch (JavaModelException jme) {
					exception[0]= jme;
				}
			}
		};
		try {
			PlatformUI.getWorkbench().getProgressService().busyCursorWhile(r);
		} catch (InvocationTargetException e) {
			JDIDebugUIPlugin.log(e);
		} catch (InterruptedException e) {
			JDIDebugUIPlugin.log(e);
		}
		if (exception[0] != null) {
			throw exception[0];
		}
		if (monitorCanceled[0]) {
			return null;
		}

		int flags= JavaElementLabelProvider.SHOW_DEFAULT;
		PackageSelectionDialog dialog= new PackageSelectionDialog(shell, new JavaElementLabelProvider(flags));
		dialog.setIgnoreCase(false);
		dialog.setElements(packageList.toArray()); // XXX inefficient
		return dialog;
	}

	/**
	 * Return an object that implements <code>ILaunchConfigurationTab</code> for the
	 * specified vm install type ID.
	 */
	public ILaunchConfigurationTab getVMInstallTypePage(String vmInstallTypeID) {
		if (fVmInstallTypePageMap == null) {
			initializeVMInstallTypePageMap();
		}
		IConfigurationElement configElement = fVmInstallTypePageMap.get(vmInstallTypeID);
		ILaunchConfigurationTab tab = null;
		if (configElement != null) {
			try {
				tab = (ILaunchConfigurationTab) configElement.createExecutableExtension("class"); //$NON-NLS-1$
			} catch(CoreException ce) {
				log(new Status(IStatus.ERROR, getUniqueIdentifier(), IJavaDebugUIConstants.INTERNAL_ERROR, "An error occurred retrieving a VMInstallType page.", ce));  //$NON-NLS-1$
			}
		}
		return tab;
	}

	protected void initializeVMInstallTypePageMap() {
		fVmInstallTypePageMap = new HashMap<>(10);

		IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(getUniqueIdentifier(), IJavaDebugUIConstants.EXTENSION_POINT_VM_INSTALL_TYPE_PAGE);
		IConfigurationElement[] infos= extensionPoint.getConfigurationElements();
		for (int i = 0; i < infos.length; i++) {
			String id = infos[i].getAttribute("vmInstallTypeID"); //$NON-NLS-1$
			fVmInstallTypePageMap.put(id, infos[i]);
		}
	}

	/**
	 * Returns a shared utility Java debug model presentation. Clients should not
	 * dispose the presentation.
	 *
	 * @return a Java debug model presentation
	 */
	public IDebugModelPresentation getModelPresentation() {
		if (fUtilPresentation == null) {
			fUtilPresentation = DebugUITools.newDebugModelPresentation(JDIDebugModel.getPluginIdentifier());
		}
		return fUtilPresentation;
	}

	/**
	 * Displays the given preference page.
	 *
	 * @param id pref page id
	 * @param page pref page
	 * @deprecated use <code>JDIDebugUIPlugin#showPreferencePage(String pageId)</code>, which uses the <code>PreferenceUtils</code> framework for opening pages.
	 */
	@Deprecated
	public static void showPreferencePage(String id, IPreferencePage page) {
		final IPreferenceNode targetNode = new PreferenceNode(id, page);

		PreferenceManager manager = new PreferenceManager();
		manager.addToRoot(targetNode);
		final PreferenceDialog dialog = new PreferenceDialog(JDIDebugUIPlugin.getActiveWorkbenchShell(), manager);
		final boolean [] result = new boolean[] { false };
		BusyIndicator.showWhile(JDIDebugUIPlugin.getStandardDisplay(), new Runnable() {
			@Override
			public void run() {
				dialog.create();
				dialog.setMessage(targetNode.getLabelText());
				result[0]= (dialog.open() == Window.OK);
			}
		});
	}

	/**
	 * Displays the given preference page
	 * @param pageId the fully qualified id of the preference page, e.g. <code>org.eclipse.jdt.debug.ui.preferences.VMPreferencePage</code>
	 *
	 * @since 3.3
	 */
	public static void showPreferencePage(String pageId) {
		PreferencesUtil.createPreferenceDialogOn(getShell(), pageId, new String[] { pageId }, null).open();
	}

	/**
	 * Returns the text tools used by this plug-in
	 *
	 * @return
	 */
	public JavaTextTools getJavaTextTools() {
		if (fTextTools == null) {
			fTextTools = new JavaTextTools(PreferenceConstants.getPreferenceStore());
		}
		return fTextTools;
	}
}

