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