/******************************************************************************* * Copyright (c) 2016 Ericsson * 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: * Isaac Arvestad (Ericsson) - initial API and implementation *******************************************************************************/ package org.eclipse.swtbot.generator.client.views; import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.IElementChangedListener; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.text.source.VerticalRuler; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.swtbot.generator.client.Recorder; import org.eclipse.swtbot.generator.client.Recorder.ConnectionState; import org.eclipse.swtbot.generator.client.RecorderClientStatusListener; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * RecorderClientView shows SWTBot recorder output as it is received from a * RecorderServer. */ public class RecorderClientView extends ViewPart implements RecorderClientStatusListener, IElementChangedListener { /** * The ID of the view as specified by the extension. */ public static final String ID = "org.eclipse.swtbot.generator.client.view.recorder.client"; private static int DEFAULT_PORT_NUMBER = 8000; private static String IS_CONNECTED_LABEL = "Connected"; private static String TRYING_CONNECT_LABEL = "Connecting..."; private static String NOT_CONNECTED_LABEL = "No connection"; private static String CONNECT_LABEL = " Connect "; private static String CANCEL_CONNECT_LABEL = "Cancel"; private static String DISCONNECT_LABEL = "Disconnect"; private static String ADD_TO_METHOD_TOGGLE_LABEL = "Add to method: "; private static String START_RECORDING_LABEL = "Record"; private static String STOP_RECORDING_LABEL = "Stop recording"; private Button connectButton; private Text portText; private Label statusLabel; private ComboViewer availableMethodsDropDown; private Button addToMethodToggle; private Button refreshAvailableMethodsButton; private Button recordingButton; private SourceViewer viewer; /** * Initialize the Recorder singleton. If it already is initialized, reset * it. */ public RecorderClientView() { if (Recorder.INSTANCE.isInitialized() == false) { Recorder.INSTANCE.initialize(); } else { Recorder.INSTANCE.reset(); } Recorder.INSTANCE.addStatusListener(this); } @Override public void dispose() { Recorder.INSTANCE.removeStatusListener(this); super.dispose(); } /** * Updates the UI state for any connection state change. */ public void updateUI() { updateUIForConnectionState(Recorder.INSTANCE.getConnectionState()); portText.setText(String.valueOf(Recorder.INSTANCE.getPort())); } /** * This is a callback that will allow us to create the viewer and initialize * it. */ public void createPartControl(Composite parent) { Group group = new Group(parent, SWT.NONE); group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); group.setLayout(new GridLayout(1, false)); Composite launchContainer = new Composite(group, SWT.NONE); launchContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); launchContainer.setLayout(new GridLayout(2, false)); connectButton = new Button(launchContainer, SWT.PUSH); connectButton.setText(CONNECT_LABEL); connectButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); connectButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (Recorder.INSTANCE.getConnectionState() == ConnectionState.CONNECTED) { Recorder.INSTANCE.interruptRecorderClient(); } else if (Recorder.INSTANCE.getConnectionState() == ConnectionState.CONNECTING) { Recorder.INSTANCE.interruptRecorderClient(); } else { Recorder.INSTANCE.startRecorderClient(getPort()); } updateUIForConnectionState(Recorder.INSTANCE.getConnectionState()); } }); Composite portLaunchContainer = new Composite(launchContainer, SWT.NONE); portLaunchContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); portLaunchContainer.setLayout(new GridLayout(2, false)); Label portLabel = new Label(portLaunchContainer, SWT.NONE); portLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); portLabel.setText("port:"); portText = new Text(portLaunchContainer, SWT.RIGHT); portText.setText(String.valueOf(DEFAULT_PORT_NUMBER)); portText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); portText.addVerifyListener(new VerifyListener() { @Override public void verifyText(VerifyEvent e) { String port = portText.getText() + e.text; try { Integer.parseInt(port); } catch (NumberFormatException e2) { if (port.length() > 0) { e.doit = false; } } } }); statusLabel = new Label(group, SWT.SHADOW_IN); statusLabel.setText("No connection"); Label horizontalSeparator = new Label(group, SWT.SEPARATOR | SWT.HORIZONTAL); horizontalSeparator.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); Composite methodSelectionContainer = new Composite(group, SWT.NONE); methodSelectionContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); methodSelectionContainer.setLayout(new GridLayout(3, false)); addToMethodToggle = new Button(methodSelectionContainer, SWT.CHECK); addToMethodToggle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); addToMethodToggle.setText(ADD_TO_METHOD_TOGGLE_LABEL); addToMethodToggle.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (Recorder.INSTANCE.isInsertingDirectlyInEditor()) { Recorder.INSTANCE.setInsertingDirectlyInEditor(false); availableMethodsDropDown.getControl().setEnabled(false); refreshAvailableMethodsButton.setEnabled(false); } else { Recorder.INSTANCE.setInsertingDirectlyInEditor(true); availableMethodsDropDown.getControl().setEnabled(true); refreshAvailableMethodsButton.setEnabled(true); // If the list is empty we refresh. if (availableMethodsDropDown.getInput() == null || ((IMethod[]) availableMethodsDropDown.getInput()).length == 0) { updateMethodSelectionDropDown(); } } } }); addToMethodToggle.setEnabled(false); availableMethodsDropDown = new ComboViewer(methodSelectionContainer); availableMethodsDropDown.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); availableMethodsDropDown.getControl().setEnabled(false); availableMethodsDropDown.setContentProvider(ArrayContentProvider.getInstance()); availableMethodsDropDown.setLabelProvider(new LabelProvider() { @Override public String getText(Object element) { IMethod method = (IMethod) element; String parameterNamesString = ""; try { String[] parameterNames = method.getParameterNames(); for (int i = 0; i < parameterNames.length; i++) { if (i == 0) { parameterNamesString += parameterNames[i]; } else { parameterNamesString += ", " + parameterNames[i]; } } } catch (JavaModelException e) { e.printStackTrace(); } return method.getElementName() + "(" + parameterNamesString + ")"; } }); availableMethodsDropDown.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); Recorder.INSTANCE.setSelectedMethod((IMethod) selection.getFirstElement()); } }); refreshAvailableMethodsButton = new Button(methodSelectionContainer, SWT.PUSH); refreshAvailableMethodsButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); refreshAvailableMethodsButton.setEnabled(false); refreshAvailableMethodsButton.setImage(AbstractUIPlugin .imageDescriptorFromPlugin("org.eclipse.swtbot.generator.client", "icons/refresh.gif").createImage()); refreshAvailableMethodsButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { updateMethodSelectionDropDown(); } }); recordingButton = new Button(group, SWT.PUSH); recordingButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); recordingButton.setText(START_RECORDING_LABEL); recordingButton.setEnabled(false); recordingButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (Recorder.INSTANCE.isRecording()) { recordingButton.setText(START_RECORDING_LABEL); Recorder.INSTANCE.setRecording(false); } else { recordingButton.setText(STOP_RECORDING_LABEL); Recorder.INSTANCE.setRecording(true); } } }); viewer = new SourceViewer(group, new VerticalRuler(0), SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); viewer.setDocument(Recorder.INSTANCE.getDocument()); viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); getSite().setSelectionProvider(viewer); JavaCore.addElementChangedListener(this); updateUIForConnectionState(Recorder.INSTANCE.getConnectionState()); updateMethodSelectionDropDown(); } /** * Passing the focus request to the viewer's control. */ public void setFocus() { viewer.getControl().setFocus(); } /** * @return The currently chosen port. */ private int getPort() { return Integer.parseInt(portText.getText()); } @Override public void connectionStarted() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { updateMethodSelectionDropDown(); updateUIForConnectionState(ConnectionState.CONNECTED); } }); } @Override public void connectionEnded() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { updateUIForConnectionState(ConnectionState.DISCONNECTED); } }); } /** * Updates all UI elements to match a new connection state. * * @param state * The new connection state. */ private void updateUIForConnectionState(ConnectionState state) { switch (state) { case CONNECTED: statusLabel.setText(IS_CONNECTED_LABEL); recordingButton.setText(START_RECORDING_LABEL); connectButton.setText(DISCONNECT_LABEL); addToMethodToggle.setSelection(false); recordingButton.setEnabled(true); connectButton.setEnabled(true); portText.setEnabled(false); addToMethodToggle.setEnabled(true); availableMethodsDropDown.getControl().setEnabled(false); refreshAvailableMethodsButton.setEnabled(false); break; case CONNECTING: statusLabel.setText(TRYING_CONNECT_LABEL); recordingButton.setText(START_RECORDING_LABEL); connectButton.setText(CANCEL_CONNECT_LABEL); addToMethodToggle.setSelection(false); recordingButton.setEnabled(false); connectButton.setEnabled(true); portText.setEnabled(false); addToMethodToggle.setEnabled(false); availableMethodsDropDown.getControl().setEnabled(false); refreshAvailableMethodsButton.setEnabled(false); break; case DISCONNECTED: statusLabel.setText(NOT_CONNECTED_LABEL); recordingButton.setText(START_RECORDING_LABEL); connectButton.setText(CONNECT_LABEL); addToMethodToggle.setSelection(false); recordingButton.setEnabled(false); connectButton.setEnabled(true); portText.setEnabled(true); addToMethodToggle.setEnabled(false); availableMethodsDropDown.getControl().setEnabled(false); refreshAvailableMethodsButton.setEnabled(false); break; } } /** * Updates the method selection drop down if the change event is likely to * affect the currently available methods in the method selection drop down. */ @Override public void elementChanged(ElementChangedEvent event) { if (Recorder.INSTANCE.shouldUpdateMethodSelectionViewer(event)) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { updateMethodSelectionDropDown(); } }); } } /** * Returns the active editor or null if it fails. * * @return The active editor. */ private IEditorPart getActiveEditor() { IWorkbench workbench = PlatformUI.getWorkbench(); if (workbench == null) { return null; } IWorkbenchWindow workbenchWindow = workbench.getActiveWorkbenchWindow(); if (workbenchWindow == null) { return null; } IWorkbenchPage workbenchPage = workbenchWindow.getActivePage(); if (workbenchPage == null) { return null; } return workbenchPage.getActiveEditor(); } /** * Updates the method selection drop down. */ @SuppressWarnings("restriction") private void updateMethodSelectionDropDown() { IEditorPart activeEditor = getActiveEditor(); if (activeEditor == null) { Recorder.INSTANCE.setSelectedMethod(null); Recorder.INSTANCE.setSelectedMethodDocument(null); availableMethodsDropDown.setInput(null); } else if (activeEditor instanceof JavaEditor) { ITypeRoot root = EditorUtility.getEditorInputJavaElement(activeEditor, false); IType type = root.findPrimaryType(); final IMethod[] methods; try { methods = type.getMethods(); IMethod methodToSelect = null; if (Recorder.INSTANCE.getSelectedMethod() != null) { methodToSelect = findSimilarMethod(Recorder.INSTANCE.getSelectedMethod(), methods); } IDocument currentActiveDocument = ((JavaEditor) activeEditor).getDocumentProvider() .getDocument(activeEditor.getEditorInput()); Recorder.INSTANCE.setSelectedMethod(methodToSelect); Recorder.INSTANCE.setSelectedMethodDocument(currentActiveDocument); availableMethodsDropDown.setInput(methods); // If methodToSelect is non-null we want to select it to // maintain the user's previous selection. if (methodToSelect != null) { ISelection selection = new StructuredSelection(methodToSelect); availableMethodsDropDown.setSelection(selection); } } catch (JavaModelException e) { e.printStackTrace(); } } } /** * Tries to find a similar method in an array of IMethod instances using * <code>IMethod.isSimilar</code> * * @param method * The method to search for. * @param methods * The array to search through. * @return The first similar method found or null if no similar method was * found. */ private IMethod findSimilarMethod(IMethod method, IMethod[] methods) { IMethod similarMethod = null; for (IMethod candidate : methods) { if (method.isSimilar(candidate)) { similarMethod = candidate; break; } } return similarMethod; } }