package context.arch.interpreter; import context.arch.BaseObject; import context.arch.comm.DataObject; import context.arch.comm.DataObjects; import context.arch.discoverer.Discoverer; import context.arch.storage.Attribute; import context.arch.storage.Attributes; import context.arch.util.Error; /** * This class is the basic interpreter. * * TODO: use-cases? May be useful e.g. to represent temperature as Celsius when input represents it in Fahrenheit * * @see context.arch.BaseObject */ public abstract class Interpreter extends BaseObject { /** Debug flag */ public static boolean DEBUG = false; /** * Tag for interpreter */ public static final String INTERPRET = "interpret"; /** * The tag for the type of this object */ public static final String INTERPRETER_TYPE = "interpreter"; /** * Tag for interpreterReply */ public static final String INTERPRET_REPLY = "interpretReply"; /** * Default port to use for interpreters */ public static final int DEFAULT_PORT = 7000; protected Attributes inAttributes; protected Attributes outAttributes; /** * Constructor that creates a BaseObject with the given port and sets the * incoming and outgoing attributes. * * @param port Port number to create the BaseObject on * @see context.arch.BaseObject */ public Interpreter(int port) { super(port); debugprintln(DEBUG, "Interpreter construction after super () port=" + port); inAttributes = setInAttributes(); outAttributes = setOutAttributes(); } /** * Returns the type of the object * This method should be overridden * * @return String */ public String getType(){ return Interpreter.INTERPRETER_TYPE; } /** * This method is meant to handle any internal methods that the baseObject doesn't * handle. In particular, this method handles interpret requests. It ensures * that the ID of the incoming request matches this interpreter. If the * method is an INTERPRET method, it sends it to the interpreter. Otherwise * runInterpreterMethod() is called. * * @param data DataObject containing the method to run and parameters * @return DataObject containing the results of running the method * @see #runInterpreterMethod(DataObject,String) */ public DataObject runUserMethod(DataObject data) { debugprintln (DEBUG, "Interpreter <runUserMethod>"); DataObject interpreter = data.getDataObject(ID); String error = null; // Test the id if (interpreter == null) { error = Error.INVALID_ID_ERROR; return (new Error(error)).toDataObject(); } else { String queryId = (String)(interpreter.getValue()); if (!queryId.equals(getId())) { error = Error.INVALID_ID_ERROR; return (new Error(error)).toDataObject(); } } String methodType = data.getName(); if (methodType.equals(INTERPRET)) { return callInterpreter(data,error); } else { return runInterpreterMethod(data,error); } } /** * This method ensures that the incoming attributes are correct and calls * interpretData(). It returns the interpreted results. * * @param data Incoming interpret request * @param error Incoming error, if any * @return interpreted results * @see #interpretData(AttributeNameValues) */ public DataObject callInterpreter(DataObject data, String error) { DataObjects v = new DataObjects(); DataObject result = new DataObject(INTERPRET_REPLY,v); Attributes dataToInterpret = null; Error err = new Error(error); if (err.getError() == null) { dataToInterpret = Attributes.fromDataObject(data); if (dataToInterpret == null) { err.setError(Error.MISSING_PARAMETER_ERROR); } else if (!canHandle(dataToInterpret)) { err.setError(Error.INVALID_ATTRIBUTE_ERROR); } } if (err.getError() == null) { Attributes interpreted = interpretData(dataToInterpret); if (interpreted != null) { v.addElement(interpreted.toDataObject()); err.setError(Error.NO_ERROR); } else { err.setError(Error.INVALID_DATA_ERROR); } } v.addElement(err.toDataObject()); return result; } /** * This abstract method interprets the given data and returns it. * * @param data AttributeNameValues containing data to be interpreted * @return AttributeNameValues object containing the interpreted data */ protected abstract Attributes interpretData(Attributes data); /** * This is an empty method that should be overridden by objects * that subclass from this class. It is called when another component * tries to run a method on the interpreter, but it's not an interpret * request. * * @param data DataObject containing the data for the method * @param error String containing the incoming error value * @return DataObject containing the method results */ protected DataObject runInterpreterMethod(DataObject data, String error) { debugprintln (DEBUG, "Interpreter <runInterpreterMethod>"); Error err = new Error(Error.UNKNOWN_METHOD_ERROR); return err.toDataObject (); } /** * This method checks the list of incoming attributes to ensure that the * interpreter can handle these attributes. * * @param inAtts List of incoming attributes to check * @return whether the list of attributes is valid */ private boolean canHandle(Attributes inAtts) { for (int i = 0; i < inAtts.size(); i++) { Attribute<?> inAtt = inAtts.get(i); if (!isInAttribute(inAtt.getName())) { return false; } } return true; } /** * Returns the attribute type with the given name for incoming attributes * * @param name Name of the attribute to get */ protected Class<?> getInAttributeType(String name) { return inAttributes.get(name).getType(); } /** * Adds an incoming attribute * * @param name Name of the attribute to set * @param type Type of the attribute */ protected <T extends Comparable<? super T>> void addInAttribute(String name, Class<T> type) { inAttributes.add(new Attribute<T>(name, type)); } /** * Checks if the given incoming attribute is an attribute of this interpreter * * @param name Name of the attribute to check */ protected boolean isInAttribute(String name) { return inAttributes.containsKey(name); } /** * Returns the attribute type with the given name for outgoing attributes * * @param name Name of the attribute to get */ protected Class<?> getOutAttributeType(String name) { return outAttributes.get(name).getClass(); } /** * Adds an outgoing attribute * * @param name Name of the attribute to set * @param type Type of the attribute */ protected <T extends Comparable<? super T>> void addOutAttribute(String name, Class<T> type) { outAttributes.add(new Attribute<T>(name, type)); } /** * Checks if the given outgoing attribute is an attribute of this interpreter * * @param name Name of the attribute to check */ protected boolean isOutAttribute(String name) { return outAttributes.containsKey(name); } /** * Sets the incoming attributes for the interpreter */ protected abstract Attributes setInAttributes(); /** * Sets the outgoing attributes for the interpreter */ protected abstract Attributes setOutAttributes(); /** * Overloads the BaseObject method */ public DataObject getUserDataObject(){ DataObject result; // Get the incoming attributes DataObject doInAtt_ = setInAttributes().toDataObject(); DataObjects vInAtt = new DataObjects(); vInAtt.addElement(doInAtt_); DataObject doInAtt = new DataObject(Discoverer.INCOMING_ATTRIBUTE_NAME_VALUES, vInAtt); // Get the outgoing attributes DataObject doOutAtt_ = setOutAttributes().toDataObject(); DataObjects vOutAtt = new DataObjects(); vOutAtt.addElement(doOutAtt_); DataObject doOutAtt = new DataObject(Discoverer.OUTGOING_ATTRIBUTE_NAME_VALUES, vOutAtt); DataObjects v = new DataObjects(); v.addElement(doInAtt); v.addElement(doOutAtt); // Get getInterpreterDescription DataObject doDescrip = getInterpreterDescription(); if (doDescrip != null) { for (DataObject child : doDescrip.getChildren()){ v.addElement(child); } } result = new DataObject(Discoverer.TEMP_DEST, v); return result; } /** * Returns the interpreter description that should be overloaded */ public DataObject getInterpreterDescription(){ return null; } }