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.support.xsd;
14  
15  import java.io.File;
16  import java.io.IOException;
17  import java.net.MalformedURLException;
18  import java.net.URL;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.StringTokenizer;
27  
28  import javax.xml.namespace.QName;
29  
30  import org.apache.log4j.Logger;
31  import org.apache.xmlbeans.SchemaAnnotation;
32  import org.apache.xmlbeans.SchemaLocalElement;
33  import org.apache.xmlbeans.SchemaParticle;
34  import org.apache.xmlbeans.SchemaType;
35  import org.apache.xmlbeans.SchemaTypeLoader;
36  import org.apache.xmlbeans.SchemaTypeSystem;
37  import org.apache.xmlbeans.SimpleValue;
38  import org.apache.xmlbeans.XmlAnySimpleType;
39  import org.apache.xmlbeans.XmlBase64Binary;
40  import org.apache.xmlbeans.XmlBeans;
41  import org.apache.xmlbeans.XmlCursor;
42  import org.apache.xmlbeans.XmlException;
43  import org.apache.xmlbeans.XmlHexBinary;
44  import org.apache.xmlbeans.XmlObject;
45  import org.apache.xmlbeans.XmlOptions;
46  import org.w3c.dom.Document;
47  import org.w3c.dom.Element;
48  import org.w3c.dom.NamedNodeMap;
49  import org.w3c.dom.Node;
50  
51  import com.eviware.soapui.SoapUI;
52  import com.eviware.soapui.impl.wsdl.support.Constants;
53  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
54  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlLoader;
55  import com.eviware.soapui.model.settings.SettingsListener;
56  import com.eviware.soapui.settings.WsdlSettings;
57  import com.eviware.soapui.support.StringUtils;
58  import com.eviware.soapui.support.Tools;
59  
60  /***
61   * XML-Schema related tools
62   * 
63   * @author Ole.Matzura
64   */
65  
66  public class SchemaUtils
67  {
68     private final static Logger log = Logger.getLogger( SchemaUtils.class );
69  	private static Map<String,XmlObject> defaultSchemas = new HashMap<String,XmlObject>();
70  	
71     static
72     {
73     	initDefaultSchemas();
74     	
75     	SoapUI.getSettings().addSettingsListener( new SettingsListener() {
76  
77  			public void settingChanged( String name, String newValue, String oldValue )
78  			{
79  				if( name.equals( WsdlSettings.SCHEMA_DIRECTORY ))
80  				{
81  					log.info( "Reloading default schemas.." );
82  					initDefaultSchemas();
83  				}
84  			}});
85     }
86  
87  	public static void initDefaultSchemas()
88  	{
89  		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
90     	Thread.currentThread().setContextClassLoader( SoapUI.class.getClassLoader() );
91     	
92     	try
93  		{
94     		defaultSchemas.clear();
95     		loadDefaultSchema( SoapUI.class.getResource("/xop.xsd") );
96     		loadDefaultSchema( SoapUI.class.getResource("/XMLSchema.xsd") );
97     		loadDefaultSchema( SoapUI.class.getResource("/xml.xsd") );
98     		loadDefaultSchema( SoapUI.class.getResource("/swaref.xsd") );
99     		loadDefaultSchema( SoapUI.class.getResource("/xmime200505.xsd") );
100    		loadDefaultSchema( SoapUI.class.getResource("/xmime200411.xsd") );
101    		loadDefaultSchema( SoapUI.class.getResource("/soapEnvelope.xsd") );
102    		loadDefaultSchema( SoapUI.class.getResource("/soapEncoding.xsd") );
103    		loadDefaultSchema( SoapUI.class.getResource("/soapEnvelope12.xsd") );
104    		loadDefaultSchema( SoapUI.class.getResource("/soapEncoding12.xsd") );
105    		
106    		String schemaDirectory = SoapUI.getSettings().getString( WsdlSettings.SCHEMA_DIRECTORY, null );
107    		if( StringUtils.hasContent( schemaDirectory ) )
108    			loadSchemaDirectory( schemaDirectory );
109 		}
110 		catch (Exception e)
111 		{
112 			SoapUI.logError( e );
113 		}
114 		finally
115    	{
116    		Thread.currentThread().setContextClassLoader( contextClassLoader );
117    	}
118 	}
119 
120 	private static void loadSchemaDirectory( String schemaDirectory ) throws IOException, MalformedURLException
121 	{
122 		File dir = new File( schemaDirectory );
123 		if( dir.exists() && dir.isDirectory() )
124 		{
125 			String[] xsdFiles = dir.list();
126 			int cnt = 0;
127 			
128 			if( xsdFiles != null && xsdFiles.length > 0 )
129 			{
130 				for( int c = 0; c < xsdFiles.length; c++ )
131 				{
132 					try
133 					{
134 						String xsdFile = xsdFiles[c];
135 						if( xsdFile.endsWith( ".xsd" ))
136 						{
137 							String filename = schemaDirectory + File.separator + xsdFile;
138 							loadDefaultSchema( new URL( "file:" + filename ) );
139 							cnt++;
140 						}
141 					}
142 					catch( Exception e )
143 					{
144 						SoapUI.logError( e );
145 					}
146 				}
147 			}
148 			
149 			if( cnt == 0 )
150 				log.warn( "Missing schema files in  schemaDirectory [" + schemaDirectory + "]" );
151 		}
152 		else log.warn( "Failed to open schemaDirectory [" + schemaDirectory + "]" );
153 	}
154    
155    private static void loadDefaultSchema( URL url ) throws XmlException, IOException
156    {
157    	XmlObject xmlObject = XmlObject.Factory.parse( url );
158 		String targetNamespace = getTargetNamespace( xmlObject );
159 		
160 		if( defaultSchemas.containsKey( targetNamespace  ))
161 			log.warn( "Overriding schema for targetNamespace " + targetNamespace );
162 		
163 		defaultSchemas.put(  targetNamespace, xmlObject );
164 		
165 		log.info( "Added default schema from " + url.getPath() + " with targetNamespace " + targetNamespace );
166    }
167    
168    public static SchemaTypeLoader loadSchemaTypes(String wsdlUrl, SoapVersion soapVersion, WsdlLoader loader ) throws SchemaException
169    {
170    	ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
171    	Thread.currentThread().setContextClassLoader( SoapUI.class.getClassLoader() );
172    	
173    	try
174 		{
175 			log.info("Loading schema types from [" + wsdlUrl + "]");
176 			ArrayList<XmlObject> schemas = new ArrayList<XmlObject>(getSchemas(
177 					wsdlUrl, loader).values());
178 
179 			return buildSchemaTypes(schemas, soapVersion);
180 		}
181 		catch (Exception e)
182 		{
183 			throw new SchemaException( "Error loading schema types", e );
184 		}
185 		finally
186    	{
187    		Thread.currentThread().setContextClassLoader( contextClassLoader );
188    	}
189    }
190 
191    public static SchemaTypeLoader buildSchemaTypes(List<XmlObject> schemas, SoapVersion soapVersion) throws SchemaException
192    {
193       XmlOptions options = new XmlOptions();
194       options.setCompileNoValidation();
195       options.setCompileNoPvrRule();
196       options.setCompileDownloadUrls();
197       options.setCompileNoUpaRule();
198       options.setValidateTreatLaxAsSkip();
199       
200       for( int c = 0; c < schemas.size(); c++ )
201       {
202       	XmlObject xmlObject = schemas.get(c);
203       	if( xmlObject == null || !((Document)xmlObject.getDomNode()).getDocumentElement().getNamespaceURI().equals( Constants.XSD_NS ))
204       	{
205       		schemas.remove( c );
206       		c--;
207       	}
208       }
209       
210       if( !SoapUI.getSettings().getBoolean( WsdlSettings.STRICT_SCHEMA_TYPES ))
211       {
212          Set<String> mdefNamespaces = new HashSet<String>();
213 
214          for (XmlObject xObj: schemas) 
215          {
216          	mdefNamespaces.add( getTargetNamespace( xObj ) );
217          }  
218          
219          options.setCompileMdefNamespaces(mdefNamespaces);
220       }
221       
222       ArrayList errorList = new ArrayList();
223       options.setErrorListener( errorList );
224 
225       XmlCursor cursor = null;
226       
227       try
228 		{
229       	// remove imports
230 			for( int c = 0; c < schemas.size(); c++ )
231 			{
232 				XmlObject s = schemas.get( c );
233 				
234 				Map map = new HashMap();
235 				cursor = s.newCursor();
236 				cursor.toStartDoc(); 
237 				if( toNextContainer(cursor))
238 					cursor.getAllNamespaces( map );
239 				else
240 					log.warn( "Can not get namespaces for " + s );
241 				
242 				String tns = getTargetNamespace(s);
243 				
244 			//	log.info( "schema for [" + tns + "] contained [" + map.toString() + "] namespaces" );
245 
246 				
247 				if( defaultSchemas.containsKey( tns ))
248 				{
249 					schemas.remove( c );
250 					c--;
251 				}
252 				else
253 				{
254 					removeImports(s);
255 				}
256 				
257 				cursor.dispose();
258 				cursor = null;
259 			}
260 
261 //      	schemas.add( soapVersion.getSoapEncodingSchema());
262 //      	schemas.add( soapVersion.getSoapEnvelopeSchema());
263       	schemas.addAll( defaultSchemas.values() );
264       	
265       	SchemaTypeSystem sts = XmlBeans.compileXsd(
266       			schemas.toArray(new XmlObject[schemas.size()]), XmlBeans.getBuiltinTypeSystem(), options);
267       	return XmlBeans.typeLoaderUnion(new SchemaTypeLoader[] { sts, XmlBeans.getBuiltinTypeSystem() });
268 		}
269 		catch (Exception e)
270 		{
271 			SoapUI.logError( e );
272 			throw new SchemaException( e, errorList );
273 		}
274 		finally
275 		{
276 			for( int c = 0; c < errorList.size(); c++ )
277 			{
278 				log.warn( "Error: " + errorList.get( c ));
279 			}
280 			
281 			if( cursor != null )
282 				cursor.dispose();
283 		}
284    }
285 
286 	public static boolean toNextContainer(XmlCursor cursor)
287 	{
288 		while( !cursor.isContainer() && !cursor.isEnddoc() )
289 			cursor.toNextToken();
290 		
291 		return cursor.isContainer();
292 	}
293 
294 	public static String getTargetNamespace(XmlObject s)
295 	{
296 		return ((Document)s.getDomNode()).getDocumentElement().getAttribute( "targetNamespace" );
297 	}
298    
299    public static Map<String,XmlObject> getSchemas( String wsdlUrl, WsdlLoader loader ) throws SchemaException
300    {
301    	Map<String,XmlObject> result = new HashMap<String,XmlObject>();
302    	getSchemas( wsdlUrl, result, loader, null, false );
303    	return result;
304    }
305    
306    /***
307     * Returns a map mapping urls to corresponding XmlSchema XmlObjects for the specified wsdlUrl
308     */
309    
310    public static void getSchemas( String wsdlUrl, Map<String,XmlObject> existing,  WsdlLoader loader, String tns, boolean add ) throws SchemaException
311    {
312    	if( existing.containsKey( wsdlUrl ))
313    		return; 
314    
315    	if( add )
316    		existing.put( wsdlUrl, null );
317    	
318    	log.info( "Getting schema " + wsdlUrl );
319    	
320    	ArrayList errorList = new ArrayList();
321    	
322    	Map<String,XmlObject> result = new HashMap<String,XmlObject>();
323    	
324    	boolean common = false;
325    	
326       try
327 		{
328 			XmlOptions options = new XmlOptions();
329 			options.setCompileNoValidation();
330 			options.setSaveUseOpenFrag();
331 			options.setErrorListener( errorList );
332 			options.setSaveSyntheticDocumentElement(new QName( Constants.XSD_NS, "schema"));
333 
334 			XmlObject xmlObject = loader.loadXmlObject(wsdlUrl, options);
335 
336 			Document dom = (Document) xmlObject.getDomNode();
337 			Node domNode = dom.getDocumentElement();
338 			if (domNode.getLocalName().equals("schema")
339 					&& domNode.getNamespaceURI().equals(
340 							Constants.XSD_NS))
341 			{
342 				// set targetNamespace (this happens if we are following an include statement)
343 				if( tns != null )
344 				{
345 					Element elm = ((Element)domNode);
346 					if( !elm.hasAttribute( "targetNamespace" ))
347 					{
348 						common = true;
349 						elm.setAttribute( "targetNamespace", tns );
350 					}
351 					
352 					// check for namespace prefix for targetNamespace
353 					NamedNodeMap attributes = elm.getAttributes();
354 					int c = 0;
355 					for( ; c < attributes.getLength(); c++ )
356 					{
357 						Node item = attributes.item( c );
358 						if( item.getNodeValue().equals( tns ) && item.getNodeName().startsWith( "xmlns" ))
359 							break;
360 					}
361 					
362 					if( c == attributes.getLength() )
363 						elm.setAttribute( "xmlns", tns );
364 				}
365 				
366 				if( common )
367 					result.put(wsdlUrl +"@" + tns, xmlObject);
368 				else
369 					result.put(wsdlUrl , xmlObject);
370 			}
371 			else
372 			{
373 				XmlObject[] schemas = xmlObject
374 						.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:schema");
375 
376 				for (int i = 0; i < schemas.length; i++)
377 				{
378 					XmlCursor xmlCursor = schemas[i].newCursor();
379 					String xmlText = xmlCursor.getObject().xmlText(options);
380 					schemas[i] = XmlObject.Factory.parse(xmlText, options);
381 					schemas[i].documentProperties().setSourceName(wsdlUrl);
382 					
383 					result.put(wsdlUrl + "@" + (i+1), schemas[i]);
384 				}
385 
386 				XmlObject[] wsdlImports = xmlObject
387 						.selectPath("declare namespace s='" + Constants.WSDL11_NS + "' .//s:import/@location");
388 				for (int i = 0; i < wsdlImports.length; i++)
389 				{
390 					String location = ((SimpleValue) wsdlImports[i]).getStringValue();
391 					if (location != null)
392 					{
393 						if ( !location.startsWith("file:") && location.indexOf("://") == -1 )
394 							location = Tools.joinRelativeUrl(wsdlUrl, location);
395 
396 						getSchemas(location, existing, loader, null, true );
397 					}
398 				}
399 			}
400 
401 			existing.putAll( result );
402 			
403 			XmlObject[] schemas = result.values().toArray(
404 					new XmlObject[result.size()]);
405 
406 			for (int c = 0; c < schemas.length; c++)
407 			{
408 				xmlObject = schemas[c];
409 
410 				XmlObject[] schemaImports = xmlObject
411 						.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:import/@schemaLocation");
412 				for (int i = 0; i < schemaImports.length; i++)
413 				{
414 					String location = ((SimpleValue) schemaImports[i])
415 							.getStringValue();
416 					if (location != null)
417 					{
418 						if ( !location.startsWith("file:") && location.indexOf("://") == -1 )
419 							location = Tools.joinRelativeUrl(wsdlUrl, location);
420 
421 						getSchemas(location, existing, loader, null, false );
422 					}
423 				}
424 
425 				XmlObject[] schemaIncludes = xmlObject
426 						.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:include/@schemaLocation");
427 				for (int i = 0; i < schemaIncludes.length; i++)
428 				{
429 					String location = ((SimpleValue) schemaIncludes[i])
430 							.getStringValue();
431 					if (location != null)
432 					{
433 						String targetNS = getTargetNamespace( xmlObject );
434 						
435 						if ( !location.startsWith("file:") && location.indexOf("://") == -1 )
436 							location = Tools.joinRelativeUrl(wsdlUrl, location);
437 
438 						getSchemas(location, existing, loader, targetNS, false );
439 					}
440 				}
441 			}
442 		}
443 		catch (Exception e)
444 		{
445 			SoapUI.logError( e );
446 			throw new SchemaException( e, errorList );
447 		}
448    }
449    
450    /***
451     * Returns a map mapping urls to corresponding XmlObjects for the specified wsdlUrl
452     */
453    
454    public static Map<String,XmlObject> getDefinitionParts( WsdlLoader loader ) throws Exception
455    {
456    	HashMap<String, XmlObject> result = new HashMap<String,XmlObject>();
457 		getDefinitionParts( loader.getBaseURI(), result, loader );
458 		return result;
459    }
460    
461    public static void getDefinitionParts( String wsdlUrl, Map<String,XmlObject> existing, WsdlLoader loader ) throws Exception
462    {
463    	if( existing.containsKey( wsdlUrl ))
464    		return;
465    	
466       XmlObject xmlObject = loader.loadXmlObject( wsdlUrl, null );
467       existing.put( wsdlUrl, xmlObject );
468 
469     	XmlObject [] wsdlImports = xmlObject.selectPath("declare namespace s='" + Constants.WSDL11_NS + "' .//s:import");
470       for (int i = 0; i < wsdlImports.length; i++)
471       {
472          String location = wsdlImports[i].getDomNode().getAttributes().getNamedItem( "location" ).getNodeValue();
473          if( location != null )
474          {
475          	if ( !location.startsWith("file:") && location.indexOf("://") == -1 )
476 					location = Tools.joinRelativeUrl(wsdlUrl, location);
477          	
478            	getDefinitionParts( location, existing, loader );
479          }
480       }
481       
482       XmlObject[] schemaImports = xmlObject.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:import/@schemaLocation");
483       for (int i = 0; i < schemaImports.length; i++)
484       {
485          String location = ((SimpleValue)schemaImports[i]).getStringValue();
486          if( location != null )
487          {
488          	if ( !location.startsWith("file:") && location.indexOf("://") == -1 )
489 					location = Tools.joinRelativeUrl(wsdlUrl, location);
490 
491          	getDefinitionParts( location, existing, loader );
492          }
493       }
494       
495       XmlObject[] schemaIncludes = xmlObject.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:include/@schemaLocation");
496       for (int i = 0; i < schemaIncludes.length; i++)
497       {
498          String location = ((SimpleValue)schemaIncludes[i]).getStringValue();
499          if( location != null  )
500          {
501          	if ( !location.startsWith("file:") && location.indexOf("://") == -1 )
502 					location = Tools.joinRelativeUrl(wsdlUrl, location);
503 
504          	getDefinitionParts( location, existing, loader );
505          }
506       }
507    }
508    
509    /***
510     * Extracts namespaces - used in tool integrations for mapping..
511     */
512 
513 	public static Collection<String> extractNamespaces( SchemaTypeSystem schemaTypes, boolean removeDefault )
514 	{
515 		Set<String> namespaces = new HashSet<String>();
516 		SchemaType[] globalTypes = schemaTypes.globalTypes();
517 		for( int c = 0; c < globalTypes.length; c++ )
518 		{
519 			namespaces.add( globalTypes[c].getName().getNamespaceURI() );
520 		}
521 		
522 		if( removeDefault )
523 		{
524 			namespaces.removeAll( defaultSchemas.keySet() );
525 			namespaces.remove( Constants.SOAP11_ENVELOPE_NS );
526 			namespaces.remove( Constants.SOAP_ENCODING_NS );
527 		}
528 		
529 		return namespaces;
530 	}
531    
532    /***
533     * Used when creating a TypeSystem from a complete collection of SchemaDocuments so that referenced 
534     * types are not downloaded (again)
535     */
536    
537 	public static void removeImports(XmlObject xmlObject) throws XmlException
538 	{
539 		 XmlObject[] imports = xmlObject
540        	.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:import");
541 		 
542 		 for( int c = 0; c < imports.length; c++ )
543 		 {
544 			 XmlCursor cursor = imports[c].newCursor();
545 			 cursor.removeXml();
546 			 cursor.dispose();
547 		 }
548 		 
549 		 XmlObject[] includes = xmlObject
550      		.selectPath("declare namespace s='" + Constants.XSD_NS + "' .//s:include");
551 		 
552 		 for( int c = 0; c < includes.length; c++ )
553 		 {
554 			 XmlCursor cursor = includes[c].newCursor();
555 			 cursor.removeXml();
556 			 cursor.dispose();
557 		 }
558 	}
559 
560 	public static boolean isInstanceOf( SchemaType schemaType, SchemaType baseType )
561 	{
562 		if( schemaType == null )
563 			return false;
564 		return schemaType.equals(baseType) ? true : isInstanceOf( schemaType.getBaseType(), baseType );
565 	}
566 
567 	public static boolean isBinaryType(SchemaType schemaType)
568 	{
569 		return isInstanceOf( schemaType, XmlHexBinary.type ) ||
570 				isInstanceOf( schemaType, XmlBase64Binary.type );
571 	}
572 
573 	public static String getDocumentation( SchemaParticle particle, SchemaType schemaType )
574 	{
575 		String result = null;
576 		String xsPrefix = null;
577 		
578 		if( particle instanceof SchemaLocalElement )
579 		{
580 			SchemaAnnotation annotation = ((SchemaLocalElement)particle).getAnnotation();
581 			if( annotation != null )
582 			{
583 				XmlObject[] userInformation = annotation.getUserInformation();
584 				if( userInformation != null && userInformation.length > 0 )
585 				{
586 					XmlObject xmlObject = userInformation[0];
587 					XmlCursor cursor = xmlObject.newCursor();
588 					xsPrefix = cursor.prefixForNamespace( "http://www.w3.org/2001/XMLSchema" );
589 					cursor.dispose();
590 					
591 					result = xmlObject.xmlText(); // XmlUtils.getElementText( ( Element ) userInformation[0].getDomNode());
592 				}
593 			}
594 		}
595 		
596 		if( result == null && schemaType != null && schemaType.getAnnotation() != null )
597 		{
598 			XmlObject[] userInformation = schemaType.getAnnotation().getUserInformation();
599 			if( userInformation != null && userInformation.length > 0 )
600 			{
601 				XmlObject xmlObject = userInformation[0];
602 				XmlCursor cursor = xmlObject.newCursor();
603 				xsPrefix = cursor.prefixForNamespace( "http://www.w3.org/2001/XMLSchema" );
604 				cursor.dispose();
605 				result = xmlObject.xmlText(); // = XmlUtils.getElementText( ( Element ) userInformation[0].getDomNode());
606 			}
607 		}
608 		
609 		if( result != null )
610 		{
611 			result = result.trim();
612 			if( result.startsWith( "<" ) && result.endsWith( ">" ))
613 			{
614 				int ix = result.indexOf( '>' );
615 				if( ix > 0 )
616 				{
617 					result = result.substring( ix+1);
618 				}
619 				
620 				ix = result.lastIndexOf( '<' );
621 				if( ix >= 0 )
622 				{
623 					result = result.substring( 0, ix );
624 				}
625 			}
626 			
627 			if( xsPrefix == null || xsPrefix.length() == 0 )
628 				xsPrefix = "xs:";
629 			else 
630 				xsPrefix += ":";
631 			
632 			//result = result.trim().replaceAll( "<" + xsPrefix + "br/>", "\n" ).trim();
633 			result = result.trim().replaceAll( xsPrefix, "" ).trim();
634 			
635 			result = toHtml( result );
636 		}
637 		
638 		return result;
639 	}
640 
641 	public static String toHtml( String string )
642 	{
643 		StringTokenizer st = new StringTokenizer( string, "\r\n" );
644 		StringBuffer buf = new StringBuffer( "<html><body>" );
645 		boolean added = false;
646 		
647 		while( st.hasMoreElements() )
648 		{
649 			String str = st.nextToken().trim();
650 			if( str.length() > 0 )
651 			{
652 				if( str.equals( "<br/>" ))
653 				{
654 					buf.append( "<br>" );
655 					added = false;
656 					continue;
657 				}
658 					
659 				if( added )
660 					buf.append( "<br>" );
661 				
662 				buf.append( str );
663 				added = true;
664 			}
665 		}
666 		buf.append( "</body></html>" );
667 		string = buf.toString();
668 		return string;
669 	}
670 
671 	public static String [] getEnumerationValues( SchemaType schemaType, boolean addNull )
672 	{
673 		if( schemaType != null )
674 		{
675 			XmlAnySimpleType[] enumerationValues = schemaType.getEnumerationValues();
676 			if( enumerationValues != null && enumerationValues.length > 0 )
677 			{
678 				if( addNull )
679 				{
680 					String [] values = new String[enumerationValues.length+1];
681 					values[0] = null;
682 					
683 					for( int c = 1; c < values.length; c++ )
684 						values[c] = enumerationValues[c-1].getStringValue();
685 					
686 					return values;
687 				}
688 				else
689 				{
690 					String [] values = new String[enumerationValues.length];
691 					
692 					for( int c = 0; c < values.length; c++ )
693 						values[c] = enumerationValues[c].getStringValue();
694 					
695 					return values;
696 				}
697 			}
698 		}
699 		
700 		return new String[0];
701 	}
702 
703 	public static Collection<? extends QName> getExcludedTypes()
704 	{
705 	   String excluded = SoapUI.getSettings().getString( WsdlSettings.EXCLUDED_TYPES, null );
706 	   return SettingUtils.string2QNames(excluded);
707 	}
708 }