| /* Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.xmlbeans.impl.schema; |
| |
| import org.apache.xmlbeans.QNameSet; |
| import org.apache.xmlbeans.SchemaParticle; |
| import org.apache.xmlbeans.SchemaType; |
| import org.apache.xmlbeans.XmlErrorCodes; |
| import org.apache.xmlbeans.impl.xb.xsdschema.Attribute; |
| import org.apache.xmlbeans.impl.xb.xsdschema.Element; |
| import org.apache.xmlbeans.impl.xb.xsdschema.KeyrefDocument.Keyref; |
| import org.apache.xmlbeans.impl.xb.xsdschema.TopLevelElement; |
| |
| import javax.xml.namespace.QName; |
| import java.math.BigInteger; |
| import java.util.*; |
| |
| public class StscResolver { |
| /** |
| * Does a topo walk of all the types to resolve them. |
| */ |
| public static void resolveAll() { |
| // resolve tree of types |
| StscState state = StscState.get(); |
| |
| SchemaType[] documentTypes = state.documentTypes(); |
| for (int i = 0; i < documentTypes.length; i++) { |
| resolveSubstitutionGroup((SchemaTypeImpl) documentTypes[i]); |
| } |
| |
| List allSeenTypes = new ArrayList(); |
| allSeenTypes.addAll(Arrays.asList(state.documentTypes())); |
| allSeenTypes.addAll(Arrays.asList(state.attributeTypes())); |
| allSeenTypes.addAll(Arrays.asList(state.redefinedGlobalTypes())); |
| allSeenTypes.addAll(Arrays.asList(state.globalTypes())); |
| |
| for (int i = 0; i < allSeenTypes.size(); i++) { |
| SchemaType gType = (SchemaType) allSeenTypes.get(i); |
| resolveType((SchemaTypeImpl) gType); |
| allSeenTypes.addAll(Arrays.asList(gType.getAnonymousTypes())); |
| } |
| |
| // Resolve all keyref refs |
| resolveIdentityConstraints(); |
| } |
| |
| public static boolean resolveType(SchemaTypeImpl sImpl) { |
| if (sImpl.isResolved()) { |
| return true; |
| } |
| if (sImpl.isResolving()) { |
| StscState.get().error("Cyclic dependency error", XmlErrorCodes.CYCLIC_DEPENDENCY, sImpl.getParseObject()); |
| return false; // cyclic dependency error |
| } |
| // System.out.println("Resolving " + sImpl); |
| |
| sImpl.startResolving(); |
| |
| if (sImpl.isDocumentType()) { |
| resolveDocumentType(sImpl); |
| } else if (sImpl.isAttributeType()) { |
| resolveAttributeType(sImpl); |
| } else if (sImpl.isSimpleType()) { |
| StscSimpleTypeResolver.resolveSimpleType(sImpl); |
| } else { |
| StscComplexTypeResolver.resolveComplexType(sImpl); |
| } |
| |
| sImpl.finishResolving(); |
| // System.out.println("Finished resolving " + sImpl); |
| return true; |
| } |
| |
| public static boolean resolveSubstitutionGroup(SchemaTypeImpl sImpl) { |
| assert sImpl.isDocumentType(); |
| |
| if (sImpl.isSGResolved()) { |
| return true; |
| } |
| if (sImpl.isSGResolving()) { |
| StscState.get().error("Cyclic dependency error", XmlErrorCodes.CYCLIC_DEPENDENCY, sImpl.getParseObject()); |
| return false; // cyclic dependency error |
| } |
| |
| sImpl.startResolvingSGs(); |
| |
| // Resolve substitution group |
| |
| TopLevelElement elt = (TopLevelElement) sImpl.getParseObject(); |
| SchemaTypeImpl substitutionGroup = null; |
| QName eltName = new QName(sImpl.getTargetNamespace(), elt.getName()); |
| |
| // BUG: How do I tell if the type is in this compilation unit? |
| if (elt.isSetSubstitutionGroup()) { |
| substitutionGroup = StscState.get().findDocumentType(elt.getSubstitutionGroup(), |
| sImpl.getChameleonNamespace(), sImpl.getTargetNamespace()); |
| |
| if (substitutionGroup == null) { |
| StscState.get().notFoundError(elt.getSubstitutionGroup(), SchemaType.ELEMENT, elt.xgetSubstitutionGroup(), true); |
| } |
| // recovery - ignore substitution group |
| else if (!resolveSubstitutionGroup(substitutionGroup)) { |
| substitutionGroup = null; |
| } else { |
| sImpl.setSubstitutionGroup(elt.getSubstitutionGroup()); |
| } |
| } |
| |
| // Walk up the chain of subtitution groups adding this schematype to each head's |
| // member list |
| while (substitutionGroup != null) { |
| |
| substitutionGroup.addSubstitutionGroupMember(eltName); |
| |
| if (substitutionGroup.getSubstitutionGroup() == null) { |
| break; |
| } |
| |
| substitutionGroup = StscState.get().findDocumentType( |
| substitutionGroup.getSubstitutionGroup(), substitutionGroup.getChameleonNamespace(), null/*no dependency added*/); |
| |
| assert substitutionGroup != null : "Could not find document type for: " + substitutionGroup.getSubstitutionGroup(); |
| |
| if (!resolveSubstitutionGroup(substitutionGroup)) { |
| substitutionGroup = null; // cyclic dependency - no subst group |
| } |
| |
| } |
| |
| sImpl.finishResolvingSGs(); |
| return true; |
| |
| } |
| |
| public static void resolveDocumentType(SchemaTypeImpl sImpl) { |
| assert sImpl.isResolving(); |
| |
| assert sImpl.isDocumentType(); |
| |
| |
| // translate the global element associated with this document type |
| // and construct a content model which allows just that element |
| |
| List<SchemaType> anonTypes = new ArrayList<>(); |
| |
| SchemaGlobalElementImpl element = |
| (SchemaGlobalElementImpl) |
| StscTranslator.translateElement( |
| (Element) sImpl.getParseObject(), |
| sImpl.getTargetNamespace(), sImpl.isChameleon(), null, null, |
| anonTypes, sImpl); |
| |
| SchemaLocalElementImpl contentModel = null; |
| |
| if (element != null) { |
| StscState.get().addGlobalElement(element); |
| |
| contentModel = new SchemaLocalElementImpl(); |
| |
| contentModel.setParticleType(SchemaParticle.ELEMENT); |
| StscTranslator.copyGlobalElementToLocalElement(element, contentModel); |
| contentModel.setMinOccurs(BigInteger.ONE); |
| contentModel.setMaxOccurs(BigInteger.ONE); |
| |
| contentModel.setTransitionNotes(QNameSet.EMPTY, true); |
| } |
| |
| Map elementPropertyModel = |
| StscComplexTypeResolver.buildContentPropertyModelByQName( |
| contentModel, sImpl); |
| |
| SchemaTypeImpl baseType = sImpl.getSubstitutionGroup() == null ? |
| BuiltinSchemaTypeSystem.ST_ANY_TYPE : |
| StscState.get().findDocumentType(sImpl.getSubstitutionGroup(), |
| sImpl.isChameleon() ? sImpl.getTargetNamespace() : null, null/*already added*/); |
| |
| sImpl.setBaseTypeRef(baseType.getRef()); |
| sImpl.setBaseDepth(baseType.getBaseDepth() + 1); |
| sImpl.setDerivationType(SchemaType.DT_RESTRICTION); |
| sImpl.setComplexTypeVariety(SchemaType.ELEMENT_CONTENT); |
| |
| sImpl.setContentModel( |
| contentModel, new SchemaAttributeModelImpl(), |
| elementPropertyModel, Collections.EMPTY_MAP, false); |
| |
| sImpl.setWildcardSummary( |
| QNameSet.EMPTY, false, QNameSet.EMPTY, false); |
| |
| sImpl.setAnonymousTypeRefs(makeRefArray(anonTypes)); |
| |
| |
| } |
| |
| public static void resolveAttributeType(SchemaTypeImpl sImpl) { |
| assert sImpl.isResolving(); |
| |
| assert sImpl.isAttributeType(); |
| |
| List<SchemaType> anonTypes = new ArrayList<>(); |
| |
| SchemaGlobalAttributeImpl attribute = |
| (SchemaGlobalAttributeImpl) StscTranslator.translateAttribute( |
| (Attribute) sImpl.getParseObject(), sImpl.getTargetNamespace(), null, |
| sImpl.isChameleon(), anonTypes, sImpl, null, false); |
| |
| SchemaAttributeModelImpl attributeModel = new SchemaAttributeModelImpl(); |
| |
| if (attribute != null) { |
| StscState.get().addGlobalAttribute(attribute); |
| |
| SchemaLocalAttributeImpl attributeCopy = new SchemaLocalAttributeImpl(); |
| StscTranslator.copyGlobalAttributeToLocalAttribute(attribute, attributeCopy); |
| attributeModel.addAttribute(attributeCopy); |
| } |
| |
| sImpl.setBaseTypeRef(BuiltinSchemaTypeSystem.ST_ANY_TYPE.getRef()); |
| sImpl.setBaseDepth(sImpl.getBaseDepth() + 1); |
| sImpl.setDerivationType(SchemaType.DT_RESTRICTION); |
| sImpl.setComplexTypeVariety(SchemaType.EMPTY_CONTENT); |
| |
| Map attributePropertyModel = |
| StscComplexTypeResolver.buildAttributePropertyModelByQName( |
| attributeModel, sImpl); |
| |
| sImpl.setContentModel( |
| null, attributeModel, Collections.EMPTY_MAP, attributePropertyModel, false); |
| |
| sImpl.setWildcardSummary( |
| QNameSet.EMPTY, false, QNameSet.EMPTY, false); |
| |
| sImpl.setAnonymousTypeRefs(makeRefArray(anonTypes)); |
| } |
| |
| private static SchemaType.Ref[] makeRefArray(Collection<SchemaType> typeList) { |
| return typeList.stream().map(SchemaType::getRef).toArray(SchemaType.Ref[]::new); |
| } |
| |
| |
| public static void resolveIdentityConstraints() { |
| StscState state = StscState.get(); |
| SchemaIdentityConstraintImpl[] idcs = state.idConstraints(); |
| |
| for (SchemaIdentityConstraintImpl idc : idcs) { |
| if (!idc.isResolved()) { |
| Keyref xsdkr = (Keyref) idc.getParseObject(); |
| QName keyName = xsdkr.getRefer(); |
| SchemaIdentityConstraintImpl key = null; |
| |
| key = state.findIdConstraint(keyName, idc.getChameleonNamespace(), idc.getTargetNamespace()); |
| if (key == null) { |
| state.notFoundError(keyName, SchemaType.IDENTITY_CONSTRAINT, xsdkr, true); |
| } else { |
| if (key.getConstraintCategory() == SchemaIdentityConstraintImpl.CC_KEYREF) { |
| state.error(XmlErrorCodes.IDENTITY_CONSTRAINT_PROPERTIES$KEYREF_REFERS_TO_KEYREF, |
| null, idc.getParseObject()); |
| } |
| |
| if (key.getFields().length != idc.getFields().length) { |
| state.error(XmlErrorCodes.IDENTITY_CONSTRAINT_PROPERTIES$KEY_KEYREF_FIELD_COUNT_EQ, |
| null, idc.getParseObject()); |
| } |
| |
| idc.setReferencedKey(key.getRef()); |
| } |
| } |
| } |
| } |
| |
| } |