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}