1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.support.editor.views.xml.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 import java.util.ArrayList;
25 import java.util.List;
26
27 import javax.swing.AbstractAction;
28 import javax.swing.Action;
29 import javax.swing.BorderFactory;
30 import javax.swing.DefaultListModel;
31 import javax.swing.JCheckBoxMenuItem;
32 import javax.swing.JComponent;
33 import javax.swing.JList;
34 import javax.swing.JPanel;
35 import javax.swing.JPopupMenu;
36 import javax.swing.JScrollPane;
37 import javax.swing.JSplitPane;
38 import javax.swing.SwingUtilities;
39 import javax.swing.text.Document;
40
41 import org.apache.xmlbeans.XmlError;
42 import org.apache.xmlbeans.XmlException;
43 import org.apache.xmlbeans.XmlObject;
44 import org.apache.xmlbeans.XmlOptions;
45
46 import com.eviware.soapui.SoapUI;
47 import com.eviware.soapui.impl.wsdl.panels.teststeps.support.LineNumbersPanel;
48 import com.eviware.soapui.model.ModelItem;
49 import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
50 import com.eviware.soapui.settings.UISettings;
51 import com.eviware.soapui.support.DocumentListenerAdapter;
52 import com.eviware.soapui.support.UISupport;
53 import com.eviware.soapui.support.components.PreviewCorner;
54 import com.eviware.soapui.support.editor.EditorLocation;
55 import com.eviware.soapui.support.editor.views.AbstractXmlEditorView;
56 import com.eviware.soapui.support.editor.xml.XmlDocument;
57 import com.eviware.soapui.support.editor.xml.XmlEditor;
58 import com.eviware.soapui.support.editor.xml.XmlLocation;
59 import com.eviware.soapui.support.editor.xml.support.ValidationError;
60 import com.eviware.soapui.support.swing.SoapUISplitPaneUI;
61 import com.eviware.soapui.support.xml.JXEditTextArea;
62 import com.eviware.soapui.support.xml.actions.FormatXmlAction;
63 import com.eviware.soapui.support.xml.actions.InsertBase64FileTextAreaAction;
64 import com.eviware.soapui.support.xml.actions.LoadXmlTextAreaAction;
65 import com.eviware.soapui.support.xml.actions.SaveXmlTextAreaAction;
66
67 /***
68 * Default "XML" source editor view in soapUI
69 *
70 * @author ole.matzura
71 */
72
73 public class XmlSourceEditorView<T extends ModelItem> extends AbstractXmlEditorView<XmlDocument> implements
74 PropertyChangeListener
75 {
76 private JXEditTextArea editArea;
77 private ValidateMessageXmlAction validateXmlAction;
78 private JSplitPane splitter;
79 private JScrollPane errorScrollPane;
80 private DefaultListModel errorListModel;
81 private FormatXmlAction formatXmlAction;
82 private SaveXmlTextAreaAction saveXmlTextAreaAction;
83 private boolean updating;
84 private JPopupMenu editorPopup;
85 public boolean isLocating;
86 private JScrollPane editorScrollPane;
87 private LoadXmlTextAreaAction loadXmlTextAreaAction;
88 private JPopupMenu inputPopup;
89 private LineNumbersPanel lineNumbersPanel;
90 private JCheckBoxMenuItem toggleLineNumbersMenuItem;
91 private PreviewCorner previewCorner;
92 private final T modelItem;
93 private InsertBase64FileTextAreaAction insertBase64FileTextAreaAction;
94
95 public XmlSourceEditorView( XmlEditor<XmlDocument> xmlEditor, T modelItem )
96 {
97 super( "XML", xmlEditor, XmlSourceEditorViewFactory.VIEW_ID );
98 this.modelItem = modelItem;
99 }
100
101 protected void buildUI()
102 {
103 editArea = JXEditTextArea.createXmlEditor( false );
104 editArea.setMinimumSize( new Dimension( 50, 50 ) );
105 editArea.setCaretPosition( 0 );
106 editArea.setDiscardEditsOnSet( false );
107 editArea.setEnabled( false );
108 editArea.setBorder( BorderFactory.createMatteBorder( 0, 2, 0, 0, Color.WHITE ) );
109
110 errorListModel = new DefaultListModel();
111 JList list = new JList( errorListModel );
112 list.addMouseListener( new ValidationListMouseAdapter( list, editArea ) );
113 errorScrollPane = new JScrollPane( list );
114 errorScrollPane.setVisible( false );
115
116 splitter = new JSplitPane( JSplitPane.VERTICAL_SPLIT )
117 {
118 public void requestFocus()
119 {
120 SwingUtilities.invokeLater( new Runnable()
121 {
122
123 public void run()
124 {
125 editArea.requestFocusInWindow();
126 }
127 } );
128 }
129
130 public boolean hasFocus()
131 {
132 return editArea.hasFocus();
133 }
134 };
135
136 splitter.setUI( new SoapUISplitPaneUI() );
137 splitter.setDividerSize( 0 );
138 splitter.setOneTouchExpandable( true );
139
140 lineNumbersPanel = new LineNumbersPanel( editArea );
141 lineNumbersPanel.setVisible( SoapUI.getSettings().getBoolean( UISettings.SHOW_XML_LINE_NUMBERS ) );
142
143 editorPopup = new JPopupMenu();
144 buildPopup( editorPopup, editArea );
145
146 editArea.setRightClickPopup( editorPopup );
147 editArea.getDocument().addDocumentListener( new DocumentListenerAdapter()
148 {
149
150 public void update( Document document )
151 {
152 if( !updating && getDocument() != null )
153 {
154 updating = true;
155 getDocument().setXml( editArea.getText() );
156 updating = false;
157 }
158 }
159 } );
160
161 editArea.getInputHandler().addKeyBinding( "A+V", validateXmlAction );
162 editArea.getInputHandler().addKeyBinding( "A+F", formatXmlAction );
163 editArea.getInputHandler().addKeyBinding( "C+S", saveXmlTextAreaAction );
164 editArea.getInputHandler().addKeyBinding( "ALT+L", new ActionListener()
165 {
166
167 public void actionPerformed( ActionEvent e )
168 {
169 lineNumbersPanel.setVisible( !lineNumbersPanel.isVisible() );
170 toggleLineNumbersMenuItem.setSelected( lineNumbersPanel.isVisible() );
171 }
172 } );
173
174 JPanel p = new JPanel( new BorderLayout() );
175 p.add( editArea, BorderLayout.CENTER );
176 p.add( lineNumbersPanel, BorderLayout.WEST );
177
178 editorScrollPane = new JScrollPane( p );
179 splitter.setTopComponent( editorScrollPane );
180 splitter.setBottomComponent( errorScrollPane );
181 splitter.setDividerLocation( 1.0 );
182 splitter.setBorder( null );
183
184 previewCorner = UISupport.addPreviewCorner( getEditorScrollPane(), true );
185 }
186
187 public JScrollPane getEditorScrollPane()
188 {
189 return editorScrollPane;
190 }
191
192 public T getModelItem()
193 {
194 return modelItem;
195 }
196
197 protected void buildPopup( JPopupMenu inputPopup, JXEditTextArea editArea )
198 {
199 this.inputPopup = inputPopup;
200 validateXmlAction = new ValidateMessageXmlAction();
201 formatXmlAction = new FormatXmlAction( editArea );
202 saveXmlTextAreaAction = new SaveXmlTextAreaAction( editArea, "Save" );
203 loadXmlTextAreaAction = new LoadXmlTextAreaAction( editArea, "Load" );
204 insertBase64FileTextAreaAction = new InsertBase64FileTextAreaAction( editArea, "Insert File as Base64" );
205 toggleLineNumbersMenuItem = new JCheckBoxMenuItem( "Show Line Numbers", lineNumbersPanel.isVisible() );
206 toggleLineNumbersMenuItem.setAccelerator( UISupport.getKeyStroke( "alt L" ) );
207 toggleLineNumbersMenuItem.addActionListener( new ActionListener()
208 {
209
210 public void actionPerformed( ActionEvent e )
211 {
212 lineNumbersPanel.setVisible( toggleLineNumbersMenuItem.isSelected() );
213 }
214 } );
215
216 inputPopup.add( validateXmlAction );
217 inputPopup.add( formatXmlAction );
218 inputPopup.addSeparator();
219 inputPopup.add( editArea.getUndoAction() );
220 inputPopup.add( editArea.getRedoAction() );
221 inputPopup.add( editArea.createCopyAction() );
222 inputPopup.add( editArea.createCutAction() );
223 inputPopup.add( editArea.createPasteAction() );
224 inputPopup.addSeparator();
225 inputPopup.add( editArea.getFindAndReplaceAction() );
226 inputPopup.addSeparator();
227 inputPopup.add( editArea.getGoToLineAction() );
228 inputPopup.add( toggleLineNumbersMenuItem );
229
230 inputPopup.addSeparator();
231 inputPopup.add( saveXmlTextAreaAction );
232 inputPopup.add( loadXmlTextAreaAction );
233 inputPopup.add( insertBase64FileTextAreaAction );
234 }
235
236 @Override
237 public void release()
238 {
239 super.release();
240 inputPopup.removeAll();
241 previewCorner.release();
242 }
243
244 private final static class ValidationListMouseAdapter extends MouseAdapter
245 {
246 private final JList list;
247
248 private final JXEditTextArea textArea;
249
250 public ValidationListMouseAdapter( JList list, JXEditTextArea textArea )
251 {
252 this.list = list;
253 this.textArea = textArea;
254 }
255
256 public void mouseClicked( MouseEvent e )
257 {
258 if( e.getClickCount() < 2 )
259 return;
260
261 int ix = list.getSelectedIndex();
262 if( ix == -1 )
263 return;
264
265 Object obj = list.getModel().getElementAt( ix );
266 if( obj instanceof ValidationError )
267 {
268 ValidationError error = ( ValidationError )obj;
269 if( error.getLineNumber() >= 0 )
270 {
271 textArea.setCaretPosition( textArea.getLineStartOffset( error.getLineNumber() - 1 ) );
272 textArea.requestFocus();
273 }
274 else
275 Toolkit.getDefaultToolkit().beep();
276 }
277 else
278 Toolkit.getDefaultToolkit().beep();
279 }
280 }
281
282 public JXEditTextArea getInputArea()
283 {
284 getComponent();
285 return editArea;
286 }
287
288 public void setEditable( boolean enabled )
289 {
290 getComponent();
291 editArea.setEditable( enabled );
292 }
293
294 protected ValidationError[] validateXml( String xml )
295 {
296 try
297 {
298 XmlObject.Factory.parse( xml, new XmlOptions().setLoadLineNumbers() );
299 }
300 catch( XmlException e )
301 {
302 List<ValidationError> result = new ArrayList<ValidationError>();
303
304 if( e.getErrors() != null )
305 {
306 for( Object error : e.getErrors() )
307 {
308 if( error instanceof XmlError )
309 result.add( new com.eviware.soapui.model.testsuite.AssertionError( ( XmlError )error ) );
310 else
311 result.add( new com.eviware.soapui.model.testsuite.AssertionError( error.toString() ) );
312 }
313 }
314
315 if( result.isEmpty() )
316 result.add( new com.eviware.soapui.model.testsuite.AssertionError( e.toString() ) );
317
318 return result.toArray( new ValidationError[result.size()] );
319 }
320
321 return null;
322 }
323
324 public class ValidateMessageXmlAction extends AbstractAction
325 {
326 public ValidateMessageXmlAction()
327 {
328 super( "Validate" );
329 putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt V" ) );
330 }
331
332 public void actionPerformed( ActionEvent e )
333 {
334 if( validate() )
335 UISupport.showInfoMessage( "Validation OK" );
336 }
337 }
338
339 public boolean activate( XmlLocation location )
340 {
341 super.activate( location );
342
343 if( location != null )
344 setLocation( location );
345
346 editArea.requestFocus();
347
348 return true;
349 }
350
351 public JComponent getComponent()
352 {
353 if( splitter == null )
354 buildUI();
355
356 return splitter;
357 }
358
359 public XmlLocation getEditorLocation()
360 {
361 return new XmlLocation( getCurrentLine() + 1, getCurrentColumn() );
362 }
363
364 public void setLocation( XmlLocation location )
365 {
366 int line = location.getLine() - 1;
367 if( location != null && line >= 0 )
368 {
369 int caretLine = editArea.getCaretLine();
370 int offset = editArea.getLineStartOffset( line );
371
372 try
373 {
374 editArea.setCaretPosition( offset + location.getColumn() );
375 int scrollLine = line + ( line > caretLine ? 3 : -3 );
376 if( scrollLine >= editArea.getLineCount() )
377 scrollLine = editArea.getLineCount() - 1;
378 else if( scrollLine < 0 )
379 scrollLine = 0;
380
381 editArea.scrollTo( scrollLine, location.getColumn() );
382 }
383 catch( RuntimeException e )
384 {
385 }
386 }
387 }
388
389 public int getCurrentLine()
390 {
391 if( editArea == null )
392 return -1;
393 return editArea.getCaretLine();
394 }
395
396 public int getCurrentColumn()
397 {
398 if( editArea == null )
399 return -1;
400 return editArea.getCaretColumn();
401 }
402
403 public String getText()
404 {
405 if( editArea == null )
406 return null;
407 return editArea.getText();
408 }
409
410 public boolean validate()
411 {
412 ValidationError[] errors = validateXml( PropertyExpander.expandProperties( getModelItem(), editArea.getText() ) );
413
414 errorListModel.clear();
415 if( errors == null || errors.length == 0 )
416 {
417 splitter.setDividerLocation( 1.0 );
418 splitter.setDividerSize( 0 );
419 errorScrollPane.setVisible( false );
420 return true;
421 }
422 else
423 {
424 Toolkit.getDefaultToolkit().beep();
425 for( int c = 0; c < errors.length; c++ )
426 {
427 errorListModel.addElement( errors[c] );
428 }
429 errorScrollPane.setVisible( true );
430 splitter.setDividerLocation( 0.8 );
431 splitter.setDividerSize( 10 );
432 return false;
433 }
434 }
435
436 public void setXml( String xml )
437 {
438 if( !updating )
439 {
440 updating = true;
441
442 if( xml == null )
443 {
444 editArea.setText( "" );
445 editArea.setEnabled( false );
446 }
447 else
448 {
449 int caretPosition = editArea.getCaretPosition();
450
451 editArea.setEnabled( true );
452 editArea.setText( xml );
453
454 editArea.setCaretPosition( caretPosition < xml.length() ? caretPosition : 0 );
455 }
456
457 updating = false;
458 }
459 }
460
461 public boolean saveDocument( boolean validate )
462 {
463 return validate ? validate() : true;
464 }
465
466 public void locationChanged( EditorLocation<XmlDocument> location )
467 {
468 isLocating = true;
469 setLocation( location );
470 isLocating = false;
471 }
472
473 public JPopupMenu getEditorPopup()
474 {
475 return editorPopup;
476 }
477
478 public boolean hasFocus()
479 {
480 return editArea.hasFocus();
481 }
482
483 public boolean isInspectable()
484 {
485 return true;
486 }
487
488 public ValidateMessageXmlAction getValidateXmlAction()
489 {
490 return validateXmlAction;
491 }
492 }