package ecologylab.bigsemantics.actions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import ecologylab.bigsemantics.actions.exceptions.ForLoopException;
import ecologylab.bigsemantics.actions.exceptions.IfActionException;
import ecologylab.bigsemantics.actions.exceptions.SemanticActionExecutionException;
import ecologylab.bigsemantics.collecting.SemanticsGlobalScope;
import ecologylab.bigsemantics.documentparsers.DocumentParser;
import ecologylab.bigsemantics.metadata.Metadata;
import ecologylab.bigsemantics.metametadata.MetaMetadata;
import ecologylab.bigsemantics.tools.GenericIterable;
import ecologylab.collections.Scope;
import ecologylab.generic.Debug;
import ecologylab.serialization.SimplTypesScope;
import ecologylab.serialization.formatenums.StringFormat;
/**
* This is the handler for semantic actions. * It has a <code>handleSemanticAction</code> method
* which decides what action to take when a semantic action is passed to it. There is one
* SemanticActionHandler created for each DocumentParser.connect()
*
* @author amathur
*/
// TODO Might want to implement lexical scoping in variables.
public class SemanticActionHandler
extends Debug
implements SemanticActionStandardMethods, SemanticsConstants, SemanticActionNamedArguments
{
static final Scope<Object> BUILT_IN_SCOPE = new Scope<Object>();
static
{
BUILT_IN_SCOPE.put(FALSE, false);
BUILT_IN_SCOPE.put(TRUE, true);
BUILT_IN_SCOPE.put(NULL, null);
}
private SemanticsGlobalScope semanticsScope;
private DocumentParser documentParser;
/**
* This is a map of return value and objects from semantic action. The key being the return_value
* of the semantic action. TODO remane this also to some thing like objectMap or variableMap.
*/
private Scope<Object> semanticActionVariableMap;
private Map<SemanticAction, Map<String, Object>> actionStates = new HashMap<SemanticAction, Map<String, Object>>();
/**
* Error handler for the semantic actions.
*/
private SemanticActionErrorHandler errorHandler;
boolean requestWaiting = false;
MetaMetadata metaMetadata;
Metadata metadata;
public SemanticActionHandler(SemanticsGlobalScope semanticsScope, DocumentParser documentParser)
{
this(semanticsScope, documentParser, new Scope<Object>(BUILT_IN_SCOPE));
}
public SemanticActionHandler(SemanticsGlobalScope semanticsScope, DocumentParser documentParser,
Scope<Object> semanticActionVariableMap)
{
this.semanticsScope = semanticsScope;
this.documentParser = documentParser;
semanticActionVariableMap.put(
SemanticsConstants.PURLCONNECTION_MIME,
documentParser.getDownloadController().getHttpResponse().getMimeType());
this.semanticActionVariableMap = semanticActionVariableMap;
}
public Scope<Object> getSemanticActionVariableMap()
{
return semanticActionVariableMap;
}
public void takeSemanticActions()
{
if (metaMetadata != null && metadata != null)
takeSemanticActions(metaMetadata, metadata);
}
public void takeSemanticActions(Metadata metadata)
{
takeSemanticActions((MetaMetadata) metadata.getMetaMetadata(), metadata);
}
public void takeSemanticActions(MetaMetadata metaMetadata, Metadata metadata)
{
takeSemanticActions(metaMetadata, metadata, metaMetadata.getSemanticActions());
}
public void takeSemanticActions(MetaMetadata metaMetadata, Metadata metadata, ArrayList<? extends SemanticAction> semanticActions)
{
if (semanticActions == null)
{
System.out.println("[ParserBase] warning: no semantic actions exist");
return;
}
if (requestWaiting)
{
requestWaiting = false;
}
else
{
this.metaMetadata = metaMetadata;
this.metadata = metadata;
//FIXME -- should not have SemanticActionsKeyWords && SemanticActionsNamedArguments as separate sets !!!
semanticActionVariableMap.put(DOCUMENT_TYPE, documentParser);
semanticActionVariableMap.put(SemanticsConstants.METADATA, metadata);
semanticActionVariableMap.put(TRUE_PURL, documentParser.getTruePURL());
preSemanticActionsHook(metadata);
}
for (int i = 0; i < semanticActions.size(); i++)
{
SemanticAction action = semanticActions.get(i);
handleSemanticAction(action, documentParser, semanticsScope);
if (requestWaiting)
return;
}
postSemanticActionsHook(metadata);
recycle();
}
/**
* main entry to handle semantic actions. FOR loops and IFs are handled directly (they have built-
* in semantics and are not overridable). otherwise it will can the perform() method on the action
* object.
*
* @param action
* @param parser
* @param infoCollector
*/
public void handleSemanticAction(SemanticAction action, DocumentParser parser, SemanticsGlobalScope infoCollector)
{
int state = getActionState(action, "state", SemanticAction.INIT);
if (state == SemanticAction.FIN || requestWaiting)
return;
// debug("["+parser+"] semantic action: " + action.getActionName() + ", SA class: " + action.getClassSimpleName() + "\n");
action.setSemanticActionHandler(this);
// here state must be INTER or INIT
// if state == INIT, we check pre-conditions
// if state == INTER, because this must have been started, we skip checking pre-conditions
if (state == SemanticAction.INIT)
{
if (!checkConditionsIfAny(action))
{
if (!(action instanceof IfSemanticAction))
warning(String.format(
"Semantic action %s not taken since (some) pre-requisite conditions are not met",
action));
return;
}
}
else
{
debug("re-entering actions with pre-conditions; checking pre-conditions skipped: " + action.getActionName());
}
action.setSessionScope(infoCollector);
action.setSemanticActionHandler(this);
action.setDocumentParser(parser);
final String actionName = action.getActionName();
try
{
if (SemanticActionStandardMethods.FOR_EACH.equals(actionName))
{
handleForLoop((ForEachSemanticAction) action, parser, infoCollector);
}
else if (SemanticActionStandardMethods.IF.equals(actionName))
{
handleIf((IfSemanticAction) action, parser, infoCollector);
}
else
{
handleGeneralAction(action);
}
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("The action " + actionName
+ " could not be executed. Please see the stack trace for errors.");
}
finally
{
if (!requestWaiting)
setActionState(action, "state", SemanticAction.FIN);
}
}
/**
* handle semantic actions other than FOR and IF.
*
* @param action
*/
public void handleGeneralAction(SemanticAction action)
{
// get the object on which the action has to be taken
String objectName = action.getObject();
if (objectName == null)
objectName = SemanticsConstants.METADATA;
Object object = semanticActionVariableMap.get(objectName);
try
{
Object returnValue = null;
returnValue = action.perform(object);
if (requestWaiting)
return;
if (action.getReturnObjectName() != null && returnValue != null)
{
semanticActionVariableMap.put(action.getReturnObjectName(), returnValue);
}
}
catch (Exception e)
{
e.printStackTrace(); // only for debug
if (e instanceof SemanticActionExecutionException)
throw (RuntimeException) e;
throw new SemanticActionExecutionException(e, action, semanticActionVariableMap);
}
}
public synchronized void handleForLoop(ForEachSemanticAction action, DocumentParser parser,
SemanticsGlobalScope infoCollector)
{
try
{
// get all the action which have to be performed in loop
ArrayList<SemanticAction> nestedSemanticActions = action.getNestedSemanticActionList();
// get the collection object name on which we have to loop
String collectionObjectName = action.getCollection();
// if(checkPreConditionFlagsIfAny(action))
{
// get the actual collection object
Object collectionObject = semanticActionVariableMap.get(collectionObjectName);
if (collectionObject == null)
{
error("Can't execute loop because collection is null: " + SimplTypesScope.serialize(action, StringFormat.XML));
return;
}
GenericIterable gItr = new GenericIterable(collectionObject);
// debug(documentType.purl().toString()); <<Annoying as hell !
Iterator itr = gItr.iterator();
int collectionSize = gItr.size();
// set the size of collection in the for loop action.
if (action.getSize() != null)
{
// we have the size value. so we add it in parameters
semanticActionVariableMap.put(action.getSize(), collectionSize);
}
int start = 0;
int end = collectionSize;
if (action.getStart() != null)
{
start = Integer.parseInt(action.getStart());
}
if (action.getEnd() != null)
{
end = Integer.parseInt(action.getEnd());
}
if (getActionState(action, "state", SemanticAction.INIT) == SemanticAction.INTER)
{
start = getActionState(action, "current_index", 0);
}
setActionState(action, "state", SemanticAction.INTER);
// start the loop over each object
for (int i = start; i < end; i++)
{
setActionState(action, "current_index", i);
Object item = gItr.get(i);
// put it in semantic action return value map
semanticActionVariableMap.put(action.getAs(), item);
// see if current index is needed
if (action.getCurIndex() != null)
{
// set the value of this variable in parameters
semanticActionVariableMap.put(action.getCurIndex(), i);
}
// now take all the actions nested inside for loop
if (nestedSemanticActions != null)
for (SemanticAction nestedSemanticAction : nestedSemanticActions)
{
handleSemanticAction(nestedSemanticAction, parser, infoCollector);
}
if (requestWaiting)
break;
// at the end of each iteration clear flags so that we can do the next iteration
action.setNestedActionState("state", SemanticAction.INIT);
}
}
}
catch (Exception e)
{
e.printStackTrace();
throw new ForLoopException(e, action, semanticActionVariableMap);
}
}
public void handleIf(IfSemanticAction action, DocumentParser parser, SemanticsGlobalScope infoCollector)
{
// conditions have been checked in handleSemanticAction()
try
{
setActionState(action, "state", SemanticAction.INTER);
ArrayList<SemanticAction> nestedSemanticActions = action.getNestedSemanticActionList();
if (nestedSemanticActions != null)
for (SemanticAction nestedSemanticAction : nestedSemanticActions)
handleSemanticAction(nestedSemanticAction, parser, infoCollector);
}
catch (Exception e)
{
e.printStackTrace();
throw new IfActionException(e, action, semanticActionVariableMap);
}
}
/**
* This function checks for the pre-condition flag values for this action and returns the "anded"
* result.
*
* @param action
* @return true if conditions are satisfied; false otherwise.
*/
protected boolean checkConditionsIfAny(SemanticAction action)
{
ArrayList<Condition> conditions = action.getChecks();
if (conditions != null)
{
// loop over all the flags to be checked
for (Condition condition : conditions)
{
boolean flag = condition.evaluate(this);
if (!flag)
return false;
}
}
return true;
}
/*********************** hooks ************************/
public void preSemanticActionsHook(Metadata metadata)
{
}
public void postSemanticActionsHook(Metadata metadata)
{
}
/*********************** used by the library ************************/
public void recycle()
{
semanticActionVariableMap.clear();
semanticActionVariableMap = null;
}
public <T> T getActionState(SemanticAction action, String name, T defaultValue)
{
if (actionStates.containsKey(action))
{
Map<String, Object> states = actionStates.get(action);
Object state = states.get(name);
if (state != null)
return (T) state;
}
return defaultValue;
}
public <T> void setActionState(SemanticAction action, String name, T value)
{
if (!actionStates.containsKey(action))
actionStates.put(action, new HashMap<String, Object>());
Map<String, Object> states = actionStates.get(action);
states.put(name, value);
}
}