/* Copyright (C) 2006 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Nomad 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Created on Dec 10, 2006
*/
package net.sf.nmedit.jpatch;
import java.awt.Color;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import net.sf.nmedit.jpatch.PConnectorDescriptor;
import net.sf.nmedit.jpatch.PParameterDescriptor;
import net.sf.nmedit.jpatch.impl.PBasicConnectorDescriptor;
import net.sf.nmedit.jpatch.impl.PBasicDescriptor;
import net.sf.nmedit.jpatch.impl.PBasicLightDescriptor;
import net.sf.nmedit.jpatch.impl.PBasicModuleDescriptor;
import net.sf.nmedit.jpatch.impl.PBasicParameterDescriptor;
import net.sf.nmedit.jpatch.impl.PBasicRoles;
import net.sf.nmedit.jpatch.js.JSContext;
import net.sf.nmedit.jpatch.js.JSFormatter;
import net.sf.nmedit.nmutils.Hex;
import org.mozilla.javascript.Function;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
public class ModuleDescriptionsParser
{
private ModuleDescriptionsParser()
{
super();
}
public static void parse(ModuleDescriptions descriptions, InputStream in)
throws ParserConfigurationException, SAXException, IOException
{
if (!(in instanceof BufferedInputStream))
in = new BufferedInputStream(in);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
DocumentHandler handler = new DocumentHandler(descriptions);
try
{
parser.parse(in, handler);
}
catch (SAXException e)
{
Locator l = handler.locator;
if (l == null)
throw e;
SAXException se = new SAXException("error in line:col="+l.getLineNumber()+":"+l.getColumnNumber());
se.initCause(e);
throw se;
}
}
private static class DocumentHandler extends DefaultHandler
{
// !!! order is important
static final int ModuleDescriptions = 0;
static final int header = 1;
static final int defs = 2;
static final int annotation = 3;
static final int body = 4;
static final int module = 5;
static final int comment = 6;
static final int parameter = 7;
static final int connector = 8;
static final int attribute = 9;
static final int vendor = 10;
static final int property = 11;
static final int device = 12;
static final int model = 13;
static final int version = 14;
static final int defsignal = 15;
static final int deftype = 16;
static final int signal = 17;
static final int enumeration = 18;
static final int image = 19;
static final int section = 20;
static final int title = 21;
static final int p = 22;
static final int pre = 23;
static final int code = 24;
static final int list = 25;
static final int item = 26;
static final int link = 27;
static final int mail = 28;
static final int light = 29;
static final int doc = 30;
static final int script = 31;
static final int documentation = 32;
Locator locator = null;
static String[] elements = new String[]
{
"ModuleDescriptions",
"header",
"defs",
"annotation",
"body",
"module",
"comment",
"parameter",
"connector",
"attribute",
"vendor",
"property",
"device",
"model",
"version",
"def-signal",
"def-type",
"signal",
"enumeration",
"image",
"section",
"title",
"p",
"pre",
"code",
"list",
"item",
"link",
"mail",
"light",
"doc",
"script",
"documentation"
};
static String[] attributes = new String[]
{
"id",
"version",
"url"
};
static final int attString = 0;
static final int attBoolean = 1;
static final int attInteger = 2;
static final int attFloat = 3;
static final int attDouble = 4;
static final int attColor = 5;
static String[] attTypes = new String[]
{
"string",
"boolean",
"integer",
"float",
"double",
"color"
};
static Map<String,Integer> elementMap = new HashMap<String,Integer>();
static Map<String,Integer> attributeMap = new HashMap<String,Integer>();
static Map<String,Integer> attTypeMap = new HashMap<String,Integer>();
static Map<String, Integer> ledTypes = new HashMap<String, Integer>();
Map<PBasicParameterDescriptor, String> extensions = new HashMap<PBasicParameterDescriptor, String>();
static
{
configMap(elements, elementMap);
configMap(attributes, attributeMap);
configMap(attTypes, attTypeMap);
ledTypes.put("led", PLightDescriptor.TYPE_LED);
ledTypes.put("led-array", PLightDescriptor.TYPE_LED_ARRAY);
ledTypes.put("meter", PLightDescriptor.TYPE_METER);
}
private Map<String,String> strCache = new HashMap<String, String>(1000);
private int instcount = 0;
private String str(String stringId)
{
String s;
if (stringId == null)
{
s = null;
}
else
{
s = strCache.get(stringId);
if (s == null)
strCache.put(s = stringId, stringId);
// System.out.println("strings: "+(++instcount)+"/"+(strCache.size()));*/
}
return s;
}
private static final String INDEX = "index";
private static final String CLASS = "class";
private static final String TYPE = "type";
private static final String KEY = "key";
private static final String VALUE = "value";
private static final String NAME = "name";
private static final String ROLE = "role";
private static final String SIGNAL = "signal";
private static final String COMPONENTID = "component-id";
private int getLEDType(String typeValue)
{
if (typeValue == null)
return PLightDescriptor.TYPE_UNKNOWN;
Integer t = ledTypes.get(typeValue);
if (t == null)
return PLightDescriptor.TYPE_UNKNOWN;
else
return t.intValue();
}
int currentElement = -1;
public DocumentHandler( ModuleDescriptions descriptions )
{
this.moduleDescriptions = descriptions;
}
private static void configMap( String[] names, Map<String, Integer> map )
{
for (int i=0;i<names.length;i++)
map.put(names[i], i);
}
public final int getAttTypeId(String element)
{
return getIdFromMap(element, attTypeMap);
}
public final int getElementId(String element)
{
return getIdFromMap(element, elementMap);
}
public final int getAttributeId(String attribute)
{
return getIdFromMap(attribute, attributeMap);
}
private PBasicModuleDescriptor moduled = null;
private PBasicParameterDescriptor parameterd = null;
private PBasicConnectorDescriptor connectord = null;
private PBasicLightDescriptor lightd = null;
private List<PParameterDescriptor> parameterList = new ArrayList<PParameterDescriptor>(100);
private List<PConnectorDescriptor> connectorList = new ArrayList<PConnectorDescriptor>(100);
private List<PLightDescriptor> lightList = new ArrayList<PLightDescriptor>(100);
ModuleDescriptions moduleDescriptions ;
PSignalTypes signalTypes;
PSimpleTypes typedef;
StringBuffer docText = new StringBuffer();
private boolean docElement = false;
private Map<Function, Formatter> formatterMap = new HashMap<Function, Formatter>();
private transient JSContext jsc;
private Formatter getFormatter(String src) throws SAXException
{
if (jsc == null)
jsc = moduleDescriptions.getJSContext();
Function jsFunction = jsc.getFormatterFunction(src);
if (jsFunction == null)
throw new SAXException("invalid function: "+src);
Formatter formatter = formatterMap.get(jsFunction);
if (formatter == null)
{
formatter = new JSFormatter(jsc, jsFunction);
formatterMap.put(jsFunction, formatter);
}
return formatter;
}
public final int getIdFromMap(String name, Map<String,Integer> map)
{
Integer id = map.get(name);
return id == null ? -1 : id.intValue();
}
public void startDocument ()
throws SAXException
{
// no op
}
public void endDocument ()
throws SAXException
{
// no op
}
public InputSource resolveEntity (String publicId, String systemId)
throws IOException, SAXException
{
return null;
}
public void notationDecl (String name, String publicId, String systemId)
throws SAXException
{
// no op
}
public void unparsedEntityDecl (String name, String publicId,
String systemId, String notationName)
throws SAXException
{
// no op
}
public void startElement (String uri, String localName,
String qName, Attributes attributes)
throws SAXException
{
final int eid = getElementId(qName);
if (eid < 0)
throw new SAXException("unknown element "+qName);
switch (eid)
{
case ModuleDescriptions:
{
String version = attributes.getValue("version");
if (!"1.4".equals(version))
throw new SAXException("incompatible version "+version);
}
break ;
case doc:
docElement = true;
docText.setLength(0);
break;
case documentation:
{
String lang = attributes.getValue("lang");
String src = attributes.getValue("src");
moduleDescriptions.setDocumentationSrc(lang, src);
break;
}
case script:
{
String type = attributes.getValue("type");
if (!"text/javascript".equals(type))
throw new SAXException("unsupported script type:"+type);
String src = attributes.getValue("src");
URL script = src == null ? null : moduleDescriptions.getResourceClassLoader().getResource(src);
if (script == null)
throw new SAXException("script not found: "+src);
try
{
moduleDescriptions.getJSContext().addScript(script);
}
catch (IOException e)
{
throw new SAXException("error parsing script: "+src, e);
}
}
break;
case header:
break;
case body:
break;
case module:
{
String name = attributes.getValue(NAME);
if (name == null)
throw new SAXException("module name must not be null");
String componentId = attributes.getValue(COMPONENTID);
if (componentId == null)
throw new SAXException("component-id missing in module: "+name);
moduled = new PBasicModuleDescriptor(moduleDescriptions, name, str(componentId), true);
initRoles(moduled, attributes);
String index = attributes.getValue(INDEX);
if (index != null)
moduled.setAttribute(INDEX, Integer.parseInt(index));
moduled.setAttribute("fullname", str(attributes.getValue("fullname")));
moduled.setAttribute("category", str(attributes.getValue("category")));
moduled.setAttribute(CLASS, str(attributes.getValue(CLASS)));
parameterList.clear();
connectorList.clear();
lightList.clear();
extensions.clear();
}
break;
case defsignal:
{
if (signalTypes == null)
{
signalTypes = new PSignalTypes("signals");
moduleDescriptions.setSignals(signalTypes);
}
}
break;
case signal:
{
if (signalTypes == null)
throw new SAXException("internal error signal(def=null)");
int key = Integer.parseInt(attributes.getValue(KEY));
String type = attributes.getValue(TYPE);
if (type == null)
throw new SAXException("signal type name not specified");
boolean noSignal = "true".equals(attributes.getValue("nosignal"));
String scolor = attributes.getValue("color");
Color color = null;
if (scolor != null)
color = Hex.htmlHexColor(scolor);
if (color == null)
color = noSignal ? Color.WHITE : Color.BLACK;
PSignal signal = signalTypes.create(key, str(type), color, noSignal);
//System.out.println(signal);
}
break;
case deftype:
{
String name = attributes.getValue(NAME);
if (name == null)
throw new SAXException("type name not specified");
typedef = new PSimpleTypes(str(name));
moduleDescriptions.addType(typedef);
}
break;
case enumeration:
{
if (typedef == null)
throw new SAXException("internal error type(def=null)");
int key = Integer.parseInt(attributes.getValue(KEY));
String value = attributes.getValue(VALUE);
if (value == null)
throw new SAXException("value[key="+key+"] not specified in type "+typedef);
typedef.putValue(key, value);
}
break;
case image:
{
if (moduled == null)
throw new SAXException("no module associated with image");
String src = attributes.getValue("src");
if (src==null)
throw new SAXException("image has no such attribute: 'src'");
String type = attributes.getValue(TYPE);
if (type==null)
throw new SAXException("image has no such attribute: 'type'");
int width = Integer.parseInt(attributes.getValue("width"));
int height = Integer.parseInt(attributes.getValue("height"));
if (width<=0)
throw new SAXException("invalid image width: "+width);
if (height<=0)
throw new SAXException("invalid image height: "+height);
ImageSource is = new ImageSource(src, width, height);
moduled.setAttribute(str(type), is);
}
break ;
case light:
{
String name = attributes.getValue(NAME);
if (name == null)
throw new SAXParseException("light name must not be null", locator);
String componentId = attributes.getValue(COMPONENTID);
if (componentId == null)
throw new SAXException("component-id missing in light: "+name);
String minValue = attributes.getValue("minValue");
String maxValue = attributes.getValue("maxValue");
String defaultValue = attributes.getValue("defaultValue");
lightd = new PBasicLightDescriptor(moduled, name, str(componentId));
initRoles(lightd, attributes);
String index = attributes.getValue(INDEX);
if (index != null)
lightd.setAttribute(INDEX, Integer.parseInt(index));
lightd.setType(getLEDType(attributes.getValue(TYPE)));
if (minValue != null)
lightd.setMinValue(Integer.parseInt(minValue));
if (maxValue != null)
lightd.setMaxValue(Integer.parseInt(maxValue));
if (defaultValue != null)
lightd.setDefaultValue(Integer.parseInt(defaultValue));
lightd.setAttribute(CLASS, attributes.getValue(CLASS));
lightList.add(lightd);
}
break;
case parameter:
{
if (moduled == null)
throw new SAXException("no module associated with parameter");
String name = attributes.getValue(NAME);
if (name == null)
throw new SAXParseException("parameter name must not be null", locator);
String componentId = attributes.getValue(COMPONENTID);
if (componentId == null)
throw new SAXException("component-id missing in parameter: "+name);
String minValue = attributes.getValue("minValue");
String maxValue = attributes.getValue("maxValue");
String defaultValue = attributes.getValue("defaultValue");
parameterd = new PBasicParameterDescriptor(moduled, str(name), componentId);
initRoles(parameterd, attributes);
String extension = attributes.getValue("extension");
if (extension != null)
extensions.put(parameterd, extension);
String index = attributes.getValue(INDEX);
if (index != null)
parameterd.setAttribute(INDEX, Integer.parseInt(index));
if (minValue != null)
parameterd.setMinValue(Integer.parseInt(minValue));
if (maxValue != null)
parameterd.setMaxValue(Integer.parseInt(maxValue));
if (defaultValue != null)
parameterd.setDefaultValue(Integer.parseInt(defaultValue));
parameterd.setAttribute(CLASS, str(attributes.getValue(CLASS)));
String fmt = attributes.getValue("formatter");
if (fmt != null)
{
Formatter formatter = getFormatter(fmt);
parameterd.setFormatter(formatter);
}
parameterList.add(parameterd);
}
break;
case attribute:
{
String name = attributes.getValue(NAME);
String type = attributes.getValue(TYPE);
String svalue = attributes.getValue(VALUE);
int typeid = -1;
if (type != null)
{
typeid = getAttTypeId(type);
if (typeid <0 )
throw new SAXException("unknown attribute type "+type);
}
Object value;
switch (typeid)
{
case attBoolean:
if (svalue.equals("yes")||svalue.equals("true"))
value = Boolean.TRUE;
else if (svalue.equals("no")||svalue.equals("false"))
value = Boolean.FALSE;
else
throw new SAXException("not a boolean value "+svalue);
break;
case attDouble:
value = Double.parseDouble(svalue);
break;
case attFloat:
value = Float.parseFloat(svalue);
break;
case attInteger:
value = Integer.parseInt(svalue);
break;
case attColor:
value = Hex.htmlHexColor(svalue);
break;
default:
value = str(svalue);
break;
}
if (parameterd != null)
parameterd.setAttribute(name, value);
else if (connectord != null)
connectord.setAttribute(name, value);
else if (lightd != null)
lightd.setAttribute(name, value);
else if (moduled != null)
moduled.setAttribute(name, value);
else
{
throw new SAXException("attribute not associated with (module|parameter|connector)");
}
}
break;
case connector:
{
if (moduled == null)
throw new SAXException("no module associated with connector");
String name = attributes.getValue(NAME);
if (name == null)
throw new SAXException("connector name must not be null");
String componentId = attributes.getValue(COMPONENTID);
if (componentId == null)
throw new SAXException("component-id missing in connector: "+name);
String type = attributes.getValue(TYPE);
if (type == null)
throw new SAXException("connector type (input|output) not specified");
boolean isOutput;
if ("input".equals(type))
isOutput = false;
else if ("output".equals(type))
isOutput = true;
else
throw new SAXException("connector must be one of (input|output): "+type);
PSignal sig = null;
String signalName = attributes.getValue(SIGNAL);
if (signalName != null)
{
if (signalTypes == null)
throw new SAXException("signal definitions missing");
sig = signalTypes.getTypeByName(signalName);
if (sig==null)
throw new SAXException("signal '"+signalName+"' not defined in "+signalTypes);
}
connectord = new PBasicConnectorDescriptor(moduled, str(name), str(componentId), sig, isOutput);
initRoles(connectord, attributes);
String index = attributes.getValue(INDEX);
if (index != null)
connectord.setAttribute(INDEX, Integer.parseInt(index));
connectorList.add(connectord);
}
break;
}
currentElement = eid;
}
private void initRoles(PBasicDescriptor descriptor, Attributes attributes)
{
String roleString = attributes.getValue(ROLE);
if (roleString!=null)
{
PRoles roles = PBasicRoles.parseRoles(roleString);
if (!roles.isEmpty())
descriptor.setRoles(roles);
}
}
public void endElement (String uri, String localName, String qName)
throws SAXException
{
final int eid = getElementId(qName);
switch (eid)
{
case doc:
{
if (docText.length()>0)
{
PDescriptor descriptor;
if (parameterd != null)
descriptor = parameterd;
else if (connectord != null)
descriptor = connectord;
else if (lightd != null)
descriptor = lightd;
else if (moduled != null)
descriptor = moduled;
else
descriptor = null;
if (descriptor != null)
descriptor.setAttribute("doc", docText.toString());
}
docElement = false;
break;
}
case parameter:
{
parameterd = null;
}
break;
case connector:
{
connectord = null;
}
break;
case light:
{
lightd = null;
}
break;
case module:
{
if (moduled==null)
throw new SAXException("internal error endElement(module)");
moduled.setParameters(parameterList);
moduled.setConnectors(connectorList);
moduled.setLights(lightList);
// set extensions
if (!extensions.isEmpty())
{
for (PBasicParameterDescriptor dst: extensions.keySet())
{
String id = extensions.get(dst);
PParameterDescriptor ext = moduled.getParameterByComponentId(id);
if (ext == null)
throw new PRuntimeException("extension [component-id='"+id+"'] not found for parameter: "+dst);
dst.setExtensionDescriptor(ext);
}
}
moduleDescriptions.add(moduled);
moduled = null;
}
break;
}
}
public void setDocumentLocator (Locator locator)
{
this.locator = locator;
}
public void startPrefixMapping (String prefix, String uri)
throws SAXException
{
// no op
}
public void endPrefixMapping (String prefix)
throws SAXException
{
// no op
}
public void characters (char ch[], int start, int length)
throws SAXException
{
if (docElement)
docText.append(ch, start, length);
}
public void ignorableWhitespace (char ch[], int start, int length)
throws SAXException
{
// no op
}
public void processingInstruction (String target, String data)
throws SAXException
{
// no op
}
public void skippedEntity (String name)
throws SAXException
{
// no op
}
/*
public void warning (SAXParseException e)
throws SAXException
{
super.warning(e);
}
public void error (SAXParseException e)
throws SAXException
{
super.error(e);
}
public void fatalError (SAXParseException e)
throws SAXException
{
super.fatalError(e);
}*/
public ModuleDescriptions getResult()
{
// TODO Auto-generated method stub
return null;
}
}
}