View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2009 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.teststeps.support;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Color;
17  import java.awt.Dimension;
18  import java.awt.Font;
19  import java.awt.event.ActionEvent;
20  import java.awt.event.ActionListener;
21  import java.awt.event.FocusAdapter;
22  import java.awt.event.FocusEvent;
23  import java.beans.PropertyChangeEvent;
24  import java.beans.PropertyChangeListener;
25  
26  import javax.swing.AbstractAction;
27  import javax.swing.Action;
28  import javax.swing.BorderFactory;
29  import javax.swing.JCheckBoxMenuItem;
30  import javax.swing.JPanel;
31  import javax.swing.KeyStroke;
32  import javax.swing.event.CaretListener;
33  import javax.swing.text.Document;
34  
35  import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
36  import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
37  import org.fife.ui.rtextarea.RTextScrollPane;
38  
39  import com.eviware.soapui.impl.wsdl.WsdlProject;
40  import com.eviware.soapui.model.settings.Settings;
41  import com.eviware.soapui.model.settings.SettingsListener;
42  import com.eviware.soapui.model.support.ModelSupport;
43  import com.eviware.soapui.settings.UISettings;
44  import com.eviware.soapui.support.DocumentListenerAdapter;
45  import com.eviware.soapui.support.UISupport;
46  import com.eviware.soapui.support.actions.FindAndReplaceDialog;
47  import com.eviware.soapui.support.actions.FindAndReplaceable;
48  import com.eviware.soapui.support.components.JEditorStatusBar.JEditorStatusBarTarget;
49  import com.eviware.soapui.support.scripting.groovy.GroovyScriptEngineFactory;
50  import com.eviware.soapui.support.scripting.groovy.SoapUIGroovyScriptEngine;
51  import com.eviware.soapui.support.scripting.js.JsScriptEngineFactory;
52  import com.eviware.soapui.support.swing.RSyntaxAreaPopupMenu;
53  
54  /***
55   * Groovy editor wrapper
56   * 
57   * @author ole.matzura
58   */
59  
60  public class GroovyEditor extends JPanel implements JEditorStatusBarTarget, PropertyChangeListener
61  {
62  	private RSyntaxTextArea editArea;
63  	private GroovyEditorModel model;
64  	private InternalSettingsListener settingsListener;
65  	private GroovyDocumentListener groovyDocumentListener;
66  	private RTextScrollPane scrollPane;
67  	private JCheckBoxMenuItem toggleLineNumbersMenuItem;
68  	private boolean updating;
69  
70  	// private JPanel lineNumbersPanel;
71  
72  	public GroovyEditor( GroovyEditorModel model )
73  	{
74  		super( new BorderLayout() );
75  		this.model = model;
76  		
77  		model.addPropertyChangeListener( this );
78  
79  		Settings settings = model.getSettings();
80  		Font editorFont = UISupport.getEditorFont( settings );
81  
82  		editArea = new RSyntaxTextArea();
83  		editArea.restoreDefaultSyntaxScheme();
84  
85  		String defaultScriptLanguage = ((WsdlProject)ModelSupport.getModelItemProject( model.getModelItem() )).getDefaultScriptLanguage();
86  		if( defaultScriptLanguage.equals( GroovyScriptEngineFactory.ID ))
87  			editArea.setSyntaxEditingStyle( SyntaxConstants.SYNTAX_STYLE_GROOVY );
88  		else if( defaultScriptLanguage.equals( JsScriptEngineFactory.ID ))
89  			editArea.setSyntaxEditingStyle( SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT );
90  		
91  		editArea.setBorder( BorderFactory.createMatteBorder( 0, 2, 0, 0, Color.WHITE ) );
92  
93  		editArea.setText( model.getScript() );
94  		editArea.setCaretPosition( 0 );
95  		editArea.setHighlightCurrentLine( false );
96  		Action runAction = model.getRunAction();
97  		if( runAction != null )
98  		{
99  			editArea.getInputMap().put( KeyStroke.getKeyStroke( "alt ENTER" ), "run-action" );
100 			editArea.getActionMap().put( "run-action", runAction );
101 		}
102 
103 		editArea.getInputMap().put( KeyStroke.getKeyStroke( "F3" ), "find-action" );
104 		editArea.getInputMap().put( KeyStroke.getKeyStroke( "ctrl F" ), "find-action" );
105 		RSyntaxTextAreaFindAndReplaceable findAndReplaceable = new RSyntaxTextAreaFindAndReplaceable();
106 		editArea.getActionMap().put( "find-action", new FindAndReplaceDialog( findAndReplaceable ) );
107 
108 		groovyDocumentListener = new GroovyDocumentListener();
109 		editArea.getDocument().addDocumentListener( groovyDocumentListener );
110 
111 		settingsListener = new InternalSettingsListener();
112 		settings.addSettingsListener( settingsListener );
113 
114 		//scrollPane = new RTextScrollPane( 500, 300, editArea, true );
115 		scrollPane = new RTextScrollPane( editArea, true );
116 		scrollPane.setPreferredSize(new Dimension(500, 300));
117 		add( scrollPane );
118 
119 		UISupport.addPreviewCorner( scrollPane, true );
120 
121 		addFocusListener( new FocusAdapter()
122 		{
123 			public void focusGained( FocusEvent e )
124 			{
125 				editArea.requestFocusInWindow();
126 			}
127 		} );
128 
129 		RSyntaxAreaPopupMenu popup = RSyntaxAreaPopupMenu.add( editArea );
130 		popup.add( new FindAndReplaceDialog( findAndReplaceable ) );
131 		popup.addSeparator();
132 		popup.add( new GoToLineAction() );
133 
134 		toggleLineNumbersMenuItem = new JCheckBoxMenuItem( "Show Line Numbers", scrollPane.getLineNumbersEnabled() );
135 		toggleLineNumbersMenuItem.setAccelerator( UISupport.getKeyStroke( "alt L" ) );
136 		toggleLineNumbersMenuItem.addActionListener( new ActionListener()
137 		{
138 			public void actionPerformed( ActionEvent e )
139 			{
140 				enableLineNumbers( toggleLineNumbersMenuItem.isSelected() );
141 			}
142 		} );
143 
144 		editArea.getInputMap().put( KeyStroke.getKeyStroke( "alt L" ), new AbstractAction()
145 		{
146 			public void actionPerformed( ActionEvent e )
147 			{
148 				enableLineNumbers( !scrollPane.getLineNumbersEnabled() );
149 			}
150 		} );
151 
152 		popup.add( toggleLineNumbersMenuItem );
153 		editArea.setPopupMenu( popup );
154 
155 		enableLineNumbers( settings.getBoolean( UISettings.SHOW_GROOVY_LINE_NUMBERS ) );
156 	}
157 
158 	@Override
159 	public void setEnabled( boolean enabled )
160 	{
161 		super.setEnabled( enabled );
162 		
163 		editArea.setEnabled( enabled );
164 	}
165 
166 	public void enableLineNumbers( boolean enable )
167 	{
168 		scrollPane.setLineNumbersEnabled( enable );
169 		try
170 		{
171 			/*if( scrollPane.getLineNumbersEnabled() )
172 				( ( LineNumberBorder )scrollPane.getViewportBorder() )
173 						.setBackground( StandaloneSoapUICore.SoapUITheme.BACKGROUND_COLOR );*/
174 		}
175 		catch( Exception e )
176 		{
177 			e.printStackTrace();
178 		}
179 
180 		toggleLineNumbersMenuItem.setSelected( enable );
181 	}
182 
183 	public RSyntaxTextArea getEditArea()
184 	{
185 		return editArea;
186 	}
187 
188 	public void release()
189 	{
190 		if( model != null )
191 		{
192 			model.getSettings().removeSettingsListener( settingsListener );
193 			model.removePropertyChangeListener( this );
194 		}
195 
196 		model = null;
197 		editArea.getDocument().removeDocumentListener( groovyDocumentListener );
198 	}
199 
200 	public void selectError( String message )
201 	{
202 		int ix = message == null ? -1 : message.indexOf( "@ line " );
203 		if( ix >= 0 )
204 		{
205 			try
206 			{
207 				int ix2 = message.indexOf( ',', ix );
208 				int line = ix2 == -1 ? Integer.parseInt( message.substring( ix + 6 ).trim() ) : Integer.parseInt( message
209 						.substring( ix + 6, ix2 ).trim() );
210 				int column = 0;
211 				if( ix2 != -1 )
212 				{
213 					ix = message.indexOf( "column ", ix2 );
214 					if( ix >= 0 )
215 					{
216 						ix2 = message.indexOf( '.', ix );
217 						column = ix2 == -1 ? Integer.parseInt( message.substring( ix + 7 ).trim() ) : Integer
218 								.parseInt( message.substring( ix + 7, ix2 ).trim() );
219 					}
220 				}
221 
222 				editArea.setCaretPosition( editArea.getLineStartOffset( line - 1 ) + column - 1 );
223 			}
224 			catch( Exception ex )
225 			{
226 			}
227 
228 			editArea.requestFocus();
229 		}
230 	}
231 
232 	private final class GroovyDocumentListener extends DocumentListenerAdapter
233 	{
234 		public void update( Document document )
235 		{
236 			if( !updating )
237 			{
238 				GroovyEditor.this.model.setScript( editArea.getText() );
239 			}
240 		}
241 	}
242 
243 	private final class InternalSettingsListener implements SettingsListener
244 	{
245 		public void settingChanged( String name, String newValue, String oldValue )
246 		{
247 			if( name.equals( UISettings.EDITOR_FONT ) )
248 			{
249 				Font newFont = Font.decode( newValue );
250 				setEditorFont( newFont );
251 				invalidate();
252 			}
253 		}
254 	}
255 
256 	public void setEditorFont( Font newFont )
257 	{
258 		editArea.setFont( newFont );
259 	}
260 
261 	public void addCaretListener( CaretListener listener )
262 	{
263 		editArea.addCaretListener( listener );
264 	}
265 
266 	public int getCaretPosition()
267 	{
268 		return editArea.getCaretPosition();
269 	}
270 
271 	public int getLineOfOffset( int offset ) throws Exception
272 	{
273 		return editArea.getLineOfOffset( offset );
274 	}
275 
276 	public int getLineStartOffset( int line ) throws Exception
277 	{
278 		return editArea.getLineStartOffset( line );
279 	}
280 
281 	public void removeCaretListener( CaretListener listener )
282 	{
283 		editArea.removeCaretListener( listener );
284 	}
285 
286 	private final class GoToLineAction extends AbstractAction
287 	{
288 		public GoToLineAction()
289 		{
290 			super( "Go To Line" );
291 			putValue( Action.SHORT_DESCRIPTION, "Moves the caret to the specified line" );
292 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "menu alt L" ) );
293 		}
294 
295 		public void actionPerformed( ActionEvent e )
296 		{
297 			String line = UISupport.prompt( "Enter line-number to (1.." + ( editArea.getLineCount() ) + ")", "Go To Line",
298 					String.valueOf( editArea.getCaretLineNumber() + 1 ) );
299 
300 			if( line != null )
301 			{
302 				try
303 				{
304 					int ln = Integer.parseInt( line ) - 1;
305 					if( ln >= 0 && ln < editArea.getLineCount() )
306 					{
307 						editArea.scrollRectToVisible( editArea.modelToView( editArea.getLineStartOffset( ln ) ) );
308 						editArea.setCaretPosition( getLineStartOffset( ln ) );
309 					}
310 				}
311 				catch( Exception e1 )
312 				{
313 				}
314 			}
315 		}
316 	}
317 
318 	private class RSyntaxTextAreaFindAndReplaceable implements FindAndReplaceable
319 	{
320 		public boolean isEditable()
321 		{
322 			return editArea.isEditable();
323 		}
324 
325 		public int getCaretPosition()
326 		{
327 			return editArea.getCaretPosition();
328 		}
329 
330 		public String getText()
331 		{
332 			return editArea.getText();
333 		}
334 
335 		public void select( int start, int end )
336 		{
337 			editArea.select( start, end );
338 		}
339 
340 		public int getSelectionStart()
341 		{
342 			return editArea.getSelectionStart();
343 		}
344 
345 		public int getSelectionEnd()
346 		{
347 			return editArea.getSelectionEnd();
348 		}
349 
350 		public void setSelectedText( String txt )
351 		{
352 			editArea.replaceSelection( txt );
353 		}
354 
355 		public String getSelectedText()
356 		{
357 			return editArea.getSelectedText();
358 		}
359 	}
360 
361 	public void propertyChange( PropertyChangeEvent evt )
362 	{
363 		if( evt.getPropertyName().equals( "script" ))
364 		{
365 			updating = true;
366 			editArea.setText( String.valueOf( evt.getNewValue() ));
367 			updating = false;
368 		}
369 	}
370 }