001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.sequencer.xsd; 017 018import static org.modeshape.sequencer.sramp.SrampLexicon.DESCRIPTION; 019import static org.modeshape.sequencer.xsd.XsdLexicon.IMPORT; 020import java.io.InputStream; 021import java.io.Reader; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028import java.util.concurrent.atomic.AtomicLong; 029import javax.jcr.NamespaceRegistry; 030import javax.jcr.Node; 031import javax.jcr.PropertyType; 032import javax.jcr.RepositoryException; 033import javax.jcr.Value; 034import org.eclipse.emf.common.util.AbstractEnumerator; 035import org.eclipse.emf.common.util.EList; 036import org.eclipse.emf.ecore.EObject; 037import org.eclipse.xsd.XSDAnnotation; 038import org.eclipse.xsd.XSDAttributeDeclaration; 039import org.eclipse.xsd.XSDAttributeGroupContent; 040import org.eclipse.xsd.XSDAttributeGroupDefinition; 041import org.eclipse.xsd.XSDAttributeUse; 042import org.eclipse.xsd.XSDComplexTypeContent; 043import org.eclipse.xsd.XSDComplexTypeDefinition; 044import org.eclipse.xsd.XSDCompositor; 045import org.eclipse.xsd.XSDConcreteComponent; 046import org.eclipse.xsd.XSDDerivationMethod; 047import org.eclipse.xsd.XSDElementDeclaration; 048import org.eclipse.xsd.XSDEnumerationFacet; 049import org.eclipse.xsd.XSDFacet; 050import org.eclipse.xsd.XSDImport; 051import org.eclipse.xsd.XSDInclude; 052import org.eclipse.xsd.XSDModelGroup; 053import org.eclipse.xsd.XSDModelGroupDefinition; 054import org.eclipse.xsd.XSDParticle; 055import org.eclipse.xsd.XSDParticleContent; 056import org.eclipse.xsd.XSDPatternFacet; 057import org.eclipse.xsd.XSDProcessContents; 058import org.eclipse.xsd.XSDProhibitedSubstitutions; 059import org.eclipse.xsd.XSDRedefine; 060import org.eclipse.xsd.XSDRepeatableFacet; 061import org.eclipse.xsd.XSDSchema; 062import org.eclipse.xsd.XSDSimpleFinal; 063import org.eclipse.xsd.XSDSimpleTypeDefinition; 064import org.eclipse.xsd.XSDTypeDefinition; 065import org.eclipse.xsd.XSDWildcard; 066import org.eclipse.xsd.util.XSDParser; 067import org.modeshape.common.annotation.NotThreadSafe; 068import org.modeshape.common.util.SizeMeasuringInputStream; 069import org.modeshape.common.util.SizeMeasuringReader; 070import org.modeshape.common.util.StringUtil; 071import org.modeshape.jcr.api.sequencer.Sequencer; 072import org.modeshape.sequencer.sramp.AbstractResolvingReader; 073import org.modeshape.sequencer.sramp.SrampLexicon; 074import org.modeshape.sequencer.sramp.SymbolSpace; 075import org.modeshape.sequencer.xsd.XsdSequencer.MimeTypeConstants; 076import org.w3c.dom.Element; 077import org.w3c.dom.NamedNodeMap; 078import org.xml.sax.InputSource; 079 080/** 081 * A class that can parse XML Schema Documents and create a node structure based on the schema information. 082 * <p> 083 * This class can be subclassed and any of the 'process' methods overridden to customize the derived graph structure. 084 * </p> 085 */ 086@NotThreadSafe 087public class XsdReader extends AbstractResolvingReader { 088 089 /** 090 * In XML Schema, there is a distinct symbol space within each target namespace for each kind of <a 091 * href="http://www.w3.org/TR/xmlschema-1/#concepts-data-model">declaration and definition component</a>, except that within a 092 * target namespace the simple type definitions and complex type definitions share a single symbol space. See the <a 093 * href="http://www.w3.org/TR/xmlschema-1/#concepts-nameSymbolSpaces">specification</a> for details. 094 */ 095 public static final SymbolSpace ATTRIBUTE_DECLARATIONS = new SymbolSpace("AttributeDeclarations"); 096 public static final SymbolSpace ELEMENT_DECLARATION = new SymbolSpace("ElementDeclarations"); 097 public static final SymbolSpace TYPE_DEFINITIONS = new SymbolSpace("TypeDeclarations"); 098 public static final SymbolSpace ATTRIBUTE_GROUP_DEFINITIONS = new SymbolSpace("AttributeGroupDeclarations"); 099 public static final SymbolSpace MODEL_GROUP_DEFINITIONS = new SymbolSpace("ModelGroupDeclarations"); 100 public static final SymbolSpace IDENTITY_CONSTRAINT_DEFINITIONS = new SymbolSpace("IdentityConstraintDeclarations"); 101 102 /** 103 * A set of attribute names which should be ignored when/if they appear without a schema 104 */ 105 private static final Set<String> IGNORED_ATTRIBUTES_IMPORT = removePrefix(XsdLexicon.NAMESPACE, XsdLexicon.SCHEMA_LOCATION); 106 private static final Set<String> IGNORED_ATTRIBUTES_INCLUDE = removePrefix(XsdLexicon.SCHEMA_LOCATION); 107 private static final Set<String> IGNORED_ATTRIBUTES_REDEFINE = removePrefix(XsdLexicon.SCHEMA_LOCATION); 108 private static final Set<String> IGNORED_ATTRIBUTES_SIMPLE_TYPE_DEF = removePrefix(XsdLexicon.NAMESPACE, XsdLexicon.NC_NAME); 109 private static final Set<String> IGNORED_ATTRIBUTES_COMPLEX_TYPE_DEF = removePrefix(XsdLexicon.NAMESPACE, 110 XsdLexicon.BASE_TYPE_NAME, 111 XsdLexicon.BASE_TYPE_NAMESPACE, 112 XsdLexicon.BLOCK, 113 XsdLexicon.FINAL, 114 XsdLexicon.ABSTRACT, XsdLexicon.MIXED); 115 private static final Set<String> IGNORED_ATTRIBUTES_ELEMENT_DECL = removePrefix(XsdLexicon.TYPE_NAME, XsdLexicon.TYPE_NAMESPACE, 116 XsdLexicon.TYPE_REFERENCE, XsdLexicon.FORM, 117 XsdLexicon.FINAL, XsdLexicon.BLOCK); 118 private static final Set<String> IGNORED_ATTRIBUTES_ATTR_DECL = removePrefix(XsdLexicon.TYPE_NAME, XsdLexicon.TYPE_NAMESPACE); 119 private static final Set<String> IGNORED_ATTRIBUTES_COMPLEX_TYPE_CONTENT = removePrefix(XsdLexicon.METHOD); 120 private static final Set<String> IGNORED_ATTRIBUTES_ATTR_GROUP_DEF = removePrefix(XsdLexicon.REF, XsdLexicon.NC_NAME, 121 XsdLexicon.NAMESPACE); 122 private static final Set<String> IGNORED_ATTRIBUTES_WILDCARD = removePrefix(XsdLexicon.NAMESPACE, XsdLexicon.PROCESS_CONTENTS); 123 private static final Set<String> IGNORED_ATTRIBUTES_ATTR_USE = removePrefix(XsdLexicon.USE); 124 125 public XsdReader( Sequencer.Context context ) { 126 super(context); 127 } 128 129 @Override 130 public void read( InputSource source, 131 Node outputNode ) throws Exception { 132 logger.debug("Processing XSD '{0}'", outputNode); 133 Reader reader = null; 134 InputStream stream = null; 135 try { 136 // Parse the XSD, measuring the number of bytes as we read ... 137 Map<?, ?> options = new HashMap<Object, Object>(); 138 XSDParser parser = new XSDParser(options); 139 AtomicLong contentSize = new AtomicLong(); 140 if (source.getCharacterStream() != null) { 141 reader = new SizeMeasuringReader(source.getCharacterStream(), contentSize); 142 source = new InputSource(reader); 143 } else { 144 stream = new SizeMeasuringInputStream(source.getByteStream(), contentSize); 145 source = new InputSource(stream); 146 } 147 parser.parse(source); 148 149 // Get some metadata about the XSD ... 150 String encoding = parser.getEncoding(); 151 152 // Convert the XSD to content ... 153 XSDSchema schema = parser.getSchema(); 154 process(schema, encoding, contentSize.get(), outputNode); 155 156 } finally { 157 try { 158 if (reader != null) reader.close(); 159 } catch (Exception e) { 160 logger.debug(e, "Cannot close reader stream "); 161 } finally { 162 try { 163 if (stream != null) stream.close(); 164 } catch (Exception e) { 165 logger.debug(e, "Cannot close reader stream "); 166 } 167 } 168 } 169 } 170 171 /** 172 * Read an XSDSchema instance and create the node hierarchy under the given root node. 173 * 174 * @param schema the schema object; may not be null 175 * @param encoding the encoding for the XSD; may be null if the encoding is not specified 176 * @param contentSize the size of the XML Schema Document content; may not be negative 177 * @param rootNode the root node that will be populated with the XML Schema Document information 178 * @throws Exception if there is a probelm reading the XSD content 179 */ 180 protected void process( XSDSchema schema, 181 String encoding, 182 long contentSize, 183 Node rootNode ) throws Exception { 184 assert schema != null; 185 186 logger.debug("Target namespace: '{0}'", schema.getTargetNamespace()); 187 rootNode.setProperty(SrampLexicon.CONTENT_TYPE, MimeTypeConstants.APPLICATION_XML); 188 if (encoding != null) { 189 rootNode.setProperty(SrampLexicon.CONTENT_ENCODING, encoding); 190 } 191 rootNode.setProperty(SrampLexicon.CONTENT_SIZE, contentSize); 192 193 // Parse the annotations first to aggregate them all into a single 'sramp:description' property ... 194 @SuppressWarnings( "unchecked" ) 195 List<XSDAnnotation> annotations = schema.getAnnotations(); 196 processAnnotations(annotations, rootNode); 197 processNonSchemaAttributes(schema, rootNode, Collections.<String>emptySet()); 198 199 // Parse the objects ... 200 for (EObject obj : schema.eContents()) { 201 if (obj instanceof XSDSimpleTypeDefinition) { 202 processSimpleTypeDefinition((XSDSimpleTypeDefinition)obj, rootNode); 203 } else if (obj instanceof XSDComplexTypeDefinition) { 204 processComplexTypeDefinition((XSDComplexTypeDefinition)obj, rootNode); 205 } else if (obj instanceof XSDElementDeclaration) { 206 processElementDeclaration((XSDElementDeclaration)obj, rootNode); 207 } else if (obj instanceof XSDAttributeDeclaration) { 208 processAttributeDeclaration((XSDAttributeDeclaration)obj, rootNode, false); 209 } else if (obj instanceof XSDImport) { 210 processImport((XSDImport)obj, rootNode); 211 } else if (obj instanceof XSDInclude) { 212 processInclude((XSDInclude)obj, rootNode); 213 } else if (obj instanceof XSDRedefine) { 214 processRedefine((XSDRedefine)obj, rootNode); 215 } else if (obj instanceof XSDAttributeGroupDefinition) { 216 processAttributeGroupDefinition((XSDAttributeGroupDefinition)obj, rootNode); 217 } else if (obj instanceof XSDAnnotation) { 218 // already processed above ... 219 } 220 } 221 222 // Resolve any outstanding, unresolved references ... 223 resolveReferences(); 224 } 225 226 protected void processImport( XSDImport xsdImport, 227 Node parentNode ) throws RepositoryException { 228 logger.debug("Import: '{0}' with location '{1}' ", xsdImport.getNamespace(), xsdImport.getSchemaLocation()); 229 Node importNode = parentNode.addNode(IMPORT, IMPORT); 230 importNode.setProperty(XsdLexicon.NAMESPACE, xsdImport.getNamespace()); 231 importNode.setProperty(XsdLexicon.SCHEMA_LOCATION, xsdImport.getSchemaLocation()); 232 processNonSchemaAttributes(xsdImport, importNode, IGNORED_ATTRIBUTES_IMPORT); 233 } 234 235 protected void processInclude( XSDInclude xsdInclude, 236 Node parentNode ) throws RepositoryException { 237 logger.debug("Include: '{0}' ", xsdInclude.getSchemaLocation()); 238 Node includeNode = parentNode.addNode(XsdLexicon.INCLUDE, XsdLexicon.INCLUDE); 239 includeNode.setProperty(XsdLexicon.SCHEMA_LOCATION, xsdInclude.getSchemaLocation()); 240 processNonSchemaAttributes(xsdInclude, includeNode, IGNORED_ATTRIBUTES_INCLUDE); 241 } 242 243 protected void processRedefine( XSDRedefine redefine, 244 Node parentNode ) throws RepositoryException { 245 logger.debug("Include: '{0}' ", redefine.getSchemaLocation()); 246 Node redefineNode = parentNode.addNode(XsdLexicon.REDEFINE, XsdLexicon.REDEFINE); 247 redefineNode.setProperty(XsdLexicon.SCHEMA_LOCATION, redefine.getSchemaLocation()); 248 processNonSchemaAttributes(redefine, redefineNode, IGNORED_ATTRIBUTES_REDEFINE); 249 } 250 251 protected void processSimpleTypeDefinition( XSDSimpleTypeDefinition type, 252 Node node ) throws RepositoryException { 253 boolean isAnonymous = type.getName() == null; 254 String nodeName = isAnonymous ? XsdLexicon.SIMPLE_TYPE : type.getName(); 255 // This is a normal simple type definition ... 256 logger.debug("Simple type: '{0}' in ns '{1}' ", type.getName(), type.getTargetNamespace()); 257 258 Node typeNode = node.addNode(nodeName, XsdLexicon.SIMPLE_TYPE_DEFINITION); 259 typeNode.setProperty(XsdLexicon.NAMESPACE, type.getTargetNamespace()); 260 if (!isAnonymous) { 261 typeNode.setProperty(XsdLexicon.NC_NAME, type.getName()); 262 registerForSymbolSpace(TYPE_DEFINITIONS, type.getTargetNamespace(), type.getName(), typeNode.getIdentifier()); 263 } 264 processTypeFacets(type, typeNode, type.getBaseType()); 265 processNonSchemaAttributes(type, typeNode, IGNORED_ATTRIBUTES_SIMPLE_TYPE_DEF); 266 } 267 268 protected void processTypeFacets( XSDSimpleTypeDefinition type, 269 Node typeNode, 270 XSDTypeDefinition baseType ) throws RepositoryException { 271 if (baseType == null) { 272 baseType = type.getBaseType(); 273 } 274 if (baseType == type) { 275 // The base type is the anytype ... 276 baseType = type.getSchema() 277 .getSchemaForSchema() 278 .resolveSimpleTypeDefinition("http://www.w3.org/2001/XMLSchema", "anyType"); 279 } 280 if (baseType != null) { 281 typeNode.setProperty(XsdLexicon.BASE_TYPE_NAME, baseType.getName()); 282 typeNode.setProperty(XsdLexicon.BASE_TYPE_NAMESPACE, baseType.getTargetNamespace()); 283 setReference(typeNode, 284 XsdLexicon.BASE_TYPE_REFERENCE, 285 TYPE_DEFINITIONS, 286 baseType.getTargetNamespace(), 287 baseType.getName()); 288 } 289 290 processFacet(type.getEffectiveMaxLengthFacet(), typeNode, XsdLexicon.MAX_LENGTH, PropertyType.LONG); 291 processFacet(type.getMaxLengthFacet(), typeNode, XsdLexicon.MAX_LENGTH, PropertyType.LONG); 292 processFacet(type.getEffectiveMinLengthFacet(), typeNode, XsdLexicon.MIN_LENGTH, PropertyType.LONG); 293 processFacet(type.getMinLengthFacet(), typeNode, XsdLexicon.MIN_LENGTH, PropertyType.LONG); 294 processFacet(type.getEffectiveMaxFacet(), typeNode, XsdLexicon.MAX_VALUE_EXCLUSIVE, PropertyType.LONG); 295 processFacet(type.getMaxExclusiveFacet(), typeNode, XsdLexicon.MAX_VALUE_EXCLUSIVE, PropertyType.LONG); 296 processFacet(type.getEffectiveMinFacet(), typeNode, XsdLexicon.MIN_VALUE_EXCLUSIVE, PropertyType.LONG); 297 processFacet(type.getMinExclusiveFacet(), typeNode, XsdLexicon.MIN_VALUE_EXCLUSIVE, PropertyType.LONG); 298 processFacet(type.getMaxInclusiveFacet(), typeNode, XsdLexicon.MAX_VALUE_INCLUSIVE, PropertyType.LONG); 299 processFacet(type.getMinInclusiveFacet(), typeNode, XsdLexicon.MIN_VALUE_INCLUSIVE, PropertyType.LONG); 300 processFacet(type.getEffectiveTotalDigitsFacet(), typeNode, XsdLexicon.TOTAL_DIGITS, PropertyType.LONG); 301 processFacet(type.getTotalDigitsFacet(), typeNode, XsdLexicon.TOTAL_DIGITS, PropertyType.LONG); 302 processFacet(type.getEffectiveFractionDigitsFacet(), typeNode, XsdLexicon.FRACTION_DIGITS, PropertyType.LONG); 303 processFacet(type.getFractionDigitsFacet(), typeNode, XsdLexicon.FRACTION_DIGITS, PropertyType.LONG); 304 305 processFacet(type.getEffectiveWhiteSpaceFacet(), typeNode, XsdLexicon.WHITESPACE, PropertyType.STRING); 306 processFacet(type.getWhiteSpaceFacet(), typeNode, XsdLexicon.WHITESPACE, PropertyType.STRING); 307 308 processFacet(type.getEffectivePatternFacet(), typeNode, XsdLexicon.PATTERN, PropertyType.STRING); 309 @SuppressWarnings( "unchecked" ) 310 List<XSDPatternFacet> patternFacets = type.getPatternFacets(); 311 processFacetsList(patternFacets, typeNode, XsdLexicon.PATTERN); 312 313 processFacet(type.getEffectiveEnumerationFacet(), typeNode, XsdLexicon.ENUMERATED_VALUES, PropertyType.STRING); 314 @SuppressWarnings( "unchecked" ) 315 List<XSDEnumerationFacet> enumFacets = type.getEnumerationFacets(); 316 processFacetsList(enumFacets, typeNode, XsdLexicon.ENUMERATED_VALUES); 317 318 @SuppressWarnings( "unchecked" ) 319 List<XSDSimpleFinal> finalFacets2 = type.getFinal(); 320 processEnumerators(finalFacets2, typeNode, XsdLexicon.FINAL); 321 322 processAnnotation(type.getAnnotation(), typeNode); 323 } 324 325 protected void processComplexTypeDefinition( XSDComplexTypeDefinition type, 326 Node parentNode ) throws RepositoryException { 327 logger.debug("Complex type: '{0}' in ns '{1}' ", type.getName(), type.getTargetNamespace()); 328 boolean isAnonymous = type.getName() == null; 329 330 String nodeName = isAnonymous ? XsdLexicon.COMPLEX_TYPE : type.getName(); 331 Node typeNode = parentNode.addNode(nodeName, XsdLexicon.COMPLEX_TYPE_DEFINITION); 332 typeNode.setProperty(XsdLexicon.NAMESPACE, type.getTargetNamespace()); 333 if (!isAnonymous) { 334 typeNode.setProperty(XsdLexicon.NC_NAME, type.getName()); 335 registerForSymbolSpace(TYPE_DEFINITIONS, type.getTargetNamespace(), type.getName(), typeNode.getIdentifier()); 336 } 337 XSDTypeDefinition baseType = type.getBaseType(); 338 if (baseType == type) { 339 // The base type is the anytype ... 340 baseType = type.getSchema() 341 .getSchemaForSchema() 342 .resolveComplexTypeDefinition("http://www.w3.org/2001/XMLSchema", "anyType"); 343 } 344 if (baseType != null) { 345 typeNode.setProperty(XsdLexicon.BASE_TYPE_NAME, baseType.getName()); 346 typeNode.setProperty(XsdLexicon.BASE_TYPE_NAMESPACE, baseType.getTargetNamespace()); 347 } 348 typeNode.setProperty(XsdLexicon.ABSTRACT, type.isAbstract()); 349 typeNode.setProperty(XsdLexicon.MIXED, type.isMixed()); 350 351 @SuppressWarnings( "unchecked" ) 352 List<XSDProhibitedSubstitutions> blocks = type.getBlock(); 353 processEnumerators(blocks, typeNode, XsdLexicon.BLOCK); 354 355 @SuppressWarnings( "unchecked" ) 356 List<XSDSimpleFinal> finalFacets = type.getFinal(); 357 processEnumerators(finalFacets, typeNode, XsdLexicon.FINAL); 358 359 processComplexTypeContent(type.getContent(), typeNode); 360 361 processAnnotation(type.getAnnotation(), typeNode); 362 processNonSchemaAttributes(type, typeNode, IGNORED_ATTRIBUTES_COMPLEX_TYPE_DEF); 363 } 364 365 protected Node processElementDeclaration( XSDElementDeclaration decl, 366 Node parentNode ) throws RepositoryException { 367 if (decl == null) { 368 return null; 369 } 370 logger.debug("Element declaration: '{0}' in ns '{1}' ", decl.getName(), decl.getTargetNamespace()); 371 Node declarationNode; 372 if (decl.getName() != null) { 373 // Normal element declaration ... 374 declarationNode = parentNode.addNode(decl.getName(), XsdLexicon.ELEMENT_DECLARATION); 375 declarationNode.setProperty(XsdLexicon.NC_NAME, decl.getName()); 376 declarationNode.setProperty(XsdLexicon.NAMESPACE, decl.getTargetNamespace()); 377 } else { 378 assert decl.isFeatureReference() : "expected element reference"; 379 XSDElementDeclaration resolved = decl.getResolvedElementDeclaration(); 380 declarationNode = parentNode.addNode(resolved.getName(), XsdLexicon.ELEMENT_DECLARATION); 381 declarationNode.setProperty(XsdLexicon.REF_NAME, resolved.getName()); 382 declarationNode.setProperty(XsdLexicon.REF_NAMESPACE, resolved.getTargetNamespace()); 383 setReference(declarationNode, XsdLexicon.REF, ELEMENT_DECLARATION, resolved.getTargetNamespace(), resolved.getName()); 384 } 385 if (decl.isGlobal()) { 386 registerForSymbolSpace(ELEMENT_DECLARATION, 387 decl.getTargetNamespace(), 388 decl.getName(), 389 declarationNode.getIdentifier()); 390 } 391 392 declarationNode.setProperty(XsdLexicon.ABSTRACT, decl.isAbstract()); 393 declarationNode.setProperty(XsdLexicon.NILLABLE, decl.isNillable()); 394 395 XSDTypeDefinition type = decl.getType(); 396 if (type != null) { 397 declarationNode.setProperty(XsdLexicon.TYPE_NAME, type.getName()); 398 declarationNode.setProperty(XsdLexicon.TYPE_NAMESPACE, type.getTargetNamespace()); 399 setReference(declarationNode, XsdLexicon.TYPE_REFERENCE, TYPE_DEFINITIONS, type.getTargetNamespace(), type.getName()); 400 } 401 402 if (decl.getAnonymousTypeDefinition() == type) { 403 // It's anonymous, so we need to process the definition here ... 404 if (type instanceof XSDComplexTypeDefinition) { 405 processComplexTypeDefinition((XSDComplexTypeDefinition)type, declarationNode); 406 } else if (type instanceof XSDSimpleTypeDefinition) { 407 processSimpleTypeDefinition((XSDSimpleTypeDefinition)type, declarationNode); 408 } 409 } 410 processEnumerator(decl.getForm(), declarationNode, XsdLexicon.FORM); 411 412 @SuppressWarnings( "unchecked" ) 413 List<XSDProhibitedSubstitutions> finals = decl.getLexicalFinal(); 414 processEnumerators(finals, declarationNode, XsdLexicon.FINAL); 415 416 @SuppressWarnings( "unchecked" ) 417 List<XSDProhibitedSubstitutions> blocks = decl.getBlock(); 418 processEnumerators(blocks, declarationNode, XsdLexicon.BLOCK); 419 420 processAnnotation(decl.getAnnotation(), declarationNode); 421 processNonSchemaAttributes(type, declarationNode, IGNORED_ATTRIBUTES_ELEMENT_DECL); 422 return declarationNode; 423 } 424 425 protected Node processAttributeDeclaration( XSDAttributeDeclaration decl, 426 Node parentNode, 427 boolean isUse ) throws RepositoryException { 428 if (decl == null) { 429 return null; 430 } 431 logger.debug("Attribute declaration: '{0}' in ns '{1}' ", decl.getName(), decl.getTargetNamespace()); 432 433 Node attributeDeclarationNode = parentNode.addNode(decl.getName(), XsdLexicon.ATTRIBUTE_DECLARATION); 434 attributeDeclarationNode.setProperty(XsdLexicon.NC_NAME, decl.getName()); 435 attributeDeclarationNode.setProperty(XsdLexicon.NAMESPACE, decl.getTargetNamespace()); 436 if (decl.isGlobal() && !isUse) { 437 registerForSymbolSpace(ATTRIBUTE_DECLARATIONS, 438 decl.getTargetNamespace(), 439 decl.getName(), 440 attributeDeclarationNode.getIdentifier()); 441 } 442 XSDTypeDefinition type = decl.getType(); 443 if (type != null) { 444 attributeDeclarationNode.setProperty(XsdLexicon.TYPE_NAME, type.getName()); 445 attributeDeclarationNode.setProperty(XsdLexicon.TYPE_NAMESPACE, type.getTargetNamespace()); 446 } 447 processAnnotation(decl.getAnnotation(), attributeDeclarationNode); 448 processNonSchemaAttributes(type, attributeDeclarationNode, IGNORED_ATTRIBUTES_ATTR_DECL); 449 return attributeDeclarationNode; 450 } 451 452 protected void processComplexTypeContent( XSDComplexTypeContent content, 453 Node parentNode ) throws RepositoryException { 454 if (content == null) { 455 return; 456 } 457 458 XSDComplexTypeDefinition owner = (XSDComplexTypeDefinition)content.eContainer(); 459 460 if (content instanceof XSDParticle) { 461 processParticle((XSDParticle)content, parentNode); 462 } else if (content instanceof XSDSimpleTypeDefinition) { 463 Node contentNode = parentNode.addNode(XsdLexicon.SIMPLE_CONTENT, XsdLexicon.SIMPLE_CONTENT); 464 processTypeFacets((XSDSimpleTypeDefinition)content, contentNode, owner.getBaseTypeDefinition()); 465 } 466 467 XSDDerivationMethod method = owner.getDerivationMethod(); 468 if (method != null) { 469 parentNode.setProperty(XsdLexicon.METHOD, method.getLiteral()); 470 } 471 472 @SuppressWarnings( "unchecked" ) 473 List<XSDAttributeGroupContent> attributeGroupContents = owner.getAttributeContents(); 474 if (attributeGroupContents != null) { 475 for (XSDAttributeGroupContent attributeGroup : attributeGroupContents) { 476 processAttributeGroupContent(attributeGroup, parentNode); 477 } 478 } 479 @SuppressWarnings( "unchecked" ) 480 List<XSDAttributeUse> attributeUses = owner.getAttributeUses(); 481 if (attributeUses != null) { 482 for (XSDAttributeUse attributeUse : attributeUses) { 483 processAttributeUse(attributeUse, parentNode); 484 } 485 } 486 XSDWildcard wildcard = owner.getAttributeWildcard(); 487 processWildcard(wildcard, parentNode); 488 processNonSchemaAttributes(owner, parentNode, IGNORED_ATTRIBUTES_COMPLEX_TYPE_CONTENT); 489 } 490 491 protected void processParticle( XSDParticle content, 492 Node node ) throws RepositoryException { 493 if (content == null) { 494 return; 495 } 496 XSDParticleContent particle = content.getContent(); 497 Node particleNode = null; 498 if (particle instanceof XSDModelGroupDefinition) { 499 particleNode = processModelGroupDefinition((XSDModelGroupDefinition)particle, node); 500 } else if (particle instanceof XSDElementDeclaration) { 501 particleNode = processElementDeclaration((XSDElementDeclaration)particle, node); 502 } else if (particle instanceof XSDModelGroup) { 503 particleNode = processModelGroup((XSDModelGroup)particle, node); 504 } else if (particle instanceof XSDWildcard) { 505 particleNode = processWildcard((XSDWildcard)particle, node); 506 } 507 if (particleNode != null) { 508 long minOccurs = content.getMinOccurs(); 509 long maxOccurs = content.getMaxOccurs(); 510 particleNode.setProperty(XsdLexicon.MIN_OCCURS, minOccurs); 511 if (maxOccurs >= 0) { 512 particleNode.setProperty(XsdLexicon.MAX_OCCURS, maxOccurs); 513 } else { 514 // unbounded ... 515 } 516 } 517 } 518 519 protected Node processModelGroupDefinition( XSDModelGroupDefinition defn, 520 Node parentNode ) throws RepositoryException { 521 if (defn == null) { 522 return null; 523 } 524 XSDModelGroup group = defn.getModelGroup(); 525 processNonSchemaAttributes(defn, parentNode, Collections.<String>emptySet()); 526 return processModelGroup(group, parentNode); 527 } 528 529 protected Node processModelGroup( XSDModelGroup group, 530 Node parentNode ) throws RepositoryException { 531 if (group == null) { 532 return null; 533 } 534 XSDCompositor compositor = group.getCompositor(); 535 String primaryTypeName = getPrimaryTypeFromCompositor(compositor); 536 537 Node childNode = parentNode.addNode(primaryTypeName, primaryTypeName); 538 @SuppressWarnings( "unchecked" ) 539 List<XSDParticle> particles = group.getParticles(); 540 for (XSDParticle particle : particles) { 541 processParticle(particle, childNode); 542 } 543 processNonSchemaAttributes(group, childNode, Collections.<String>emptySet()); 544 return childNode; 545 } 546 547 private String getPrimaryTypeFromCompositor( XSDCompositor compositor ) { 548 String primaryTypeName = null; 549 switch (compositor.getValue()) { 550 case XSDCompositor.ALL: 551 primaryTypeName = XsdLexicon.ALL; 552 break; 553 case XSDCompositor.CHOICE: 554 primaryTypeName = XsdLexicon.CHOICE; 555 break; 556 case XSDCompositor.SEQUENCE: 557 primaryTypeName = XsdLexicon.SEQUENCE; 558 break; 559 default: 560 assert false : "should not get here"; 561 } 562 return primaryTypeName; 563 } 564 565 protected void processAttributeGroupContent( XSDAttributeGroupContent content, 566 Node parentNode ) throws RepositoryException { 567 if (content == null) { 568 return; 569 } 570 if (content instanceof XSDAttributeGroupDefinition) { 571 processAttributeGroupDefinition((XSDAttributeGroupDefinition)content, parentNode); 572 return; 573 } 574 if (content instanceof XSDAttributeUse) { 575 processAttributeUse((XSDAttributeUse)content, parentNode); 576 return; 577 } 578 assert false : "Invalid attribute group content type"; 579 } 580 581 protected void processAttributeGroupDefinition( XSDAttributeGroupDefinition defn, 582 Node parentNode ) throws RepositoryException { 583 if (defn == null) { 584 return; 585 } 586 Node attributeGroupNode = null; 587 if (defn.isAttributeGroupDefinitionReference()) { 588 XSDAttributeGroupDefinition resolved = defn.getResolvedAttributeGroupDefinition(); 589 logger.debug("Attribute Group definition (ref): '{0}' in ns '{1}' ", 590 resolved.getName(), 591 resolved.getTargetNamespace()); 592 attributeGroupNode = parentNode.addNode(resolved.getName(), XsdLexicon.ATTRIBUTE_GROUP); 593 setReference(attributeGroupNode, 594 XsdLexicon.REF, 595 ATTRIBUTE_GROUP_DEFINITIONS, 596 resolved.getTargetNamespace(), 597 resolved.getName()); 598 } else { 599 logger.debug("Attribute Group definition: '{0}' in ns '{1}' ", defn.getName(), defn.getTargetNamespace()); 600 attributeGroupNode = parentNode.addNode(defn.getName(), XsdLexicon.ATTRIBUTE_GROUP); 601 registerForSymbolSpace(ATTRIBUTE_GROUP_DEFINITIONS, 602 defn.getTargetNamespace(), 603 defn.getName(), 604 attributeGroupNode.getIdentifier()); 605 attributeGroupNode.setProperty(XsdLexicon.NC_NAME, defn.getName()); 606 attributeGroupNode.setProperty(XsdLexicon.NAMESPACE, defn.getTargetNamespace()); 607 608 for (Object child : defn.getContents()) { 609 if (child instanceof XSDAttributeUse) { 610 processAttributeUse((XSDAttributeUse)child, attributeGroupNode); 611 } else if (child instanceof XSDWildcard) { 612 processWildcard((XSDWildcard)child, attributeGroupNode); 613 } 614 } 615 } 616 processAnnotation(defn.getAnnotation(), attributeGroupNode); 617 processNonSchemaAttributes(defn, attributeGroupNode, IGNORED_ATTRIBUTES_ATTR_GROUP_DEF); 618 } 619 620 protected Node processWildcard( XSDWildcard wildcard, 621 Node parentNode ) throws RepositoryException { 622 if (wildcard == null) { 623 return null; 624 } 625 logger.debug("Any Attribute"); 626 627 Node anyAttributeNode = parentNode.addNode(XsdLexicon.ANY_ATTRIBUTE, XsdLexicon.ANY_ATTRIBUTE); 628 629 @SuppressWarnings( "unchecked" ) 630 EList<String> nsConstraints = wildcard.getNamespaceConstraint(); 631 if (nsConstraints != null && !nsConstraints.isEmpty()) { 632 Set<String> values = new HashSet<String>(); 633 for (String nsConstraint : nsConstraints) { 634 if (nsConstraint == null) continue; 635 nsConstraint = nsConstraint.trim(); 636 if (nsConstraint.length() == 0) continue; 637 values.add(nsConstraint); 638 } 639 if (!values.isEmpty()) { 640 anyAttributeNode.setProperty(XsdLexicon.NAMESPACE, values.toArray(new String[values.size()])); 641 } 642 } 643 if (wildcard.getProcessContents() != null) { 644 XSDProcessContents processContents = wildcard.getProcessContents(); 645 anyAttributeNode.setProperty(XsdLexicon.PROCESS_CONTENTS, processContents.getLiteral()); 646 } 647 processAnnotation(wildcard.getAnnotation(), anyAttributeNode); 648 processNonSchemaAttributes(wildcard, anyAttributeNode, IGNORED_ATTRIBUTES_WILDCARD); 649 return anyAttributeNode; 650 } 651 652 protected void processAttributeUse( XSDAttributeUse use, 653 Node parentNode ) throws RepositoryException { 654 // Process the attribute declaration ... 655 Node attributeDeclaration = processAttributeDeclaration(use.getAttributeDeclaration(), parentNode, true); 656 if (use.getUse() != null) { 657 attributeDeclaration.setProperty(XsdLexicon.USE, use.getUse().getLiteral()); 658 } 659 processNonSchemaAttributes(use, attributeDeclaration, IGNORED_ATTRIBUTES_ATTR_USE); 660 } 661 662 protected void processNonSchemaAttributes( XSDConcreteComponent component, 663 Node node, 664 Set<String> excludeAttributes) throws RepositoryException { 665 if (component == null) { 666 return; 667 } 668 Element element = component.getElement(); 669 if (element == null) { 670 return; 671 } 672 673 NamedNodeMap attributes = element.getAttributes(); 674 if (attributes == null) { 675 return; 676 } 677 678 for (int i = 0, len = attributes.getLength(); i != len; ++i) { 679 org.w3c.dom.Node attribute = attributes.item(i); 680 if (attribute.getNodeType() != org.w3c.dom.Node.ATTRIBUTE_NODE) { 681 continue; 682 } 683 String namespaceUri = attribute.getNamespaceURI(); 684 if (!XsdLexicon.Namespace.URI.equals(namespaceUri)) { 685 // Record any attribute that is not in the XSD namespace ... 686 String localName = attribute.getLocalName(); 687 if (excludeAttributes.contains(localName)) { 688 continue; 689 } 690 String value = attribute.getNodeValue(); 691 if (value == null) continue; 692 if (namespaceUri != null) { 693 NamespaceRegistry namespaceRegistry = node.getSession().getWorkspace().getNamespaceRegistry(); 694 String prefix = registerNamespace(namespaceRegistry, namespaceUri, attribute.getPrefix()); 695 String propertyName = prefix + ":" + localName; 696 node.setProperty(propertyName, value); 697 } else { 698 node.setProperty(localName, value); 699 } 700 } 701 } 702 } 703 704 protected void processAnnotation( XSDAnnotation annotation, 705 Node node ) throws RepositoryException { 706 if (annotation == null) { 707 return; 708 } 709 StringBuilder sb = new StringBuilder(); 710 for (Object obj : annotation.getUserInformation()) { 711 Element element = (Element)obj; 712 if (element.getLocalName().equals("documentation")) { 713 String content = element.getTextContent(); 714 if (content != null) sb.append(content); 715 } 716 } 717 if (sb.length() != 0) { 718 String content = sb.toString(); 719 content = content.trim(); 720 if (content.length() != 0) { 721 node.setProperty(DESCRIPTION, content); 722 } 723 } 724 } 725 726 protected void processAnnotations( List<XSDAnnotation> annotations, 727 Node parentNode ) throws RepositoryException { 728 assert annotations != null; 729 StringBuilder sb = new StringBuilder(); 730 for (XSDAnnotation annotation : annotations) { 731 for (Object obj : annotation.getUserInformation()) { 732 Element element = (Element)obj; 733 if (element.getLocalName().equals("documentation")) { 734 String content = element.getTextContent(); 735 if (content != null) sb.append(content); 736 } 737 } 738 sb.append(System.getProperty("line.separator")); 739 } 740 if (sb.length() != 0) { 741 String content = sb.toString(); 742 content = content.trim(); 743 if (content.length() != 0) { 744 parentNode.setProperty(DESCRIPTION, content); 745 } 746 } 747 } 748 749 /** 750 * Given an {@link XSDFacet}, determines the JCR property type based on the value of the facet. 751 * 752 * @param facetValue a String representing the lexical value of the facet, which can be null. 753 * @param defaultPropertyType a given property type, of which we expected the string value to be convertible to. 754 * @return a property type to which the string value can be converted 755 */ 756 private int determineJCRPropertyTypeForFacet( String facetValue, 757 int defaultPropertyType ) { 758 switch (defaultPropertyType) { 759 case PropertyType.LONG: { 760 try { 761 Long.valueOf(facetValue); 762 return PropertyType.LONG; 763 } catch (NumberFormatException e) { 764 return PropertyType.DECIMAL; 765 } 766 } 767 default: { 768 return defaultPropertyType; 769 } 770 } 771 } 772 773 protected void processFacet( XSDFacet facet, 774 Node node, 775 String propertyName, 776 int propertyType ) throws RepositoryException { 777 if (facet == null) { 778 return; 779 } 780 String lexicalValue = facet.getLexicalValue(); 781 if (lexicalValue != null) { 782 int actualPropertyType = determineJCRPropertyTypeForFacet(lexicalValue, propertyType); 783 Value value = context.valueFactory().createValue(facet.getLexicalValue(), actualPropertyType); 784 node.setProperty(propertyName, value); 785 } else if (facet instanceof XSDRepeatableFacet) { 786 Set<String> values = getRepeatableFacetValues((XSDRepeatableFacet)facet); 787 if (!values.isEmpty()) { 788 node.setProperty(propertyName, values.toArray(new String[values.size()])); 789 } 790 } 791 } 792 793 private Set<String> getRepeatableFacetValues( XSDRepeatableFacet facet ) { 794 EList<?> facetValues = null; 795 if (facet instanceof XSDPatternFacet) { 796 facetValues = ((XSDPatternFacet)facet).getValue(); 797 } else if (facet instanceof XSDEnumerationFacet) { 798 facetValues = ((XSDEnumerationFacet)facet).getValue(); 799 } 800 801 Set<String> values = new HashSet<String>(); 802 if (facetValues != null && !facetValues.isEmpty()) { 803 for (Object enumValue : facetValues) { 804 values.add(enumValue.toString()); 805 } 806 } 807 return values; 808 } 809 810 protected <Facet extends XSDFacet> void processFacetsList( List<Facet> facets, 811 Node node, 812 String propertyName ) throws RepositoryException { 813 if (facets == null) { 814 return; 815 } 816 817 Set<String> values = new HashSet<String>(); 818 for (XSDFacet facet : facets) { 819 String lexicalValue = facet.getLexicalValue(); 820 if (lexicalValue != null) { 821 values.add(facet.getLexicalValue()); 822 } else if (facet instanceof XSDRepeatableFacet) { 823 values.addAll(getRepeatableFacetValues((XSDRepeatableFacet)facet)); 824 } 825 } 826 827 if (!values.isEmpty()) { 828 node.setProperty(propertyName, values.toArray(new String[values.size()])); 829 } 830 } 831 832 protected <Enumerator extends AbstractEnumerator> void processEnumerators( List<Enumerator> enumerators, 833 Node node, 834 String propertyName ) throws RepositoryException { 835 if (enumerators == null) { 836 return; 837 } 838 Set<String> values = new HashSet<String>(); 839 for (Enumerator enumValue : enumerators) { 840 String value = enumValue.getLiteral(); 841 if (value != null) { 842 values.add(value); 843 } 844 } 845 if (!values.isEmpty()) { 846 node.setProperty(propertyName, values.toArray(new String[values.size()])); 847 } 848 } 849 850 protected <Enumerator extends AbstractEnumerator> void processEnumerator( Enumerator enumerator, 851 Node node, 852 String propertyName ) throws RepositoryException { 853 if (enumerator != null && enumerator.getLiteral() != null) { 854 node.setProperty(propertyName, enumerator.getLiteral()); 855 } 856 } 857 858 protected static Set<String> removePrefix( String... attributeNames ) { 859 if (attributeNames.length == 0) { 860 return Collections.emptySet(); 861 } 862 Set<String> result = new HashSet<>(attributeNames.length); 863 for (String attributeName : attributeNames) { 864 if (!StringUtil.isBlank(attributeName) && attributeName.contains(":")) { 865 attributeName = attributeName.substring(attributeName.indexOf(":") + 1); 866 } 867 result.add(attributeName); 868 } 869 return result; 870 } 871}