/**
* ***************************************************************
* 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.core.CaseInsensitiveString;
import jade.content.*;
import jade.content.abs.*;
import jade.content.schema.ObjectSchema;
import jade.content.onto.Ontology;
import jade.content.onto.OntologyException;
import jade.content.onto.BasicOntology;
import jade.content.lang.StringCodec;
import jade.content.lang.sl.SLVocabulary;
import jade.lang.acl.ISO8601;
import jade.util.leap.Iterator;
import java.util.Date;
import jade.util.Logger;
/**
* MIDP implementation of the SLCodec. Actually the MIDP version of the SLCodec just extends SimpleSLCodec
* @version $Date: 2011-03-21 14:19:47 +0100(lun, 21 mar 2011) $ $Revision: 6396 $
**/
class SimpleSLCodec extends StringCodec {
private int indent = 0;
private Logger logger = Logger.getMyLogger(this.getClass().getName());
public SimpleSLCodec() {
super(jade.domain.FIPANames.ContentLanguage.FIPA_SL);
}
/**
* Encodes a content into a string using a given ontology.
* @param ontology the ontology
* @param content the content as an abstract descriptor.
* @return the content as a string.
* @throws CodecException
*/
public String encode(Ontology ontology, AbsContentElement content) throws CodecException {
StringBuffer str = new StringBuffer("(");
if (content instanceof AbsContentElementList) {
for (Iterator i=((AbsContentElementList)content).iterator(); i.hasNext(); ) {
AbsObject abs = (AbsObject) i.next();
stringify(abs, ontology, str);
str.append(" ");
}
}
else {
stringify(content, ontology, str);
}
str.append(")");
return str.toString();
}
private void stringify(AbsObject val, Ontology onto, StringBuffer str) throws CodecException {
if (val instanceof AbsPrimitive)
stringifyPrimitive((AbsPrimitive) val, str);
else if (val instanceof AbsVariable)
stringifyVariable((AbsVariable) val, str);
else if (val instanceof AbsAggregate)
stringifyAggregate((AbsAggregate)val, onto, str);
else
stringifyComplex(val, onto, str);
}
private void stringifyComplex(AbsObject val, Ontology onto, StringBuffer str) throws CodecException {
str.append("(");
str.append(val.getTypeName());
ObjectSchema s = null;
try {
s = onto.getSchema(val.getTypeName());
}
catch (OntologyException oe) {
throw new CodecException("Error getting the schema for element "+val, oe);
}
if (val instanceof AbsConcept && !s.getEncodingByOrder()) {
encodeSlotsByName(val, val.getNames(), onto, str);
}
else {
encodeSlotsByOrder(val, s.getNames(), onto, str);
}
str.append(")");
}
/**
* 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.
*/
private void encodeSlotsByOrder(AbsObject val, String[] slotNames, Ontology onto, StringBuffer str) throws CodecException {
boolean lastSlotEmpty = false;
for (int i=0; i<slotNames.length; i++) {
AbsObject s = val.getAbsObject(slotNames[i]);
if (s != null) {
if (lastSlotEmpty) {
throw new CodecException("Non-empty slot "+slotNames[i]+" follows empty slot "+slotNames[i-1]);
}
str.append(" ");
stringify(s, onto, str);
}
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.
*/
private void encodeSlotsByName(AbsObject val, String[] slotNames, Ontology onto, StringBuffer str) throws CodecException {
for (int i=0; i<slotNames.length; i++) {
AbsObject s = val.getAbsObject(slotNames[i]);
if (s != null) {
str.append(" :");
str.append(slotNames[i]);
str.append(" ");
stringify(s, onto, str);
}
}
}
private void stringifyAggregate(AbsAggregate val, Ontology onto, StringBuffer str) throws CodecException {
str.append("(");
str.append(val.getTypeName());
for (Iterator i=val.iterator(); i.hasNext(); ) {
str.append(" ");
stringify((AbsObject)i.next(), onto, str);
}
str.append(")");
}
private void stringifyVariable(AbsVariable val, StringBuffer str) throws CodecException {
str.append("?");
str.append(val.getName());
}
private void stringifyPrimitive(AbsPrimitive val, StringBuffer str) throws CodecException {
String type = val.getTypeName();
if (type.equals(BasicOntology.STRING)) {
String s = val.getString();
if (CaseInsensitiveString.equalsIgnoreCase("true",s) || CaseInsensitiveString.equalsIgnoreCase("false",s)) {
s = '"'+s+'"'; // quote it to avoid confusion with the boolean primitives
} else if (!SimpleSLTokenizer.isAWord(s)) {
s = SimpleSLTokenizer.quoteString(s);
}
str.append(s);
}
else if (type.equals(BasicOntology.DATE))
str.append(ISO8601.toString(val.getDate()));
else if (type.equals(BasicOntology.BYTE_SEQUENCE))
throw new CodecException("SL_does_not_allow_encoding_sequencesOfBytes");
else
str.append(val.getObject().toString());
}
/**
* Decodes the content to an abstract description using a
* given ontology.
* @param ontology the ontology.
* @param content the content as a string.
* @return the content as an abstract description.
* @throws CodecException
*/
public AbsContentElement decode(Ontology ontology, String content) throws CodecException {
SimpleSLTokenizer p = new SimpleSLTokenizer(content);
try {
p.consumeChar('(');
AbsContentElement abs = (AbsContentElement) parse(p, ontology);
if (!p.nextToken().equals(")")) {
AbsContentElementList l = new AbsContentElementList();
l.add(abs);
do {
AbsContentElement abs1 = (AbsContentElement) parse(p, ontology);
l.add(abs1);
} while (!p.nextToken().equals(")"));
abs = l;
}
p.consumeChar(')');
return abs;
}
catch (ClassCastException cce) {
throw new CodecException("Error converting to AbsContentElement", cce);
}
}
private AbsObject parse(SimpleSLTokenizer p, Ontology o) throws CodecException {
AbsObject abs = null;
if (p.isOpenBracket()) {
abs = parseComplex(p, o);
}
else {
abs = parseSimple(p);
}
return abs;
}
private AbsObject parseComplex(SimpleSLTokenizer p, Ontology o) throws CodecException {
AbsObject abs = null;
p.consumeChar('(');
String name = p.getElement();
if(logger.isLoggable(Logger.FINE))
logger.log(Logger.FINE,"Parse complex descriptor: "+name);
++indent;
try {
ObjectSchema s = o.getSchema(name);
abs = s.newInstance();
if (abs instanceof AbsAggregate) {
fillAggregate((AbsAggregate) abs, p, o);
}
else if (p.nextToken().startsWith(":")) {
fillSlotsByName((AbsConcept) abs, p, o);
}
else {
fillSlotsByOrder(abs, s, p, o);
}
}
catch (CodecException ce) {
throw ce;
}
catch (Throwable t) {
throw new CodecException("Unexpeceted error parsing "+name, t);
}
indent--;
p.consumeChar(')');
if(logger.isLoggable(Logger.FINE))
logger.log(Logger.FINE,abs.toString());
return abs;
}
private void fillSlotsByOrder(AbsObject abs, ObjectSchema s, SimpleSLTokenizer p, Ontology o) throws CodecException {
String[] slotNames = s.getNames();
int i = 0;
while (!p.isClosedBracket()) {
AbsObject val = parse(p, o);
try {
AbsHelper.setAttribute(abs, slotNames[i], val);
++i;
}
catch (OntologyException oe) {
throw new CodecException("Can't assign "+val+" to slot "+slotNames[i]+" of "+abs);
}
}
}
private void fillSlotsByName(AbsConcept abs, SimpleSLTokenizer p, Ontology o) throws CodecException {
while (!p.isClosedBracket()) {
String slotName = p.getElement();
try {
AbsTerm val = (AbsTerm) parse(p, o);
abs.set(slotName, val);
}
catch (ClassCastException cce) {
throw new CodecException("Non Term value for slot "+slotName+" of Concept "+abs);
}
}
}
private void fillAggregate(AbsAggregate abs, SimpleSLTokenizer p, Ontology o) throws CodecException {
int i = 0;
while (!p.isClosedBracket()) {
try {
AbsTerm val = (AbsTerm) parse(p, o);
abs.add(val);
++i;
}
catch (ClassCastException cce) {
throw new CodecException("Non Term value for element "+i+" of Aggregate "+abs);
}
}
}
private AbsObject parseSimple(SimpleSLTokenizer p) throws CodecException {
String val = p.getElement();
if(logger.isLoggable(Logger.FINE))
logger.log(Logger.FINE,"Parse simple descriptor: "+val+". Next is "+p.nextToken());
try {
return AbsPrimitive.wrap(Long.parseLong(val));
}
catch (Exception e) {
}
//#MIDP_EXCLUDE_BEGIN
// Float
try {
// Note that Double.parseDouble() does not exist in PJava
return AbsPrimitive.wrap(Double.valueOf(val).doubleValue());
}
catch (Exception e) {
}
//#MIDP_EXCLUDE_END
// Date
try {
return AbsPrimitive.wrap(ISO8601.toDate(val));
}
catch (Exception e) {
}
// Boolean
if (val.equals("true")) {
return AbsPrimitive.wrap(true);
}
if (val.equals("false")) {
return AbsPrimitive.wrap(false);
}
// Variable
if (val.startsWith("?")) {
return new AbsVariable(val.substring(1, val.length()), null);
}
// String
if (val.startsWith("\"")) {
return AbsPrimitive.wrap(val.substring(1, val.length()-1));
}
else {
return AbsPrimitive.wrap(val);
}
}
/**
*/
public AbsContentElement decode(String content) throws CodecException {
throw new CodecException("Unsupported operation");
}
/**
*/
public String encode(AbsContentElement content) throws CodecException {
throw new CodecException("Unsupported operation");
}
public Ontology getInnerOntology() {
return SLOntology.getInstance();
}
}