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