/**
*
*/
package ecologylab.bigsemantics.actions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import ecologylab.bigsemantics.collecting.Seeding;
import ecologylab.bigsemantics.collecting.SemanticsGlobalScope;
import ecologylab.bigsemantics.collecting.SemanticsSite;
import ecologylab.bigsemantics.documentparsers.DocumentParser;
import ecologylab.bigsemantics.html.documentstructure.LinkType;
import ecologylab.bigsemantics.html.documentstructure.SemanticAnchor;
import ecologylab.bigsemantics.html.documentstructure.SemanticInLinks;
import ecologylab.bigsemantics.metadata.Metadata;
import ecologylab.bigsemantics.metadata.builtins.Document;
import ecologylab.bigsemantics.metadata.scalar.MetadataScalarBase;
import ecologylab.bigsemantics.metametadata.Argument;
import ecologylab.bigsemantics.metametadata.MetaMetadata;
import ecologylab.bigsemantics.seeding.Seed;
import ecologylab.collections.Scope;
import ecologylab.net.ParsedURL;
import ecologylab.serialization.ElementState;
import ecologylab.serialization.annotations.simpl_collection;
import ecologylab.serialization.annotations.simpl_inherit;
import ecologylab.serialization.annotations.simpl_map;
import ecologylab.serialization.annotations.simpl_nowrap;
import ecologylab.serialization.annotations.simpl_scalar;
import ecologylab.serialization.annotations.simpl_scope;
import ecologylab.serialization.annotations.simpl_tag;
/**
* This is the abstract class which defines the semantic action. All the semantic actions must
* extend it. To add a new semantic action following steps needed to be taken.
* <li> 1) Create a class for that semantic action which extends SemanticAction class.
* <li> 2) Write all the custom code for that
* semantic action in this new class file. [Example see <code>ForEachSemanticAction.java</code> or
* <code>IfSemanticAction</code> which implements for_each semantic action.]
* <li> 3) Modify the <code>handleSemanticAction</code> method of
* <code>SemanticActionHandle.java</code> to add case
* for new semantic action.
* <li> 4) Add a new method in <code>SemanticActionHandler.java </code> to
* handle this action. Mostly this method should be abstract unless the action is a flow control
* action like FOR LOOP.
* <li> 5) For code clarity and readability define a constant for the new action
* name in <code>SemanticActionStandardMethods.java</code>
*
* @author amathur
*
*/
@simpl_inherit
public abstract class SemanticAction extends ElementState implements SemanticActionNamedArguments
{
@simpl_collection
@simpl_scope(ConditionTranslationScope.CONDITION_SCOPE)
@simpl_nowrap
private ArrayList<Condition> checks;
/**
* The map of arguments for this semantic action.
*/
@simpl_nowrap
@simpl_map("arg")
private HashMap<String, Argument> args;
/**
* Object on which the Action is to be taken
*/
@simpl_scalar
@simpl_tag("object")
private String objectStr;
/**
* The value returned from the action
*/
@simpl_scalar
private String name;
/**
* Error string for this action
*/
@simpl_scalar
private String error;
protected SemanticsGlobalScope sessionScope;
protected SemanticActionHandler semanticActionHandler;
protected DocumentParser documentParser;
public SemanticAction()
{
args = new HashMap<String, Argument>();
}
public ArrayList<Condition> getChecks()
{
return checks;
}
public String getObject()
{
return objectStr;
}
public void setObject(String object)
{
this.objectStr = object;
}
public String getReturnObjectName()
{
return name;
}
public boolean hasArguments()
{
return args != null && args.size() > 0;
}
public String getArgumentValueName(String argName)
{
String result = null;
if (args != null)
{
Argument argument = args.get(argName);
if (argument != null)
{
result = argument.getValue();
}
}
return result;
}
public String getArgumentAltValueName(String argName)
{
String result = null;
if (args != null)
{
Argument argument = args.get(argName);
if (argument != null)
{
result = argument.getAltValue();
}
}
return result;
}
public Object getArgumentObject(String argName)
{
Object result = null;
if (args != null)
{
Argument argument = args.get(argName);
if (argument != null)
{
String argumentValueName = argument.getValue();
if (argumentValueName != null)
{
Scope semanticActionVariableMap = semanticActionHandler.getSemanticActionVariableMap();
result = semanticActionVariableMap.get(argumentValueName);
if (result == null)
{
argumentValueName = argument.getAltValue();
if (argumentValueName != null)
{
result = semanticActionVariableMap.get(argumentValueName);
}
}
}
if (result != null && result instanceof MetadataScalarBase)
result = ((MetadataScalarBase) result).getValue();
}
}
return result;
}
public int getArgumentInteger(String argName, int defaultValue)
{
Integer value = (Integer) getArgumentObject(argName);
return (value != null) ? value : defaultValue;
}
public boolean getArgumentBoolean(String argName, boolean defaultValue)
{
Boolean value = (Boolean) getArgumentObject(argName);
return (value != null) ? value : defaultValue;
}
public float getArgumentFloat(String argName, float defaultValue)
{
Float value = (Float) getArgumentObject(argName);
return (value != null) ? value : defaultValue;
}
public final String getError()
{
return error;
}
public final void setError(String error)
{
this.error = error;
}
public void setSessionScope(SemanticsGlobalScope infoCollector)
{
this.sessionScope = infoCollector;
}
public void setSemanticActionHandler(SemanticActionHandler handler)
{
this.semanticActionHandler = handler;
}
public void setDocumentParser(DocumentParser documentParser)
{
this.documentParser = documentParser;
}
/**
* return the name of the action.
*
* @return
*/
public abstract String getActionName();
/**
* handle error during action performing.
*/
public abstract void handleError();
/**
* Perform this semantic action. User defined semantic actions should override this method.
*
* @param obj
* The object the action operates on.
* @return The result of this semantic action (if any), or null.
* @throws IOException
*/
public abstract Object perform(Object obj) throws IOException;
/**
* Register a user defined semantic action to the system. This method should be called before
* compiling or using the MetaMetadata repository.
* <p />
* To override an existing semantic action, subclass your own semantic action class, use the same
* tag (indicated in @simpl_tag), and override perform().
*
* @param semanticActionClass
* @param canBeNested
* indicates if this semantic action can be nested by other semantic actions, like
* <code>for</code> or <code>if</code>. if so, it will also be registered to
* NestedSemanticActionTranslationScope.
*/
public static void register(Class<? extends SemanticAction>... semanticActionClasses)
{
for (Class<? extends SemanticAction> semanticActionClass : semanticActionClasses)
{
SemanticActionTranslationScope.get().addTranslation(semanticActionClass);
}
}
protected MetaMetadata getMetaMetadata()
{
return getMetaMetadata(this);
}
static public MetaMetadata getMetaMetadata(ElementState that)
{
if (that instanceof MetaMetadata)
{
return (MetaMetadata) that;
}
ElementState parent = that.parent();
return (parent == null) ? null : getMetaMetadata(parent);
}
/**
* @return
*/
protected Document resolveSourceDocument()
{
Document sourceDocument = (Document) getArgumentObject(SOURCE_DOCUMENT);
if (sourceDocument == null)
sourceDocument = documentParser.getDocument();
return sourceDocument;
}
public Document getOrCreateDocument(DocumentParser documentParser, LinkType linkType)
{
Document result = (Document) getArgumentObject(DOCUMENT);
// get the ancestor container
Document sourceDocument = resolveSourceDocument();
// get the seed. Non null only for search types .
Seed seed = documentParser.getSeed();
if (result == null)
{
ParsedURL outlinkPurl = (ParsedURL) getArgumentObject(LOCATION);
if (outlinkPurl != null)
result = sessionScope.getOrConstructDocument(outlinkPurl);
}
if (result == null)
result = sourceDocument; //direct binding?!
if (result != null && !result.isRecycled() && (result.getLocation() != null))
{
result.setSemanticsSessionScope(sessionScope);
Metadata mixin = (Metadata) getArgumentObject(MIXIN);
if (mixin != null)
result.addMixin(mixin);
if (seed != null)
{
seed.bindToDocument(result);
}
String anchorText = (String) getArgumentObject(ANCHOR_TEXT);
// create anchor text from Document title if there is none passed in directly, and we won't
// be setting metadata
if (anchorText == null)
anchorText = result.getTitle();
// work to avoid honey pots!
String anchorContextString = (String) getArgumentObject(ANCHOR_CONTEXT);
boolean citationSignificance = getArgumentBoolean(CITATION_SIGNIFICANCE, false);
float significanceVal = getArgumentFloat(SIGNIFICANCE_VALUE, 1);
boolean traversable = getArgumentBoolean(TRAVERSABLE, true);
boolean ignoreContextForTv = getArgumentBoolean(IGNORE_CONTEXT_FOR_TV, false);
ParsedURL location = result.getLocation();
if (traversable)
{
Seeding seeding = sessionScope.getSeeding();
if (seeding != null)
seeding.traversable(location);
}
boolean anchorIsInSource = false;
if (sourceDocument != null)
{
// Chain the significance from the ancestor
SemanticInLinks sourceInLinks = sourceDocument.getSemanticInlinks();
if (sourceInLinks != null)
{
significanceVal *= sourceInLinks.meanSignificance();
anchorIsInSource = sourceInLinks.containsKey(location);
}
}
if(! anchorIsInSource)
{
//By default use the boost, unless explicitly stated in this site's MMD
SemanticsSite site = result.getSite();
boolean useSemanticBoost = !site.ignoreSemanticBoost();
if (citationSignificance)
linkType = LinkType.CITATION_SEMANTIC_ACTION;
else if (useSemanticBoost && linkType == LinkType.OTHER_SEMANTIC_ACTION)
linkType = LinkType.SITE_BOOSTED_SEMANTIC_ACTION;
SemanticAnchor semanticAnchor = new SemanticAnchor(linkType, location, null,
sourceDocument.getLocation(), significanceVal);// this is not fromContentBody,
// but is fromSemanticActions
if(ignoreContextForTv)
semanticAnchor.addAnchorContextToTV(anchorText, null);
else
semanticAnchor.addAnchorContextToTV(anchorText, anchorContextString);
result.addSemanticInlink(semanticAnchor, sourceDocument);
}
else
{
debug("Ignoring inlink, because ancestor contains the same, we don't want cycles in the graph just yet: sourceContainer -- outlinkPurl :: " + sourceDocument + " -- " + location);
}
}
// adding the return value to map
Scope semanticActionVariableMap = semanticActionHandler.getSemanticActionVariableMap();
if(semanticActionVariableMap != null)
semanticActionVariableMap.put(getReturnObjectName(), result);
else
error("semanticActionVariableMap is null !! Not frequently reproducible either. Place a breakpoint here to try fixing it next time.");
// set flags if any
// setFlagIfAny(action, localContainer);
// return the value. Right now no need for it. Later it might be used.
return result;
}
/**
* Lookup the container in the named arguments map, and return it if its there. Otherwise, use
* location and metadata arguments from that map to create the container.
* @param documentParser
* @param linkType Changes to significanceVal based on LinkType
*
* @return
*/
// public OldContainerI getOrCreateContainer(DocumentParser documentParser, LinkType linkType)
// {
// OldContainerI result = (OldContainerI) getArgumentObject(CONTAINER);
// if (result == null)
// {
// result = getOrCreateDocument(documentParser, linkType);
// if (result == null)
// result = documentParser.getContainer();
// }
// return result;
// }
static final int INIT = 0; // this action has not been started
static final int INTER = 10; // this action has been started but not yet finished
static final int FIN = 20; // this action has already been finished
void setNestedActionState(String name, Object value)
{
}
public SemanticActionHandler getSemanticActionHandler()
{
SemanticActionHandler result = this.semanticActionHandler;
if (result == null)
{
ElementState parentES = parent();
if (parentES != null && parentES instanceof SemanticAction)
{
SemanticAction parent = (SemanticAction) parentES;
result = parent.getSemanticActionHandler();
this.semanticActionHandler = result;
}
}
return result;
}
}