package xdi2.messaging.container.interceptor.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xdi2.core.Graph;
import xdi2.core.constants.XDIConstants;
import xdi2.core.constants.XDIDictionaryConstants;
import xdi2.core.features.nodetypes.XdiEntityInstanceUnordered;
import xdi2.core.syntax.XDIAddress;
import xdi2.core.syntax.XDIArc;
import xdi2.core.syntax.XDIStatement;
import xdi2.messaging.MessageEnvelope;
import xdi2.messaging.container.MessagingContainer;
import xdi2.messaging.container.Prototype;
import xdi2.messaging.container.exceptions.Xdi2MessagingException;
import xdi2.messaging.container.execution.ExecutionContext;
import xdi2.messaging.container.execution.ExecutionResult;
import xdi2.messaging.container.interceptor.InterceptorResult;
import xdi2.messaging.container.interceptor.MessageEnvelopeInterceptor;
import xdi2.messaging.container.interceptor.OperationInterceptor;
import xdi2.messaging.container.interceptor.TargetInterceptor;
import xdi2.messaging.operations.Operation;
import xdi2.messaging.operations.SetOperation;
/**
* This interceptor can replace XDI variables in a $add operation with automatically generated persistent addresses.
*
* @author markus
*/
public class VariablesInterceptor extends AbstractInterceptor<MessagingContainer> implements MessageEnvelopeInterceptor, OperationInterceptor, TargetInterceptor, Prototype<VariablesInterceptor> {
private static final Logger log = LoggerFactory.getLogger(VariablesInterceptor.class);
/*
* Prototype
*/
@Override
public VariablesInterceptor instanceFor(PrototypingContext prototypingContext) {
// done
return this;
}
/*
* MessageEnvelopeInterceptor
*/
@Override
public InterceptorResult before(MessageEnvelope messageEnvelope, ExecutionContext executionContext, ExecutionResult executionResult) throws Xdi2MessagingException {
resetVariablesPerMessageEnvelope(executionContext);
return InterceptorResult.DEFAULT;
}
@Override
public InterceptorResult after(MessageEnvelope messageEnvelope, ExecutionContext executionContext, ExecutionResult executionResult) throws Xdi2MessagingException {
return InterceptorResult.DEFAULT;
}
@Override
public void exception(MessageEnvelope messageEnvelope, ExecutionContext executionContext, ExecutionResult executionResult, Exception ex) {
}
/*
* OperationInterceptor
*/
@Override
public InterceptorResult before(Operation operation, Graph operationResultGraph, ExecutionContext executionContext) throws Xdi2MessagingException {
resetVariablesPerOperation(executionContext);
return InterceptorResult.DEFAULT;
}
@Override
public InterceptorResult after(Operation operation, Graph operationResultGraph, ExecutionContext executionContext) throws Xdi2MessagingException {
// add $is statements for all the substituted variables
for (Entry<XDIArc, XDIArc> entry : getVariablesPerOperation(executionContext).entrySet()) {
XDIAddress subject = XDIAddress.create(entry.getKey().toString());
XDIAddress predicate = XDIDictionaryConstants.XDI_ADD_IS;
XDIAddress object = XDIAddress.create(entry.getValue().toString());
XDIStatement statement = XDIStatement.fromComponents(subject, predicate, object);
operationResultGraph.setStatement(statement);
}
// done
return InterceptorResult.DEFAULT;
}
/*
* TargetInterceptor
*/
@Override
public XDIStatement targetStatement(XDIStatement targetStatement, Operation operation, Graph operationResultGraph, ExecutionContext executionContext) throws Xdi2MessagingException {
if (! (operation instanceof SetOperation)) return targetStatement;
XDIAddress substitutedTargetSubject = substituteAddress(targetStatement.getSubject(), executionContext);
Object substitutedTargetPredicate = targetStatement.getPredicate() instanceof XDIAddress ? substituteAddress((XDIAddress) targetStatement.getPredicate(), executionContext) : targetStatement.getPredicate();
Object substitutedTargetObject = substituteObject(targetStatement.getObject(), executionContext);
if (substitutedTargetSubject == targetStatement.getSubject() && substitutedTargetPredicate == targetStatement.getPredicate() && substitutedTargetObject == targetStatement.getObject()) return targetStatement;
return XDIStatement.fromComponents(substitutedTargetSubject, substitutedTargetPredicate, substitutedTargetObject);
}
@Override
public XDIAddress targetAddress(XDIAddress targetAddress, Operation operation, Graph operationResultGraph, ExecutionContext executionContext) throws Xdi2MessagingException {
if (! (operation instanceof SetOperation)) return targetAddress;
return substituteAddress(targetAddress, executionContext);
}
/*
* Substitution helper methods
*/
private static XDIAddress substituteAddress(XDIAddress XDIaddress, ExecutionContext executionContext) {
List<XDIArc> substitutedXDIArcs = null;
// substitute address
for (int i=0; i<XDIaddress.getNumXDIArcs(); i++) {
XDIArc XDIarc = XDIaddress.getXDIArc(i);
XDIArc substitutedXDIArc = substituteXDIArc(XDIarc, executionContext);
if (substitutedXDIArc == null) continue;
if (log.isDebugEnabled()) log.debug("Substituted " + XDIarc + " for " + substitutedXDIArc);
// substitute arc
if (substitutedXDIArcs == null) {
substitutedXDIArcs = new ArrayList<XDIArc> (XDIaddress.getNumXDIArcs());
for (int ii=0; ii<XDIaddress.getNumXDIArcs(); ii++) substitutedXDIArcs.add(XDIaddress.getXDIArc(ii));
}
substitutedXDIArcs.set(i, substitutedXDIArc);
}
// no substitutions?
if (substitutedXDIArcs == null) return XDIaddress;
// build new target address
StringBuilder newTargetAddress = new StringBuilder();
for (XDIArc substitutedArc : substitutedXDIArcs) newTargetAddress.append(substitutedArc.toString());
return XDIAddress.create(newTargetAddress.toString());
}
private static Object substituteObject(Object object, ExecutionContext executionContext) {
if (! (object instanceof XDIAddress)) return object;
return substituteAddress((XDIAddress) object, executionContext);
}
private static XDIArc substituteXDIArc(XDIArc XDIarc, ExecutionContext executionContext) {
if (! XDIConstants.XDI_ADD_COMMON_VARIABLE.equals(XDIarc)) return null;
if (! XDIarc.getXRef().isEmpty()) return null;
// substitute the arc
XDIArc newArc = getVariablesPerMessageEnvelope(executionContext).get(XDIarc);
if (newArc == null) {
newArc = XdiEntityInstanceUnordered.createXDIArc();
putVariablePerMessageEnvelope(executionContext, XDIarc, newArc);
putVariablePerOperation(executionContext, XDIarc, newArc);
}
// done
return newArc;
}
/*
* ExecutionContext helper methods
*/
private static final String EXECUTIONCONTEXT_KEY_VARIABLES_PER_MESSAGEENVELOPE = VariablesInterceptor.class.getCanonicalName() + "#variablespermessageenvelope";
private static final String EXECUTIONCONTEXT_KEY_VARIABLES_PER_OPERATION = VariablesInterceptor.class.getCanonicalName() + "#variablesperoperation";
@SuppressWarnings("unchecked")
private static Map<XDIArc, XDIArc> getVariablesPerMessageEnvelope(ExecutionContext executionContext) {
return (Map<XDIArc, XDIArc>) executionContext.getMessageEnvelopeAttribute(EXECUTIONCONTEXT_KEY_VARIABLES_PER_MESSAGEENVELOPE);
}
private static void putVariablePerMessageEnvelope(ExecutionContext executionContext, XDIArc key, XDIArc value) {
getVariablesPerMessageEnvelope(executionContext).put(key, value);
}
private static void resetVariablesPerMessageEnvelope(ExecutionContext executionContext) {
executionContext.putMessageEnvelopeAttribute(EXECUTIONCONTEXT_KEY_VARIABLES_PER_MESSAGEENVELOPE, new HashMap<XDIArc, String> ());
}
@SuppressWarnings("unchecked")
private static Map<XDIArc, XDIArc> getVariablesPerOperation(ExecutionContext executionContext) {
return (Map<XDIArc, XDIArc>) executionContext.getOperationAttribute(EXECUTIONCONTEXT_KEY_VARIABLES_PER_OPERATION);
}
private static void putVariablePerOperation(ExecutionContext executionContext, XDIArc key, XDIArc value) {
getVariablesPerOperation(executionContext).put(key, value);
}
private static void resetVariablesPerOperation(ExecutionContext executionContext) {
executionContext.putOperationAttribute(EXECUTIONCONTEXT_KEY_VARIABLES_PER_OPERATION, new HashMap<XDIArc, String> ());
}
}