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.teststeps.assertions.basic;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Dimension;
17  import java.awt.event.ActionEvent;
18  import java.awt.event.WindowAdapter;
19  import java.awt.event.WindowEvent;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import javax.swing.AbstractAction;
24  import javax.swing.Action;
25  import javax.swing.BorderFactory;
26  import javax.swing.JButton;
27  import javax.swing.JCheckBox;
28  import javax.swing.JDialog;
29  import javax.swing.JPanel;
30  import javax.swing.JScrollPane;
31  import javax.swing.JSplitPane;
32  import javax.swing.JTextArea;
33  import javax.swing.SwingUtilities;
34  
35  import junit.framework.ComparisonFailure;
36  
37  import org.apache.log4j.Logger;
38  import org.apache.xmlbeans.XmlAnySimpleType;
39  import org.apache.xmlbeans.XmlCursor;
40  import org.apache.xmlbeans.XmlObject;
41  import org.apache.xmlbeans.XmlOptions;
42  import org.apache.xmlbeans.XmlQName;
43  import org.custommonkey.xmlunit.Diff;
44  import org.custommonkey.xmlunit.Difference;
45  import org.custommonkey.xmlunit.DifferenceEngine;
46  import org.custommonkey.xmlunit.DifferenceListener;
47  import org.custommonkey.xmlunit.XMLAssert;
48  import org.w3c.dom.Attr;
49  import org.w3c.dom.Element;
50  import org.w3c.dom.Node;
51  
52  import com.eviware.soapui.SoapUI;
53  import com.eviware.soapui.config.TestAssertionConfig;
54  import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
55  import com.eviware.soapui.impl.wsdl.WsdlInterface;
56  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
57  import com.eviware.soapui.impl.wsdl.support.assertions.AssertedXPathImpl;
58  import com.eviware.soapui.impl.wsdl.support.assertions.AssertedXPathsContainer;
59  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
60  import com.eviware.soapui.impl.wsdl.teststeps.WsdlMessageAssertion;
61  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
62  import com.eviware.soapui.impl.wsdl.teststeps.assertions.AbstractTestAssertionFactory;
63  import com.eviware.soapui.model.TestModelItem;
64  import com.eviware.soapui.model.iface.MessageExchange;
65  import com.eviware.soapui.model.iface.SubmitContext;
66  import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
67  import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
68  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils;
69  import com.eviware.soapui.model.support.XPathReference;
70  import com.eviware.soapui.model.support.XPathReferenceContainer;
71  import com.eviware.soapui.model.support.XPathReferenceImpl;
72  import com.eviware.soapui.model.testsuite.Assertable;
73  import com.eviware.soapui.model.testsuite.AssertionError;
74  import com.eviware.soapui.model.testsuite.AssertionException;
75  import com.eviware.soapui.model.testsuite.RequestAssertion;
76  import com.eviware.soapui.model.testsuite.ResponseAssertion;
77  import com.eviware.soapui.model.testsuite.TestProperty;
78  import com.eviware.soapui.model.testsuite.TestStep;
79  import com.eviware.soapui.support.StringUtils;
80  import com.eviware.soapui.support.UISupport;
81  import com.eviware.soapui.support.components.JUndoableTextArea;
82  import com.eviware.soapui.support.components.JXToolBar;
83  import com.eviware.soapui.support.types.StringList;
84  import com.eviware.soapui.support.xml.XmlObjectConfigurationBuilder;
85  import com.eviware.soapui.support.xml.XmlObjectConfigurationReader;
86  import com.eviware.soapui.support.xml.XmlUtils;
87  import com.jgoodies.forms.builder.ButtonBarBuilder;
88  
89  /***
90   * Assertion that matches a specified XPath expression and its expected result
91   * against the associated WsdlTestRequests response message
92   * 
93   * @author Ole.Matzura
94   */
95  
96  public class XPathContainsAssertion extends WsdlMessageAssertion implements RequestAssertion, ResponseAssertion,
97  		XPathReferenceContainer
98  {
99  	private final static Logger log = Logger.getLogger( XPathContainsAssertion.class );
100 	private String expectedContent;
101 	private String path;
102 	private JDialog configurationDialog;
103 	private JTextArea pathArea;
104 	private JTextArea contentArea;
105 	private boolean configureResult;
106 	private boolean allowWildcards;
107 	private boolean ignoreNamspaceDifferences;
108 
109 	public static final String ID = "XPath Match";
110 	public static final String LABEL = "XPath Match";
111 	private JCheckBox allowWildcardsCheckBox;
112 	private JCheckBox ignoreNamspaceDifferencesCheckBox;
113 
114 	public XPathContainsAssertion( TestAssertionConfig assertionConfig, Assertable assertable )
115 	{
116 		super( assertionConfig, assertable, true, true, true, true );
117 
118 		XmlObjectConfigurationReader reader = new XmlObjectConfigurationReader( getConfiguration() );
119 		path = reader.readString( "path", null );
120 		expectedContent = reader.readString( "content", null );
121 		allowWildcards = reader.readBoolean( "allowWildcards", false );
122 		ignoreNamspaceDifferences = reader.readBoolean( "ignoreNamspaceDifferences", false );
123 	}
124 
125 	public String getExpectedContent()
126 	{
127 		return expectedContent;
128 	}
129 
130 	public void setExpectedContent( String expectedContent )
131 	{
132 		this.expectedContent = expectedContent;
133 		setConfiguration( createConfiguration() );
134 	}
135 
136 	/***
137 	 * @deprecated
138 	 */
139 
140 	@Deprecated
141 	public void setContent( String content )
142 	{
143 		setExpectedContent( content );
144 	}
145 
146 	public String getPath()
147 	{
148 		return path;
149 	}
150 
151 	public void setPath( String path )
152 	{
153 		this.path = path;
154 		setConfiguration( createConfiguration() );
155 	}
156 
157 	public boolean isAllowWildcards()
158 	{
159 		return allowWildcards;
160 	}
161 
162 	public void setAllowWildcards( boolean allowWildcards )
163 	{
164 		this.allowWildcards = allowWildcards;
165 	}
166 
167 	public boolean isIgnoreNamspaceDifferences()
168 	{
169 		return ignoreNamspaceDifferences;
170 	}
171 
172 	public void setIgnoreNamspaceDifferences( boolean ignoreNamspaceDifferences )
173 	{
174 		this.ignoreNamspaceDifferences = ignoreNamspaceDifferences;
175 	}
176 
177 	@Override
178 	protected String internalAssertResponse( MessageExchange messageExchange, SubmitContext context )
179 			throws AssertionException
180 	{
181 		if( !messageExchange.hasResponse() )
182 			return "Missing Response";
183 		else
184 			return assertContent( messageExchange.getResponseContentAsXml(), context, "Response" );
185 	}
186 
187 	public String assertContent( String response, SubmitContext context, String type ) throws AssertionException
188 	{
189 		try
190 		{
191 			if( path == null )
192 				return "Missing path for XPath assertion";
193 			if( expectedContent == null )
194 				return "Missing content for XPath assertion";
195 
196 			XmlObject xml = XmlObject.Factory.parse( response );
197 			String expandedPath = PropertyExpander.expandProperties( context, path );
198 			XmlObject[] items = xml.selectPath( expandedPath );
199 			AssertedXPathsContainer assertedXPathsContainer = ( AssertedXPathsContainer )context
200 					.getProperty( AssertedXPathsContainer.ASSERTEDXPATHSCONTAINER_PROPERTY );
201 
202 			XmlObject contentObj = null;
203 			String expandedContent = PropertyExpander.expandProperties( context, expectedContent );
204 
205 			// stupid check for text selection for those situation that the
206 			// selected
207 			// text actually contains xml which should be compared as a string.
208 			if( !expandedPath.endsWith( "text()" ) )
209 			{
210 				try
211 				{
212 					contentObj = XmlObject.Factory.parse( expandedContent );
213 				}
214 				catch( Exception e )
215 				{
216 					// this is ok.. it just means that the content to match is not
217 					// xml
218 					// but
219 					// (hopefully) just a string
220 				}
221 			}
222 
223 			if( items.length == 0 )
224 				throw new Exception( "Missing content for xpath [" + path + "] in " + type );
225 
226 			XmlOptions options = new XmlOptions();
227 			options.setSavePrettyPrint();
228 			options.setSaveOuter();
229 
230 			for( int c = 0; c < items.length; c++ )
231 			{
232 				try
233 				{
234 					AssertedXPathImpl assertedXPathImpl = null;
235 					if( assertedXPathsContainer != null )
236 					{
237 						String xpath = XmlUtils.createAbsoluteXPath( items[c].getDomNode() );
238 						if( xpath != null )
239 						{
240 							XmlObject xmlObj = items[c]; // XmlObject.Factory.parse(
241 																	// items[c].xmlText( options
242 																	// ) );
243 
244 							assertedXPathImpl = new AssertedXPathImpl( this, xpath, xmlObj );
245 							assertedXPathsContainer.addAssertedXPath( assertedXPathImpl );
246 						}
247 					}
248 
249 					if( contentObj == null )
250 					{
251 						if( items[c] instanceof XmlAnySimpleType && !( items[c] instanceof XmlQName ) )
252 						{
253 							String value = ( ( XmlAnySimpleType )items[c] ).getStringValue();
254 							String expandedValue = PropertyExpander.expandProperties( context, value );
255 							XMLAssert.assertEquals( expandedContent, expandedValue );
256 						}
257 						else
258 						{
259 							Node domNode = items[c].getDomNode();
260 							if( domNode.getNodeType() == Node.ELEMENT_NODE )
261 							{
262 								String expandedValue = PropertyExpander.expandProperties( context, XmlUtils
263 										.getElementText( ( Element )domNode ) );
264 								XMLAssert.assertEquals( expandedContent, expandedValue );
265 							}
266 							else
267 							{
268 								String expandedValue = PropertyExpander.expandProperties( context, domNode.getNodeValue() );
269 								XMLAssert.assertEquals( expandedContent, expandedValue );
270 							}
271 						}
272 					}
273 					else
274 					{
275 						compareValues( contentObj.xmlText( options ), items[c].xmlText( options ), items[c] );
276 					}
277 
278 					break;
279 				}
280 				catch( Throwable e )
281 				{
282 					if( c == items.length - 1 )
283 						throw e;
284 				}
285 			}
286 		}
287 		catch( Throwable e )
288 		{
289 			String msg = "";
290 
291 			if( e instanceof ComparisonFailure )
292 			{
293 				ComparisonFailure cf = ( ComparisonFailure )e;
294 				String expected = cf.getExpected();
295 				String actual = cf.getActual();
296 
297 				// if( expected.length() > ERROR_LENGTH_LIMIT )
298 				// expected = expected.substring(0, ERROR_LENGTH_LIMIT) + "..";
299 				//				
300 				// if( actual.length() > ERROR_LENGTH_LIMIT )
301 				// actual = actual.substring(0, ERROR_LENGTH_LIMIT) + "..";
302 
303 				msg = "XPathContains comparison failed, expecting [" + expected + "], actual was [" + actual + "]";
304 			}
305 			else
306 			{
307 				msg = "XPathContains assertion failed for path [" + path + "] : " + e.getClass().getSimpleName() + ":"
308 						+ e.getMessage();
309 			}
310 
311 			throw new AssertionException( new AssertionError( msg ) );
312 		}
313 
314 		return type + " matches content for [" + path + "]";
315 	}
316 
317 	private void compareValues( String expandedContent, String expandedValue, XmlObject object ) throws Exception
318 	{
319 		Diff diff = new Diff( expandedContent, expandedValue );
320 		InternalDifferenceListener internalDifferenceListener = new InternalDifferenceListener();
321 		diff.overrideDifferenceListener( internalDifferenceListener );
322 
323 		if( !diff.identical() )
324 			throw new Exception( diff.toString() );
325 
326 		StringList nodesToRemove = internalDifferenceListener.getNodesToRemove();
327 
328 		if( !nodesToRemove.isEmpty() )
329 		{
330 			for( String node : nodesToRemove )
331 			{
332 				if( node == null )
333 					continue;
334 
335 				int ix = node.indexOf( "\n/" );
336 				if( ix != -1 )
337 					node = node.substring( 0, ix + 1 ) + "/" + node.substring( ix + 1 );
338 				else if( node.startsWith( "/" ) )
339 					node = "/" + node;
340 
341 				XmlObject[] paths = object.selectPath( node );
342 				if( paths.length > 0 )
343 				{
344 					Node domNode = paths[0].getDomNode();
345 					if( domNode.getNodeType() == Node.ATTRIBUTE_NODE )
346 						( ( Attr )domNode ).getOwnerElement().removeAttributeNode( ( Attr )domNode );
347 					else
348 						domNode.getParentNode().removeChild( domNode );
349 
350 					object.set( object.copy() );
351 				}
352 			}
353 		}
354 	}
355 
356 	@Override
357 	public boolean configure()
358 	{
359 		if( configurationDialog == null )
360 			buildConfigurationDialog();
361 
362 		pathArea.setText( path );
363 		contentArea.setText( expectedContent );
364 		allowWildcardsCheckBox.setSelected( allowWildcards );
365 
366 		UISupport.showDialog( configurationDialog );
367 		return configureResult;
368 	}
369 
370 	protected void buildConfigurationDialog()
371 	{
372 		configurationDialog = new JDialog( UISupport.getMainFrame() );
373 		configurationDialog.setTitle( "XPath Match configuration" );
374 		configurationDialog.addWindowListener( new WindowAdapter()
375 		{
376 			@Override
377 			public void windowOpened( WindowEvent event )
378 			{
379 				SwingUtilities.invokeLater( new Runnable()
380 				{
381 					public void run()
382 					{
383 						pathArea.requestFocusInWindow();
384 					}
385 				} );
386 			}
387 		} );
388 
389 		JPanel contentPanel = new JPanel( new BorderLayout() );
390 		contentPanel.add( UISupport.buildDescription( "Specify xpath expression and expected result",
391 				"declare namespaces with <code>declare namespace &lt;prefix&gt;='&lt;namespace&gt;';</code>", null ),
392 				BorderLayout.NORTH );
393 
394 		JSplitPane splitPane = UISupport.createVerticalSplit();
395 
396 		pathArea = new JUndoableTextArea();
397 		pathArea.setToolTipText( "Specifies the XPath expression to select from the message for validation" );
398 
399 		JPanel pathPanel = new JPanel( new BorderLayout() );
400 		JXToolBar pathToolbar = UISupport.createToolbar();
401 		addPathEditorActions( pathToolbar );
402 
403 		pathPanel.add( pathToolbar, BorderLayout.NORTH );
404 		pathPanel.add( new JScrollPane( pathArea ), BorderLayout.CENTER );
405 
406 		splitPane.setTopComponent( UISupport.addTitledBorder( pathPanel, "XPath Expression" ) );
407 
408 		contentArea = new JUndoableTextArea();
409 		contentArea.setToolTipText( "Specifies the expected result of the XPath expression" );
410 
411 		JPanel matchPanel = new JPanel( new BorderLayout() );
412 		JXToolBar contentToolbar = UISupport.createToolbar();
413 		addMatchEditorActions( contentToolbar );
414 
415 		matchPanel.add( contentToolbar, BorderLayout.NORTH );
416 		matchPanel.add( new JScrollPane( contentArea ), BorderLayout.CENTER );
417 
418 		splitPane.setBottomComponent( UISupport.addTitledBorder( matchPanel, "Expected Result" ) );
419 		splitPane.setDividerLocation( 150 );
420 		splitPane.setBorder( BorderFactory.createEmptyBorder( 0, 1, 0, 1 ) );
421 
422 		contentPanel.add( splitPane, BorderLayout.CENTER );
423 
424 		ButtonBarBuilder builder = new ButtonBarBuilder();
425 
426 		ShowOnlineHelpAction showOnlineHelpAction = new ShowOnlineHelpAction( HelpUrls.XPATHASSERTIONEDITOR_HELP_URL );
427 		builder.addFixed( UISupport.createToolbarButton( showOnlineHelpAction ) );
428 		builder.addGlue();
429 
430 		JButton okButton = new JButton( new OkAction() );
431 		builder.addFixed( okButton );
432 		builder.addRelatedGap();
433 		builder.addFixed( new JButton( new CancelAction() ) );
434 
435 		builder.setBorder( BorderFactory.createEmptyBorder( 1, 5, 5, 5 ) );
436 
437 		contentPanel.add( builder.getPanel(), BorderLayout.SOUTH );
438 
439 		configurationDialog.setContentPane( contentPanel );
440 		configurationDialog.setSize( 600, 500 );
441 		configurationDialog.setModal( true );
442 		UISupport.initDialogActions( configurationDialog, showOnlineHelpAction, okButton );
443 	}
444 
445 	protected void addPathEditorActions( JXToolBar toolbar )
446 	{
447 		toolbar.addFixed( new JButton( new DeclareNamespacesFromCurrentAction() ) );
448 	}
449 
450 	protected void addMatchEditorActions( JXToolBar toolbar )
451 	{
452 		toolbar.addFixed( new JButton( new SelectFromCurrentAction() ) );
453 		toolbar.addRelatedGap();
454 		toolbar.addFixed( new JButton( new TestPathAction() ) );
455 		allowWildcardsCheckBox = new JCheckBox( "Allow Wildcards" );
456 
457 		Dimension dim = new Dimension( 100, 20 );
458 
459 		allowWildcardsCheckBox.setSize( dim );
460 		allowWildcardsCheckBox.setPreferredSize( dim );
461 
462 		allowWildcardsCheckBox.setOpaque( false );
463 		toolbar.addRelatedGap();
464 		toolbar.addFixed( allowWildcardsCheckBox );
465 
466 		Dimension largerDim = new Dimension( 200, 20 );
467 		ignoreNamspaceDifferencesCheckBox = new JCheckBox( "Ignore namespace prefixes" );
468 		ignoreNamspaceDifferencesCheckBox.setSize( largerDim );
469 		ignoreNamspaceDifferencesCheckBox.setPreferredSize( largerDim );
470 		ignoreNamspaceDifferencesCheckBox.setOpaque( false );
471 		toolbar.addRelatedGap();
472 		toolbar.addFixed( ignoreNamspaceDifferencesCheckBox );
473 	}
474 
475 	public XmlObject createConfiguration()
476 	{
477 		XmlObjectConfigurationBuilder builder = new XmlObjectConfigurationBuilder();
478 		builder.add( "path", path );
479 		builder.add( "content", expectedContent );
480 		builder.add( "allowWildcards", allowWildcards );
481 		builder.add( "ignoreNamspaceDifferences", ignoreNamspaceDifferences );
482 		return builder.finish();
483 	}
484 
485 	public void selectFromCurrent()
486 	{
487 		XmlCursor cursor = null;
488 
489 		try
490 		{
491 			String assertableContent = getAssertable().getAssertableContent();
492 			if( assertableContent == null || assertableContent.trim().length() == 0 )
493 			{
494 				UISupport.showErrorMessage( "Missing content to select from" );
495 				return;
496 			}
497 
498 			XmlObject xml = XmlObject.Factory.parse( assertableContent );
499 
500 			String txt = pathArea == null || !pathArea.isVisible() ? getPath() : pathArea.getSelectedText();
501 			if( txt == null )
502 				txt = pathArea == null ? "" : pathArea.getText();
503 
504 			WsdlTestRunContext context = new WsdlTestRunContext( ( TestStep )getAssertable().getModelItem() );
505 
506 			String expandedPath = PropertyExpander.expandProperties( context, txt.trim() );
507 
508 			if( contentArea != null && contentArea.isVisible() )
509 				contentArea.setText( "" );
510 
511 			cursor = xml.newCursor();
512 			cursor.selectPath( expandedPath );
513 			if( !cursor.toNextSelection() )
514 			{
515 				UISupport.showErrorMessage( "No match in current response" );
516 			}
517 			else if( cursor.hasNextSelection() )
518 			{
519 				UISupport.showErrorMessage( "More than one match in current response" );
520 			}
521 			else
522 			{
523 				String stringValue = XmlUtils.getValueForMatch( cursor );
524 
525 				if( contentArea != null && contentArea.isVisible() )
526 					contentArea.setText( stringValue );
527 				else
528 					setExpectedContent( stringValue );
529 			}
530 		}
531 		catch( Throwable e )
532 		{
533 			UISupport.showErrorMessage( e.toString() );
534 			SoapUI.logError( e );
535 		}
536 		finally
537 		{
538 			if( cursor != null )
539 				cursor.dispose();
540 		}
541 	}
542 
543 	private final class InternalDifferenceListener implements DifferenceListener
544 	{
545 		private StringList nodesToRemove = new StringList();
546 
547 		public int differenceFound( Difference diff )
548 		{
549 			if( allowWildcards
550 					&& ( diff.getId() == DifferenceEngine.TEXT_VALUE.getId() || diff.getId() == DifferenceEngine.ATTR_VALUE
551 							.getId() ) )
552 			{
553 				if( diff.getControlNodeDetail().getValue().equals( "*" ) )
554 				{
555 					Node node = diff.getTestNodeDetail().getNode();
556 					String xp = XmlUtils.createAbsoluteXPath( node.getNodeType() == Node.ATTRIBUTE_NODE ? node : node
557 							.getParentNode() );
558 					nodesToRemove.add( xp );
559 					return Diff.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
560 				}
561 			}
562 			else if( ignoreNamspaceDifferences && diff.getId() == DifferenceEngine.NAMESPACE_PREFIX_ID )
563 			{
564 				return Diff.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
565 			}
566 
567 			return Diff.RETURN_ACCEPT_DIFFERENCE;
568 		}
569 
570 		public void skippedComparison( Node arg0, Node arg1 )
571 		{
572 
573 		}
574 
575 		public StringList getNodesToRemove()
576 		{
577 			return nodesToRemove;
578 		}
579 	}
580 
581 	public class OkAction extends AbstractAction
582 	{
583 		public OkAction()
584 		{
585 			super( "Save" );
586 		}
587 
588 		public void actionPerformed( ActionEvent arg0 )
589 		{
590 			setPath( pathArea.getText().trim() );
591 			setExpectedContent( contentArea.getText() );
592 			setAllowWildcards( allowWildcardsCheckBox.isSelected() );
593 			setIgnoreNamspaceDifferences( ignoreNamspaceDifferencesCheckBox.isSelected() );
594 			setConfiguration( createConfiguration() );
595 			configureResult = true;
596 			configurationDialog.setVisible( false );
597 		}
598 	}
599 
600 	public class CancelAction extends AbstractAction
601 	{
602 		public CancelAction()
603 		{
604 			super( "Cancel" );
605 		}
606 
607 		public void actionPerformed( ActionEvent arg0 )
608 		{
609 			configureResult = false;
610 			configurationDialog.setVisible( false );
611 		}
612 	}
613 
614 	public class DeclareNamespacesFromCurrentAction extends AbstractAction
615 	{
616 		public DeclareNamespacesFromCurrentAction()
617 		{
618 			super( "Declare" );
619 			putValue( Action.SHORT_DESCRIPTION, "Add namespace declaration from current message to XPath expression" );
620 		}
621 
622 		public void actionPerformed( ActionEvent arg0 )
623 		{
624 			try
625 			{
626 				String content = getAssertable().getAssertableContent();
627 				if( content != null && content.trim().length() > 0 )
628 				{
629 					pathArea.setText( XmlUtils.declareXPathNamespaces( content ) + pathArea.getText() );
630 				}
631 				else if( UISupport.confirm( "Declare namespaces from schema instead?", "Missing Response" ) )
632 				{
633 					pathArea.setText( XmlUtils.declareXPathNamespaces( ( WsdlInterface )getAssertable().getInterface() )
634 							+ pathArea.getText() );
635 				}
636 			}
637 			catch( Exception e )
638 			{
639 				log.error( e.getMessage() );
640 			}
641 		}
642 	}
643 
644 	public class TestPathAction extends AbstractAction
645 	{
646 		public TestPathAction()
647 		{
648 			super( "Test" );
649 			putValue( Action.SHORT_DESCRIPTION,
650 					"Tests the XPath expression for the current message against the Expected Content field" );
651 		}
652 
653 		public void actionPerformed( ActionEvent arg0 )
654 		{
655 			String oldPath = getPath();
656 			String oldContent = getExpectedContent();
657 			boolean oldAllowWildcards = isAllowWildcards();
658 
659 			setPath( pathArea.getText().trim() );
660 			setExpectedContent( contentArea.getText() );
661 			setAllowWildcards( allowWildcardsCheckBox.isSelected() );
662 			setIgnoreNamspaceDifferences( ignoreNamspaceDifferencesCheckBox.isSelected() );
663 
664 			try
665 			{
666 				String msg = assertContent( getAssertable().getAssertableContent(), new WsdlTestRunContext(
667 						( TestStep )getAssertable().getModelItem() ), "Response" );
668 				UISupport.showInfoMessage( msg, "Success" );
669 			}
670 			catch( AssertionException e )
671 			{
672 				UISupport.showErrorMessage( e.getMessage() );
673 			}
674 
675 			setPath( oldPath );
676 			setExpectedContent( oldContent );
677 			setAllowWildcards( oldAllowWildcards );
678 		}
679 	}
680 
681 	public class SelectFromCurrentAction extends AbstractAction
682 	{
683 		public SelectFromCurrentAction()
684 		{
685 			super( "Select from current" );
686 			putValue( Action.SHORT_DESCRIPTION,
687 					"Selects the XPath expression from the current message into the Expected Content field" );
688 		}
689 
690 		public void actionPerformed( ActionEvent arg0 )
691 		{
692 			selectFromCurrent();
693 		}
694 	}
695 
696 	@Override
697 	protected String internalAssertRequest( MessageExchange messageExchange, SubmitContext context )
698 			throws AssertionException
699 	{
700 		if( !messageExchange.hasRequest( true ) )
701 			return "Missing Request";
702 		else
703 			return assertContent( messageExchange.getRequestContent(), context, "Request" );
704 	}
705 
706 	public JTextArea getContentArea()
707 	{
708 		return contentArea;
709 	}
710 
711 	public JTextArea getPathArea()
712 	{
713 		return pathArea;
714 	}
715 
716 	@Override
717 	public PropertyExpansion[] getPropertyExpansions()
718 	{
719 		List<PropertyExpansion> result = new ArrayList<PropertyExpansion>();
720 
721 		result.addAll( PropertyExpansionUtils.extractPropertyExpansions( getAssertable().getModelItem(), this,
722 				"expectedContent" ) );
723 		result.addAll( PropertyExpansionUtils.extractPropertyExpansions( getAssertable().getModelItem(), this, "path" ) );
724 
725 		return result.toArray( new PropertyExpansion[result.size()] );
726 	}
727 
728 	public XPathReference[] getXPathReferences()
729 	{
730 		List<XPathReference> result = new ArrayList<XPathReference>();
731 
732 		if( StringUtils.hasContent( getPath() ) )
733 		{
734 			TestModelItem testStep = ( TestModelItem )getAssertable().getModelItem();
735 			TestProperty property = testStep instanceof WsdlTestRequestStep ? testStep.getProperty( "Response" )
736 					: testStep.getProperty( "Request" );
737 			result.add( new XPathReferenceImpl( "XPath for " + getName() + " XPathContainsAssertion in "
738 					+ testStep.getName(), property, this, "path" ) );
739 		}
740 
741 		return result.toArray( new XPathReference[result.size()] );
742 	}
743 
744 	public static class Factory extends AbstractTestAssertionFactory
745 	{
746 		public Factory()
747 		{
748 			super( XPathContainsAssertion.ID, XPathContainsAssertion.LABEL, XPathContainsAssertion.class );
749 		}
750 	}
751 }