/** * *************************************************************** * JADE - Java Agent DEvelopment Framework is a framework to develop * multi-agent systems in compliance with the FIPA specifications. * Copyright (C) 2000 CSELT S.p.A. * * GNU Lesser General Public License * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * ************************************************************** */ package jade.content.lang.sl; import jade.content.onto.Ontology; import jade.content.onto.OntologyException; import jade.content.abs.*; import jade.content.schema.ObjectSchema; import jade.content.lang.StringCodec; //#MIDP_EXCLUDE_BEGIN import jade.lang.acl.ISO8601; import jade.util.leap.Iterator; import jade.domain.FIPANames; import jade.core.CaseInsensitiveString; import java.util.Date; import java.io.StringReader; import java.io.BufferedReader; // only for debugging purposes in the main import java.io.InputStreamReader; // only for debugging purposes in the main //#MIDP_EXCLUDE_END /** * The codec class for the <b><i>FIPA-SL</i>n</b> languages. This class * implements the <code>Codec</code> interface and allows converting * back and forth between strings and frames, according to the SL * grammar. * By default the class implements full SL grammar, otherwise the proper * value must be used in the constructor. * @author Fabio Bellifemine - TILAB * @author Nicolas Lhuillier - Motorola (added support for byte[] primitive) * @version $Date: 2008-03-18 14:37:09 +0100 (mar, 18 mar 2008) $ $Revision: 6022 $ */ /*#MIDP_INCLUDE_BEGIN public class SLCodec extends SimpleSLCodec { #MIDP_INCLUDE_END*/ //#MIDP_EXCLUDE_BEGIN public class SLCodec extends StringCodec { private transient SLParser parser; private transient ExtendedSLParser extendedParser; private SL0Ontology slOnto; // ontology of the content language private Ontology domainOnto = null; // application ontology /** This is the StringBuffer used by the encode method **/ private transient StringBuffer buffer = null; /** This variable is true, when meta symbols are allowed (metas are a semantics-specific extension to the SL Grammar) **/ private boolean metaAllowed = true; //FIXME set/unset this variable to do private boolean preserveJavaTypes = false; /** * Construct a Codec object for the full SL-language (FIPA-SL). */ public SLCodec() { this(3, false); } /** * Create an SLCodec for the full SL-language (FIPA-SL) specifying whether or not java primitive types * (long, int, float, double) must be preserved. * This is achieved by encoding long values as <numeric-value>L and float values as <numeric-valueF. * It should be noticed that this encoding is NOT FIPA SL standard * @param preserveJavaTypes Indicates whether or not java primitive types must be preserved */ public SLCodec(boolean preserveJavaTypes) { this(3, preserveJavaTypes); } /** * Construct a Codec object for the given profile of SL-language. * @param slType specify 0 for FIPA-SL0, 1 for FIPA-SL1, 2 for FIPA-SL2, any other value can be used for full FIPA-SL */ public SLCodec(int slType) { this(slType, false); } /** * Create an SLCodec for the given profile of SL-language specifying whether or not java primitive types * (long, int, float, double) must be preserved. * @param slType specify 0 for FIPA-SL0, 1 for FIPA-SL1, 2 for FIPA-SL2, any other value can be used for full FIPA-SL * @param preserveJavaTypes Indicates whether or not java primitive types must be preserved */ public SLCodec(int slType, boolean preserveJavaTypes) { super((slType==0 ? FIPANames.ContentLanguage.FIPA_SL0 : (slType==1 ? FIPANames.ContentLanguage.FIPA_SL1 : (slType==2 ? FIPANames.ContentLanguage.FIPA_SL2 : FIPANames.ContentLanguage.FIPA_SL )))); if ((slType < 0) || (slType > 2)) // if outside range, set to full SL slType = 3; slOnto = (SL0Ontology) (slType == 0 ? SL0Ontology.getInstance() : (slType == 1 ? SL1Ontology.getInstance() : (slType == 2 ? SL2Ontology.getInstance() : SLOntology.getInstance()))); this.preserveJavaTypes = preserveJavaTypes; initParser(); } private void initParser() { int slType = jade.domain.FIPANames.ContentLanguage.FIPA_SL0.equals(getName()) ? 0 : jade.domain.FIPANames.ContentLanguage.FIPA_SL2.equals(getName()) ? 1 : jade.domain.FIPANames.ContentLanguage.FIPA_SL2.equals(getName()) ? 2 : 3; if (preserveJavaTypes) { extendedParser = new ExtendedSLParser(new StringReader("")); extendedParser.setSLType(slType); } else { parser = new SLParser(new StringReader("")); parser.setSLType(slType); } } public boolean getPreserveJavaTypes() { return preserveJavaTypes; } /** * Encodes a content into a String. * @param content the content as an abstract descriptor. * @return the content as a String. * @throws CodecException */ public String encode(AbsContentElement content) throws CodecException { return encode(null, content); } /** * Encodes a content into a String. * @param ontology the ontology * @param content the content as an abstract descriptor. * @return the content as a String. * @throws CodecException */ public synchronized String encode(Ontology ontology, AbsContentElement content) throws CodecException { try { domainOnto = ontology; buffer = new StringBuffer("("); if (content instanceof AbsContentElementList) { for (Iterator i=((AbsContentElementList)content).iterator(); i.hasNext(); ) { AbsObject o = (AbsObject)i.next(); encodeAndAppend(o); buffer.append(' '); } } else encodeAndAppend(content); buffer.append(')'); return buffer.toString(); } finally { buffer = null; //frees the memory } } /** * Encode a string, taking care of quoting separated words and * escaping strings, if necessary. * And append it to the buffer. **/ private void encodeAndAppend(String val) { // if the slotName is a String of words then quote it. If it is a meta (i.e. startsWith "??") do not quote it. String out = ( (SimpleSLTokenizer.isAWord(val) || (metaAllowed && val.startsWith("??")) ) ? val : SimpleSLTokenizer.quoteString(val)); buffer.append(out); } /** Encode the passed Abstract Predicate and append its encoding to buffer **/ private void encodeAndAppend(AbsPredicate val) throws CodecException { String propositionSymbol = val.getTypeName(); if (val.getCount() > 0) { // predicate with arguments String[] slotNames = getSlotNames(val); buffer.append('('); if (slOnto.isUnaryLogicalOp(propositionSymbol)) { // Unary logical operator of the SL language (NOT) buffer.append(propositionSymbol); buffer.append(' '); try { encodeAndAppend((AbsPredicate)val.getAbsObject(slotNames[0])); } catch (RuntimeException e) { throw new CodecException("A UnaryLogicalOp requires a formula argument",e); } } else if (slOnto.isBinaryLogicalOp(propositionSymbol)) { // Bynary logical operator of the SL language (AND, OR) buffer.append(propositionSymbol); buffer.append(' '); try { encodeAndAppend((AbsPredicate)val.getAbsObject(slotNames[0])); buffer.append(' '); encodeAndAppend((AbsPredicate)val.getAbsObject(slotNames[1])); } catch (RuntimeException e) { throw new CodecException("A BinaryLogicalOp requires 2 formula arguments",e); } } else if (slOnto.isQuantifier(propositionSymbol)) { // Quantifier operator of the SL language (EXISTS, FORALL) buffer.append(propositionSymbol); buffer.append(' '); try { encodeAndAppend((AbsVariable)val.getAbsObject(slotNames[0])); //FIXME. The hypothesis is that the first slot is the variable buffer.append(' '); encodeAndAppend((AbsPredicate)val.getAbsObject(slotNames[1])); } catch (RuntimeException e) { throw new CodecException("A Quantifier requires a variable and a formula arguments",e); } } else if (slOnto.isModalOp(propositionSymbol)) { // Modal operator of the SL language (B, I, U, PG) buffer.append(propositionSymbol); buffer.append(' '); try { encodeAndAppend((AbsTerm)val.getAbsObject(slotNames[0])); buffer.append(' '); encodeAndAppend((AbsPredicate)val.getAbsObject(slotNames[1])); } catch (RuntimeException e) { throw new CodecException("A ModalOp requires a term and a formula arguments",e); } } else if (slOnto.isActionOp(propositionSymbol)) { // Action operator of the SL language (DONE, FEASIBLE) buffer.append(propositionSymbol); buffer.append(' '); try { encodeAndAppend((AbsTerm)val.getAbsObject(slotNames[0])); //FIXME check it is an action expression AbsPredicate ap = (AbsPredicate)val.getAbsObject(slotNames[1]); if (ap != null) { // Second argument is optional buffer.append(' '); encodeAndAppend(ap); } } catch (RuntimeException e) { throw new CodecException("An ActionOp requires an actionexpression and (optionally) a formula arguments",e); } } else if (slOnto.isBinaryTermOp(propositionSymbol)) { // Binary term operator of the SL language (RESULT, =) buffer.append(propositionSymbol); buffer.append(' '); try { encodeAndAppend((AbsTerm)val.getAbsObject(slotNames[0])); buffer.append(' '); encodeAndAppend((AbsTerm)val.getAbsObject(slotNames[1])); } catch (RuntimeException e) { throw new CodecException("A BinaryTermOp requires 2 term arguments",e); } } else { encodeAndAppend(propositionSymbol); // Predicate in the ontology try { encodeSlotsByOrder(val, slotNames); } catch (RuntimeException e) { throw new CodecException("SL allows predicates with term arguments only",e); } } buffer.append(')'); } else // Proposition encodeAndAppend(propositionSymbol); } private void encodeAndAppend(AbsIRE val) throws CodecException { buffer.append('('); encodeAndAppend(val.getTypeName()); buffer.append(' '); encodeAndAppend(val.getTerm()); buffer.append(' '); encodeAndAppend(val.getProposition()); buffer.append(')'); } private void encodeAndAppend(AbsVariable val) throws CodecException { String var = val.getName(); if (var.charAt(0) == '?') { encodeAndAppend(var); } else { buffer.append('?'); encodeAndAppend(var); } } private void encodeAndAppend(AbsConcept val) throws CodecException { String functionSymbol = val.getTypeName(); buffer.append('('); String[] slotNames = getSlotNames(val); if (slOnto.isSLFunctionWithoutSlotNames(functionSymbol)) { // A Functional operator of the SL language (ACTION, + ...) // The form is: functionSymbol Term* buffer.append(functionSymbol); try { encodeSlotsByOrder(val, slotNames); } catch (RuntimeException e) { throw new CodecException("A FunctionalOperator requires 1 or 2 Term arguments",e); } } else { // A generic term in the ontology. The form can be both // functionSymbol Parameter* or functionSymbol Term*. Get the // preferred way from the ontology. encodeAndAppend(functionSymbol); try { // FIXME: To improve performances the two operations that imply // retrieving a schema from the ontology (getting slot names and // getting the preferred encoding type) should be carried out at // the same time. if (getEncodingByOrder(val)) { encodeSlotsByOrder(val, slotNames); } else { encodeSlotsByName(val, slotNames); } } catch (RuntimeException e) { throw new CodecException("A FunctionalTerm requires Terms arguments",e); } } buffer.append(')'); } private void encodeAndAppend(AbsAggregate val) throws CodecException { buffer.append('('); encodeAndAppend(val.getTypeName()); for (Iterator i=val.iterator(); i.hasNext(); ) { buffer.append(' '); encodeAndAppend((AbsObject)i.next()); } buffer.append(')'); } private void encodeAndAppend(AbsPrimitive val) throws CodecException { Object v = val.getObject(); if (v instanceof Date) buffer.append(ISO8601.toString((Date)v)); else if (v instanceof Number) { buffer.append(v.toString()); if (preserveJavaTypes) { if (v instanceof Long) { buffer.append('L'); } else if (v instanceof Float) { buffer.append('F'); } } } else if (v instanceof byte[]) { // Note: Currently uses Java default charset, may need to use another one byte[] b = (byte[]) v; buffer.append('#'); buffer.append(b.length); buffer.append('"'); // FIXME: Should we use base64 encoding? buffer.append(new String(b)); } else if (v instanceof Boolean) buffer.append(v.toString()); else { String vs = v.toString(); if ( (CaseInsensitiveString.equalsIgnoreCase("true",vs)) || (CaseInsensitiveString.equalsIgnoreCase("false",vs)) ) { // quote true and false to avoid confusion with booleans buffer.append('"'); buffer.append(vs); buffer.append('"'); } else encodeAndAppend(vs); } } private void encodeAndAppend(AbsObject val) throws CodecException { if (val instanceof AbsPrimitive) encodeAndAppend( (AbsPrimitive)val); else if (val instanceof AbsPredicate) encodeAndAppend( (AbsPredicate)val); else if (val instanceof AbsIRE) encodeAndAppend( (AbsIRE)val); else if (val instanceof AbsVariable) encodeAndAppend( (AbsVariable)val); // if (val instanceof AbsAgentAction) return toString( (AbsAgentAction)val); else if (val instanceof AbsAggregate) encodeAndAppend( (AbsAggregate)val); else if (val instanceof AbsConcept) encodeAndAppend( (AbsConcept)val); else throw new CodecException("SLCodec cannot encode this object "+val); } /** * Decodes the content to an abstract description. * @param content the content as a String. * @return the content as an abstract description. * @throws CodecException */ public AbsContentElement decode(String content) throws CodecException { return decode(null, content); } /** * Decodes the content to an abstract description. * @param ontology the ontology. * @param content the content as a String. * @return the content as an abstract description. * @throws CodecException */ public synchronized AbsContentElement decode(Ontology ontology, String content) throws CodecException { try { AbsContentElementList tuple = null; if (preserveJavaTypes) { extendedParser.reinit(ontology, content); tuple = extendedParser.Content(); } else { parser.reinit(ontology, content); tuple = parser.Content(); } if (tuple.size() > 1) return tuple; else // if there is a single ContentExpression than return just it, not the tuple return tuple.get(0); } catch(Throwable e) { // both ParseException and TokenMgrError throw new CodecException("Parse exception", e); } } /** * Decodes the content to an abstract description, where the content is known to be a Term. * @param ontology the ontology. * @param cterm the term as a String. * @return the content as an abstract description. * @throws CodecException * @since JADE 3.4 */ public synchronized AbsTerm decodeTerm(Ontology ontology, String term) throws CodecException { try { if (preserveJavaTypes) { extendedParser.reinit(ontology, term); return extendedParser.Term(); } else { parser.reinit(ontology, term); return parser.Term(); } } catch(Throwable e) { // both ParseException and TokenMgrError throw new CodecException("Parse exception", e); } } /** * Encodes the content into a String, where the content is known to be a Term. * @param ontology the ontology. * @param term the termt as an abstract descriptor * @return the content as a String * @throws CodecException * @since JADE 3.4 */ public synchronized String encodeTerm(Ontology ontology, AbsTerm term) throws CodecException { try { domainOnto = ontology; buffer = new StringBuffer(); encodeAndAppend(term); return buffer.toString(); } finally { buffer = null; //frees the memory } } /** * Decodes the content to an abstract description, where the content is known to be a Well-formed Formula * @param ontology the ontology. * @param formula the content as a String. * @return the content as an abstract description. * @throws CodecException * @since JADE 3.4 */ public synchronized AbsPredicate decodeFormula(Ontology ontology, String formula) throws CodecException { try { if (preserveJavaTypes) { extendedParser.reinit(ontology, formula); return extendedParser.Wff(); } else { parser.reinit(ontology, formula); return parser.Wff(); } } catch(Throwable e) { // both ParseException and TokenMgrError throw new CodecException("Parse exception", e); } } /** * Encodes the content into a String, where the content is known to be a Well-formed Formula * @param ontology the ontology. * @param formula the formula as an abstract descriptor * @return the content as a String * @throws CodecException * @since JADE 3.4 */ public synchronized String encodeFormula(Ontology ontology, AbsPredicate formula) throws CodecException { try { domainOnto = ontology; buffer = new StringBuffer(); encodeAndAppend(formula); return buffer.toString(); } finally { buffer = null; //frees the memory } } public static void main(String[] args) { SLCodec codec = null; char contentType = 'C'; try { codec = new SLCodec(Integer.parseInt(args[0])); contentType = (args.length > 1 ? args[1].charAt(0) : 'C'); } catch (Exception e) { System.out.println("usage: SLCodec SLLevel [ContentType]\n where SLLevel can be 0 for SL0, 1 for SL1, 2 for SL2, 3 or more for full SL \n and where ContentType is a char representing the type of content to be parsed: C for a contentexpression (default), T for a term, F for a formula"); System.exit(0); } while (true) { try { System.out.println("insert an SL " + (contentType == 'F' ? "Well-Formed Formula" : (contentType == 'T' ? "Term" : "Content Expression")) + " to parse (all the expression on a single line!): "); BufferedReader buff = new BufferedReader(new InputStreamReader(System.in)); String str = buff.readLine(); System.out.println("\n\n"); if (contentType == 'F') { AbsPredicate result = codec.decodeFormula(null, str); System.out.println("DUMP OF THE DECODE OUTPUT (just for debugging):"); System.out.println(result); System.out.println("\n\n"); System.out.println("AFTER ENCODE:"); System.out.println(codec.encodeFormula(null, result)); System.out.println("\n\n"); } else if (contentType == 'T') { AbsTerm result = codec.decodeTerm(null, str); System.out.println("DUMP OF THE DECODE OUTPUT (just for debugging):"); System.out.println(result); System.out.println("\n\n"); System.out.println("AFTER ENCODE:"); System.out.println(codec.encodeTerm(null, result)); System.out.println("\n\n"); } else { AbsContentElement result = codec.decode(str); System.out.println("DUMP OF THE DECODE OUTPUT (just for debugging):"); System.out.println(result); System.out.println("\n\n"); System.out.println("AFTER ENCODE:"); System.out.println(codec.encode(result)); System.out.println("\n\n"); } } catch(Exception pe) { pe.printStackTrace(); //System.exit(0); } } } /** * @return the ontology containing the schemas of the operator * defined in this language */ public Ontology getInnerOntology() { return slOnto; } private String[] getSlotNames(AbsObject abs) throws CodecException { String[] slotNames = null; String type = abs.getTypeName(); if (domainOnto != null) { // If an ontology is specified, get the slot names from it // (and not directly from the abstract descriptor val) to preserve // the order try { ObjectSchema s = domainOnto.getSchema(type); if (s == null) { throw new CodecException("No schema found for symbol "+type); } slotNames = s.getNames(); } catch (OntologyException oe) { throw new CodecException("Error getting schema for symbol "+type, oe); } } else { slotNames = abs.getNames(); } return slotNames; } private boolean getEncodingByOrder(AbsObject abs) throws CodecException { if (domainOnto != null) { String type = abs.getTypeName(); try { ObjectSchema s = domainOnto.getSchema(type); return s.getEncodingByOrder(); } catch (Exception e) { // Just ignore it } } return false; } /** * Encode the slots of an abstract descriptor by order, i.e. * without writing the slot names. Also take into account that, in * order to ensure a correct parsing, empty slots can only occur at * the end. * Append this encoded string to buffer. */ private void encodeSlotsByOrder(AbsObject val, String[] slotNames) throws CodecException { boolean lastSlotEmpty = false; for (int i=0; i<slotNames.length; i++) { AbsTerm t = (AbsTerm)val.getAbsObject(slotNames[i]); if (t != null) { if (lastSlotEmpty) { throw new CodecException("Non-empty slot "+slotNames[i]+" follows empty slot "+slotNames[i-1]); } buffer.append(' '); encodeAndAppend(t); } else { lastSlotEmpty = true; } } } /** * Encode the slots of an abstract descriptor by name, i.e. * writing for each non-empty slot the slot name followed by the * slot value. * Append this encoded string to buffer. */ private void encodeSlotsByName(AbsObject val, String[] slotNames) throws CodecException { for (int i=0; i<slotNames.length; i++) { AbsTerm t = (AbsTerm)val.getAbsObject(slotNames[i]); if (t != null) { // if this isn't un unnamed slot, then encode it otherwise just encode its value if (!slotNames[i].startsWith(this.UNNAMEDPREFIX)) { buffer.append(" :"); encodeAndAppend(slotNames[i]); } buffer.append(' '); encodeAndAppend(t); } } } /** * Restore parser after deserialization. <br> * The readResolve method is called when ObjectInputStream has read an object from the stream * and is preparing to return it to the caller. <br> * The readResolve method is not invoked on the object until the object is fully constructed. */ protected Object readResolve() throws java.io.ObjectStreamException { initParser(); return this; } //#MIDP_EXCLUDE_END }