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.wadl.inference.support;
14  
15  import java.util.ArrayList;
16  import java.util.HashMap;
17  import java.util.List;
18  import java.util.Map;
19  
20  import org.apache.xmlbeans.XmlAnySimpleType;
21  import org.apache.xmlbeans.XmlBase64Binary;
22  import org.apache.xmlbeans.XmlBoolean;
23  import org.apache.xmlbeans.XmlDate;
24  import org.apache.xmlbeans.XmlDateTime;
25  import org.apache.xmlbeans.XmlDecimal;
26  import org.apache.xmlbeans.XmlGDay;
27  import org.apache.xmlbeans.XmlGMonth;
28  import org.apache.xmlbeans.XmlGYear;
29  import org.apache.xmlbeans.XmlGYearMonth;
30  import org.apache.xmlbeans.XmlHexBinary;
31  import org.apache.xmlbeans.XmlInteger;
32  import org.apache.xmlbeans.XmlNegativeInteger;
33  import org.apache.xmlbeans.XmlNonNegativeInteger;
34  import org.apache.xmlbeans.XmlNonPositiveInteger;
35  import org.apache.xmlbeans.XmlPositiveInteger;
36  import org.apache.xmlbeans.XmlString;
37  import org.apache.xmlbeans.XmlTime;
38  
39  /***
40   * XML Schema inferred from gathered XML data.
41   * 
42   * @author Dain Nilsson
43   */
44  public class TypeInferrer
45  {
46  	private static TypeInferrer ref;
47  	private TypeTree types;
48  	private Map<XmlAnySimpleType, TypeTree> typeTable;
49  
50  	/***
51  	 * Get the instance of the XmlAnySimpleType with the type xs:<typeName>.
52  	 * 
53  	 * @param typeName
54  	 * @return Returns the XmlAnySimpleType, if available. Otherwise returns
55  	 *         null.
56  	 */
57  	public static XmlAnySimpleType getType( String typeName )
58  	{
59  		for( XmlAnySimpleType item : getRef().typeTable.keySet() )
60  			if( item.schemaType().getName().getLocalPart().equals( typeName ) )
61  				return item;
62  		return null;
63  	}
64  
65  	/***
66  	 * Get the XmlAnySimpleType that describes a simple value that is empty.
67  	 * 
68  	 * @return Returns the xs:string XmlAnySimpleType.
69  	 */
70  	public static XmlAnySimpleType getBlankType()
71  	{
72  		return getRef().types.type;
73  	}
74  
75  	/***
76  	 * Given a value and a type, a new type will be returned that validates
77  	 * values for both the given type, and the new value.
78  	 * 
79  	 * @param value
80  	 *           The new value to expand the type for.
81  	 * @param baseType
82  	 *           The type to be expanded.
83  	 * @return The new expanded type.
84  	 */
85  	public static XmlAnySimpleType expandTypeForValue( String value, XmlAnySimpleType baseType )
86  	{
87  		return getRef().expandTypeForValueInternal( baseType, value );
88  	}
89  
90  	/***
91  	 * Given a simple value, infers the type of the value.
92  	 * 
93  	 * @param value
94  	 *           The value to assign a type to.
95  	 * @return Returns the inferred type for the given value.
96  	 */
97  	public static XmlAnySimpleType inferSimpleType( String value )
98  	{
99  		return getRef().inferSimpleTypeRec( value, getRef().types );
100 	}
101 
102 	/***
103 	 * Validates a string against an XmlAnySimpleType.
104 	 * 
105 	 * @param value
106 	 *           The value to validate.
107 	 * @param type
108 	 *           The XmlAnySimpleType to validate against.
109 	 * @return True if the value validates, false if not.
110 	 */
111 	public static boolean validateSimpleType( String value, XmlAnySimpleType type )
112 	{
113 		try
114 		{
115 			type.setStringValue( value );
116 			return type.validate();
117 		}
118 		catch( Exception e )
119 		{
120 			return false;
121 		}
122 	}
123 
124 	private static TypeInferrer getRef()
125 	{
126 		if( ref == null )
127 			ref = new TypeInferrer();
128 		return ref;
129 	}
130 
131 	private TypeInferrer()
132 	{
133 		typeTable = new HashMap<XmlAnySimpleType, TypeTree>();
134 		TypeTree xmlbool = new TypeTree( XmlBoolean.Factory.newInstance() );
135 		typeTable.put( xmlbool.type, xmlbool );
136 		TypeTree xmlbool2 = new TypeTree( XmlBoolean.Factory.newInstance() );
137 		typeTable.put( xmlbool2.type, xmlbool2 );
138 		TypeTree xmlnegint = new TypeTree( XmlNegativeInteger.Factory.newInstance() );
139 		typeTable.put( xmlnegint.type, xmlnegint );
140 		TypeTree xmlposint = new TypeTree( XmlPositiveInteger.Factory.newInstance() );
141 		typeTable.put( xmlposint.type, xmlposint );
142 		TypeTree xmlnonnegint = new TypeTree( XmlNonNegativeInteger.Factory.newInstance() );
143 		typeTable.put( xmlnonnegint.type, xmlnonnegint );
144 		xmlnonnegint.addChild( xmlposint );
145 		xmlnonnegint.addChild( xmlbool );
146 		TypeTree xmlnonposint = new TypeTree( XmlNonPositiveInteger.Factory.newInstance() );
147 		typeTable.put( xmlnonposint.type, xmlnonposint );
148 		xmlnonposint.addChild( xmlnegint );
149 		TypeTree xmlint = new TypeTree( XmlInteger.Factory.newInstance() );
150 		typeTable.put( xmlint.type, xmlint );
151 		xmlint.addChild( xmlnonnegint );
152 		xmlint.addChild( xmlnonposint );
153 		TypeTree xmldec = new TypeTree( XmlDecimal.Factory.newInstance() );
154 		typeTable.put( xmldec.type, xmldec );
155 		xmldec.addChild( xmlint );
156 		TypeTree xmldate = new TypeTree( XmlDate.Factory.newInstance() );
157 		typeTable.put( xmldate.type, xmldate );
158 		TypeTree xmltime = new TypeTree( XmlTime.Factory.newInstance() );
159 		typeTable.put( xmltime.type, xmltime );
160 		TypeTree xmldatetime = new TypeTree( XmlDateTime.Factory.newInstance() );
161 		typeTable.put( xmldatetime.type, xmldatetime );
162 		TypeTree xmlhexbin = new TypeTree( XmlHexBinary.Factory.newInstance() );
163 		typeTable.put( xmlhexbin.type, xmlhexbin );
164 		TypeTree xmlb64bin = new TypeTree( XmlBase64Binary.Factory.newInstance() );
165 		typeTable.put( xmlb64bin.type, xmlb64bin );
166 		TypeTree xmlgyearmonth = new TypeTree( XmlGYearMonth.Factory.newInstance() );
167 		typeTable.put( xmlgyearmonth.type, xmlgyearmonth );
168 		TypeTree xmlgyear = new TypeTree( XmlGYear.Factory.newInstance() );
169 		typeTable.put( xmlgyear.type, xmlgyear );
170 		TypeTree xmlgmonth = new TypeTree( XmlGMonth.Factory.newInstance() );
171 		typeTable.put( xmlgmonth.type, xmlgmonth );
172 		TypeTree xmlgday = new TypeTree( XmlGDay.Factory.newInstance() );
173 		typeTable.put( xmlgday.type, xmlgday );
174 		TypeTree xmlstring = new TypeTree( XmlString.Factory.newInstance() );
175 		typeTable.put( xmlstring.type, xmlstring );
176 		xmlstring.addChild( xmldec );
177 		xmlstring.addChild( xmldate );
178 		xmlstring.addChild( xmltime );
179 		xmlstring.addChild( xmldatetime );
180 		xmlstring.addChild( xmlbool2 );
181 		xmlstring.addChild( xmlgyearmonth );
182 		xmlstring.addChild( xmlgyear );
183 		xmlstring.addChild( xmlgmonth );
184 		xmlstring.addChild( xmlgday );
185 		xmlstring.addChild( xmlhexbin );
186 		// xmlstring.addChild(xmlb64bin);
187 		types = xmlstring;
188 	}
189 
190 	private XmlAnySimpleType expandTypeForValueInternal( XmlAnySimpleType type, String value )
191 	{
192 		TypeTree p = typeTable.get( type );
193 		while( !validateSimpleType( value, p.type ) )
194 		{
195 			p = p.parent;
196 		}
197 		return p.type;
198 	}
199 
200 	private XmlAnySimpleType inferSimpleTypeRec( String value, TypeTree p )
201 	{
202 		for( TypeTree item : p.children )
203 			if( validateSimpleType( value, item.type ) )
204 				return inferSimpleTypeRec( value, item );
205 		return p.type;
206 	}
207 
208 	private class TypeTree
209 	{
210 		public XmlAnySimpleType type;
211 		public TypeTree parent;
212 		public List<TypeTree> children;
213 
214 		public TypeTree( XmlAnySimpleType type )
215 		{
216 			this.type = type;
217 			children = new ArrayList<TypeTree>();
218 		}
219 
220 		public void addChild( TypeTree type )
221 		{
222 			children.add( type );
223 			type.parent = this;
224 		}
225 	}
226 
227 }