/*
* Copyright (c) 2014-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.model.common.expression.evaluator;
import java.util.Collection;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition;
import com.evolveum.midpoint.model.common.expression.ExpressionEvaluationContext;
import com.evolveum.midpoint.model.common.expression.ExpressionEvaluator;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.schema.util.ObjectResolver;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.util.exception.ExpressionEvaluationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssociationFromLinkExpressionEvaluatorType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowAssociationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowDiscriminatorType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
/**
* @author Radovan Semancik
*
*/
public class AssociationFromLinkExpressionEvaluator
implements ExpressionEvaluator<PrismContainerValue<ShadowAssociationType>,
PrismContainerDefinition<ShadowAssociationType>> {
private static final Trace LOGGER = TraceManager.getTrace(AssociationFromLinkExpressionEvaluator.class);
private AssociationFromLinkExpressionEvaluatorType evaluatorType;
private PrismContainerDefinition<ShadowAssociationType> outputDefinition;
private ObjectResolver objectResolver;
private PrismContext prismContext;
AssociationFromLinkExpressionEvaluator(AssociationFromLinkExpressionEvaluatorType evaluatorType,
PrismContainerDefinition<ShadowAssociationType> outputDefinition, ObjectResolver objectResolver, PrismContext prismContext) {
this.evaluatorType = evaluatorType;
this.outputDefinition = outputDefinition;
this.objectResolver = objectResolver;
this.prismContext = prismContext;
}
/* (non-Javadoc)
* @see com.evolveum.midpoint.common.expression.ExpressionEvaluator#evaluate(java.util.Collection, java.util.Map, boolean, java.lang.String, com.evolveum.midpoint.schema.result.OperationResult)
*/
@Override
public PrismValueDeltaSetTriple<PrismContainerValue<ShadowAssociationType>> evaluate(ExpressionEvaluationContext context) throws SchemaException,
ExpressionEvaluationException, ObjectNotFoundException {
String desc = context.getContextDescription();
Object orderOneObject = context.getVariables().get(ExpressionConstants.VAR_ORDER_ONE_OBJECT);
if (orderOneObject == null) {
throw new ExpressionEvaluationException("No order one object variable in "+desc+"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}
if (!(orderOneObject instanceof AbstractRoleType)) {
throw new ExpressionEvaluationException("Order one object variable in "+desc+" is not a role, it is "+orderOneObject.getClass().getName()
+"; the expression may be used in a wrong place. It is only supposed to work in a role.");
}
AbstractRoleType thisRole = (AbstractRoleType)orderOneObject;
LOGGER.trace("Evaluating association from link on: {}", thisRole);
RefinedObjectClassDefinition rAssocTargetDef = (RefinedObjectClassDefinition) context.getVariables().get(ExpressionConstants.VAR_ASSOCIATION_TARGET_OBJECT_CLASS_DEFINITION);
if (rAssocTargetDef == null) {
throw new ExpressionEvaluationException("No association target object class definition variable in "+desc+"; the expression may be used in a wrong place. It is only supposed to create an association.");
}
ShadowDiscriminatorType projectionDiscriminator = evaluatorType.getProjectionDiscriminator();
if (projectionDiscriminator == null) {
throw new ExpressionEvaluationException("No projectionDiscriminator in "+desc);
}
ShadowKindType kind = projectionDiscriminator.getKind();
if (kind == null) {
throw new ExpressionEvaluationException("No kind in projectionDiscriminator in "+desc);
}
String intent = projectionDiscriminator.getIntent();
PrismContainer<ShadowAssociationType> output = outputDefinition.instantiate();
QName assocName = context.getMappingQName();
String resourceOid = rAssocTargetDef.getResourceType().getOid();
Collection<SelectorOptions<GetOperationOptions>> options = null;
// Always process the first role (myself) regardless of recursion setting
gatherAssociationsFromAbstractRole(thisRole, output, resourceOid, kind, intent, assocName, options, desc, context);
if (thisRole instanceof OrgType && matchesForRecursion((OrgType)thisRole)) {
gatherAssociationsFromAbstractRoleRecurse((OrgType)thisRole, output, resourceOid, kind, intent, assocName, options, desc,
context);
}
return ItemDelta.toDeltaSetTriple(output, null);
}
private void gatherAssociationsFromAbstractRole(AbstractRoleType thisRole,
PrismContainer<ShadowAssociationType> output, String resourceOid, ShadowKindType kind,
String intent, QName assocName, Collection<SelectorOptions<GetOperationOptions>> options,
String desc, ExpressionEvaluationContext params) throws SchemaException {
for (ObjectReferenceType linkRef: thisRole.getLinkRef()) {
ShadowType shadowType;
try {
shadowType = objectResolver.resolve(linkRef, ShadowType.class, options, desc, params.getTask(), params.getResult());
} catch (ObjectNotFoundException e) {
// Linked shadow not found. This may happen e.g. if the account is deleted and model haven't got
// the chance to react yet. Just ignore such shadow.
LOGGER.trace("Ignoring shadow "+linkRef.getOid()+" linked in "+thisRole+" because it no longer exists");
continue;
}
if (ShadowUtil.matches(shadowType, resourceOid, kind, intent)) {
PrismContainerValue<ShadowAssociationType> newValue = output.createNewValue();
ShadowAssociationType shadowAssociationType = newValue.asContainerable();
shadowAssociationType.setName(assocName);
ObjectReferenceType shadowRef = new ObjectReferenceType();
shadowRef.setOid(linkRef.getOid());
shadowAssociationType.setShadowRef(shadowRef);
}
}
}
private void gatherAssociationsFromAbstractRoleRecurse(OrgType thisOrg,
PrismContainer<ShadowAssociationType> output, String resourceOid, ShadowKindType kind,
String intent, QName assocName, Collection<SelectorOptions<GetOperationOptions>> options,
String desc, ExpressionEvaluationContext params) throws SchemaException, ObjectNotFoundException {
gatherAssociationsFromAbstractRole(thisOrg, output, resourceOid, kind, intent, assocName, options, desc, params);
for (ObjectReferenceType parentOrgRef: thisOrg.getParentOrgRef()) {
OrgType parent = objectResolver.resolve(parentOrgRef, OrgType.class, options, desc, params.getTask(), params.getResult());
if (matchesForRecursion(parent)) {
gatherAssociationsFromAbstractRoleRecurse(parent, output, resourceOid, kind, intent, assocName, options, desc, params);
}
}
}
private boolean matchesForRecursion(OrgType thisOrg) {
for (String recurseUpOrgType: evaluatorType.getRecurseUpOrgType()) {
thisOrg.getOrgType().contains(recurseUpOrgType);
return true;
}
return false;
}
/* (non-Javadoc)
* @see com.evolveum.midpoint.common.expression.ExpressionEvaluator#shortDebugDump()
*/
@Override
public String shortDebugDump() {
return "associationFromLink";
}
}