View Javadoc

1   // $Id: XsModelWalker.java 165 2009-05-28 21:46:38Z agony $
2   /*
3    * xsAnalyzer - XML schema analyzing tool. Copyright (C) 2008 Michael Engelhardt
4    * 
5    * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
6    * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
7    * version.
8    * 
9    * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
10   * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11   * 
12   * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
13   * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
14   */
15  /**
16   * 
17   */
18  package de.mindcrimeilab.xsanalyzer;
19  
20  import java.beans.PropertyChangeListener;
21  import java.beans.PropertyChangeSupport;
22  import java.util.HashSet;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Set;
26  import java.util.Stack;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.xerces.xs.XSAttributeDeclaration;
31  import org.apache.xerces.xs.XSAttributeUse;
32  import org.apache.xerces.xs.XSComplexTypeDefinition;
33  import org.apache.xerces.xs.XSConstants;
34  import org.apache.xerces.xs.XSElementDeclaration;
35  import org.apache.xerces.xs.XSModel;
36  import org.apache.xerces.xs.XSModelGroup;
37  import org.apache.xerces.xs.XSNamedMap;
38  import org.apache.xerces.xs.XSObject;
39  import org.apache.xerces.xs.XSObjectList;
40  import org.apache.xerces.xs.XSParticle;
41  import org.apache.xerces.xs.XSSimpleTypeDefinition;
42  import org.apache.xerces.xs.XSTerm;
43  import org.apache.xerces.xs.XSTypeDefinition;
44  
45  import de.mindcrimeilab.xsanalyzer.util.XSModelHelper;
46  import de.mindcrimeilab.xsanalyzer.xsext.AnonymousTypeFactory;
47  
48  /**
49   * References:
50   * <ul>
51   * <li>[1] http://www.w3.org/Submission/2004/SUBM-xmlschema-api-20040309/xml-schema -api.html</li>
52   * </ul>
53   * 
54   * @author Michael Engelhardt<me@mindcrime-ilab.de>
55   * @author $Author: agony $
56   * @version $Revision: 165 $
57   * 
58   */
59  public class XsModelWalker {
60  
61      public static final String PC_CURRENT_COMPONENT_NAME = XsModelWalker.class.getName() + ".pc.currentComponentName";
62  
63      private static final Log logger = LogFactory.getLog("xsAnalyzerApplicationLogger");
64  
65      private final PropertyChangeSupport changeSupport;
66  
67      private final Stack<XSObject> seenComponents;
68  
69      private final List<XsComponentWorker> workerList;
70  
71      private final Set<XSObject> analyzedComponents;
72  
73      public XsModelWalker() {
74          changeSupport = new PropertyChangeSupport(this);
75          seenComponents = new Stack<XSObject>();
76          analyzedComponents = new HashSet<XSObject>();
77          workerList = new LinkedList<XsComponentWorker>();
78      }
79  
80      // ------------- PropertyChange Support -----------------------
81      /**
82       * @param listener
83       * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
84       */
85      public void addPropertyChangeListener(PropertyChangeListener listener) {
86          changeSupport.addPropertyChangeListener(listener);
87      }
88  
89      /**
90       * @param propertyName
91       * @param listener
92       * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.lang.String,
93       *      java.beans.PropertyChangeListener)
94       */
95      public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
96          changeSupport.addPropertyChangeListener(propertyName, listener);
97      }
98  
99      /**
100      * @return
101      * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners()
102      */
103     public PropertyChangeListener[] getPropertyChangeListeners() {
104         return changeSupport.getPropertyChangeListeners();
105     }
106 
107     /**
108      * @param propertyName
109      * @return
110      * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners(java.lang.String)
111      */
112     public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
113         return changeSupport.getPropertyChangeListeners(propertyName);
114     }
115 
116     /**
117      * @param propertyName
118      * @return
119      * @see java.beans.PropertyChangeSupport#hasListeners(java.lang.String)
120      */
121     public boolean hasListeners(String propertyName) {
122         return changeSupport.hasListeners(propertyName);
123     }
124 
125     /**
126      * @param listener
127      * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
128      */
129     public void removePropertyChangeListener(PropertyChangeListener listener) {
130         changeSupport.removePropertyChangeListener(listener);
131     }
132 
133     /**
134      * @param propertyName
135      * @param listener
136      * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.lang.String,
137      *      java.beans.PropertyChangeListener)
138      */
139     public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
140         changeSupport.removePropertyChangeListener(propertyName, listener);
141     }
142 
143     // ------------- Worker Handling-----------------------
144 
145     public boolean addWorker(XsComponentWorker worker) {
146         return workerList.add(worker);
147     }
148 
149     public boolean addWorkers(List<XsComponentWorker> workerList) {
150         return workerList.addAll(workerList);
151     }
152 
153     public boolean removeWorker(XsComponentWorker worker) {
154         return workerList.remove(worker);
155     }
156 
157     public void clearWorkers() {
158         workerList.clear();
159     }
160 
161     public void walkModel(XSModel model) {
162         List<? extends XSObject> globalComponents = initialize(model);
163 
164         for (XSObject object : globalComponents) {
165             XsModelWalker.logger.debug("Global component [{" + object.getNamespace() + "}:" + object.getName() + "]");
166             visitComponent(object, null);
167         }
168     }
169 
170     private void visitComponent(XSObject object, XSObject parent) {
171         if (seenComponents.contains(object) || analyzedComponents.contains(object)) { return; }
172         seenComponents.push(object);
173 
174         changeSupport.firePropertyChange(XsModelWalker.PC_CURRENT_COMPONENT_NAME, null, object.getName());
175 
176         final XSTypeDefinition typeDefinition;
177         switch (object.getType()) {
178             case XSConstants.TYPE_DEFINITION:
179                 XSTypeDefinition type = (XSTypeDefinition) object;
180                 if (type.getAnonymous()) {
181                     XsModelWalker.logger.debug("Handling anonymous type");
182                     type = AnonymousTypeFactory.getProxy(type);
183                 }
184                 typeDefinition = onVisitTypeDefinition(type, parent);
185                 break;
186             case XSConstants.ELEMENT_DECLARATION:
187                 XSElementDeclaration elementDeclaration = (XSElementDeclaration) object;
188                 typeDefinition = onVisitElementDeclaration(elementDeclaration, parent);
189 
190                 break;
191             case XSConstants.ATTRIBUTE_DECLARATION:
192                 throw new RuntimeException("Not implemented yet - XSConstants.ATTRIBUTE_DECLARATION !");
193             case XSConstants.NOTATION_DECLARATION:
194                 throw new RuntimeException("Not implemented yet - XSConstants.NOTATION_DECLARATION !");
195             case XSConstants.MODEL_GROUP_DEFINITION:
196                 throw new RuntimeException("Not implemented yet - XSConstants.MODEL_GROUP_DEFINITION !");
197             default:
198                 XsModelWalker.logger.info("Unknown component type [" + object.getType() + "]");
199                 typeDefinition = null;
200                 break;
201         }
202 
203         if (null != typeDefinition) {
204             // iterate in depth...
205             XsModelWalker.logger.debug("==> of base type [{" + typeDefinition.getNamespace() + "}:" + typeDefinition.getName() + "]");
206             visitComponent(typeDefinition, object);
207         }
208 
209         seenComponents.pop();
210         analyzedComponents.add(object);
211     }
212 
213     private XSTypeDefinition onVisitTypeDefinition(XSTypeDefinition type, XSObject parent) {
214         final XSTypeDefinition baseType;
215         switch (type.getTypeCategory()) {
216             case XSTypeDefinition.COMPLEX_TYPE:
217                 XsModelWalker.logger.debug("==> Found complex type definition");
218                 XSComplexTypeDefinition ctypedef = (XSComplexTypeDefinition) type;
219                 baseType = onVisitComplexTypeDefinition(ctypedef, parent);
220 
221                 break;
222             case XSTypeDefinition.SIMPLE_TYPE:
223                 XsModelWalker.logger.debug("==> Found simple type definition");
224                 XSSimpleTypeDefinition stypedef = (XSSimpleTypeDefinition) type;
225                 baseType = onVisitSimpleTypeDefinition(stypedef, parent);
226                 break;
227             default:
228                 baseType = null;
229                 break;
230         }
231 
232         // both may contain attribute definitions
233 
234         // simple types may return null - depending on type definition
235         // (list|atomic...)
236         return baseType;
237     }
238 
239     /**
240      * 
241      * @param stypedef
242      * 
243      *            TODO rework method - base type depends on content model atomic - getBaseType list - getMemberTypes...
244      */
245     private XSTypeDefinition onVisitSimpleTypeDefinition(XSSimpleTypeDefinition stypedef, XSObject parent) {
246         executeWorker(stypedef, parent);
247 
248         // TODO handle embedded components (union, list)?
249         switch (stypedef.getVariety()) {
250             case XSSimpleTypeDefinition.VARIETY_ABSENT:
251                 // anySimpleType
252                 XsModelWalker.logger.debug("==> Found simple type variety absent");
253                 break;
254             case XSSimpleTypeDefinition.VARIETY_ATOMIC:
255                 // atomic (restriction)
256                 XsModelWalker.logger.debug("==> Found simple type variety atomic");
257                 break;
258             case XSSimpleTypeDefinition.VARIETY_LIST:
259                 // list
260                 XsModelWalker.logger.debug("==> Found simple type variety list");
261                 XSSimpleTypeDefinition itemType = stypedef.getItemType();
262                 visitComponent(itemType, stypedef);
263                 break;
264             case XSSimpleTypeDefinition.VARIETY_UNION:
265                 // union
266                 XsModelWalker.logger.debug("==> Found simple type variety union");
267                 XSObjectList unionedTypes = stypedef.getMemberTypes();
268                 if (null != unionedTypes) {
269                     for (int i = 0; i < unionedTypes.getLength(); ++i) {
270                         visitComponent(unionedTypes.item(i), stypedef);
271                     }
272                 }
273                 break;
274             default:
275                 XsModelWalker.logger.warn("-------------- UNKNOWN SIMPLE TYPE VARIETY OF TYPE [" + stypedef.getVariety() + "]");
276                 throw new RuntimeException("Unexpected branch selection - Not implemented");
277         }
278 
279         return XSModelHelper.getBaseType(stypedef);
280     }
281 
282     private XSTypeDefinition onVisitElementDeclaration(XSElementDeclaration elementDeclaration, XSObject parent) {
283         executeWorker(elementDeclaration, parent);
284         XSTypeDefinition typedef = elementDeclaration.getTypeDefinition();
285         return typedef;
286     }
287 
288     private XSTypeDefinition onVisitComplexTypeDefinition(XSComplexTypeDefinition ctypedef, XSObject parent) {
289         executeWorker(ctypedef, parent);
290         // handle attributes
291         XSObjectList attributes = ctypedef.getAttributeUses();
292         onVisitAttributes(attributes, ctypedef);
293 
294         XSParticle particle = ctypedef.getParticle();
295         if (null != particle) {
296             XSTerm term = particle.getTerm();
297             // according to [1]#Interface-XSParticle this should be either a
298             // model group, an element or a wildcard declaration
299             onVisitTerm(ctypedef, term);
300         }
301 
302         return ctypedef.getBaseType();
303     }
304 
305     /**
306      * @param ctypedef
307      * @param term
308      */
309     private void onVisitTerm(XSComplexTypeDefinition ctypedef, XSTerm term) {
310         short termType = term.getType();
311 
312         switch (termType) {
313             case XSConstants.MODEL_GROUP:
314                 XsModelWalker.logger.debug("==> found model group");
315                 XSModelGroup group = (XSModelGroup) term;
316                 XSObjectList list = group.getParticles();
317                 for (int i = 0; i < list.getLength(); ++i) {
318                     XSParticle part = (XSParticle) list.item(i);
319                     XSTerm item = part.getTerm();
320                     XsModelWalker.logger.debug("==--> embedded [{" + item.getNamespace() + "}:" + item.getName() + "]");
321                     onVisitTerm(ctypedef, item);
322                 }
323                 break;
324             case XSConstants.ELEMENT_DECLARATION:
325                 XsModelWalker.logger.debug("==> found element declaration");
326                 visitComponent(term, ctypedef);
327                 break;
328             case XSConstants.WILDCARD:
329                 XsModelWalker.logger.debug("==> found wildcard");
330                 // ignore wildcards, no usable content - almost everything is
331                 // useable..
332                 break;
333             default:
334                 XsModelWalker.logger.warn("-------------- UNKNOWN PARTICLE TYPE OF TYPE [" + termType + "]");
335                 throw new RuntimeException("Unexpected branch selection - Not implemented");
336         }
337     }
338 
339     private void onVisitAttributes(XSObjectList attributeUsesList, XSObject parent) {
340         for (int i = 0; i < attributeUsesList.getLength(); ++i) {
341             XSAttributeUse xsAttribute = (XSAttributeUse) attributeUsesList.item(i);
342             // attribute declaration contains interesting properties of the
343             // attribute
344             XSAttributeDeclaration xsAttributeType = xsAttribute.getAttrDeclaration();
345 
346             XsModelWalker.logger.debug("--> Attribute [{" + xsAttributeType.getNamespace() + "}:" + xsAttributeType.getName() + "]");
347 
348             executeWorker(xsAttributeType, parent);
349 
350             // handle
351             /*
352              * TODO check if we really need this...
353              * 
354              * A {scope} of global identifies attribute declarations available for use in complex type definitions
355              * throughout the schema. Locally scoped declarations are available for use only within the complex type
356              * definition identified by the {scope} property. This property is ·absent· in the case of declarations
357              * within attribute group definitions: their scope will be determined when they are used in the construction
358              * of complex type definitions.
359              */
360             XSComplexTypeDefinition ctypedef = xsAttributeType.getEnclosingCTDefinition();
361             if (null != ctypedef) {
362                 visitComponent(ctypedef, parent);
363             }
364 
365             visitComponent(xsAttributeType.getTypeDefinition(), parent);
366         }
367 
368     }
369 
370     private List<? extends XSObject> initialize(XSModel model) {
371         List<? extends XSObject> global = new LinkedList<XSObject>();
372 
373         /*
374          * Add globally defined schema components to list. This list will be the starting point for schema iteration.
375          * 
376          * Trying to add components in an optimized way assuming that most schemas will have a number of simple types
377          * which are derived from basic xsd types.
378          * 
379          * Complex types will be extend or restrict simple types.
380          * 
381          * Elements will be use complex types as well as simple types.
382          */
383         XSNamedMap typesMap = model.getComponents(XSConstants.TYPE_DEFINITION);
384         XSModelHelper.addComponents(typesMap, global);
385 
386         // Global element definitions
387         XSNamedMap elementsMap = model.getComponents(XSConstants.ELEMENT_DECLARATION);
388 
389         XSModelHelper.addComponents(elementsMap, global);
390 
391         return global;
392     }
393 
394     private void executeWorker(XSObject object, XSObject parent) {
395         for (XsComponentWorker worker : workerList) {
396             if (worker.isSupported(object)) {
397                 worker.execute(object, parent);
398             }
399         }
400     }
401 }