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