content: /*******************************************************************************
   * 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.wst.jsdt.internal.ui.infoviews;
  
  import org.eclipse.core.runtime.IAdaptable;
  import org.eclipse.jface.action.IAction;
  import org.eclipse.jface.action.IMenuListener;
  import org.eclipse.jface.action.IMenuManager;
  import org.eclipse.jface.action.IToolBarManager;
  import org.eclipse.jface.action.MenuManager;
  import org.eclipse.jface.action.Separator;
  import org.eclipse.jface.resource.ColorRegistry;
  import org.eclipse.jface.resource.JFaceResources;
  import org.eclipse.jface.text.ITextSelection;
  import org.eclipse.jface.util.IPropertyChangeListener;
  import org.eclipse.jface.util.PropertyChangeEvent;
  import org.eclipse.jface.viewers.ISelection;
  import org.eclipse.jface.viewers.ISelectionProvider;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.swt.SWT;
  import org.eclipse.swt.graphics.Color;
  import org.eclipse.swt.graphics.RGB;
  import org.eclipse.swt.widgets.Composite;
  import org.eclipse.swt.widgets.Control;
  import org.eclipse.swt.widgets.Display;
  import org.eclipse.swt.widgets.Menu;
  import org.eclipse.swt.widgets.Shell;
  import org.eclipse.ui.IActionBars;
  import org.eclipse.ui.IPartListener2;
  import org.eclipse.ui.ISelectionListener;
  import org.eclipse.ui.IWorkbenchPart;
  import org.eclipse.ui.IWorkbenchPartReference;
  import org.eclipse.ui.PlatformUI;
  import org.eclipse.ui.actions.ActionFactory;
  import org.eclipse.ui.part.ViewPart;
  import org.eclipse.ui.texteditor.ITextEditorActionConstants;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.ILocalVariable;
  import org.eclipse.wst.jsdt.core.IMember;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor;
  import org.eclipse.wst.jsdt.internal.ui.util.SelectionUtil;
  import org.eclipse.wst.jsdt.ui.IContextMenuConstants;
  import org.eclipse.wst.jsdt.ui.JSdocContentAccess;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels;
  import org.eclipse.wst.jsdt.ui.actions.SelectionDispatchAction;
  
  /**
   * Abstract class for views which show information for a given element.
   *
   * 
   */
  public abstract class AbstractInfoView extends ViewPart implements ISelectionListener, IMenuListener, IPropertyChangeListener {
  
  
  	/** JavaScriptElementLabels flags used for the title */
  	private final long TITLE_FLAGS=  JavaScriptElementLabels.ALL_FULLY_QUALIFIED
  		| JavaScriptElementLabels.M_PRE_RETURNTYPE | JavaScriptElementLabels.M_PARAMETER_TYPES | JavaScriptElementLabels.M_PARAMETER_NAMES | JavaScriptElementLabels.M_EXCEPTIONS
  		| JavaScriptElementLabels.F_PRE_TYPE_SIGNATURE | JavaScriptElementLabels.M_PRE_TYPE_PARAMETERS | JavaScriptElementLabels.T_TYPE_PARAMETERS
  		| JavaScriptElementLabels.USE_RESOLVED;
  	private final long LOCAL_VARIABLE_TITLE_FLAGS= TITLE_FLAGS & ~JavaScriptElementLabels.F_FULLY_QUALIFIED | JavaScriptElementLabels.F_POST_QUALIFIED;
  	
  	/** JavaScriptElementLabels flags used for the tool tip text */
  	private static final long TOOLTIP_LABEL_FLAGS= JavaScriptElementLabels.DEFAULT_QUALIFIED | JavaScriptElementLabels.ROOT_POST_QUALIFIED | JavaScriptElementLabels.APPEND_ROOT_PATH |
  			JavaScriptElementLabels.M_PARAMETER_TYPES | JavaScriptElementLabels.M_PARAMETER_NAMES | JavaScriptElementLabels.M_APP_RETURNTYPE | JavaScriptElementLabels.M_EXCEPTIONS |
  			JavaScriptElementLabels.F_APP_TYPE_SIGNATURE | JavaScriptElementLabels.T_TYPE_PARAMETERS;
  
  
  	/*
  	 * @see IPartListener2
  	 */
  	private IPartListener2 fPartListener= new IPartListener2() {
  		public void partVisible(IWorkbenchPartReference ref) {
  			if (ref.getId().equals(getSite().getId())) {
  				IWorkbenchPart activePart= ref.getPage().getActivePart();
  				if (activePart != null)
  					selectionChanged(activePart, ref.getPage().getSelection());
  				startListeningForSelectionChanges();
  			}
  		}
  		public void partHidden(IWorkbenchPartReference ref) {
  			if (ref.getId().equals(getSite().getId()))
  				stopListeningForSelectionChanges();
  		}
  		public void partInputChanged(IWorkbenchPartReference ref) {
  			if (!ref.getId().equals(getSite().getId()))
  				computeAndSetInput(ref.getPart(false));
  		}
  		public void partActivated(IWorkbenchPartReference ref) {
  		}
  		public void partBroughtToTop(IWorkbenchPartReference ref) {
  		}
  		public void partClosed(IWorkbenchPartReference ref) {
  		}
  		public void partDeactivated(IWorkbenchPartReference ref) {
  		}
  		public void partOpened(IWorkbenchPartReference ref) {
  		}
  	};
  
  
  	/** The current input. */
  	protected IJavaScriptElement fCurrentViewInput;
  	/** The copy to clipboard action. */
  	private SelectionDispatchAction fCopyToClipboardAction;
  	/** The goto input action. */
  	private GotoInputAction fGotoInputAction;
  	/** Counts the number of background computation requests. */
  	private volatile int fComputeCount;
  	
  	/**
  	 * Background color.
  	 * 
  	 */
  	private Color fBackgroundColor;
  	private RGB fBackgroundColorRGB;
  	
  
  	/**
  	 * Set the input of this view.
  	 *
  	 * @param input the input object
  	 */
  	abstract protected void setInput(Object input);
  
  	/**
  	 * Computes the input for this view based on the given element.
  	 *
  	 * @param element the element from which to compute the input
  	 * @return	the input or <code>null</code> if the input was not computed successfully
  	 */
  	abstract protected Object computeInput(Object element);
  
  	/**
  	 * Create the part control.
  	 *
   	 * @param parent the parent control
  	 * @see IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
  	 */
  	abstract protected void internalCreatePartControl(Composite parent);
  
  	/**
  	 * Set the view's foreground color.
  	 *
  	 * @param color the SWT color
  	 */
  	abstract protected void setForeground(Color color);
  
  	/**
  	 * Set the view's background color.
  	 *
  	 * @param color the SWT color
  	 */
  	abstract protected void setBackground(Color color);
  
  	/**
  	 * Returns the view's primary control.
  	 *
  	 * @return the primary control
  	 */
  	abstract Control getControl();
  
  	/**
  	 * Returns the context ID for the Help system
  	 *
  	 * @return	the string used as ID for the Help context
  	 * 
  	 */
  	abstract protected String getHelpContextId();
  
  	/*
  	 * @see IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
  	 */
  	public final void createPartControl(Composite parent) {
  		internalCreatePartControl(parent);
  		inititalizeColors();
  		getSite().getWorkbenchWindow().getPartService().addPartListener(fPartListener);
  		createContextMenu();
  		createActions();
  		fillActionBars(getViewSite().getActionBars());
  		PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), getHelpContextId());
  	}
  
  	/**
  	 * Creates the actions and action groups for this view.
  	 */
  	protected void createActions() {
  		fGotoInputAction= new GotoInputAction(this);
  		fGotoInputAction.setEnabled(false);
  		fCopyToClipboardAction= new CopyToClipboardAction(getViewSite());
  
  		ISelectionProvider provider= getSelectionProvider();
  		if (provider != null)
  			provider.addSelectionChangedListener(fCopyToClipboardAction);
  	}
  
  	/**
  	 * Creates the context menu for this view.
  	 */
  	protected void createContextMenu() {
  		MenuManager menuManager= new MenuManager("#PopupMenu"); //$NON-NLS-1$
  		menuManager.setRemoveAllWhenShown(true);
  		menuManager.addMenuListener(this);
  		Menu contextMenu= menuManager.createContextMenu(getControl());
  		getControl().setMenu(contextMenu);
  		getSite().registerContextMenu(menuManager, getSelectionProvider());
  	}
  
  	/*
  	 * @see IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager)
  	 */
  	public void menuAboutToShow(IMenuManager menu) {
  		menu.add(new Separator(IContextMenuConstants.GROUP_OPEN));
  		menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
  		menu.add(new Separator(IContextMenuConstants.GROUP_ADDITIONS));
  
  		IAction action;
  
  		action= getCopyToClipboardAction();
  		if (action != null)
  			menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, action);
  
  		action= getSelectAllAction();
  		if (action != null)
  			menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, action);
  
  		menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, fGotoInputAction);
  	}
  
  	protected IAction getSelectAllAction() {
  		return null;
  	}
  
  	protected IAction getCopyToClipboardAction() {
  		return fCopyToClipboardAction;
  	}
  
  	/**
  	 * Returns the input of this view.
  	 *
  	 * @return input the input object or <code>null</code> if not input is set
  	 */
  	protected IJavaScriptElement getInput() {
  		return fCurrentViewInput;
  	}
  
  	// Helper method
  	ISelectionProvider getSelectionProvider() {
  		return getViewSite().getSelectionProvider();
  	}
  
  	/**
  	 * Fills the actions bars.
  	 * <p>
  	 * Subclasses may extend.
  	 *
  	 * @param actionBars the action bars
  	 */
  	protected void fillActionBars(IActionBars actionBars) {
  		IToolBarManager toolBar= actionBars.getToolBarManager();
  		fillToolBar(toolBar);
  
  		IAction action;
  
  		action= getCopyToClipboardAction();
  		if (action != null)
  			actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), action);
  
  		action= getSelectAllAction();
  		if (action != null)
  			actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), action);
  	}
  
  	/**
  	 * Fills the tool bar.
  	 * <p>
  	 * Default is to do nothing.</p>
  	 *
  	 * @param tbm the tool bar manager
  	 */
  	protected void fillToolBar(IToolBarManager tbm) {
  		tbm.add(fGotoInputAction);
  	}
  
  	/*
  	 * @see org.eclipse.wst.jsdt.internal.ui.infoviews.AbstractInfoView#inititalizeColors()
  	 * 
  	 */
  	private void inititalizeColors() {
  		if (getSite().getShell().isDisposed())
  			return;
  
  		Display display= getSite().getShell().getDisplay();
  		if (display == null || display.isDisposed())
  			return;
  
  		setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
  		
  		ColorRegistry registry= JFaceResources.getColorRegistry();
  		registry.addListener(this);
  		
  		fBackgroundColorRGB= registry.getRGB(getBackgroundColorKey());
  		Color bgColor;
  		if (fBackgroundColorRGB == null) {
  			bgColor= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
  			fBackgroundColorRGB= bgColor.getRGB();
  		} else {
  			bgColor= new Color(display, fBackgroundColorRGB);
  			fBackgroundColor= bgColor;
  		}
  		
  		setBackground(bgColor);
  	}
  	
  	/**
  	 * The preference key for the background color.
  	 * 
  	 * @return the background color key
  	 * 
  	 */
  	abstract protected String getBackgroundColorKey();
  	
  	public void propertyChange(PropertyChangeEvent event) {
  		if (getBackgroundColorKey().equals(event.getProperty()))
  			inititalizeColors();
  	}
  
  	/**
  	 * Start to listen for selection changes.
  	 */
  	protected void startListeningForSelectionChanges() {
  		getSite().getPage().addPostSelectionListener(this);
  	}
  
  	/**
  	 * Stop to listen for selection changes.
  	 */
  	protected void stopListeningForSelectionChanges() {
  		getSite().getPage().removePostSelectionListener(this);
  	}
  
  	/*
  	 * @see ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection)
  	 */
  	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
  		if (part.equals(this))
  			return;
  
  		computeAndSetInput(part);
  	}
  
  	/**
  	 * Tells whether the new input should be ignored
  	 * if the current input is the same.
  	 *
  	 * @param je the new input
  	 * @param selection the current selection from the part that provides the input
  	 * @return <code>true</code> if the new input should be ignored
  	 */
  	protected boolean isIgnoringNewInput(IJavaScriptElement je, IWorkbenchPart part, ISelection selection) {
  		return fCurrentViewInput != null && fCurrentViewInput.equals(je) && je != null;
  	}
  
  	/**
  	 * Finds and returns the Java element selected in the given part.
  	 *
  	 * @param part the workbench part for which to find the selected Java element
  	 * @param selection the selection
  	 * @return the selected Java element
  	 */
  	protected IJavaScriptElement findSelectedJavaElement(IWorkbenchPart part, ISelection selection) {
  		Object element = null;
  		try {
  			if (part instanceof JavaEditor && selection instanceof ITextSelection) {
  				IJavaScriptElement[] elements= TextSelectionConverter.codeResolve((JavaEditor)part, (ITextSelection)selection);
  				if (elements != null) {
  					if (elements.length > 1) {
  						for (int i = 0; i < elements.length; i++) {
  							if (elements[i] == null)
  								continue;
  							int elementType = elements[i].getElementType();
  							if ((elementType == IJavaScriptElement.METHOD || elementType == IJavaScriptElement.FIELD || elementType == IJavaScriptElement.TYPE || elementType == IJavaScriptElement.INITIALIZER) && JSdocContentAccess.getContentReader((IMember) elements[i], true) != null) {
  								return elements[i];
  							}
  							if (elementType == IJavaScriptElement.LOCAL_VARIABLE && JSdocContentAccess.getContentReader((ILocalVariable) elements[i], true) != null) {
  								return elements[i];
  							}
  						}
  					}
  					else if (elements.length > 0) {
  						return elements[0];
  					}
  				}
  				else {
  					return null;
  				}
  			} else if (selection instanceof IStructuredSelection) {
  				element= SelectionUtil.getSingleElement(selection);
  			} else {
  				return null;
  			}
  		} catch (JavaScriptModelException e) {
  			return null;
  		}
  
  		return findJavaElement(element);
  	}
  
  	/**
  	 * Tries to get a Java element out of the given element.
  	 *
  	 * @param element an object
  	 * @return the Java element represented by the given element or <code>null</code>
  	 */
  	private IJavaScriptElement findJavaElement(Object element) {
  
  		if (element == null)
  			return null;
  
  		IJavaScriptElement je= null;
  		if (element instanceof IAdaptable)
  			je= (IJavaScriptElement)((IAdaptable)element).getAdapter(IJavaScriptElement.class);
  
  		return je;
  	}
  
  	/**
  	 * Finds and returns the type for the given CU.
  	 *
  	 * @param cu	the compilation unit
  	 * @return	the type with same name as the given CU or the first type in the CU
  	 */
  	protected IType getTypeForCU(IJavaScriptUnit cu) {
  
  		if (cu == null || !cu.exists())
  			return null;
  
  		// Use primary type if possible
  		IType primaryType= cu.findPrimaryType();
  		if (primaryType != null)
  			return primaryType;
  
  		// Use first top-level type
  		try {
  			IType[] types= cu.getTypes();
  			if (types.length > 0)
  				return types[0];
  			else
  				return null;
  		} catch (JavaScriptModelException ex) {
  			return null;
  		}
  	}
  
  	/*
  	 * @see IWorkbenchPart#dispose()
  	 */
  	final public void dispose() {
  		// cancel possible running computation
  		fComputeCount++;
  
  		getSite().getWorkbenchWindow().getPartService().removePartListener(fPartListener);
  
  		ISelectionProvider provider= getSelectionProvider();
  		if (provider != null)
  			provider.removeSelectionChangedListener(fCopyToClipboardAction);
  
  		JFaceResources.getColorRegistry().removeListener(this);
  		fBackgroundColorRGB= null;
  		if (fBackgroundColor != null) {
  			fBackgroundColor.dispose();
  			fBackgroundColor= null;
  		}
  		
  		internalDispose();
  
  	}
  
  	/*
  	 * @see IWorkbenchPart#dispose()
  	 */
  	abstract protected void internalDispose();
  
  	/**
  	 * Determines all necessary details and delegates the computation into
  	 * a background thread.
  	 *
  	 * @param part the workbench part
  	 */
  	private void computeAndSetInput(final IWorkbenchPart part) {
  
  		final int currentCount= ++fComputeCount;
  
  		ISelectionProvider provider= part.getSite().getSelectionProvider();
  		if (provider == null)
  			return;
  
  		final ISelection selection= provider.getSelection();
  		if (selection == null || selection.isEmpty())
  			return;
  
  		Thread thread= new Thread("Info view input computer") { //$NON-NLS-1$
  			public void run() {
  				if (currentCount != fComputeCount)
  					return;
  
  				final IJavaScriptElement je= findSelectedJavaElement(part, selection);
  
  				if (isIgnoringNewInput(je, part, selection))
  					return;
  
  				// The actual computation
  				final Object input= computeInput(je);
  				if (input == null)
  					return;
  
  				Shell shell= getSite().getShell();
  				if (shell.isDisposed())
  					return;
  
  				Display display= shell.getDisplay();
  				if (display.isDisposed())
  					return;
  
  				display.asyncExec(new Runnable() {
  					/*
  					 * @see java.lang.Runnable#run()
  					 */
  					public void run() {
  
  						if (fComputeCount != currentCount || getViewSite().getShell().isDisposed())
  							return;
  
  						fCurrentViewInput= je;
  						doSetInput(input);
  					}
  				});
  			}
  		};
  
  		thread.setDaemon(true);
  		thread.setPriority(Thread.MIN_PRIORITY);
  		thread.start();
  	}
  
  	private void doSetInput(Object input) {
  		setInput(input);
  
  		fGotoInputAction.setEnabled(true);
  
  		IJavaScriptElement inputElement= getInput();
  		
  		long flags;
  		if (inputElement instanceof ILocalVariable)
  			flags= LOCAL_VARIABLE_TITLE_FLAGS;
  		else
  			flags= TITLE_FLAGS;
  		
  		setContentDescription(JavaScriptElementLabels.getElementLabel(inputElement, flags));
  		setTitleToolTip(JavaScriptElementLabels.getElementLabel(inputElement, TOOLTIP_LABEL_FLAGS));
  	}
  }
  