001package com.hfg.xml.xsd;
002
003
004import java.util.ArrayList;
005import java.util.HashMap;
006import java.util.List;
007import java.util.Map;
008import java.util.Set;
009
010import com.hfg.util.collection.CollectionUtil;
011import com.hfg.util.CompareUtil;
012import com.hfg.util.StringUtil;
013import com.hfg.xml.XMLName;
014import com.hfg.xml.XMLNamespace;
015import com.hfg.xml.XMLNode;
016
017//------------------------------------------------------------------------------
018/**
019 Representation of an element in an XML Schema Definition (XSD) specification.
020 <div>
021 @author J. Alex Taylor, hairyfatguy.com
022 </div>
023 */
024//------------------------------------------------------------------------------
025// com.hfg XML/HTML Coding Library
026//
027// This library is free software; you can redistribute it and/or
028// modify it under the terms of the GNU Lesser General Public
029// License as published by the Free Software Foundation; either
030// version 2.1 of the License, or (at your option) any later version.
031//
032// This library is distributed in the hope that it will be useful,
033// but WITHOUT ANY WARRANTY; without even the implied warranty of
034// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
035// Lesser General Public License for more details.
036//
037// You should have received a copy of the GNU Lesser General Public
038// License along with this library; if not, write to the Free Software
039// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
040//
041// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
042// jataylor@hairyfatguy.com
043//------------------------------------------------------------------------------
044
045public class XsdElement extends XsdContent implements Comparable<XsdElement>
046{
047   private String  mName;
048   private String  mTypeString;
049   private XsdType mType;
050   private String  mAnnotation;
051   private XMLName mRef;
052   private XMLNamespace mNamespace;
053
054   private static final Map<XsdElement, Map<String, List<Integer>>> sSubtagIndexMap = new HashMap<>();
055
056
057   //###########################################################################
058   // CONSTRUCTORS
059   //###########################################################################
060
061   //---------------------------------------------------------------------------
062   public XsdElement(String inName)
063   {
064      mName = inName;
065   }
066
067   //---------------------------------------------------------------------------
068   public XsdElement(XMLNode inTag)
069   {
070      super(inTag);
071
072      inTag.verifyTagName(XsdXML.ELEMENT.getLocalName());
073
074      mName = inTag.getAttributeValue(XsdXML.NAME_ATT.getLocalName());
075
076      mTypeString = inTag.getAttributeValue(XsdXML.TYPE_ATT.getLocalName());
077
078      String refString = inTag.getAttributeValue(XsdXML.REF_ATT.getLocalName());
079      if (StringUtil.isSet(refString))
080      {
081         mRef = new XMLName(refString);
082      }
083
084      for (XMLNode subtag : inTag.getXMLNodeSubtags())
085      {
086         if (subtag.getTagName().equals(XsdXML.ANNOTATION.getLocalName()))
087         {
088            setAnnotation(subtag.getRequiredSubtagByName(XsdXML.DOCUMENTATION.getLocalName()).getUnescapedContent());
089         }
090      }
091   }
092
093   //###########################################################################
094   // PUBLIC METHODS
095   //###########################################################################
096
097   //---------------------------------------------------------------------------
098   @Override
099   public String toString()
100   {
101      return getQualifiedName() + (mAnnotation != null ? " (" + mAnnotation + ")" : "");
102   }
103
104   //------------------------------------------------------------------------
105   @Override
106   public int hashCode()
107   {
108      int hashCode = 31 * getQualifiedName().hashCode();
109      if (getTypeName() != null)
110      {
111         hashCode    += 31 * getTypeName().hashCode();
112      }
113
114      return hashCode;
115   }
116
117   //------------------------------------------------------------------------
118   @Override
119   public boolean equals(Object inObj2)
120   {
121      boolean result = false;
122
123      if (inObj2 instanceof XsdElement)
124      {
125         result = (0 == compareTo((XsdElement) inObj2));
126      }
127
128      return result;
129   }
130
131   //------------------------------------------------------------------------
132   @Override
133   public int compareTo(XsdElement inXsdElement2)
134   {
135      int result;
136      if (inXsdElement2 != null)
137      {
138         result = CompareUtil.compare(getQualifiedName(), inXsdElement2.getQualifiedName());
139
140         if (0 == result)
141         {
142            result = CompareUtil.compare(getTypeName(), inXsdElement2.getTypeName());
143         }
144      }
145      else
146      {
147         result = -1;
148      }
149
150      return result;
151   }
152
153   //---------------------------------------------------------------------------
154   public String getLocalName()
155   {
156      return mName;
157   }
158
159   //---------------------------------------------------------------------------
160   public String getQualifiedName()
161   {
162      return (mNamespace != null && mNamespace.getPrefix() != null ? mNamespace.getPrefix() + ":" : "") + mName;
163   }
164
165   //---------------------------------------------------------------------------
166   public XMLName getRef()
167   {
168      if (mRef != null
169          && null == mRef.getNamespace()
170          && getNamespace() != null)
171      {
172         // Transfer the namespace if one wasn't defined on the ref
173         mRef.setNamespace(getNamespace());
174      }
175      return mRef;
176   }
177
178   //---------------------------------------------------------------------------
179   public XsdElement setType(XsdType inValue)
180   {
181      mType = inValue;
182      return this;
183   }
184
185   //---------------------------------------------------------------------------
186   public XsdType getType()
187   {
188      return mType;
189   }
190
191   //---------------------------------------------------------------------------
192   public String getTypeName()
193   {
194      return mTypeString;
195   }
196
197   //---------------------------------------------------------------------------
198   public String getQualifiedTypeName()
199   {
200      return mTypeString;
201   }
202
203   //---------------------------------------------------------------------------
204   @Override
205   public XsdElement setMaxOccurs(int inValue)
206   {
207      super.setMaxOccurs(inValue);
208      return this;
209   }
210
211   //---------------------------------------------------------------------------
212   @Override
213   public XsdElement setMinOccurs(int inValue)
214   {
215      super.setMinOccurs(inValue);
216      return this;
217   }
218
219   //---------------------------------------------------------------------------
220   public XsdElement setAnnotation(String inValue)
221   {
222      mAnnotation = inValue;
223      return this;
224   }
225
226   //---------------------------------------------------------------------------
227   public String getAnnotation()
228   {
229      return mAnnotation;
230   }
231
232
233   //------------------------------------------------------------------------
234   public Map<String, List<Integer>> getSubtagIndexMap()
235   {
236      if (null == sSubtagIndexMap.get(this))
237      {
238         sSubtagIndexMap.put(this, generateSubtagIndex());
239      }
240
241      return sSubtagIndexMap.get(this);
242   }
243
244   //---------------------------------------------------------------------------
245   public XsdElement setNamespace(XMLNamespace inValue)
246   {
247      mNamespace = inValue;
248
249      if (mTypeString != null
250          && ! mTypeString.contains(":")
251          && inValue.getPrefix() != null)
252      {
253         mTypeString = inValue.getPrefix() + ":"  + mTypeString;
254      }
255
256      return this;
257   }
258
259   //---------------------------------------------------------------------------
260   public XMLNamespace getNamespace()
261   {
262      return mNamespace;
263   }
264
265   //###########################################################################
266   // PROTECTED METHODS
267   //###########################################################################
268
269   //---------------------------------------------------------------------------
270   protected XsdElement setName(String inValue)
271   {
272      mName = inValue;
273      return this;
274   }
275
276   //###########################################################################
277   // PRIVATE METHODS
278   //###########################################################################
279
280   //------------------------------------------------------------------------
281   private Map<String, List<Integer>> generateSubtagIndex()
282   {
283      Map<String, List<Integer>> subtagIndexMap = new HashMap<>(10);
284
285      if (getType() != null
286          && getType() instanceof XsdComplexType)
287      {
288         recursivelyGenerateSubtagIndex(subtagIndexMap, new ArrayList<>(), (XsdComplexType) getType());
289      }
290
291      return subtagIndexMap;
292   }
293
294   //------------------------------------------------------------------------
295   private void recursivelyGenerateSubtagIndex(Map<String, List<Integer>> inSubtagIndexMap, List<Integer> inLevelSortList, XsdComplexType inXsdComplexType)
296   {
297      // Start w/ any base type subtags
298      if (inXsdComplexType.getBaseType() != null)
299      {
300         recursivelyGenerateSubtagIndex(inSubtagIndexMap, inLevelSortList, inXsdComplexType.getBaseType());
301      }
302
303      List<XsdContent> xsdContents = inXsdComplexType.getContent();
304      if (CollectionUtil.hasValues(xsdContents))
305      {
306         for (XsdContent xsdContent : xsdContents)
307         {
308            if (xsdContent instanceof XsdSequence)
309            {
310               parseSequence((XsdSequence) xsdContent, inSubtagIndexMap, inLevelSortList);
311            }
312            else if (xsdContent instanceof XsdChoice)
313            {
314               parseChoice((XsdChoice) xsdContent, inSubtagIndexMap, inLevelSortList);
315            }
316            else if (xsdContent instanceof XsdGroup)
317            {
318               parseGroup((XsdGroup) xsdContent, inSubtagIndexMap, inLevelSortList);
319            }
320         }
321      }
322   }
323
324   //------------------------------------------------------------------------
325   private void parseSequence(XsdSequence inXsdSequence, Map<String, List<Integer>> inSubtagIndexMap, List<Integer> inLevelSortList)
326   {
327      List<XsdContent> xsdContents = inXsdSequence.getContent();
328      if (CollectionUtil.hasValues(xsdContents))
329      {
330         int index = 0;
331
332         for (XsdContent xsdContent : xsdContents)
333         {
334            List<Integer> levelSortList = new ArrayList<>(inLevelSortList.size() + 1);
335            levelSortList.addAll(inLevelSortList);
336            levelSortList.add(index++);
337
338            if (xsdContent instanceof XsdElement)
339            {
340               inSubtagIndexMap.put(((XsdElement) xsdContent).getLocalName(), levelSortList);
341            }
342            else if (xsdContent instanceof XsdSequence)
343            {
344               parseSequence((XsdSequence) xsdContent, inSubtagIndexMap, levelSortList);
345            }
346            else if (xsdContent instanceof XsdChoice)
347            {
348               parseChoice((XsdChoice) xsdContent, inSubtagIndexMap, levelSortList);
349            }
350            else if (xsdContent instanceof XsdGroup)
351            {
352               parseGroup((XsdGroup) xsdContent, inSubtagIndexMap, levelSortList);
353            }
354         }
355      }
356   }
357
358   //------------------------------------------------------------------------
359   private void parseChoice(XsdChoice inXsdChoice, Map<String, List<Integer>> inSubtagIndexMap, List<Integer> inLevelSortList)
360   {
361      Set<XsdContent> xsdContents = inXsdChoice.getOptions();
362      if (CollectionUtil.hasValues(xsdContents))
363      {
364         int index = 0;
365
366         for (XsdContent xsdContent : xsdContents)
367         {
368            List<Integer> levelSortList = new ArrayList<>(inLevelSortList.size() + 1);
369            levelSortList.addAll(inLevelSortList);
370            levelSortList.add(index);
371
372            if (xsdContent instanceof XsdElement)
373            {
374               inSubtagIndexMap.put(((XsdElement) xsdContent).getLocalName(), levelSortList);
375            }
376            else if (xsdContent instanceof XsdSequence)
377            {
378               parseSequence((XsdSequence) xsdContent, inSubtagIndexMap, levelSortList);
379            }
380            else if (xsdContent instanceof XsdChoice)
381            {
382               parseChoice((XsdChoice) xsdContent, inSubtagIndexMap, levelSortList);
383            }
384            else if (xsdContent instanceof XsdGroup)
385            {
386               parseGroup((XsdGroup) xsdContent, inSubtagIndexMap, levelSortList);
387            }
388         }
389      }
390   }
391
392   //------------------------------------------------------------------------
393   private void parseGroup(XsdGroup inXsdGroup, Map<String, List<Integer>> inSubtagIndexMap, List<Integer> inLevelSortList)
394   {
395      List<XsdContent> xsdContents = inXsdGroup.getContent();
396      if (CollectionUtil.hasValues(xsdContents))
397      {
398         int index = 0;
399
400         for (XsdContent xsdContent : xsdContents)
401         {
402            List<Integer> levelSortList = new ArrayList<>(inLevelSortList.size() + 1);
403            levelSortList.addAll(inLevelSortList);
404            levelSortList.add(index++);
405
406            if (xsdContent instanceof XsdElement)
407            {
408               inSubtagIndexMap.put(((XsdElement) xsdContent).getLocalName(), levelSortList);
409            }
410            else if (xsdContent instanceof XsdSequence)
411            {
412               parseSequence((XsdSequence) xsdContent, inSubtagIndexMap, levelSortList);
413            }
414            else if (xsdContent instanceof XsdChoice)
415            {
416               parseChoice((XsdChoice) xsdContent, inSubtagIndexMap, levelSortList);
417            }
418            else if (xsdContent instanceof XsdGroup)
419            {
420               parseGroup((XsdGroup) xsdContent, inSubtagIndexMap, levelSortList);
421            }
422         }
423      }
424   }
425
426}