/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.ui.support;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.support.HasHelpUrl;
import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
import com.eviware.soapui.impl.support.components.ModelItemXmlEditor;
import com.eviware.soapui.impl.support.components.RequestMessageXmlEditor;
import com.eviware.soapui.impl.support.components.ResponseMessageXmlEditor;
import com.eviware.soapui.impl.wsdl.mock.WsdlMockResponse;
import com.eviware.soapui.impl.wsdl.panels.mockoperation.MockRequestXmlDocument;
import com.eviware.soapui.impl.wsdl.panels.mockoperation.MockResponseXmlDocument;
import com.eviware.soapui.impl.wsdl.submit.transports.http.DocumentContent;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.mock.MockRequest;
import com.eviware.soapui.model.mock.MockResponse;
import com.eviware.soapui.model.mock.MockResult;
import com.eviware.soapui.model.mock.MockRunner;
import com.eviware.soapui.settings.UISettings;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.actions.ChangeSplitPaneOrientationAction;
import com.eviware.soapui.support.components.JEditorStatusBarWithProgress;
import com.eviware.soapui.support.components.JXToolBar;
import com.eviware.soapui.support.editor.views.xml.source.XmlSourceEditorView;
import com.eviware.soapui.support.editor.xml.XmlDocument;
import com.eviware.soapui.support.swing.SoapUISplitPaneUI;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JToggleButton;
import javax.swing.KeyStroke;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* Abstract base DesktopPanel for MockResponses
*
* @author Ole.Matzura
*/
public abstract class AbstractMockResponseDesktopPanel<ModelItemType extends ModelItem, MockResponseType extends MockResponse> extends
ModelItemDesktopPanel<ModelItemType> implements HasHelpUrl {
private JEditorStatusBarWithProgress statusBar;
private JButton splitButton;
private MockRunner mockRunner;
private JSplitPane requestSplitPane;
private MoveFocusAction moveFocusAction;
private ClosePanelAction closePanelAction = new ClosePanelAction();
private ModelItemXmlEditor<?, ?> requestEditor;
private MockResponseMessageEditor responseEditor;
private JTabbedPane requestTabs;
private JPanel requestTabPanel;
private JToggleButton tabsButton;
public boolean responseHasFocus;
private InternalPropertyChangeListener propertyChangeListener = new InternalPropertyChangeListener();
private MockResponseType mockResponse;
public AbstractMockResponseDesktopPanel(ModelItemType modelItem) {
super(modelItem);
}
protected void init(MockResponseType mockResponse) {
this.mockResponse = mockResponse;
add(buildContent(), BorderLayout.CENTER);
add(buildToolbar(), BorderLayout.NORTH);
add(buildStatusLabel(), BorderLayout.SOUTH);
setPreferredSize(new Dimension(600, 500));
mockResponse.addPropertyChangeListener(propertyChangeListener);
addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
if (!hasRequestEditor() || requestTabs.getSelectedIndex() == 1 || responseHasFocus) {
responseEditor.requestFocus();
} else {
requestEditor.requestFocus();
}
}
});
try {
// required to avoid deadlock in UI when opening attachments inspector
if (mockResponse.getAttachmentCount() > 0) {
mockResponse.getMockOperation().getOperation().getInterface().getDefinitionContext().loadIfNecessary();
}
} catch (Exception e) {
e.printStackTrace();
}
}
protected MockResponseType getMockResponse() {
return mockResponse;
}
public final ModelItemXmlEditor<?, ?> getRequestEditor() {
return requestEditor;
}
public final MockResponseMessageEditor getResponseEditor() {
return responseEditor;
}
public MockRunner getSubmit() {
return mockRunner;
}
protected JComponent buildStatusLabel() {
statusBar = new JEditorStatusBarWithProgress();
statusBar.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 0));
return statusBar;
}
public JEditorStatusBarWithProgress getStatusBar() {
return statusBar;
}
protected JComponent buildContent() {
moveFocusAction = new MoveFocusAction();
responseEditor = buildResponseEditor();
JComponent responseEditorPanel = createResponseEditorPanel(responseEditor);
if (hasRequestEditor()) {
return buildEverythingPanel(responseEditorPanel);
} else {
return responseEditorPanel;
}
}
private JComponent createResponseEditorPanel(MockResponseMessageEditor responseEditor) {
if (hasTopEditorPanel()) {
JSplitPane responseEditorSplit = UISupport.createVerticalSplit();
responseEditorSplit.add(addTopEditorPanel());
responseEditorSplit.add(addBottomEditorPanel(responseEditor));
responseEditorSplit.setDividerLocation(200);
return responseEditorSplit;
} else {
JComponent responseEditorPanel = new JPanel();
responseEditorPanel.setLayout(new BoxLayout(responseEditorPanel, BoxLayout.Y_AXIS));
responseEditorPanel.add(responseEditor);
return responseEditorPanel;
}
}
protected Component addBottomEditorPanel(MockResponseMessageEditor responseEditor) {
return responseEditor;
}
private JComponent buildEverythingPanel(JComponent responseEditorPanel) {
requestSplitPane = UISupport.createHorizontalSplit();
requestSplitPane.setResizeWeight(0.5);
requestSplitPane.setBorder(null);
splitButton = createActionButton(new ChangeSplitPaneOrientationAction(requestSplitPane), true);
JComponent component;
tabsButton = new JToggleButton(new ChangeToTabsAction());
tabsButton.setPreferredSize(UISupport.TOOLBAR_BUTTON_DIMENSION);
requestEditor = buildRequestEditor();
requestTabs = new JTabbedPane();
requestTabPanel = UISupport.createTabPanel(requestTabs, true);
if (mockResponse.getSettings().getBoolean(UISettings.START_WITH_REQUEST_TABS)) {
requestTabs.addTab("Last Request", requestEditor);
requestTabs.addTab("Mock Response", responseEditorPanel);
splitButton.setEnabled(false);
tabsButton.setSelected(true);
component = requestTabPanel;
requestTabs.setSelectedIndex(1);
} else {
requestSplitPane.setTopComponent(requestEditor); // means left
requestSplitPane.setBottomComponent(responseEditorPanel); // means right
requestSplitPane.setDividerLocation(0.5);
component = requestSplitPane;
}
return component;
}
public boolean hasRequestEditor() {
return true;
}
public JComponent addTopEditorPanel() {
return new JPanel();
}
public boolean hasTopEditorPanel() {
return false;
}
/**
* Override this method if you are not bidirectional.
*
* @return true
*/
protected boolean isBidirectional() {
return true;
}
protected MockResponseMessageEditor buildResponseEditor() {
return new MockResponseMessageEditor(new MockResponseXmlDocument(mockResponse));
}
protected ModelItemXmlEditor<?, ?> buildRequestEditor() {
return new MockRequestMessageEditor(new MockRequestXmlDocument(mockResponse));
}
protected JComponent buildToolbar() {
JXToolBar toolbar = UISupport.createToolbar();
createToolbar(toolbar);
toolbar.add(Box.createHorizontalGlue());
if (hasRequestEditor()) {
toolbar.add(tabsButton);
toolbar.add(splitButton);
}
toolbar.add(UISupport.createToolbarButton(new ShowOnlineHelpAction(getHelpUrl())));
return toolbar;
}
/**
* Override this method if you want to but your own things in the toolbar.
*
* @param toolbar this is the actual toolbar for you to manipulate. Don't remove stuff on it please....
*/
protected void createToolbar(JXToolBar toolbar) {
}
public void setEnabled(boolean enabled) {
if (hasRequestEditor()) {
requestEditor.getSourceEditor().setEditable(enabled);
}
responseEditor.getSourceEditor().setEditable(enabled);
statusBar.setIndeterminate(!enabled);
}
private final class InternalPropertyChangeListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(WsdlMockResponse.MOCKRESULT_PROPERTY)) {
MockResult mockResult = mockResponse.getMockResult();
MockRequest mockRequest = mockResult == null ? null : mockResult.getMockRequest();
if (hasRequestEditor()) {
requestEditor.getDocument().setDocumentContent(new DocumentContent(mockRequest == null ? "" : mockRequest.getHttpRequest().getContentType(), mockRequest == null ? "" : mockRequest.getRequestContent()));
}
}
}
}
public class MockRequestMessageEditor extends RequestMessageXmlEditor<MockResponse, XmlDocument> {
public MockRequestMessageEditor(XmlDocument document) {
super(document, mockResponse);
}
protected XmlSourceEditorView<?> buildSourceEditor() {
XmlSourceEditorView<?> editor = getSourceEditor();
RSyntaxTextArea inputArea = editor.getInputArea();
inputArea.addFocusListener(new InputAreaFocusListener());
if (UISupport.isMac()) {
inputArea.getInputMap().put(KeyStroke.getKeyStroke("control meta TAB"), moveFocusAction);
} else {
inputArea.getInputMap().put(KeyStroke.getKeyStroke("control alt TAB"), moveFocusAction);
}
inputArea.getInputMap().put(KeyStroke.getKeyStroke("ctrl F4"), closePanelAction);
return editor;
}
}
public class MockResponseMessageEditor extends ResponseMessageXmlEditor<MockResponse, XmlDocument> {
private RSyntaxTextArea inputArea;
public MockResponseMessageEditor(XmlDocument document) {
super(document, mockResponse);
if (isBidirectional()) {
XmlSourceEditorView<?> editor = getSourceEditor();
inputArea = editor.getInputArea();
if (hasRequestEditor()) {
inputArea.addFocusListener(new ResultAreaFocusListener());
}
if (UISupport.isMac()) {
inputArea.getInputMap().put(KeyStroke.getKeyStroke("control meta TAB"), moveFocusAction);
inputArea.getInputMap().put(KeyStroke.getKeyStroke("ctrl F4"), closePanelAction);
} else {
inputArea.getInputMap().put(KeyStroke.getKeyStroke("control alt TAB"), moveFocusAction);
inputArea.getInputMap().put(KeyStroke.getKeyStroke("ctrl F4"), closePanelAction);
}
JPopupMenu inputPopup = editor.getEditorPopup();
inputPopup.insert(new JSeparator(), 2);
}
}
public RSyntaxTextArea getInputArea() {
return inputArea;
}
}
protected final class InputAreaFocusListener implements FocusListener {
public void focusGained(FocusEvent e) {
responseHasFocus = false;
if (!splitButton.isEnabled()) {
requestTabs.setSelectedIndex(0);
return;
}
if (getModelItem().getSettings().getBoolean(UISettings.NO_RESIZE_REQUEST_EDITOR)) {
return;
}
// dont resize if split has been dragged
if (((SoapUISplitPaneUI) requestSplitPane.getUI()).hasBeenDragged()) {
return;
}
int pos = requestSplitPane.getDividerLocation();
if (pos >= 600) {
return;
}
if (requestSplitPane.getMaximumDividerLocation() > 700) {
requestSplitPane.setDividerLocation(600);
} else {
requestSplitPane.setDividerLocation(0.8);
}
}
public void focusLost(FocusEvent e) {
}
}
protected final class ResultAreaFocusListener implements FocusListener {
public void focusGained(FocusEvent e) {
responseHasFocus = true;
if (!splitButton.isEnabled()) {
requestTabs.setSelectedIndex(1);
return;
}
if (getModelItem().getSettings().getBoolean(UISettings.NO_RESIZE_REQUEST_EDITOR)) {
return;
}
// dont resize if split has been dragged or result is empty
if (((SoapUISplitPaneUI) requestSplitPane.getUI()).hasBeenDragged()) {
return;
}
int pos = requestSplitPane.getDividerLocation();
int maximumDividerLocation = requestSplitPane.getMaximumDividerLocation();
if (pos + 600 < maximumDividerLocation) {
return;
}
if (maximumDividerLocation > 700) {
requestSplitPane.setDividerLocation(maximumDividerLocation - 600);
} else {
requestSplitPane.setDividerLocation(0.2);
}
}
public void focusLost(FocusEvent e) {
}
}
private class ClosePanelAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
SoapUI.getDesktop().closeDesktopPanel(getModelItem());
}
}
private class MoveFocusAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
if (!hasRequestEditor() || requestEditor.hasFocus()) {
responseEditor.requestFocus();
} else {
requestEditor.requestFocus();
}
}
}
public boolean dependsOn(ModelItem modelItem) {
return modelItem == getModelItem() || modelItem == mockResponse.getMockOperation()
|| modelItem == mockResponse.getMockOperation().getMockService()
|| modelItem == mockResponse.getMockOperation().getMockService().getProject();
}
private final class ChangeToTabsAction extends AbstractAction {
public ChangeToTabsAction() {
putValue(Action.SMALL_ICON, UISupport.createImageIcon("/toggle_tabs.gif"));
putValue(Action.SHORT_DESCRIPTION, "Toggles to tab-based layout");
}
public void actionPerformed(ActionEvent e) {
if (splitButton.isEnabled()) {
splitButton.setEnabled(false);
removeContent(requestSplitPane);
setContent(requestTabPanel);
requestTabs.addTab("Last Request", requestEditor);
requestTabs.addTab("Mock Response", responseEditor);
} else {
int selectedIndex = requestTabs.getSelectedIndex();
splitButton.setEnabled(true);
removeContent(requestTabPanel);
setContent(requestSplitPane);
requestSplitPane.setTopComponent(requestEditor);
requestSplitPane.setBottomComponent(responseEditor);
requestSplitPane.setDividerLocation(0.5);
if (selectedIndex == 0) {
requestEditor.requestFocus();
} else {
responseEditor.requestFocus();
}
}
revalidate();
}
}
public void setContent(JComponent content) {
add(content, BorderLayout.CENTER);
}
public void removeContent(JComponent content) {
remove(content);
}
public boolean onClose(boolean canCancel) {
mockResponse.removePropertyChangeListener(propertyChangeListener);
if (hasRequestEditor()) {
requestEditor.release();
requestEditor.getParent().remove(requestEditor);
requestEditor = null;
}
responseEditor.release();
responseEditor.getParent().remove(responseEditor);
responseEditor = null;
return release();
}
}