/* * 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 java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import com.evolveum.midpoint.prism.ComplexTypeDefinition; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinitionImpl; import org.apache.commons.lang.BooleanUtils; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.schema.SchemaProcessorUtil; import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition; import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.DisplayableValue; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.AttributeFetchStrategyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.LayerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PropertyAccessType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PropertyLimitationsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceAttributeDefinitionType; import org.jetbrains.annotations.NotNull; /** * @author semancik */ public class RefinedAttributeDefinitionImpl<T> extends ResourceAttributeDefinitionImpl<T> implements RefinedAttributeDefinition<T> { private static LayerType DEFAULT_LAYER = LayerType.MODEL; private String displayName; private String description; private boolean tolerant = true; private boolean isExclusiveStrong = false; protected boolean secondaryIdentifier = false; private boolean isDisplayNameAttribute = false; private List<String> intolerantValuePattern; private List<String> tolerantValuePattern; private ResourceAttributeDefinition<T> attributeDefinition; private AttributeFetchStrategyType fetchStrategy; private MappingType outboundMappingType; private List<MappingType> inboundMappingTypes; private Map<LayerType,PropertyLimitations> limitationsMap = new HashMap<>(); private QName matchingRuleQName = null; private Integer modificationPriority; private Boolean readReplaceMode; private PropertyAccessType accessOverride = new PropertyAccessType(); private boolean isVolatilityTrigger = false; protected RefinedAttributeDefinitionImpl(ResourceAttributeDefinition<T> attrDef, PrismContext prismContext) { super(attrDef.getName(), attrDef.getTypeName(), prismContext); this.attributeDefinition = attrDef; } @Override public void setNativeAttributeName(String nativeAttributeName) { throw new UnsupportedOperationException("Parts of refined attribute are immutable"); } @Override public boolean isTolerant() { return tolerant; } public void setTolerant(boolean tolerant) { this.tolerant = tolerant; } @Override public boolean isSecondaryIdentifier() { return secondaryIdentifier; } public void setSecondaryIdentifier(boolean secondaryIdentifier) { this.secondaryIdentifier = secondaryIdentifier; } @Override public boolean canAdd() { return canAdd(DEFAULT_LAYER); } @Override public boolean canAdd(LayerType layer) { if (accessOverride.isAdd() != null) { return accessOverride.isAdd(); } return limitationsMap.get(layer).getAccess().isAdd(); } @Override public boolean canRead() { return canRead(DEFAULT_LAYER); } @Override public boolean canRead(LayerType layer) { if (accessOverride.isRead() != null) { return accessOverride.isRead(); } return limitationsMap.get(layer).getAccess().isRead(); } @Override public boolean canModify() { return canModify(DEFAULT_LAYER); } @Override public boolean canModify(LayerType layer) { if (accessOverride.isModify() != null) { return accessOverride.isModify(); } return limitationsMap.get(layer).getAccess().isModify(); } @Override public void setReadOnly() { setCanRead(false); } @Override public QName getValueType() { return attributeDefinition.getValueType(); } @Override public void setMinOccurs(int minOccurs) { throw new UnsupportedOperationException("Parts of refined attribute are immutable"); } @Override public void setMaxOccurs(int maxOccurs) { throw new UnsupportedOperationException("Parts of refined attribute are immutable"); } @Override public void setCanRead(boolean read) { accessOverride.setRead(read); } @Override public void setCanModify(boolean update) { accessOverride.setModify(update); } @Override public void setCanAdd(boolean create) { accessOverride.setAdd(create); } @Override public boolean isIgnored() { return isIgnored(DEFAULT_LAYER); } @Override public boolean isIgnored(LayerType layer) { return limitationsMap.get(layer).isIgnore(); } @Override public void setIgnored(boolean ignored) { throw new UnsupportedOperationException("Parts of refined attribute are immutable"); } @Override public void setHelp(String help) { throw new UnsupportedOperationException("Parts of refined attribute are immutable"); } @Override public String getDisplayName() { return displayName; } @Override public void setDisplayName(String displayName) { this.displayName = displayName; } @Override public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public ResourceAttributeDefinition<T> getAttributeDefinition() { return attributeDefinition; } public void setAttributeDefinition(ResourceAttributeDefinition<T> attributeDefinition) { this.attributeDefinition = attributeDefinition; } @Override public MappingType getOutboundMappingType() { return outboundMappingType; } public void setOutboundMappingType(MappingType outboundMappingType) { this.outboundMappingType = outboundMappingType; } @Override public boolean hasOutboundMapping() { return outboundMappingType != null; } @Override public List<MappingType> getInboundMappingTypes() { return inboundMappingTypes; } public void setInboundMappingTypes(List<MappingType> inboundAssignmentTypes) { this.inboundMappingTypes = inboundAssignmentTypes; } @NotNull public QName getName() { return attributeDefinition.getName(); } @NotNull public QName getTypeName() { return attributeDefinition.getTypeName(); } public String getNativeAttributeName() { return attributeDefinition.getNativeAttributeName(); } public String getFrameworkAttributeName() { return attributeDefinition.getFrameworkAttributeName(); } public Collection<? extends DisplayableValue<T>> getAllowedValues() { return attributeDefinition.getAllowedValues(); } public boolean isReturnedByDefault() { return attributeDefinition.isReturnedByDefault(); } public void setReturnedByDefault(Boolean returnedByDefault) { throw new UnsupportedOperationException("Cannot change returnedByDefault"); } @Override public int getMaxOccurs() { return getMaxOccurs(DEFAULT_LAYER); } @Override public int getMaxOccurs(LayerType layer) { return limitationsMap.get(layer).getMaxOccurs(); } @Override public int getMinOccurs() { return getMinOccurs(DEFAULT_LAYER); } @Override public int getMinOccurs(LayerType layer) { return limitationsMap.get(layer).getMinOccurs(); } @Override public boolean isOptional(LayerType layer) { return limitationsMap.get(layer).getMinOccurs() == 0; } @Override public boolean isMandatory(LayerType layer) { return limitationsMap.get(layer).getMinOccurs() > 0; } @Override public boolean isMultiValue(LayerType layer) { int maxOccurs = limitationsMap.get(layer).getMaxOccurs(); return maxOccurs < 0 || maxOccurs > 1; } @Override public boolean isSingleValue(LayerType layer) { return limitationsMap.get(layer).getMaxOccurs() == 1; } @Override public boolean isExlusiveStrong() { return isExclusiveStrong; } public void setExclusiveStrong(boolean isExclusiveStrong) { this.isExclusiveStrong = isExclusiveStrong; } @Override public PropertyLimitations getLimitations(LayerType layer) { return limitationsMap.get(layer); } public String getHelp() { return attributeDefinition.getHelp(); } @Override public AttributeFetchStrategyType getFetchStrategy() { return fetchStrategy; } public void setFetchStrategy(AttributeFetchStrategyType fetchStrategy) { this.fetchStrategy = fetchStrategy; } public QName getMatchingRuleQName() { return matchingRuleQName; } public void setMatchingRuleQName(QName matchingRuleQName) { this.matchingRuleQName = matchingRuleQName; } @Override public List<String> getTolerantValuePattern(){ return tolerantValuePattern; } @Override public List<String> getIntolerantValuePattern(){ return intolerantValuePattern; } @Override public boolean isVolatilityTrigger() { return isVolatilityTrigger; } public void setVolatilityTrigger(boolean isVolatilityTrigger) { this.isVolatilityTrigger = isVolatilityTrigger; } // schemaHandlingAttrDefType may be null if we are parsing from schema only static <T> RefinedAttributeDefinition<T> parse(ResourceAttributeDefinition<T> schemaAttrDef, ResourceAttributeDefinitionType schemaHandlingAttrDefType, ObjectClassComplexTypeDefinition objectClassDef, PrismContext prismContext, String contextDescription) throws SchemaException { RefinedAttributeDefinitionImpl<T> rAttrDef = new RefinedAttributeDefinitionImpl<T>(schemaAttrDef, prismContext); if (schemaHandlingAttrDefType != null && schemaHandlingAttrDefType.getDisplayName() != null) { rAttrDef.setDisplayName(schemaHandlingAttrDefType.getDisplayName()); } else { if (schemaAttrDef.getDisplayName() != null) { rAttrDef.setDisplayName(schemaAttrDef.getDisplayName()); } } if (schemaHandlingAttrDefType != null && schemaHandlingAttrDefType.getDisplayOrder() != null) { rAttrDef.setDisplayOrder(schemaHandlingAttrDefType.getDisplayOrder()); } else { if (schemaAttrDef.getDisplayOrder() != null) { rAttrDef.setDisplayOrder(schemaAttrDef.getDisplayOrder()); } } rAttrDef.matchingRuleQName = schemaAttrDef.getMatchingRuleQName(); if (schemaHandlingAttrDefType != null) { rAttrDef.fetchStrategy = schemaHandlingAttrDefType.getFetchStrategy(); if (schemaHandlingAttrDefType.getMatchingRule() != null) { rAttrDef.matchingRuleQName = schemaHandlingAttrDefType.getMatchingRule(); } } PropertyLimitations schemaLimitations = getOrCreateLimitations(rAttrDef.limitationsMap, LayerType.SCHEMA); schemaLimitations.setMinOccurs(schemaAttrDef.getMinOccurs()); schemaLimitations.setMaxOccurs(schemaAttrDef.getMaxOccurs()); schemaLimitations.setIgnore(schemaAttrDef.isIgnored()); schemaLimitations.getAccess().setAdd(schemaAttrDef.canAdd()); schemaLimitations.getAccess().setModify(schemaAttrDef.canModify()); schemaLimitations.getAccess().setRead(schemaAttrDef.canRead()); if (schemaHandlingAttrDefType != null) { if (schemaHandlingAttrDefType.getDescription() != null) { rAttrDef.setDescription(schemaHandlingAttrDefType.getDescription()); } if (schemaHandlingAttrDefType.isTolerant() == null) { rAttrDef.tolerant = true; } else { rAttrDef.tolerant = schemaHandlingAttrDefType.isTolerant(); } if (schemaHandlingAttrDefType.isSecondaryIdentifier() == null) { rAttrDef.secondaryIdentifier = false; } else { rAttrDef.secondaryIdentifier = schemaHandlingAttrDefType.isSecondaryIdentifier(); } rAttrDef.tolerantValuePattern = schemaHandlingAttrDefType.getTolerantValuePattern(); rAttrDef.intolerantValuePattern = schemaHandlingAttrDefType.getIntolerantValuePattern(); rAttrDef.isExclusiveStrong = BooleanUtils.isTrue(schemaHandlingAttrDefType.isExclusiveStrong()); rAttrDef.isVolatilityTrigger = BooleanUtils.isTrue(schemaHandlingAttrDefType.isVolatilityTrigger()); if (schemaHandlingAttrDefType.getOutbound() != null) { rAttrDef.setOutboundMappingType(schemaHandlingAttrDefType.getOutbound()); } if (schemaHandlingAttrDefType.getInbound() != null) { rAttrDef.setInboundMappingTypes(schemaHandlingAttrDefType.getInbound()); } rAttrDef.setModificationPriority(schemaHandlingAttrDefType.getModificationPriority()); rAttrDef.setReadReplaceMode(schemaHandlingAttrDefType.isReadReplaceMode()); // may be null at this point if (schemaHandlingAttrDefType.isDisplayNameAttribute() != null && schemaHandlingAttrDefType.isDisplayNameAttribute()) { rAttrDef.isDisplayNameAttribute = true; } } PropertyLimitations previousLimitations = null; for (LayerType layer : LayerType.values()) { PropertyLimitations limitations = getOrCreateLimitations(rAttrDef.limitationsMap, layer); if (previousLimitations != null) { limitations.setMinOccurs(previousLimitations.getMinOccurs()); limitations.setMaxOccurs(previousLimitations.getMaxOccurs()); limitations.setIgnore(previousLimitations.isIgnore()); limitations.getAccess().setAdd(previousLimitations.getAccess().isAdd()); limitations.getAccess().setRead(previousLimitations.getAccess().isRead()); limitations.getAccess().setModify(previousLimitations.getAccess().isModify()); } previousLimitations = limitations; if (schemaHandlingAttrDefType != null) { if (layer != LayerType.SCHEMA) { // SCHEMA is a pseudo-layer. It cannot be overriden ... unless specified explicitly PropertyLimitationsType genericLimitationsType = MiscSchemaUtil.getLimitationsType(schemaHandlingAttrDefType.getLimitations(), null); if (genericLimitationsType != null) { applyLimitationsType(limitations, genericLimitationsType); } } PropertyLimitationsType layerLimitationsType = MiscSchemaUtil.getLimitationsType(schemaHandlingAttrDefType.getLimitations(), layer); if (layerLimitationsType != null) { applyLimitationsType(limitations, layerLimitationsType); } } } return rAttrDef; } private static void applyLimitationsType(PropertyLimitations limitations, PropertyLimitationsType layerLimitationsType) { if (layerLimitationsType.getMinOccurs() != null) { limitations.setMinOccurs(SchemaProcessorUtil.parseMultiplicity(layerLimitationsType.getMinOccurs())); } if (layerLimitationsType.getMaxOccurs() != null) { limitations.setMaxOccurs(SchemaProcessorUtil.parseMultiplicity(layerLimitationsType.getMaxOccurs())); } if (layerLimitationsType.isIgnore() != null) { limitations.setIgnore(layerLimitationsType.isIgnore()); } if (layerLimitationsType.getAccess() != null) { PropertyAccessType accessType = layerLimitationsType.getAccess(); if (accessType.isAdd() != null) { limitations.getAccess().setAdd(accessType.isAdd()); } if (accessType.isRead() != null) { limitations.getAccess().setRead(accessType.isRead()); } if (accessType.isModify() != null) { limitations.getAccess().setModify(accessType.isModify()); } } } private static PropertyLimitations getOrCreateLimitations(Map<LayerType, PropertyLimitations> limitationsMap, LayerType layer) { PropertyLimitations limitations = limitationsMap.get(layer); if (limitations == null) { limitations = new PropertyLimitations(); limitationsMap.put(layer, limitations); } return limitations; } static boolean isIgnored(ResourceAttributeDefinitionType attrDefType) throws SchemaException { List<PropertyLimitationsType> limitations = attrDefType.getLimitations(); if (limitations == null) { return false; } PropertyLimitationsType limitationsType = MiscSchemaUtil.getLimitationsType(limitations, DEFAULT_LAYER); if (limitationsType == null) { return false; } if (limitationsType.isIgnore() == null) { return false; } return limitationsType.isIgnore(); } @NotNull @Override public RefinedAttributeDefinition<T> clone() { ResourceAttributeDefinition<T> attrDefClone = this.attributeDefinition.clone(); RefinedAttributeDefinitionImpl<T> clone = new RefinedAttributeDefinitionImpl<T>(attrDefClone, prismContext); copyDefinitionData(clone); return clone; } protected void copyDefinitionData(RefinedAttributeDefinitionImpl<T> clone) { super.copyDefinitionData(clone); clone.accessOverride = this.accessOverride.clone(); clone.description = this.description; clone.displayName = this.displayName; clone.fetchStrategy = this.fetchStrategy; clone.inboundMappingTypes = this.inboundMappingTypes; clone.intolerantValuePattern = this.intolerantValuePattern; clone.isExclusiveStrong = this.isExclusiveStrong; clone.isVolatilityTrigger = this.isVolatilityTrigger; clone.limitationsMap = this.limitationsMap; clone.matchingRuleQName = this.matchingRuleQName; clone.modificationPriority = this.modificationPriority; clone.outboundMappingType = this.outboundMappingType; clone.readReplaceMode = this.readReplaceMode; clone.secondaryIdentifier = this.secondaryIdentifier; clone.tolerant = this.tolerant; clone.tolerantValuePattern = this.tolerantValuePattern; } @Override public RefinedAttributeDefinition<T> deepClone(Map<QName, ComplexTypeDefinition> ctdMap) { return (RefinedAttributeDefinition<T>) super.deepClone(ctdMap); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(super.toString()); if (getDisplayName() != null) { sb.append(",Disp"); } if (getDescription() != null) { sb.append(",Desc"); } if (getOutboundMappingType() != null) { sb.append(",OUT"); } if (getInboundMappingTypes() != null) { sb.append(",IN"); } if (Boolean.TRUE.equals(getReadReplaceMode())) { sb.append(",R+E"); } if (getModificationPriority() != null) { sb.append(",P").append(getModificationPriority()); } return sb.toString(); } /** * Return a human readable name of this class suitable for logs. */ @Override protected String getDebugDumpClassName() { return "rRAD"; } @Override public String debugDump(int indent) { return debugDump(indent, null); } @Override public String debugDump(int indent, LayerType layer) { StringBuilder sb = new StringBuilder(); sb.append(super.debugDump(indent)); if (layer == null) { sb.append("\n"); DebugUtil.debugDumpMapSingleLine(sb, limitationsMap, indent + 1); } else { PropertyLimitations limitations = limitationsMap.get(layer); if (limitations != null) { sb.append(limitations.toString()); } } return sb.toString(); } public void setModificationPriority(Integer modificationPriority) { this.modificationPriority = modificationPriority; } @Override public Integer getModificationPriority() { return modificationPriority; } @Override public Boolean getReadReplaceMode() { // "get" instead of "is" because it may be null return readReplaceMode; } public void setReadReplaceMode(Boolean readReplaceMode) { this.readReplaceMode = readReplaceMode; } @Override public boolean isDisplayNameAttribute() { return isDisplayNameAttribute; } }