View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 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.support.actions;
14  
15  import java.awt.BorderLayout;
16  import java.awt.GridLayout;
17  import java.awt.event.ActionEvent;
18  
19  import javax.swing.AbstractAction;
20  import javax.swing.Action;
21  import javax.swing.BorderFactory;
22  import javax.swing.ButtonGroup;
23  import javax.swing.JButton;
24  import javax.swing.JCheckBox;
25  import javax.swing.JComboBox;
26  import javax.swing.JDialog;
27  import javax.swing.JLabel;
28  import javax.swing.JPanel;
29  import javax.swing.JRadioButton;
30  
31  import com.eviware.soapui.support.UISupport;
32  import com.eviware.soapui.support.xml.ProxyFindAndReplacable;
33  import com.jgoodies.forms.builder.ButtonBarBuilder;
34  
35  /***
36   * Find-and-Replace dialog for a JXmlTextArea
37   * 
38   * @author Ole.Matzura
39   */
40  
41  public class FindAndReplaceDialog extends AbstractAction
42  {
43  	private final ProxyFindAndReplacable target;
44  	private JDialog dialog;
45  	private JCheckBox caseCheck;
46  	private JRadioButton allButton;
47  	private JRadioButton selectedLinesButton;
48  	private JRadioButton forwardButton;
49  	private JRadioButton backwardButton;
50  	private JCheckBox wholeWordCheck;
51  	private JButton findButton;
52  	private JButton replaceButton;
53  	private JButton replaceAllButton;
54  	private JComboBox findCombo;
55  	private JComboBox replaceCombo;
56  	private JCheckBox wrapCheck;
57  
58  	public FindAndReplaceDialog( FindAndReplaceable target )
59  	{
60  		super( "Find / Replace" );
61  		putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "F3" ) );
62  		this.target = new ProxyFindAndReplacable( target );
63  	}
64  
65  	public void actionPerformed( ActionEvent e )
66  	{
67  		show();
68  	}
69  
70  	public void show()
71  	{
72  		if( dialog == null )
73  			buildDialog();
74  
75  		replaceCombo.setEnabled( target.isEditable() );
76  		replaceAllButton.setEnabled( target.isEditable() );
77  		replaceButton.setEnabled( target.isEditable() );
78  
79  		UISupport.showDialog( dialog );
80  		findCombo.getEditor().selectAll();
81  		findCombo.requestFocus();
82  	}
83  
84  	private void buildDialog()
85  	{
86  		dialog = new JDialog( UISupport.getMainFrame(), "Find / Replace", false );
87  
88  		JPanel panel = new JPanel( new BorderLayout() );
89  		findCombo = new JComboBox();
90  		findCombo.setEditable( true );
91  		replaceCombo = new JComboBox();
92  		replaceCombo.setEditable( true );
93  
94  		// create inputs
95  		GridLayout gridLayout = new GridLayout( 2, 2 );
96  		gridLayout.setVgap( 5 );
97  		JPanel inputPanel = new JPanel( gridLayout );
98  		inputPanel.add( new JLabel( "Find:" ) );
99  		inputPanel.add( findCombo );
100 		inputPanel.add( new JLabel( "Replace with:" ) );
101 		inputPanel.add( replaceCombo );
102 		inputPanel.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) );
103 
104 		// create direction panel
105 		ButtonGroup directionGroup = new ButtonGroup();
106 		forwardButton = new JRadioButton( "Forward", true );
107 		forwardButton.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
108 		directionGroup.add( forwardButton );
109 		backwardButton = new JRadioButton( "Backward" );
110 		backwardButton.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
111 		directionGroup.add( backwardButton );
112 
113 		JPanel directionPanel = new JPanel( new GridLayout( 2, 1 ) );
114 		directionPanel.add( forwardButton );
115 		directionPanel.add( backwardButton );
116 		directionPanel.setBorder( BorderFactory.createTitledBorder( "Direction" ) );
117 
118 		// create scope panel
119 		ButtonGroup scopeGroup = new ButtonGroup();
120 		allButton = new JRadioButton( "All", true );
121 		allButton.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
122 		selectedLinesButton = new JRadioButton( "Selected Lines" );
123 		selectedLinesButton.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
124 		scopeGroup.add( allButton );
125 		scopeGroup.add( selectedLinesButton );
126 
127 		JPanel scopePanel = new JPanel( new GridLayout( 2, 1 ) );
128 		scopePanel.add( allButton );
129 		scopePanel.add( selectedLinesButton );
130 		scopePanel.setBorder( BorderFactory.createTitledBorder( "Scope" ) );
131 
132 		// create options
133 		caseCheck = new JCheckBox( "Case Sensitive" );
134 		caseCheck.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
135 		wholeWordCheck = new JCheckBox( "Whole Word" );
136 		wholeWordCheck.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
137 		wrapCheck = new JCheckBox( "Wrap Search" );
138 		wrapCheck.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
139 		JPanel optionsPanel = new JPanel( new GridLayout( 3, 1 ) );
140 		optionsPanel.add( caseCheck );
141 		optionsPanel.add( wholeWordCheck );
142 		optionsPanel.add( wrapCheck );
143 		optionsPanel.setBorder( BorderFactory.createTitledBorder( "Options" ) );
144 
145 		// create panel with options
146 		JPanel options = new JPanel( new GridLayout( 1, 2 ) );
147 
148 		JPanel radios = new JPanel( new GridLayout( 2, 1 ) );
149 		radios.add( directionPanel );
150 		radios.add( scopePanel );
151 
152 		options.add( optionsPanel );
153 		options.add( radios );
154 		options.setBorder( BorderFactory.createEmptyBorder( 0, 8, 0, 8 ) );
155 
156 		// create buttons
157 		ButtonBarBuilder builder = new ButtonBarBuilder();
158 		findButton = new JButton( new FindAction() );
159 		builder.addFixed( findButton );
160 		builder.addRelatedGap();
161 		replaceButton = new JButton( new ReplaceAction() );
162 		builder.addFixed( replaceButton );
163 		builder.addRelatedGap();
164 		replaceAllButton = new JButton( new ReplaceAllAction() );
165 		builder.addFixed( replaceAllButton );
166 		builder.addUnrelatedGap();
167 		builder.addFixed( new JButton( new CloseAction() ) );
168 		builder.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) );
169 
170 		// tie it up!
171 		panel.add( inputPanel, BorderLayout.NORTH );
172 		panel.add( options, BorderLayout.CENTER );
173 		panel.add( builder.getPanel(), BorderLayout.SOUTH );
174 
175 		dialog.getContentPane().add( panel );
176 		dialog.pack();
177 		UISupport.initDialogActions( dialog, null, findButton );
178 	}
179 
180 	private int findNext( int pos, String txt, String value )
181 	{
182 		int ix = forwardButton.isSelected() ? txt.indexOf( value, pos ) : txt.lastIndexOf( value, pos );
183 
184 		if( selectedLinesButton.isSelected() && ( ix < target.getSelectionStart() || ix > target.getSelectionEnd() ) )
185 			ix = -1;
186 
187 		if( wholeWordCheck.isSelected() )
188 		{
189 			while( ix != -1
190 					&& ( ( ix > 0 && Character.isLetterOrDigit( txt.charAt( ix - 1 ) ) ) || ( ix < txt.length()
191 							- value.length() - 1 && Character.isLetterOrDigit( txt.charAt( ix + value.length() ) ) ) ) )
192 			{
193 				ix = forwardButton.isSelected() ? ++ix : --ix;
194 				ix = forwardButton.isSelected() ? txt.indexOf( value, ix ) : txt.lastIndexOf( value, ix );
195 			}
196 		}
197 
198 		if( ix == -1 && wrapCheck.isSelected() )
199 		{
200 			if( forwardButton.isSelected() && pos > 0 )
201 				return findNext( 0, txt, value );
202 			else if( backwardButton.isSelected() && pos < txt.length() - 1 )
203 				return findNext( txt.length() - 1, txt, value );
204 		}
205 
206 		return ix;
207 	}
208 
209 	private class FindAction extends AbstractAction
210 	{
211 		String lastSearchedItem = "";
212 		int lastPositionF = -1;
213 
214 		public FindAction()
215 		{
216 			super( "Find/Find Next" );
217 		}
218 
219 		public void actionPerformed( ActionEvent e )
220 		{
221 			int pos = tweakPosition();
222 			int lastpos = tweakLastPosition();
223 
224 			String txt = target.getText();
225 
226 			if( findCombo.getSelectedItem() == null )
227 			{
228 				return;
229 			}
230 			String value = findCombo.getSelectedItem().toString();
231 			if( value.length() == 0 || ( pos == txt.length() && !wrapCheck.isSelected() ) )
232 				return;
233 
234 			if( !caseCheck.isSelected() )
235 			{
236 				value = value.toLowerCase();
237 				txt = txt.toLowerCase();
238 			}
239 
240 			if( pos == lastPositionF && value.equals( lastSearchedItem ) )
241 				if( forwardButton.isSelected() )
242 					pos += value.length() + 1;
243 				else
244 					pos -= value.length() - 1;
245 
246 			int ix = findNext( pos, txt, value );
247 
248 			lastSearchedItem = value;
249 			lastPositionF = ix;
250 
251 			if( ix != -1 )
252 			{
253 				if( selectedLinesButton.isSelected() )
254 				{
255 					target.select( ix, lastpos );
256 				}
257 				else
258 				{
259 					target.select( ix, ix + value.length() );
260 				}
261 
262 				for( int c = 0; c < findCombo.getItemCount(); c++ )
263 				{
264 					if( findCombo.getItemAt( c ).equals( value ) )
265 					{
266 						findCombo.removeItem( c );
267 						break;
268 					}
269 				}
270 
271 				findCombo.insertItemAt( value, 0 );
272 			}
273 			else
274 			{
275 				UISupport.showErrorMessage( "String [" + value + "] not found" );
276 			}
277 		}
278 	}
279 
280 	private class ReplaceAction extends AbstractAction
281 	{
282 		String lastSearchedItem = "";
283 		int lastPositionF = -1;
284 
285 		public ReplaceAction()
286 		{
287 			super( "Replace/Replace Next" );
288 		}
289 
290 		public void actionPerformed( ActionEvent e )
291 		{
292 			int pos = tweakPosition();
293 			int lastpos = tweakLastPosition();
294 
295 			String txt = target.getText();
296 
297 			if( findCombo.getSelectedItem() == null )
298 			{
299 				return;
300 			}
301 			String value = findCombo.getSelectedItem().toString();
302 			if( value.length() == 0 || txt.length() == 0 )
303 				return;
304 
305 			String newValue = replaceCombo.getSelectedItem() == null ? "" : replaceCombo.getSelectedItem().toString();
306 
307 			if( !caseCheck.isSelected() )
308 			{
309 				if( newValue.equalsIgnoreCase( value ) )
310 					return;
311 				value = value.toLowerCase();
312 				txt = txt.toLowerCase();
313 			}
314 			else if( newValue.equals( value ) )
315 				return;
316 
317 			if( pos == lastPositionF && value.equals( lastSearchedItem ) )
318 				if( forwardButton.isSelected() )
319 					pos += value.length() + 1;
320 				else
321 					pos -= value.length() - 1;
322 
323 			int ix = findNext( pos, txt, value );
324 
325 			lastSearchedItem = value;
326 			lastPositionF = ix;
327 
328 			int firstIx = ix;
329 			if( ix != -1 )
330 			{
331 				// System.out.println( "found match at " + ix + ", " + firstIx +
332 				// ", " + valueInNewValueIx );
333 				target.select( ix, ix + value.length() );
334 
335 				target.setSelectedText( newValue );
336 				if( selectedLinesButton.isSelected() )
337 				{
338 					target.select( ix, lastpos );
339 				}
340 				else
341 				{
342 					target.select( ix, ix + newValue.length() );
343 				}
344 
345 				// adjust firstix
346 				if( ix < firstIx )
347 					firstIx += newValue.length() - value.length();
348 
349 				txt = target.getText();
350 				if( !caseCheck.isSelected() )
351 				{
352 					txt = txt.toLowerCase();
353 				}
354 
355 				if( forwardButton.isSelected() )
356 				{
357 					ix = findNext( ix + newValue.length(), txt, value );
358 				}
359 				else
360 				{
361 					ix = findNext( ix - 1, txt, value );
362 				}
363 			}
364 			else
365 			{
366 				UISupport.showErrorMessage( "String [" + value + "] not found" );
367 			}
368 		}
369 	}
370 
371 	private class ReplaceAllAction extends AbstractAction
372 	{
373 		public ReplaceAllAction()
374 		{
375 			super( "Replace All" );
376 		}
377 
378 		public void actionPerformed( ActionEvent e )
379 		{
380 			int pos = tweakPosition();
381 			String txt = target.getDialogText();
382 
383 			if( findCombo.getSelectedItem() == null )
384 			{
385 				return;
386 			}
387 			String value = findCombo.getSelectedItem().toString();
388 			if( value.length() == 0 || txt.length() == 0 )
389 				return;
390 
391 			String newValue = replaceCombo.getSelectedItem() == null ? "" : replaceCombo.getSelectedItem().toString();
392 
393 			if( !caseCheck.isSelected() )
394 			{
395 				if( newValue.equalsIgnoreCase( value ) )
396 					return;
397 				value = value.toLowerCase();
398 				txt = txt.toLowerCase();
399 			}
400 			else if( newValue.equals( value ) )
401 				return;
402 
403 			int ix = findNext( pos, txt, value );
404 			if( ix >= 0 )
405 			{
406 
407 				int firstIx = ix;
408 				int valueInNewValueIx = !caseCheck.isSelected() ? newValue.toLowerCase().indexOf( value ) : newValue
409 						.indexOf( value );
410 
411 				target.setReplaceAll( true );
412 				target.setSBTarget();
413 				target.setNewValue( newValue );
414 				while( ix != -1 )
415 				{
416 					target.select( ix, ix + value.length() );
417 					target.setSelectedText( newValue );
418 					target.select( ix, ix + newValue.length() );
419 
420 					// adjust firstix
421 					if( ix < firstIx )
422 						firstIx += newValue.length() - value.length();
423 
424 					txt = target.getText();
425 					if( !caseCheck.isSelected() )
426 					{
427 						txt = txt.toLowerCase();
428 					}
429 
430 					if( forwardButton.isSelected() )
431 					{
432 						ix = findNext( ix + newValue.length(), txt, value );
433 					}
434 					else
435 					{
436 						ix = findNext( ix - 1, txt, value );
437 					}
438 					if( wrapCheck.isSelected() && valueInNewValueIx != -1 && ix == firstIx + valueInNewValueIx )
439 					{
440 						break;
441 					}
442 				}
443 				target.flushSBText();
444 				target.setReplaceAll( false );
445 				target.setCarretPosition( forwardButton.isSelected() );
446 			}
447 			else
448 			{
449 				UISupport.showErrorMessage( "String [" + value + "] not found" );
450 			}
451 		}
452 
453 	}
454 
455 	private class CloseAction extends AbstractAction
456 	{
457 		public CloseAction()
458 		{
459 			super( "Close" );
460 		}
461 
462 		public void actionPerformed( ActionEvent e )
463 		{
464 			dialog.setVisible( false );
465 		}
466 	}
467 
468 	private int tweakPosition()
469 	{
470 		int pos = target.getCaretPosition();
471 		if( selectedLinesButton.isSelected() )
472 		{
473 			if( forwardButton.isSelected() )
474 			{
475 				int selstart = target.getSelectionStart();
476 				if( selstart < pos && selstart != -1 )
477 					pos = selstart;
478 			}
479 
480 			else
481 			{
482 				int selend = target.getSelectionEnd();
483 				if( selend > pos && selend != -1 )
484 					pos = selend;
485 			}
486 		}
487 		else
488 		{
489 			int selstart = target.getSelectionStart();
490 			if( selstart < pos && selstart != -1 )
491 				pos = selstart;
492 		}
493 		return pos;
494 	}
495 
496 	private int tweakLastPosition()
497 	{
498 		return forwardButton.isSelected() ? target.getSelectionEnd() : target.getSelectionStart();
499 	}
500 
501 }