/* * Copyright (c) 2010-2016 Evolveum * * 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 com.evolveum.midpoint.common.refinery; import com.evolveum.midpoint.common.ResourceObjectPattern; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.marshaller.QueryConvertor; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.prism.util.ItemPathUtil; import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.processor.*; import com.evolveum.midpoint.schema.util.ObjectQueryUtil; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.ResourceTypeUtil; import com.evolveum.midpoint.schema.util.SchemaDebugUtil; import com.evolveum.midpoint.util.MiscUtil; import com.evolveum.midpoint.util.PrettyPrinter; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CountObjectsCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.PagedSearchCapabilityType; import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.xml.namespace.QName; import java.util.*; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; /** * @author semancik */ public class RefinedObjectClassDefinitionImpl implements RefinedObjectClassDefinition { private static final Trace LOGGER = TraceManager.getTrace(RefinedObjectClassDefinition.class); @NotNull private final List<RefinedAttributeDefinition<?>> attributeDefinitions = new ArrayList<>(); @NotNull private final List<RefinedAssociationDefinition> associationDefinitions = new ArrayList<>(); @NotNull private final ObjectClassComplexTypeDefinition originalObjectClassDefinition; @NotNull private final List<RefinedObjectClassDefinition> auxiliaryObjectClassDefinitions = new ArrayList<>(); @NotNull private final ResourceType resourceType; private ResourceObjectTypeDefinitionType schemaHandlingObjectTypeDefinitionType; private String intent; private ShadowKindType kind; private String displayName; private String description; private boolean isDefault; @NotNull private final List<RefinedAttributeDefinition<?>> identifiers = new ArrayList<>(); @NotNull private final List<RefinedAttributeDefinition<?>> secondaryIdentifiers = new ArrayList<>(); @NotNull private final List<ResourceObjectPattern> protectedObjectPatterns = new ArrayList<>(); private ResourceObjectReferenceType baseContext; private RefinedAttributeDefinition<?> displayNameAttributeDefinition; private RefinedAttributeDefinition<?> namingAttributeDefinition; private RefinedAttributeDefinition<?> descriptionAttributeDefinition; /** * Refined object definition. The "any" parts are replaced with appropriate schema (e.g. resource schema) */ private PrismObjectDefinition<ShadowType> objectDefinition = null; private RefinedObjectClassDefinitionImpl(@NotNull ResourceType resourceType, @NotNull ObjectClassComplexTypeDefinition objectClassDefinition) { this.resourceType = resourceType; this.originalObjectClassDefinition = objectClassDefinition; } //region General attribute definitions ======================================================== @NotNull @Override public Collection<? extends RefinedAttributeDefinition<?>> getAttributeDefinitions() { return Collections.unmodifiableList(attributeDefinitions); } @NotNull @Override public List<? extends ItemDefinition> getDefinitions() { return (List<? extends ItemDefinition>) getAttributeDefinitions(); } @Override public Collection<? extends QName> getNamesOfAttributesWithOutboundExpressions() { return getAttributeDefinitions().stream() .filter(attrDef -> attrDef.getOutboundMappingType() != null) .map(attrDef -> attrDef.getName()) .collect(Collectors.toCollection(HashSet::new)); } @Override public Collection<? extends QName> getNamesOfAttributesWithInboundExpressions() { return getAttributeDefinitions().stream() .filter(attrDef -> CollectionUtils.isNotEmpty(attrDef.getInboundMappingTypes())) .map(attrDef -> attrDef.getName()) .collect(Collectors.toCollection(HashSet::new)); } @Override public <ID extends ItemDefinition> ID findItemDefinition(@NotNull QName name, @NotNull Class<ID> clazz, boolean caseInsensitive) { for (ItemDefinition def : getDefinitions()) { if (def.isValidFor(name, clazz, caseInsensitive)) { return (ID) def; } } return null; } //endregion //region Special attribute definitions ======================================================== @NotNull @Override public Collection<RefinedAttributeDefinition<?>> getPrimaryIdentifiers() { return identifiers; } @NotNull @Override public Collection<RefinedAttributeDefinition<?>> getSecondaryIdentifiers() { return secondaryIdentifiers; } @Override public <X> RefinedAttributeDefinition<X> getDescriptionAttribute() { return substituteRefinedAttributeDefinition( () -> (RefinedAttributeDefinition<X>) descriptionAttributeDefinition, rad -> descriptionAttributeDefinition = rad, originalObjectClassDefinition::getDescriptionAttribute ); } @Override public <X> RefinedAttributeDefinition<X> getNamingAttribute() { return substituteRefinedAttributeDefinition( () -> (RefinedAttributeDefinition<X>) namingAttributeDefinition, rad -> namingAttributeDefinition = rad, originalObjectClassDefinition::getNamingAttribute ); } @Override public <X> RefinedAttributeDefinition<X> getDisplayNameAttribute() { return substituteRefinedAttributeDefinition( () -> (RefinedAttributeDefinition<X>) displayNameAttributeDefinition, rad -> displayNameAttributeDefinition = rad, originalObjectClassDefinition::getDisplayNameAttribute ); } private <X> RefinedAttributeDefinition<X> substituteRefinedAttributeDefinition( Supplier<RefinedAttributeDefinition<X>> getter, Consumer<RefinedAttributeDefinition<X>> setter, Supplier<ResourceAttributeDefinition<X>> getterOfOriginal) { RefinedAttributeDefinition<X> value = getter.get(); if (value == null) { ResourceAttributeDefinition original = getterOfOriginal.get(); if (original == null) { return null; } value = findAttributeDefinition(original.getName()); setter.accept(value); } return value; } //endregion //region General association definitions ======================================================== @NotNull @Override public Collection<RefinedAssociationDefinition> getAssociationDefinitions() { return Collections.unmodifiableList(associationDefinitions); } @Override public Collection<RefinedAssociationDefinition> getAssociationDefinitions(ShadowKindType kind) { return Collections.unmodifiableList( associationDefinitions.stream() .filter(association -> kind == association.getKind()) .collect(Collectors.toList())); } @Override public RefinedAssociationDefinition findAssociationDefinition(QName name) { return associationDefinitions.stream() .filter(a -> QNameUtil.match(a.getName(), name)) .findFirst().orElse(null); } @Override public RefinedAssociationDefinition findEntitlementAssociationDefinition(QName name) { return getEntitlementAssociationDefinitions().stream() .filter(a -> QNameUtil.match(a.getName(), name)) .findFirst().orElse(null); } @Override public Collection<QName> getNamesOfAssociations() { return getAssociationDefinitions().stream() .map(a -> a.getName()) .collect(Collectors.toCollection(HashSet::new)); } @Override public Collection<? extends QName> getNamesOfAssociationsWithOutboundExpressions() { return getAssociationDefinitions().stream() .filter(assocDef -> assocDef.getOutboundMappingType() != null) .map(a -> a.getName()) .collect(Collectors.toCollection(HashSet::new)); } //endregion //region General information ======================================================== @Override public String getDisplayName() { return displayName; } private void setDisplayName(String displayName) { this.displayName = displayName; } @Override public String getDescription() { return description; } private void setDescription(String description) { this.description = description; } @Override public ObjectClassComplexTypeDefinition getObjectClassDefinition() { return originalObjectClassDefinition; } @NotNull @Override public ResourceType getResourceType() { return resourceType; } @Override public String getResourceNamespace() { return ResourceTypeUtil.getResourceNamespace(getResourceType()); } @Override public boolean isDefault() { return isDefault; } private void setDefault(boolean isDefault) { this.isDefault = isDefault; } @Override public boolean isDefaultInAKind() { return isDefault; } @Override public ResourceObjectReferenceType getBaseContext() { return baseContext; } private void setBaseContext(ResourceObjectReferenceType baseContext) { this.baseContext = baseContext; } @Override public String getIntent() { return intent; } public void setIntent(String intent) { this.intent = intent; } @Override public ShadowKindType getKind() { if (kind != null) { return kind; } return getObjectClassDefinition().getKind(); } public void setKind(ShadowKindType kind) { this.kind = kind; } @Override public ResourceObjectVolatilityType getVolatility() { if (schemaHandlingObjectTypeDefinitionType == null) { return null; } return schemaHandlingObjectTypeDefinitionType.getVolatility(); } //endregion //region Generating and matching artifacts ======================================================== @Override public PrismObjectDefinition<ShadowType> getObjectDefinition() { if (objectDefinition == null) { objectDefinition = constructObjectDefinition(this); } return objectDefinition; } static PrismObjectDefinition<ShadowType> constructObjectDefinition(RefinedObjectClassDefinition refinedObjectClassDefinition) { // Almost-shallow clone of object definition and complex type PrismObjectDefinition<ShadowType> originalObjectDefinition = refinedObjectClassDefinition.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(ShadowType.class); return originalObjectDefinition.cloneWithReplacedDefinition(ShadowType.F_ATTRIBUTES, refinedObjectClassDefinition.toResourceAttributeContainerDefinition()); } @Override public PrismObject<ShadowType> createBlankShadow(RefinedObjectClassDefinition definition) { PrismObject<ShadowType> accountShadow; try { accountShadow = getPrismContext().createObject(ShadowType.class); } catch (SchemaException e) { // This should not happen throw new SystemException("Internal error instantiating account shadow: "+e.getMessage(), e); } ShadowType accountShadowType = accountShadow.asObjectable(); accountShadowType.setIntent(getIntent()); accountShadowType.setKind(getKind()); accountShadowType.setObjectClass(getObjectClassDefinition().getTypeName()); accountShadowType.setResourceRef(ObjectTypeUtil.createObjectRef(getResourceType())); // Setup definition PrismObjectDefinition<ShadowType> newDefinition = accountShadow.getDefinition().cloneWithReplacedDefinition( ShadowType.F_ATTRIBUTES, definition.toResourceAttributeContainerDefinition()); accountShadow.setDefinition(newDefinition); return accountShadow; } @Override public ResourceShadowDiscriminator getShadowDiscriminator() { return new ResourceShadowDiscriminator(getResourceType().getOid(), getKind(), getIntent()); } @Override public boolean matches(ShadowType shadowType) { if (shadowType == null) { return false; } if (!QNameUtil.match(getObjectClassDefinition().getTypeName(), shadowType.getObjectClass())) { return false; } if (shadowType.getKind() == null) { if (kind != ShadowKindType.ACCOUNT) { return false; } } else { if (!MiscUtil.equals(kind, shadowType.getKind())) { return false; } } if (shadowType.getIntent() != null) { // if (isDefault) { // return true; // } else { // return false; // } // } else { return MiscUtil.equals(intent, shadowType.getIntent()); } return true; } @Override public ObjectQuery createShadowSearchQuery(String resourceOid) throws SchemaException { if (getKind() == null) { return ObjectQueryUtil.createResourceAndObjectClassQuery(resourceOid, getTypeName(), getPrismContext()); } else { return ObjectQueryUtil.createResourceAndKindIntent(resourceOid, getKind(), getIntent(), getPrismContext()); } } //endregion //region Accessing parts of schema handling ======================================================== @NotNull @Override public Collection<RefinedObjectClassDefinition> getAuxiliaryObjectClassDefinitions() { return auxiliaryObjectClassDefinitions; } @Override public boolean hasAuxiliaryObjectClass(QName expectedObjectClassName) { return auxiliaryObjectClassDefinitions.stream() .anyMatch(def -> QNameUtil.match(def.getTypeName(), expectedObjectClassName)); } @Override public Collection<ResourceObjectPattern> getProtectedObjectPatterns() { return protectedObjectPatterns; } @Override public ResourcePasswordDefinitionType getPasswordDefinition() { if (schemaHandlingObjectTypeDefinitionType == null) { return null; } ResourceCredentialsDefinitionType credentials = schemaHandlingObjectTypeDefinitionType.getCredentials(); if (credentials == null) { return null; } return credentials.getPassword(); } @Override public List<MappingType> getPasswordInbound() { ResourcePasswordDefinitionType password = getPasswordDefinition(); if (password == null || password.getInbound() == null) { return null; } return password.getInbound(); } @Override public List<MappingType> getPasswordOutbound() { ResourcePasswordDefinitionType password = getPasswordDefinition(); if (password == null || password.getOutbound() == null) { return null; } return password.getOutbound(); } @Override public AttributeFetchStrategyType getPasswordFetchStrategy() { ResourcePasswordDefinitionType password = getPasswordDefinition(); if (password == null) { return AttributeFetchStrategyType.IMPLICIT; } if (password.getFetchStrategy() == null) { return AttributeFetchStrategyType.IMPLICIT; } return password.getFetchStrategy(); } @Override public ObjectReferenceType getPasswordPolicy() { ResourcePasswordDefinitionType password = getPasswordDefinition(); if (password == null || password.getPasswordPolicyRef() == null){ return null; } return password.getPasswordPolicyRef(); } @Override public ResourceActivationDefinitionType getActivationSchemaHandling(){ if (schemaHandlingObjectTypeDefinitionType == null) { return null; } return schemaHandlingObjectTypeDefinitionType.getActivation(); } @Override public ResourceBidirectionalMappingType getActivationBidirectionalMappingType(QName propertyName) { ResourceActivationDefinitionType activationSchemaHandling = getActivationSchemaHandling(); if (activationSchemaHandling == null) { return null; } if (QNameUtil.match(ActivationType.F_ADMINISTRATIVE_STATUS, propertyName)) { return activationSchemaHandling.getAdministrativeStatus(); } else if (QNameUtil.match(ActivationType.F_VALID_FROM, propertyName)) { return activationSchemaHandling.getValidFrom(); } else if (QNameUtil.match(ActivationType.F_VALID_TO, propertyName)) { return activationSchemaHandling.getValidTo(); } else if (QNameUtil.match(ActivationType.F_LOCKOUT_STATUS, propertyName)) { return activationSchemaHandling.getLockoutStatus(); } else if (QNameUtil.match(ActivationType.F_LOCKOUT_EXPIRATION_TIMESTAMP, propertyName)) { return null; // todo implement this } else { throw new IllegalArgumentException("Unknown activation property "+propertyName); } } @Override public AttributeFetchStrategyType getActivationFetchStrategy(QName propertyName) { ResourceBidirectionalMappingType biType = getActivationBidirectionalMappingType(propertyName); if (biType == null) { return AttributeFetchStrategyType.IMPLICIT; } if (biType.getFetchStrategy() == null) { return AttributeFetchStrategyType.IMPLICIT; } return biType.getFetchStrategy(); } //endregion //region Capabilities ======================================================== @Override public <T extends CapabilityType> T getEffectiveCapability(Class<T> capabilityClass) { return ResourceTypeUtil.getEffectiveCapability(getResourceType(), schemaHandlingObjectTypeDefinitionType, capabilityClass); } @Override public PagedSearchCapabilityType getPagedSearches() { return getEffectiveCapability(PagedSearchCapabilityType.class); } @Override public boolean isPagedSearchEnabled() { return getPagedSearches() != null; // null means nothing or disabled } @Override public boolean isObjectCountingEnabled() { return getEffectiveCapability(CountObjectsCapabilityType.class) != null; } //endregion //region Cloning ======================================================== @NotNull @Override public RefinedObjectClassDefinitionImpl clone() { RefinedObjectClassDefinitionImpl clone = new RefinedObjectClassDefinitionImpl(resourceType, originalObjectClassDefinition); copyDefinitionData(clone); return clone; } // assuming we are called on empty object private void copyDefinitionData(RefinedObjectClassDefinitionImpl clone) { clone.attributeDefinitions.addAll(cloneDefinitions(this.attributeDefinitions)); clone.associationDefinitions.addAll(cloneAssociations(this.associationDefinitions)); clone.auxiliaryObjectClassDefinitions.addAll(auxiliaryObjectClassDefinitions); clone.schemaHandlingObjectTypeDefinitionType = this.schemaHandlingObjectTypeDefinitionType; clone.intent = this.intent; clone.kind = this.kind; clone.displayName = this.displayName; clone.description = this.description; clone.isDefault = this.isDefault; clone.identifiers.addAll(cloneDefinitions(this.identifiers)); clone.secondaryIdentifiers.addAll(cloneDefinitions(this.secondaryIdentifiers)); clone.protectedObjectPatterns.addAll(this.protectedObjectPatterns); clone.baseContext = this.baseContext; } @NotNull @Override public RefinedObjectClassDefinition deepClone(Map<QName, ComplexTypeDefinition> ctdMap) { // TODO TODO TODO (note that in original implementation this was also missing...) return clone(); } private Collection<RefinedAssociationDefinition> cloneAssociations(Collection<RefinedAssociationDefinition> origAsoc) { return origAsoc.stream() .map(RefinedAssociationDefinition::clone) .collect(Collectors.toList()); } private List<? extends RefinedAttributeDefinition<?>> cloneDefinitions(Collection<? extends RefinedAttributeDefinition<?>> origDefs) { return origDefs.stream() .map(RefinedAttributeDefinition::clone) .collect(Collectors.toList()); } //endregion /** * Creates a derived version of this ROCD for a given layer. * TODO clone if necessary/if specified (currently there is no cloning) * * @param layerType * @return */ @Override public LayerRefinedObjectClassDefinition forLayer(@NotNull LayerType layerType) { Validate.notNull(layerType); return LayerRefinedObjectClassDefinitionImpl.wrap(this, layerType); } //region Delegations ======================================================== @NotNull @Override public QName getTypeName() { return getObjectClassDefinition().getTypeName(); } @Override public String getNativeObjectClass() { return getObjectClassDefinition().getNativeObjectClass(); } public boolean isAuxiliary() { return getObjectClassDefinition().isAuxiliary(); } public PrismContext getPrismContext() { return getResourceType().asPrismObject().getPrismContext(); } @Nullable @Override public Class<?> getCompileTimeClass() { return originalObjectClassDefinition.getCompileTimeClass(); // most probably null } @Nullable @Override public QName getExtensionForType() { return originalObjectClassDefinition.getExtensionForType(); // most probably null } @Override public boolean isContainerMarker() { return originalObjectClassDefinition.isContainerMarker(); // most probably false } @Override public boolean isObjectMarker() { return originalObjectClassDefinition.isObjectMarker(); // most probably false } @Override public boolean isXsdAnyMarker() { return originalObjectClassDefinition.isXsdAnyMarker(); } // TODO @Override public <ID extends ItemDefinition> ID findItemDefinition(@NotNull ItemPath path, @NotNull Class<ID> clazz) { if (path.size() != 1) { return null; } QName first = ItemPath.getFirstName(path); if (first == null) { return null; } return findItemDefinition(first, clazz); } // TODO @Override public <ID extends ItemDefinition> ID findNamedItemDefinition(@NotNull QName firstName, @NotNull ItemPath rest, @NotNull Class<ID> clazz) { return findItemDefinition(firstName); } @Nullable @Override public String getDefaultNamespace() { return originalObjectClassDefinition.getDefaultNamespace(); } @Override public boolean isRuntimeSchema() { return originalObjectClassDefinition.isRuntimeSchema(); } @NotNull @Override public List<String> getIgnoredNamespaces() { return originalObjectClassDefinition.getIgnoredNamespaces(); } @Nullable @Override public QName getSuperType() { return originalObjectClassDefinition.getSuperType(); } @Override public void merge(ComplexTypeDefinition otherComplexTypeDef) { throw new UnsupportedOperationException("TODO implement this"); } @Override public void revive(PrismContext prismContext) { originalObjectClassDefinition.revive(prismContext); // TODO revive attributes } @Override public boolean isIgnored() { return originalObjectClassDefinition.isIgnored(); } @Override public boolean isAbstract() { return originalObjectClassDefinition.isAbstract(); } @Override public boolean isEmpty() { return attributeDefinitions.isEmpty() && associationDefinitions.isEmpty(); } @Override public boolean isDeprecated() { return originalObjectClassDefinition.isDeprecated(); } @Override public boolean isEmphasized() { return originalObjectClassDefinition.isEmphasized(); } @Override public Integer getDisplayOrder() { return originalObjectClassDefinition.getDisplayOrder(); } @Override public String getHelp() { return originalObjectClassDefinition.getHelp(); } @Override public String getDocumentation() { return originalObjectClassDefinition.getDocumentation(); } @Override public String getDocumentationPreview() { return originalObjectClassDefinition.getDocumentationPreview(); } @Override public Class getTypeClassIfKnown() { return originalObjectClassDefinition.getTypeClassIfKnown(); } @Override public Class getTypeClass() { return originalObjectClassDefinition.getTypeClass(); } @Override public ResourceAttributeContainer instantiate(QName elementName) { return ObjectClassComplexTypeDefinitionImpl.instantiate(elementName, this); } @Override public boolean isListMarker() { return originalObjectClassDefinition.isListMarker(); } //endregion //region ==== Parsing ================================================================================= static RefinedObjectClassDefinition parse(ResourceObjectTypeDefinitionType entTypeDefType, ResourceType resourceType, RefinedResourceSchema rSchema, ShadowKindType impliedKind, PrismContext prismContext, String contextDescription) throws SchemaException { ShadowKindType kind = entTypeDefType.getKind(); if (kind == null) { kind = impliedKind; } if (kind == null) { kind = ShadowKindType.ACCOUNT; } String intent = entTypeDefType.getIntent(); if (intent == null) { intent = SchemaConstants.INTENT_DEFAULT; } RefinedObjectClassDefinition rObjectClassDef = parseRefinedObjectClass(entTypeDefType, resourceType, rSchema, prismContext, kind, intent, kind.value(), kind.value() + " type definition '"+intent+"' in " + contextDescription); if (entTypeDefType.getPagedSearches() != null) { LOGGER.warn("PagedSearches element is no more supported and is ignored. Use PagedSearchCapabilityType instead. In {}", resourceType); } return rObjectClassDef; } private static void parseProtected(RefinedObjectClassDefinition rAccountDef, ResourceObjectTypeDefinitionType accountTypeDefType) throws SchemaException { for (ResourceObjectPatternType protectedType: accountTypeDefType.getProtected()) { ResourceObjectPattern protectedPattern = convertToPattern(protectedType, rAccountDef); rAccountDef.getProtectedObjectPatterns().add(protectedPattern); } } private static ResourceObjectPattern convertToPattern(ResourceObjectPatternType patternType, RefinedObjectClassDefinition rAccountDef) throws SchemaException { ResourceObjectPattern resourceObjectPattern = new ResourceObjectPattern(rAccountDef); SearchFilterType filterType = patternType.getFilter(); if (filterType != null) { ObjectFilter filter = QueryConvertor.parseFilter(filterType, rAccountDef.getObjectDefinition()); resourceObjectPattern.addFilter(filter); return resourceObjectPattern; } // Deprecated if (patternType.getName() != null) { RefinedAttributeDefinition attributeDefinition = rAccountDef.findAttributeDefinition(new QName(SchemaConstants.NS_ICF_SCHEMA,"name")); if (attributeDefinition == null) { throw new SchemaException("No ICF NAME attribute in schema as specified in the definition of protected objects (this is deprecated syntax anyway, convert it to filter)"); } ResourceAttribute<String> attr = attributeDefinition.instantiate(); attr.setRealValue(patternType.getName()); resourceObjectPattern.addIdentifier(attr); } else if (patternType.getUid() != null) { RefinedAttributeDefinition attributeDefinition = rAccountDef.findAttributeDefinition(new QName(SchemaConstants.NS_ICF_SCHEMA,"uid")); if (attributeDefinition == null) { throw new SchemaException("No ICF UID attribute in schema as specified in the definition of protected objects (this is deprecated syntax anyway, convert it to filter)"); } ResourceAttribute<String> attr = attributeDefinition.instantiate(); attr.setRealValue(patternType.getUid()); resourceObjectPattern.addIdentifier(attr); } else { throw new SchemaException("No filter and no deprecated name/uid in resource object pattern"); } return resourceObjectPattern; } public static RefinedObjectClassDefinition parseFromSchema(ObjectClassComplexTypeDefinition objectClassDef, ResourceType resourceType, RefinedResourceSchema rSchema, PrismContext prismContext, String contextDescription) throws SchemaException { RefinedObjectClassDefinitionImpl rOcDef = new RefinedObjectClassDefinitionImpl(resourceType, objectClassDef); String intent = objectClassDef.getIntent(); if (intent == null && objectClassDef.isDefaultInAKind()) { intent = SchemaConstants.INTENT_DEFAULT; } rOcDef.setIntent(intent); if (objectClassDef.getDisplayName() != null) { rOcDef.setDisplayName(objectClassDef.getDisplayName()); } rOcDef.setDefault(objectClassDef.isDefaultInAKind()); for (ResourceAttributeDefinition attrDef : objectClassDef.getAttributeDefinitions()) { String attrContextDescription = intent + ", in " + contextDescription; RefinedAttributeDefinition rAttrDef = RefinedAttributeDefinitionImpl.parse(attrDef, null, objectClassDef, prismContext, attrContextDescription); rOcDef.processIdentifiers(rAttrDef, objectClassDef); if (rOcDef.containsAttributeDefinition(rAttrDef.getName())) { throw new SchemaException("Duplicate definition of attribute " + rAttrDef.getName() + " in " + attrContextDescription); } rOcDef.add(rAttrDef); } return rOcDef; } private static RefinedObjectClassDefinition parseRefinedObjectClass(ResourceObjectTypeDefinitionType schemaHandlingObjDefType, ResourceType resourceType, RefinedResourceSchema rSchema, PrismContext prismContext, @NotNull ShadowKindType kind, @NotNull String intent, String typeDesc, String contextDescription) throws SchemaException { ObjectClassComplexTypeDefinition objectClassDef; if (schemaHandlingObjDefType.getObjectClass() != null) { QName objectClass = schemaHandlingObjDefType.getObjectClass(); objectClassDef = rSchema.getOriginalResourceSchema().findObjectClassDefinition(objectClass); if (objectClassDef == null) { throw new SchemaException("Object class " + objectClass + " as specified in "+typeDesc+" type " + schemaHandlingObjDefType.getIntent() + " was not found in the resource schema of " + contextDescription); } } else { throw new SchemaException("Definition of "+typeDesc+" type " + schemaHandlingObjDefType.getIntent() + " does not have objectclass, in " + contextDescription); } RefinedObjectClassDefinitionImpl rOcDef = new RefinedObjectClassDefinitionImpl(resourceType, objectClassDef); rOcDef.setKind(kind); rOcDef.schemaHandlingObjectTypeDefinitionType = schemaHandlingObjDefType; rOcDef.setIntent(intent); if (schemaHandlingObjDefType.getDisplayName() != null) { rOcDef.setDisplayName(schemaHandlingObjDefType.getDisplayName()); } else { if (objectClassDef.getDisplayName() != null) { rOcDef.setDisplayName(objectClassDef.getDisplayName()); } } if (schemaHandlingObjDefType.getDescription() != null) { rOcDef.setDescription(schemaHandlingObjDefType.getDescription()); } if (schemaHandlingObjDefType.isDefault() != null) { rOcDef.setDefault(schemaHandlingObjDefType.isDefault()); } else { rOcDef.setDefault(objectClassDef.isDefaultInAKind()); } if (schemaHandlingObjDefType.getBaseContext() != null) { rOcDef.setBaseContext(schemaHandlingObjDefType.getBaseContext()); } return rOcDef; } void parseAssociations(RefinedResourceSchema rSchema) throws SchemaException { if (schemaHandlingObjectTypeDefinitionType == null) { return; } for (ResourceObjectAssociationType resourceObjectAssociationType: schemaHandlingObjectTypeDefinitionType.getAssociation()) { RefinedAssociationDefinition rAssocDef = new RefinedAssociationDefinition(resourceObjectAssociationType); ShadowKindType assocKind = rAssocDef.getKind(); RefinedObjectClassDefinition assocTarget = rSchema.getRefinedDefinition(assocKind, rAssocDef.getIntents()); rAssocDef.setAssociationTarget(assocTarget); associationDefinitions.add(rAssocDef); } } void parseAuxiliaryObjectClasses(RefinedResourceSchema rSchema) throws SchemaException { if (schemaHandlingObjectTypeDefinitionType == null) { return; } List<QName> auxiliaryObjectClassQNames = schemaHandlingObjectTypeDefinitionType.getAuxiliaryObjectClass(); for (QName auxiliaryObjectClassQName: auxiliaryObjectClassQNames) { RefinedObjectClassDefinition auxiliaryObjectClassDef = rSchema.getRefinedDefinition(auxiliaryObjectClassQName); if (auxiliaryObjectClassDef == null) { throw new SchemaException("Auxiliary object class "+auxiliaryObjectClassQName+" specified in "+this+" does not exist"); } auxiliaryObjectClassDefinitions.add(auxiliaryObjectClassDef); } } void parseAttributes(RefinedResourceSchema rSchema, String contextDescription) throws SchemaException { if (schemaHandlingObjectTypeDefinitionType == null) { // this is definition from schema. We already have all we need. return; } parseAttributesFrom(rSchema, getObjectClassDefinition(), false, contextDescription); for (RefinedObjectClassDefinition auxiliaryObjectClassDefinition: auxiliaryObjectClassDefinitions) { parseAttributesFrom(rSchema, auxiliaryObjectClassDefinition, true, contextDescription); } // Check for extra attribute definitions in the account type for (ResourceAttributeDefinitionType attrDefType : schemaHandlingObjectTypeDefinitionType.getAttribute()) { if (!containsAttributeDefinition(attrDefType.getRef()) && !RefinedAttributeDefinitionImpl.isIgnored(attrDefType)) { throw new SchemaException("Definition of attribute " + attrDefType.getRef() + " not found in object class " + originalObjectClassDefinition .getTypeName() + " as defined in " + contextDescription); } } parseProtected(this, schemaHandlingObjectTypeDefinitionType); } private void parseAttributesFrom(RefinedResourceSchema rSchema, ObjectClassComplexTypeDefinition ocDef, boolean auxiliary, String contextDescription) throws SchemaException { if (schemaHandlingObjectTypeDefinitionType == null) { // this is definition from schema. We already have all we need. return; } for (ResourceAttributeDefinition road : ocDef.getAttributeDefinitions()) { String attrContextDescription = road.getName() + ", in " + contextDescription; ResourceAttributeDefinitionType attrDefType = findAttributeDefinitionType(road.getName(), schemaHandlingObjectTypeDefinitionType, attrContextDescription); // We MUST NOT skip ignored attribute definitions here. We must include them in the schema as // the shadows will still have that attributes and we will need their type definition to work // well with them. They may also be mandatory. We cannot pretend that they do not exist. // TODO !!!! fix the cast RefinedAttributeDefinition<?> rAttrDef = (RefinedAttributeDefinition<?>) RefinedAttributeDefinitionImpl.parse(road, attrDefType, ocDef, rSchema.getPrismContext(), "in "+kind+" type " + intent + ", in " + contextDescription); if (!auxiliary) { processIdentifiers(rAttrDef, ocDef); } if (containsAttributeDefinition(rAttrDef.getName())) { if (auxiliary) { continue; } else { throw new SchemaException("Duplicate definition of attribute " + rAttrDef.getName() + " in "+kind+" type " + intent + ", in " + contextDescription); } } add(rAttrDef); if (rAttrDef.isDisplayNameAttribute()) { displayNameAttributeDefinition = rAttrDef; } } } private void processIdentifiers(RefinedAttributeDefinition rAttrDef, ObjectClassComplexTypeDefinition objectClassDef) { QName attrName = rAttrDef.getName(); if (objectClassDef.isPrimaryIdentifier(attrName)) { ((Collection)getPrimaryIdentifiers()).add(rAttrDef); } if (objectClassDef.isSecondaryIdentifier(attrName) || rAttrDef.isSecondaryIdentifier()) { ((Collection)getSecondaryIdentifiers()).add(rAttrDef); } } private ResourceAttributeDefinitionType findAttributeDefinitionType(QName attrName, ResourceObjectTypeDefinitionType rOcDefType, String contextDescription) throws SchemaException { ResourceAttributeDefinitionType foundAttrDefType = null; for (ResourceAttributeDefinitionType attrDefType : rOcDefType.getAttribute()) { if (attrDefType.getRef() != null) { QName ref = ItemPathUtil.getOnlySegmentQName(attrDefType.getRef()); if (QNameUtil.match(ref, attrName)) { if (foundAttrDefType == null) { foundAttrDefType = attrDefType; } else { throw new SchemaException("Duplicate definition of attribute " + ref + " in "+kind+" type " + rOcDefType.getIntent() + ", in " + contextDescription); } } } else { throw new SchemaException("Missing reference to the attribute schema definition in definition " + SchemaDebugUtil.prettyPrint(attrDefType) + " during processing of " + contextDescription); } } return foundAttrDefType; } private void add(RefinedAttributeDefinition<?> refinedAttributeDefinition) { attributeDefinitions.add(refinedAttributeDefinition); } //endregion //region Diagnostic output, hashCode/equals ========================================================= @Override public String debugDump() { return debugDump(0); } @Override public String debugDump(int indent) { return debugDump(indent, null, this); } public static String debugDump(int indent, LayerType layer, RefinedObjectClassDefinition _this) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indent; i++) { sb.append(INDENT_STRING); } sb.append(_this.getDebugDumpClassName()).append("("); sb.append(SchemaDebugUtil.prettyPrint(_this.getTypeName())); if (_this.isDefault()) { sb.append(",default"); } if (_this.getKind() != null) { sb.append(" ").append(_this.getKind().value()); } sb.append(","); if (_this.getIntent() != null) { sb.append("intent=").append(_this.getIntent()); } if (layer != null) { sb.append(",layer=").append(layer); } sb.append(")"); for (RefinedAttributeDefinition rAttrDef: _this.getAttributeDefinitions()) { sb.append("\n"); sb.append(rAttrDef.debugDump(indent + 1, layer)); } return sb.toString(); } /** * Return a human readable name of this class suitable for logs. */ public String getDebugDumpClassName() { return "rOCD"; } @Override public String getHumanReadableName() { if (getDisplayName() != null) { return getDisplayName(); } else if (getKind() != null) { return getKind()+":"+getIntent(); } else { return getTypeName().getLocalPart(); } } @Override public String toString() { if (getKind() == null) { return getDebugDumpClassName() + "("+PrettyPrinter.prettyPrint(getTypeName())+")"; } else { return getDebugDumpClassName() + "("+getKind()+":"+getIntent()+"="+PrettyPrinter.prettyPrint(getTypeName())+")"; } } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + associationDefinitions.hashCode(); result = prime * result + attributeDefinitions.hashCode(); result = prime * result + auxiliaryObjectClassDefinitions.hashCode(); result = prime * result + ((baseContext == null) ? 0 : baseContext.hashCode()); result = prime * result + ((description == null) ? 0 : description.hashCode()); result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); result = prime * result + ((displayNameAttributeDefinition == null) ? 0 : displayNameAttributeDefinition.hashCode()); result = prime * result + ((identifiers == null) ? 0 : identifiers.hashCode()); result = prime * result + ((intent == null) ? 0 : intent.hashCode()); result = prime * result + (isDefault ? 1231 : 1237); result = prime * result + ((kind == null) ? 0 : kind.hashCode()); result = prime * result + originalObjectClassDefinition.hashCode(); result = prime * result + ((objectDefinition == null) ? 0 : objectDefinition.hashCode()); result = prime * result + ((protectedObjectPatterns == null) ? 0 : protectedObjectPatterns.hashCode()); result = prime * result + resourceType.hashCode(); result = prime * result + ((schemaHandlingObjectTypeDefinitionType == null) ? 0 : schemaHandlingObjectTypeDefinitionType.hashCode()); result = prime * result + ((secondaryIdentifiers == null) ? 0 : secondaryIdentifiers.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } RefinedObjectClassDefinitionImpl other = (RefinedObjectClassDefinitionImpl) obj; if (!associationDefinitions.equals(other.associationDefinitions)) { return false; } if (!attributeDefinitions.equals(other.attributeDefinitions)) { return false; } if (!auxiliaryObjectClassDefinitions.equals(other.auxiliaryObjectClassDefinitions)) { return false; } if (baseContext == null) { if (other.baseContext != null) { return false; } } else if (!baseContext.equals(other.baseContext)) { return false; } if (description == null) { if (other.description != null) { return false; } } else if (!description.equals(other.description)) { return false; } if (displayName == null) { if (other.displayName != null) { return false; } } else if (!displayName.equals(other.displayName)) { return false; } if (displayNameAttributeDefinition == null) { if (other.displayNameAttributeDefinition != null) { return false; } } else if (!displayNameAttributeDefinition.equals(other.displayNameAttributeDefinition)) { return false; } if (identifiers == null) { if (other.identifiers != null) { return false; } } else if (!identifiers.equals(other.identifiers)) { return false; } if (intent == null) { if (other.intent != null) { return false; } } else if (!intent.equals(other.intent)) { return false; } if (isDefault != other.isDefault) { return false; } if (kind != other.kind) { return false; } if (!originalObjectClassDefinition.equals(other.originalObjectClassDefinition)) { return false; } if (objectDefinition == null) { if (other.objectDefinition != null) { return false; } } else if (!objectDefinition.equals(other.objectDefinition)) { return false; } if (protectedObjectPatterns == null) { if (other.protectedObjectPatterns != null) { return false; } } else if (!protectedObjectPatterns.equals(other.protectedObjectPatterns)) { return false; } if (!resourceType.equals(other.resourceType)) { return false; } if (schemaHandlingObjectTypeDefinitionType == null) { if (other.schemaHandlingObjectTypeDefinitionType != null) { return false; } } else if (!schemaHandlingObjectTypeDefinitionType.equals(other.schemaHandlingObjectTypeDefinitionType)) { return false; } if (secondaryIdentifiers == null) { if (other.secondaryIdentifiers != null) { return false; } } else if (!secondaryIdentifiers.equals(other.secondaryIdentifiers)) { return false; } return true; } //endregion //region Typing overhead ============================================================== /* * There is a natural correspondence between "type definition" classes and items in these classes: * * ComplexTypeDefinition .............................. ItemDefinition * ObjectClassComplexTypeDefinition ................... ResourceAttributeDefinition * RefinedObjectClassDefinition ....................... RefinedAttributeDefinition * LayerRefinedObjectClassDefinition .................. LayerRefinedAttributeDefinition * * It would be great if the interface of "type definition" classes, i.e. methods like getDefinitions(), * findItemDefinition, findAttributeDefinition, and so on would be parametrized on the type of item definitions * from the list above. Unfortunately, this would make clients very unintuitive, using interfaces like * * RefinedObjectClassDefinition<RefinedAttributeDefinition<?>> * * Therefore the decision is to keep clients' lives simple; at the cost of "typing overhead" - providing correct * signatures of derived types. In order to keep it manageable we put all such methods in this single section. */ @Override public <X> RefinedAttributeDefinition<X> findAttributeDefinition(@NotNull QName name) { return findItemDefinition(name, RefinedAttributeDefinition.class, false); } //endregion }