1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wadl.inference.schema.types;
14
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19
20 import javax.xml.namespace.QName;
21
22 import org.apache.xmlbeans.SchemaTypeLoader;
23 import org.apache.xmlbeans.SchemaTypeSystem;
24 import org.apache.xmlbeans.XmlBeans;
25 import org.apache.xmlbeans.XmlCursor;
26 import org.apache.xmlbeans.XmlException;
27 import org.apache.xmlbeans.XmlObject;
28
29 import com.eviware.soapui.impl.wadl.inference.ConflictHandler;
30 import com.eviware.soapui.impl.wadl.inference.schema.Content;
31 import com.eviware.soapui.impl.wadl.inference.schema.Context;
32 import com.eviware.soapui.impl.wadl.inference.schema.Particle;
33 import com.eviware.soapui.impl.wadl.inference.schema.Schema;
34 import com.eviware.soapui.impl.wadl.inference.schema.Settings;
35 import com.eviware.soapui.impl.wadl.inference.schema.Type;
36 import com.eviware.soapui.impl.wadl.inference.schema.content.EmptyContent;
37 import com.eviware.soapui.inferredSchema.ComplexTypeConfig;
38 import com.eviware.soapui.inferredSchema.ParticleConfig;
39 import com.eviware.soapui.inferredSchema.TypeReferenceConfig;
40
41 /***
42 * ComplexType corresponds to an xs:complexType. It's definition is displayed in
43 * the schema for which it belongs.
44 *
45 * @author Dain Nilsson
46 */
47 public class ComplexType implements Type
48 {
49 private String name;
50 private Schema schema;
51 private Map<QName, Particle> attributes;
52 private Content content;
53 private boolean mixed = false;
54 private boolean completed = false;
55
56 public ComplexType( Schema schema, String name, boolean completed )
57 {
58 this.schema = schema;
59 this.name = name;
60 this.completed = completed;
61 content = new EmptyContent( schema, completed );
62 attributes = new HashMap<QName, Particle>();
63 schema.addType( this );
64 }
65
66 public ComplexType( ComplexTypeConfig xml, Schema schema )
67 {
68 this.schema = schema;
69 name = xml.getName();
70 completed = xml.getCompleted();
71 mixed = xml.getMixed();
72 content = Content.Factory.parse( xml.getContent(), schema );
73 attributes = new HashMap<QName, Particle>();
74 for( ParticleConfig item : xml.getAttributeList() )
75 {
76 Particle p = Particle.Factory.parse( item, schema );
77 attributes.put( new QName( "", p.getName().getLocalPart() ), p );
78 }
79 schema.addType( this );
80 }
81
82 public void save( ComplexTypeConfig xml )
83 {
84 xml.setName( name );
85 xml.setCompleted( completed );
86 xml.setMixed( mixed );
87 List<ParticleConfig> particleList = new ArrayList<ParticleConfig>();
88 for( Particle item : attributes.values() )
89 particleList.add( item.save() );
90 xml.setAttributeArray( particleList.toArray( new ParticleConfig[0] ) );
91 xml.setContent( content.save() );
92 }
93
94 public TypeReferenceConfig save()
95 {
96 TypeReferenceConfig xml = TypeReferenceConfig.Factory.newInstance();
97 xml.setReference( new QName( schema.getNamespace(), name ) );
98 return xml;
99 }
100
101 public void setContent( Content content )
102 {
103 this.content = content;
104 }
105
106 public Type validate( Context context ) throws XmlException
107 {
108 XmlCursor cursor = context.getCursor();
109 List<QName> seen = new ArrayList<QName>();
110 cursor.push();
111 if( !mixed && isMixed( context ) )
112 {
113
114 mixed = true;
115 }
116 cursor.pop();
117 cursor.push();
118 if( cursor.toFirstAttribute() )
119 {
120 do
121 {
122 QName qname = cursor.getName();
123 if( attributes.containsKey( qname ) )
124 {
125 attributes.get( qname ).validate( context );
126 }
127 else if( qname.getNamespaceURI().equals( Settings.xsins ) )
128 {
129
130 }
131 else if( context.getHandler().callback( ConflictHandler.Event.CREATION, ConflictHandler.Type.ATTRIBUTE,
132 new QName( schema.getNamespace(), qname.getLocalPart() ), context.getPath(), "Undeclared attribute." ) )
133 {
134 if( qname.getNamespaceURI().equals( schema.getNamespace() ) || qname.getNamespaceURI().equals( "" ) )
135 {
136 newAttribute( qname ).validate( context );
137 }
138 else
139 {
140 Schema otherSchema = context.getSchemaSystem().getSchemaForNamespace( qname.getNamespaceURI() );
141 schema.putPrefixForNamespace( qname.getPrefix(), qname.getNamespaceURI() );
142 if( otherSchema == null )
143 {
144 otherSchema = context.getSchemaSystem().newSchema( qname.getNamespaceURI() );
145 }
146 Particle ref = otherSchema.getParticle( qname.getLocalPart() );
147 if( ref == null )
148 {
149 ref = otherSchema.newAttribute( qname.getLocalPart() );
150 }
151 if( completed )
152 ref.setAttribute( "use", "optional" );
153 Particle newAttribute = Particle.Factory.newReferenceInstance( schema, ref );
154 attributes.put( qname, newAttribute );
155 newAttribute.validate( context );
156 }
157 }
158 else
159 throw new XmlException( "Illegal attribute!" );
160 seen.add( qname );
161 }
162 while( cursor.toNextAttribute() );
163 }
164
165 for( QName item : attributes.keySet() )
166 {
167 if( !seen.contains( item ) && !attributes.get( item ).getAttribute( "use" ).equals( "optional" ) )
168 {
169 if( context.getHandler().callback( ConflictHandler.Event.MODIFICATION, ConflictHandler.Type.ATTRIBUTE,
170 item, context.getPath(), "Required attribute missing." ) )
171 {
172 attributes.get( item ).setAttribute( "use", "optional" );
173 }
174 else
175 throw new XmlException( "Required attribute missing!" );
176 }
177 }
178 cursor.pop();
179 if( !cursor.toFirstChild() )
180 cursor.toFirstContentToken();
181 if( !context.getAttribute( "nil" ).equals( "true" ) )
182 validateContent( context );
183 completed = true;
184 return this;
185 }
186
187 private void validateContent( Context context ) throws XmlException
188 {
189 context.getCursor().push();
190 context.putAttribute( "typeName", name );
191 Content newContent = content.validate( context );
192 context.clearAttribute( "typeName" );
193 if( content != newContent )
194 {
195 String problem = "Illegal content for complexType '" + name + "'.";
196 if( context.getHandler().callback( ConflictHandler.Event.MODIFICATION, ConflictHandler.Type.TYPE,
197 new QName( schema.getNamespace(), name ), context.getPath(), "Illegal complex content." ) )
198 {
199 content = newContent;
200 context.getCursor().pop();
201 validateContent( context );
202 return;
203 }
204 else
205 throw new XmlException( problem );
206 }
207 context.getCursor().pop();
208 }
209
210 private boolean isMixed( Context context )
211 {
212 QName name = context.getCursor().getName();
213 SchemaTypeSystem sts;
214 try
215 {
216 sts = XmlBeans.compileXsd( new XmlObject[] { XmlObject.Factory
217 .parse( "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"" + name.getNamespaceURI()
218 + "\" targetNamespace=\"" + name.getNamespaceURI() + "\">" + "<xs:element name=\""
219 + name.getLocalPart() + "\"><xs:complexType><xs:sequence>"
220 + "<xs:any processContents=\"skip\" minOccurs=\"0\" maxOccurs=\"unbounded\" /></xs:sequence>"
221 + "<xs:anyAttribute processContents=\"skip\"/></xs:complexType></xs:element></xs:schema>" ) },
222 XmlBeans.getBuiltinTypeSystem(), null );
223 SchemaTypeLoader stl = XmlBeans
224 .typeLoaderUnion( new SchemaTypeLoader[] { sts, XmlBeans.getBuiltinTypeSystem() } );
225 if( !stl.parse( context.getCursor().xmlText(), null, null ).validate() )
226 return true;
227 }
228 catch( XmlException e )
229 {
230
231 e.printStackTrace();
232 }
233 return false;
234 }
235
236 public String getName()
237 {
238 return name;
239 }
240
241 public Schema getSchema()
242 {
243 return schema;
244 }
245
246 public void setSchema( Schema schema )
247 {
248 this.schema = schema;
249 }
250
251 @Override
252 public String toString()
253 {
254 String xsdns = schema.getPrefixForNamespace( Settings.xsdns );
255 StringBuilder s = new StringBuilder( "<" + xsdns + ":complexType name=\"" + name + "\"" );
256 if( mixed )
257 s.append( " mixed=\"true\"" );
258 s.append( ">" );
259 StringBuilder attrs = new StringBuilder();
260 for( Particle item : attributes.values() )
261 {
262 attrs.append( item );
263 }
264 s.append( content.toString( attrs.toString() ) );
265 s.append( "</" + xsdns + ":complexType>" );
266 return s.toString();
267 }
268
269 private Particle newAttribute( QName qname )
270 {
271 Particle p = Particle.Factory.newAttributeInstance( schema, qname.getLocalPart() );
272 attributes.put( qname, p );
273 if( completed )
274 p.setAttribute( "use", "optional" );
275 return p;
276 }
277
278 }