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.components.PreviewCorner;
49  import com.eviware.soapui.support.swing.SoapUISplitPaneUI;
50  import com.eviware.soapui.support.xml.JXEditTextArea;
51  import com.eviware.soapui.support.xml.actions.FormatXmlAction;
52  import com.eviware.soapui.support.xml.actions.LoadXmlTextAreaAction;
53  import com.eviware.soapui.support.xml.actions.SaveXmlTextAreaAction;
54  
55  /***
56   * Default "XML" source editor view in soapUI
57   * 
58   * @author ole.matzura
59   */
60  
61  public class XmlSourceEditorView extends AbstractEditorView implements PropertyChangeListener
62  {
63  	private JXEditTextArea editArea;
64  	private ValidateMessageXmlAction validateXmlAction;
65  	private JSplitPane splitter;
66  	private JScrollPane errorScrollPane;
67  	private DefaultListModel errorListModel;
68  	private FormatXmlAction formatXmlAction;
69  	private SaveXmlTextAreaAction saveXmlTextAreaAction;
70  	private boolean updating;
71  	private JPopupMenu editorPopup;
72  	public boolean isLocating;
73  	private JScrollPane editorScrollPane;
74  	private LoadXmlTextAreaAction loadXmlTextAreaAction;
75  	private JPopupMenu inputPopup;
76  	private LineNumbersPanel lineNumbersPanel;
77  	private JCheckBoxMenuItem toggleLineNumbersMenuItem;
78  	private PreviewCorner previewCorner;
79  
80  	public XmlSourceEditorView( XmlEditor xmlEditor )
81     {
82     	super( "XML", xmlEditor );
83     }
84  	
85  	protected void buildUI()
86  	{
87  		editArea = JXEditTextArea.createXmlEditor( false );
88  		editArea.setMinimumSize(new Dimension(50, 50));
89  		editArea.setCaretPosition(0);
90  		editArea.setDiscardEditsOnSet(false);
91  		editArea.setEnabled( false );
92  		editArea.setBorder( BorderFactory.createMatteBorder( 0, 2, 0, 0, Color.WHITE) );
93  
94  		errorListModel = new DefaultListModel();
95  		JList list = new JList(errorListModel);
96  		list.addMouseListener(new ValidationListMouseAdapter(list, editArea));
97  		errorScrollPane = new JScrollPane(list);
98  		errorScrollPane.setVisible(false);
99  
100 		splitter = new JSplitPane(JSplitPane.VERTICAL_SPLIT)
101 		{
102 			public void requestFocus()
103 			{
104 				SwingUtilities.invokeLater( new Runnable() {
105 
106 					public void run()
107 					{
108 						editArea.requestFocusInWindow();
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(
130 				new DocumentListenerAdapter() {
131 
132 					public void update(Document document) 
133 					{
134 						if( !updating && getXmlDocument() != null )
135 						{
136 							updating = true;
137 							getXmlDocument().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 			public void actionPerformed( ActionEvent e )
149 			{
150 				lineNumbersPanel.setVisible( !lineNumbersPanel.isVisible() );
151 				toggleLineNumbersMenuItem.setSelected( lineNumbersPanel.isVisible() );
152 			}} );
153 		
154 		JPanel p = new JPanel( new BorderLayout() );
155 		p.add( editArea, BorderLayout.CENTER );
156 		p.add( lineNumbersPanel, BorderLayout.WEST );
157 		
158 		editorScrollPane = new JScrollPane(p);
159 		splitter.setTopComponent(editorScrollPane);
160 		splitter.setBottomComponent(errorScrollPane);
161 		splitter.setDividerLocation(1.0);
162 		splitter.setBorder(null);
163 		
164 		previewCorner = UISupport.addPreviewCorner( getEditorScrollPane(), true );
165 	}
166 
167 	public JScrollPane getEditorScrollPane()
168 	{
169 		return editorScrollPane;
170 	}
171 
172 	protected void buildPopup(JPopupMenu inputPopup, JXEditTextArea editArea )
173 	{
174 		this.inputPopup = inputPopup;
175 		validateXmlAction = new ValidateMessageXmlAction( );
176 		formatXmlAction = new FormatXmlAction(editArea);
177 		saveXmlTextAreaAction = new SaveXmlTextAreaAction( editArea, "Save" );
178 		loadXmlTextAreaAction = new LoadXmlTextAreaAction( editArea, "Load" );
179 		
180 		toggleLineNumbersMenuItem = new JCheckBoxMenuItem( "Show Line Numbers", lineNumbersPanel.isVisible());
181 		toggleLineNumbersMenuItem.setAccelerator( UISupport.getKeyStroke( "alt L" ) );
182 		toggleLineNumbersMenuItem.addActionListener( new ActionListener() {
183 
184 			public void actionPerformed( ActionEvent e )
185 			{
186 				lineNumbersPanel.setVisible( toggleLineNumbersMenuItem.isSelected() );
187 			}} );
188 		
189 		inputPopup.add(validateXmlAction);
190 		inputPopup.add(formatXmlAction);
191 		inputPopup.addSeparator();
192 		inputPopup.add(editArea.getUndoAction());
193 		inputPopup.add(editArea.getRedoAction());
194 		inputPopup.add(editArea.createCopyAction());
195 		inputPopup.add(editArea.createCutAction());
196 		inputPopup.add(editArea.createPasteAction());
197 		inputPopup.addSeparator();
198 		inputPopup.add(editArea.getFindAndReplaceAction());
199 		inputPopup.addSeparator();
200 		inputPopup.add(editArea.getGoToLineAction());
201 		inputPopup.add( toggleLineNumbersMenuItem );
202 		
203 		inputPopup.addSeparator(); 
204 		inputPopup.add(saveXmlTextAreaAction);
205 		inputPopup.add(loadXmlTextAreaAction);
206 	}
207 	
208 	@Override
209 	public void release()
210 	{
211 		super.release();
212 		inputPopup.removeAll();
213 		previewCorner.release();
214 	}
215 
216 	private final static class ValidationListMouseAdapter extends MouseAdapter 
217 	{
218 		private final JList list;
219 
220 		private final JXEditTextArea textArea;
221 
222 		public ValidationListMouseAdapter(JList list, JXEditTextArea textArea) 
223 		{
224 			this.list = list;
225 			this.textArea = textArea;
226 		}
227 
228 		public void mouseClicked(MouseEvent e) 
229 		{
230 			if (e.getClickCount() < 2)
231 				return;
232 
233 			int ix = list.getSelectedIndex();
234 			if (ix == -1)
235 				return;
236 
237 			Object obj = list.getModel().getElementAt(ix);
238 			if (obj instanceof ValidationError) 
239 			{
240 				ValidationError error = (ValidationError) obj;
241 				if (error.getLineNumber() >= 0) 
242 				{
243 					textArea.setCaretPosition(textArea.getLineStartOffset(error
244 							.getLineNumber() - 1));
245 					textArea.requestFocus();
246 				} 
247 				else
248 					Toolkit.getDefaultToolkit().beep();
249 			} 
250 			else
251 				Toolkit.getDefaultToolkit().beep();
252 		}
253 	}
254 
255 	public JXEditTextArea getInputArea()
256 	{
257 		getComponent();
258 		return editArea;
259 	}
260 
261 	public void setEditable(boolean enabled)
262 	{
263 		getComponent();
264 	   editArea.setEditable( enabled );	
265 	}
266 
267 	protected ValidationError[] validateXml( String xml )
268 	{
269 		return null;
270 	}
271 	
272 	public class ValidateMessageXmlAction extends AbstractAction
273 	{
274 		public ValidateMessageXmlAction()
275 		{
276 			super( "Validate" );
277 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt V" ));
278 		}
279 
280 		public void actionPerformed(ActionEvent e)
281 		{
282 			if( validate())
283 				UISupport.showInfoMessage( "Validation OK" ); 
284 		}
285 	}
286 
287    public boolean activate(XmlLocation location)
288 	{
289    	super.activate( location );
290    	
291    	if( location != null )
292    		setLocation( location );
293    	
294    	editArea.requestFocus();
295    	
296    	return true;
297 	}
298 
299 	public JComponent getComponent()
300 	{
301 		if( splitter == null )
302 			buildUI();
303 		
304 		return splitter;
305 	}
306 	
307 	public XmlLocation getLocation()
308 	{
309 		return new XmlLocation( getCurrentLine()+1, getCurrentColumn() );
310 	}
311 	
312 	public void setLocation(XmlLocation location)
313 	{
314 		int line = location.getLine()-1;
315 		if( location != null && line >= 0 )
316 		{
317 			int caretLine = editArea.getCaretLine();
318 			int offset = editArea.getLineStartOffset( line );
319 			
320 			try
321 			{
322 				editArea.setCaretPosition( offset + location.getColumn() );
323 				int scrollLine = line + (line > caretLine ? 3 : -3);
324 				if( scrollLine >= editArea.getLineCount() )
325 					scrollLine = editArea.getLineCount()-1;
326 				else if( scrollLine < 0 )
327 					scrollLine = 0;
328 				
329 				editArea.scrollTo( scrollLine, location.getColumn() );
330 			}
331 			catch( RuntimeException e )
332 			{
333 			}
334 		}
335 	}
336 
337 	public int getCurrentLine()
338 	{
339 		if( editArea == null )
340 			return -1;
341 		return editArea.getCaretLine();
342 	}
343 	
344 	public int getCurrentColumn()
345 	{
346 		if( editArea == null )
347 			return -1;
348 		return editArea.getCaretColumn();
349 	}
350 
351 	public String getText()
352 	{
353 		if( editArea == null )
354 			return null;
355 		return editArea.getText();
356 	}
357 
358 	public boolean validate()
359 	{
360 		ValidationError[] errors = validateXml( editArea.getText() );
361 		
362 		errorListModel.clear();
363 		if( errors == null || errors.length == 0 )
364 		{
365 			splitter.setDividerLocation( 1.0 );
366 			splitter.setDividerSize( 0 );
367 		   errorScrollPane.setVisible( false );
368 		   return true;
369 		}
370 		else
371 		{
372 			Toolkit.getDefaultToolkit().beep();
373 		   for( int c = 0; c < errors.length; c++ )
374 		   {
375 		   	errorListModel.addElement( errors[c] );
376 		   }
377 		   errorScrollPane.setVisible( true );
378 		   splitter.setDividerLocation( 0.8 );
379 		   splitter.setDividerSize( 10 );
380 		   return false;
381 		}
382 	}
383 
384 	public void setXml(String xml)
385 	{
386 		if( !updating )
387 		{
388 			updating = true;
389 			
390 			if( xml == null )
391 			{
392 				editArea.setText( "" );
393 				editArea.setEnabled( false );
394 			}
395 			else
396 			{
397 				int caretPosition = editArea.getCaretPosition();
398 				
399 				editArea.setEnabled( true );
400 				editArea.setText( xml );
401 				
402 				editArea.setCaretPosition( caretPosition < xml.length() ? caretPosition : 0 );
403 			}
404 			
405 			updating = false;
406 		}
407 	}
408 	
409 	public boolean saveDocument( boolean validate )
410 	{
411 		return validate ? validate() : true;
412 	}
413 	
414 	public void locationChanged(XmlLocation location)
415 	{
416 		isLocating = true;
417 		setLocation( location );
418 		isLocating = false;
419 	}
420 
421 	public JPopupMenu getEditorPopup()
422 	{
423 		return editorPopup;
424 	}
425 
426 	public boolean hasFocus()
427 	{
428 		return editArea.hasFocus();
429 	}
430 	
431 	public boolean isInspectable()
432 	{
433 		return true;
434 	}
435 
436 	public String getViewId()
437 	{
438 		return XmlSourceEditorFactory.VIEW_ID;
439 	}
440 }