View Javadoc

    1   /*
    2    *  soapUI, copyright (C) 2004-2007 eviware.com
    3    *
    4    *  soapUI is free software; you can redistribute it and/or modify it under the
    5    *  terms of version 2.1 of the GNU Lesser General Public License as published by
    6    *  the Free Software Foundation.
    7    *
    8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
    9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    10   *  See the GNU Lesser General Public License for more details at gnu.org.
    11   */
    12
    13  package com.eviware.soapui.impl.wsdl.panels.request.components.editor.views.source;
    14
    15  import java.awt.BorderLayout;
    16  import java.awt.Color;
    17  import java.awt.Dimension;
    18  import java.awt.Toolkit;
    19  import java.awt.event.ActionEvent;
    20  import java.awt.event.ActionListener;
    21  import java.awt.event.MouseAdapter;
    22  import java.awt.event.MouseEvent;
    23  import java.beans.PropertyChangeListener;
    24
    25  import javax.swing.AbstractAction;
    26  import javax.swing.Action;
    27  import javax.swing.BorderFactory;
    28  import javax.swing.DefaultListModel;
    29  import javax.swing.JCheckBoxMenuItem;
    30  import javax.swing.JComponent;
    31  import javax.swing.JList;
    32  import javax.swing.JPanel;
    33  import javax.swing.JPopupMenu;
    34  import javax.swing.JScrollPane;
    35  import javax.swing.JSplitPane;
    36  import javax.swing.SwingUtilities;
    37  import javax.swing.text.Document;
    38
    39  import com.eviware.soapui.SoapUI;
    40  import com.eviware.soapui.impl.wsdl.panels.request.components.editor.XmlEditor;
    41  import com.eviware.soapui.impl.wsdl.panels.request.components.editor.XmlLocation;
    42  import com.eviware.soapui.impl.wsdl.panels.request.components.editor.support.ValidationError;
    43  import com.eviware.soapui.impl.wsdl.panels.request.components.editor.views.AbstractEditorView;
    44  import com.eviware.soapui.impl.wsdl.panels.teststeps.support.LineNumbersPanel;
    45  import com.eviware.soapui.settings.UISettings;
    46  import com.eviware.soapui.support.DocumentListenerAdapter;
    47  import com.eviware.soapui.support.UISupport;
    48  import com.eviware.soapui.support.swing.SoapUISplitPaneUI;
    49  import com.eviware.soapui.support.xml.JXEditTextArea;
    50  import com.eviware.soapui.support.xml.actions.FormatXmlAction;
    51  import com.eviware.soapui.support.xml.actions.LoadXmlTextAreaAction;
    52  import com.eviware.soapui.support.xml.actions.SaveXmlTextAreaAction;
    53
    54  /***
    55   * Default "XML" source editor view in soapUI
    56   *
    57   * @author ole.matzura
    58   */
    59
    60  public class XmlSourceEditorView extends AbstractEditorView implements PropertyChangeListener
    61  {
    62  	private JXEditTextArea editArea;
    63  	private ValidateMessageXmlAction validateXmlAction;
    64  	private JSplitPane splitter;
    65  	private JScrollPane errorScrollPane;
    66  	private DefaultListModel errorListModel;
    67  	private FormatXmlAction formatXmlAction;
    68  	private SaveXmlTextAreaAction saveXmlTextAreaAction;
    69  	private boolean updating;
    70  	private JPopupMenu editorPopup;
    71  	public boolean isLocating;
    72  	private JScrollPane editorScrollPane;
    73  	private LoadXmlTextAreaAction loadXmlTextAreaAction;
    74  	private JPopupMenu inputPopup;
    75  	private LineNumbersPanel lineNumbersPanel;
    76  	private JCheckBoxMenuItem toggleLineNumbersMenuItem;
    77
    78  	public XmlSourceEditorView( XmlEditor xmlEditor )
    79     {
    80     	super( "XML", xmlEditor );
    81     }
    82
    83  	protected void buildUI()
    84  	{
    85  		editArea = JXEditTextArea.createXmlEditor();
    86  		editArea.setMinimumSize(new Dimension(50, 50));
    87  		editArea.setCaretPosition(0);
    88  		editArea.setDiscardEditsOnSet(false);
    89  		editArea.setEnabled( false );
    90  		editArea.setBorder( BorderFactory.createMatteBorder( 0, 2, 0, 0, Color.WHITE) );
    91
    92  		errorListModel = new DefaultListModel();
    93  		JList list = new JList(errorListModel);
    94  		list.addMouseListener(new ValidationListMouseAdapter(list, editArea));
    95  		errorScrollPane = new JScrollPane(list);
    96  		errorScrollPane.setVisible(false);
    97
    98  		splitter = new JSplitPane(JSplitPane.VERTICAL_SPLIT)
    99  		{
    100 			public void requestFocus()
    101 			{
    102 				SwingUtilities.invokeLater( new Runnable() {
    103
    104 					public void run()
    105 					{
    106 						editArea.requestFocusInWindow();
    107 					}} );
    108 			}
    109
    110 			public boolean hasFocus()
    111 			{
    112 				return editArea.hasFocus();
    113 			}
    114 		};
    115
    116 		splitter.setUI( new SoapUISplitPaneUI() );
    117 		splitter.setDividerSize( 0 );
    118 		splitter.setOneTouchExpandable( true );
    119
    120 		lineNumbersPanel = new LineNumbersPanel( editArea );
    121 		lineNumbersPanel.setVisible( SoapUI.getSettings().getBoolean( UISettings.SHOW_XML_LINE_NUMBERS ) );
    122
    123 		editorPopup = new JPopupMenu();
    124 		buildPopup( editorPopup, editArea );
    125
    126 		editArea.setRightClickPopup(editorPopup);
    127 		editArea.getDocument().addDocumentListener(
    128 				new DocumentListenerAdapter() {
    129
    130 					public void update(Document document)
    131 					{
    132 						if( !updating && getXmlDocument() != null )
    133 						{
    134 							updating = true;
    135 							getXmlDocument().setXml( editArea.getText());
    136 							updating = false;
    137 						}
    138 					}
    139 				});
    140
    141 		editArea.getInputHandler().addKeyBinding("A+V", validateXmlAction);
    142 		editArea.getInputHandler().addKeyBinding("A+F", formatXmlAction);
    143 		editArea.getInputHandler().addKeyBinding("C+S", saveXmlTextAreaAction);
    144 		editArea.getInputHandler().addKeyBinding( "ALT+L", new ActionListener() {
    145
    146 			public void actionPerformed( ActionEvent e )
    147 			{
    148 				lineNumbersPanel.setVisible( !lineNumbersPanel.isVisible() );
    149 				toggleLineNumbersMenuItem.setSelected( lineNumbersPanel.isVisible() );
    150 			}} );
    151
    152 		JPanel p = new JPanel( new BorderLayout() );
    153 		p.add( editArea, BorderLayout.CENTER );
    154 		p.add( lineNumbersPanel, BorderLayout.WEST );
    155
    156 		editorScrollPane = new JScrollPane(p);
    157 		splitter.setTopComponent(editorScrollPane);
    158 		splitter.setBottomComponent(errorScrollPane);
    159 		splitter.setDividerLocation(1.0);
    160 		splitter.setBorder(null);
    161 	}
    162
    163 	public JScrollPane getEditorScrollPane()
    164 	{
    165 		return editorScrollPane;
    166 	}
    167
    168 	protected void buildPopup(JPopupMenu inputPopup, JXEditTextArea editArea )
    169 	{
    170 		this.inputPopup = inputPopup;
    171 		validateXmlAction = new ValidateMessageXmlAction( );
    172 		formatXmlAction = new FormatXmlAction(editArea);
    173 		saveXmlTextAreaAction = new SaveXmlTextAreaAction( editArea, "Save" );
    174 		loadXmlTextAreaAction = new LoadXmlTextAreaAction( editArea, "Load" );
    175
    176 		toggleLineNumbersMenuItem = new JCheckBoxMenuItem( "Show Line Numbers", lineNumbersPanel.isVisible());
    177 		toggleLineNumbersMenuItem.setAccelerator( UISupport.getKeyStroke( "alt L" ) );
    178 		toggleLineNumbersMenuItem.addActionListener( new ActionListener() {
    179
    180 			public void actionPerformed( ActionEvent e )
    181 			{
    182 				lineNumbersPanel.setVisible( toggleLineNumbersMenuItem.isSelected() );
    183 			}} );
    184
    185 		inputPopup.add(validateXmlAction);
    186 		inputPopup.add(formatXmlAction);
    187 		inputPopup.addSeparator();
    188 		inputPopup.add(editArea.getUndoAction());
    189 		inputPopup.add(editArea.getRedoAction());
    190 		inputPopup.add(editArea.createCopyAction());
    191 		inputPopup.add(editArea.createCutAction());
    192 		inputPopup.add(editArea.createPasteAction());
    193 		inputPopup.addSeparator();
    194 		inputPopup.add(editArea.getFindAndReplaceAction());
    195 		inputPopup.addSeparator();
    196 		inputPopup.add(editArea.getGoToLineAction());
    197 		inputPopup.add( toggleLineNumbersMenuItem );
    198
    199 		inputPopup.addSeparator();
    200 		inputPopup.add(saveXmlTextAreaAction);
    201 		inputPopup.add(loadXmlTextAreaAction);
    202 	}
    203
    204 	@Override
    205 	public void release()
    206 	{
    207 		super.release();
    208 		inputPopup.removeAll();
    209 	}
    210
    211 	private final static class ValidationListMouseAdapter extends MouseAdapter
    212 	{
    213 		private final JList list;
    214
    215 		private final JXEditTextArea textArea;
    216
    217 		public ValidationListMouseAdapter(JList list, JXEditTextArea textArea)
    218 		{
    219 			this.list = list;
    220 			this.textArea = textArea;
    221 		}
    222
    223 		public void mouseClicked(MouseEvent e)
    224 		{
    225 			if (e.getClickCount() < 2)
    226 				return;
    227
    228 			int ix = list.getSelectedIndex();
    229 			if (ix == -1)
    230 				return;
    231
    232 			Object obj = list.getModel().getElementAt(ix);
    233 			if (obj instanceof ValidationError)
    234 			{
    235 				ValidationError error = (ValidationError) obj;
    236 				if (error.getLineNumber() >= 0)
    237 				{
    238 					textArea.setCaretPosition(textArea.getLineStartOffset(error
    239 							.getLineNumber() - 1));
    240 					textArea.requestFocus();
    241 				}
    242 				else
    243 					Toolkit.getDefaultToolkit().beep();
    244 			}
    245 			else
    246 				Toolkit.getDefaultToolkit().beep();
    247 		}
    248 	}
    249
    250 	public JXEditTextArea getInputArea()
    251 	{
    252 		getComponent();
    253 		return editArea;
    254 	}
    255
    256 	public void setEditable(boolean enabled)
    257 	{
    258 		getComponent();
    259 	   editArea.setEditable( enabled );
    260 	}
    261
    262 	protected ValidationError[] validateXml( String xml )
    263 	{
    264 		return null;
    265 	}
    266
    267 	public class ValidateMessageXmlAction extends AbstractAction
    268 	{
    269 		public ValidateMessageXmlAction()
    270 		{
    271 			super( "Validate" );
    272 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt V" ));
    273 		}
    274
    275 		public void actionPerformed(ActionEvent e)
    276 		{
    277 			if( validate())
    278 				UISupport.showInfoMessage( "Validation OK" );
    279 		}
    280 	}
    281
    282    public boolean activate(XmlLocation location)
    283 	{
    284    	super.activate( location );
    285
    286    	if( location != null )
    287    		setLocation( location );
    288
    289    	editArea.requestFocus();
    290
    291    	return true;
    292 	}
    293
    294 	public JComponent getComponent()
    295 	{
    296 		if( splitter == null )
    297 			buildUI();
    298
    299 		return splitter;
    300 	}
    301
    302 	public XmlLocation getLocation()
    303 	{
    304 		return new XmlLocation( getCurrentLine()+1, getCurrentColumn() );
    305 	}
    306
    307 	public void setLocation(XmlLocation location)
    308 	{
    309 		int line = location.getLine()-1;
    310 		if( location != null && line >= 0 )
    311 		{
    312 			int caretLine = editArea.getCaretLine();
    313 			int offset = editArea.getLineStartOffset( line );
    314
    315 			try
    316 			{
    317 				editArea.setCaretPosition( offset + location.getColumn() );
    318 				int scrollLine = line + (line > caretLine ? 3 : -3);
    319 				if( scrollLine >= editArea.getLineCount() )
    320 					scrollLine = editArea.getLineCount()-1;
    321 				else if( scrollLine < 0 )
    322 					scrollLine = 0;
    323
    324 				editArea.scrollTo( scrollLine, location.getColumn() );
    325 			}
    326 			catch( RuntimeException e )
    327 			{
    328 			}
    329 		}
    330 	}
    331
    332 	public int getCurrentLine()
    333 	{
    334 		if( editArea == null )
    335 			return -1;
    336 		return editArea.getCaretLine();
    337 	}
    338
    339 	public int getCurrentColumn()
    340 	{
    341 		if( editArea == null )
    342 			return -1;
    343 		return editArea.getCaretColumn();
    344 	}
    345
    346 	public String getText()
    347 	{
    348 		if( editArea == null )
    349 			return null;
    350 		return editArea.getText();
    351 	}
    352
    353 	public boolean validate()
    354 	{
    355 		ValidationError[] errors = validateXml( editArea.getText() );
    356
    357 		errorListModel.clear();
    358 		if( errors == null || errors.length == 0 )
    359 		{
    360 			splitter.setDividerLocation( 1.0 );
    361 			splitter.setDividerSize( 0 );
    362 		   errorScrollPane.setVisible( false );
    363 		   return true;
    364 		}
    365 		else
    366 		{
    367 			Toolkit.getDefaultToolkit().beep();
    368 		   for( int c = 0; c < errors.length; c++ )
    369 		   {
    370 		   	errorListModel.addElement( errors[c] );
    371 		   }
    372 		   errorScrollPane.setVisible( true );
    373 		   splitter.setDividerLocation( 0.8 );
    374 		   splitter.setDividerSize( 10 );
    375 		   return false;
    376 		}
    377 	}
    378
    379 	public void setXml(String xml)
    380 	{
    381 		if( !updating )
    382 		{
    383 			updating = true;
    384
    385 			if( xml == null )
    386 			{
    387 				editArea.setText( "" );
    388 				editArea.setEnabled( false );
    389 			}
    390 			else
    391 			{
    392 				editArea.setEnabled( true );
    393 				editArea.setText( xml );
    394 				editArea.setCaretPosition( 0 );
    395 			}
    396
    397 			updating = false;
    398 		}
    399 	}
    400
    401 	public boolean saveDocument( boolean validate )
    402 	{
    403 		return validate ? validate() : true;
    404 	}
    405
    406 	public void locationChanged(XmlLocation location)
    407 	{
    408 		isLocating = true;
    409 		setLocation( location );
    410 		isLocating = false;
    411 	}
    412
    413 	public JPopupMenu getEditorPopup()
    414 	{
    415 		return editorPopup;
    416 	}
    417
    418 	public boolean hasFocus()
    419 	{
    420 		return editArea.hasFocus();
    421 	}
    422
    423 	public boolean isInspectable()
    424 	{
    425 		return true;
    426 	}
    427
    428 	public String getViewId()
    429 	{
    430 		return XmlSourceEditorFactory.VIEW_ID;
    431 	}
    432 }