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