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