package com.framsticks.parsers; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import com.framsticks.params.*; import com.framsticks.params.types.DecimalParam; import com.framsticks.params.types.FloatParam; import com.framsticks.params.types.StringParam; import org.apache.log4j.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import com.framsticks.leftovers.f0.NeuroClass; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * The Class Schema, which represent f0 schema (it contains all the possible * classes definitions that can be used in f0 representation). Definitions are * loaded from XML stream. * * @author Jarek Szymczak * (please replace name and surname with my personal data) */ public class Schema { private final static Logger logger = Logger.getLogger(Schema.class); protected final Registry registry = new Registry(); /** The neuro classes (classess representing different types of neurons). */ private Map neuroClasses = new HashMap(); public static InputStream getDefaultDefinitionAsStream() { //return new FileInputStream(new File(Schema.class.getResource("/parsers/f0def.xml").getPath())); return Schema.class.getResourceAsStream("/parsers/f0def.xml"); } /** * Instantiates a new schema. * * @param inputStream * the xml stream with schema * @throws Exception * the exception if one occurred while reading the stream */ public Schema(InputStream inputStream) throws Exception { DocumentBuilderFactory factory; DocumentBuilder db; try { factory = javax.xml.parsers.DocumentBuilderFactory.newInstance(); db = factory.newDocumentBuilder(); Document document = db.parse(inputStream); NodeList classes = document.getElementsByTagName("CLASS"); for (int i = 0; i < classes.getLength(); i++) { Node classNode = classes.item(i); FramsClass framsClass = processClass(classNode); registry.putInfoIntoCache(framsClass); } classes = document.getElementsByTagName("NEUROCLASS"); for (int i = 0; i < classes.getLength(); i++) { Node classNode = classes.item(i); FramsClass framsClass = processClass(classNode); NamedNodeMap attributes = classNode.getAttributes(); int prefInputs = getIntAttribute(attributes, "INPUTS"); int prefOutput = getIntAttribute(attributes, "OUTPUT"); int prefLocation = getIntAttribute(attributes, "LOCATION"); int visualHints = getIntAttribute(attributes, "VISUALHINTS"); String symbolGlymphString = getAttribute(attributes, "SYMBOL"); int[] symbolGlymph = null; if (symbolGlymphString != null) { String[] sgha = symbolGlymphString.split(","); int length = sgha.length; symbolGlymph = new int[length]; for (int j = 0; j < length; j++) { try { symbolGlymph[j] = Integer.parseInt(sgha[j]); } catch (NumberFormatException e) { logger.error("an error occurred while parsing symbol glymph, class getId: " + framsClass.getId() + ", glymph offset: " + j); } } } neuroClasses.put(framsClass.getId(), new NeuroClass( framsClass, prefInputs, prefOutput, prefLocation, visualHints, symbolGlymph)); } } catch (IOException e) { logger.fatal("unexpected exception occurred: ", e); throw e; } catch (ParserConfigurationException e) { logger.fatal("unexpected exception occurred: ", e); throw e; } catch (SAXException e) { logger.fatal("unexpected exception occurred: ", e); throw e; } } /** * Method used for convenience, it retrieves the Integer value stored in * node under certain attribute getName. If value is not present or is other * getType than integer 0 is returned. * * @return attribute value if value exists and it's integer (0 otherwise) * */ private static int getIntAttribute(NamedNodeMap attributes, String name) { String v = getAttribute(attributes, name); if (v == null) { return 0; } try { return Integer.parseInt(v); } catch (NullPointerException e) { return 0; } catch (NumberFormatException e) { logger.fatal("attribute " + name + " should be numeric: " + v); return 0; } } private static String getAttribute(NamedNodeMap attributes, String name) { Node item = attributes.getNamedItem(name); if (item == null) { return null; } return item.getNodeValue(); } /** * Method used for convenience, it retrieves the value stored in node under * certain attribute getName. If value is not present method returns null. * * @param attributeName * the attribute getName * @param node * the node * @return attribute value if value exists (null otherwise) * */ private static String getAttributeFromNode(String attributeName, Node node) { if (node == null) { return null; } return getAttribute(node.getAttributes(), attributeName); } /** * In this method analysis of single class is performed. * * @param classNode * the class node * @return the param entry list as a class schema * @throws Exception * the exception in case of any error */ private static FramsClass processClass(Node classNode) throws Exception { String classId = null; String className = ""; String classDescription = ""; try { classId = classNode.getAttributes().getNamedItem("ID") .getNodeValue(); } catch (NullPointerException e) { throw new Exception("Class getId is not defined!"); } className = getAttributeFromNode("NAME", classNode); classDescription = getAttributeFromNode("DESCRIPTION", classNode); FramsClass framsClass = new FramsClass(classId, className, classDescription); NodeList classProperties = classNode.getChildNodes(); for (int j = 0; j < classProperties.getLength(); j++) { Node node = classProperties.item(j); if ("GROUP".equals(node.getNodeName())) { NamedNodeMap attributes = node.getAttributes(); String name = getAttribute(attributes, "NAME"); if (name == null) { logger.warn("Group name in class \"" + classId + "\" (" + className + ") is undefined"); } else { framsClass.appendGroup(new Group(name)); } } else if ("PROP".equals(node.getNodeName()) || "NEUROPROP".equals(node.getNodeName())) { NamedNodeMap attributes = node.getAttributes(); Param param = processParameter(attributes, classId); framsClass.append(param); } } return framsClass; } /** * It analyses the single property within the class * * @param attributes * the attributes of property * @param classId * the class getId * @return the param entry representing single parameter * @throws Exception * the exception in case of any error */ private static Param processParameter(NamedNodeMap attributes, String classId) throws Exception { String id = getAttribute(attributes, "ID"); if (id == null) throw new Exception("Property ID in class \"" + classId + "\" is undefined"); String type = getAttribute(attributes, "TYPE"); if (type == null) throw new Exception("TYPE of property \"" + id + "\" is undefined"); String name = getAttribute(attributes, "NAME"); String description = getAttribute(attributes, "DESCRIPTION"); int group = getIntAttribute(attributes, "GROUP"); String flagsString = getAttribute(attributes, "FLAGS"); Integer flags = 0; try { if (flagsString != null) for (String flag : flagsString.split("[^0-9]")) { if (flag.trim().equals("")) continue; flags |= Integer.parseInt(flag); } } catch (NumberFormatException e) { logger.warn("FLAGS parameter should be an Integer value or separated Integer values. FLAGS are set to: " + flags); } Map minMaxDef = new HashMap(); for (String key : new String[] { "MIN", "MAX", "DEF" }) { String value = getAttribute(attributes, key); if (value != null && !value.trim().equals("")) minMaxDef.put(key, value); } ParamBuilder builder = new ParamBuilder(); builder.setId(id).setName(name).setHelp(description).setGroup(group).setFlags(flags); if ("d".equals(type)) { Map minMaxDefInt = new HashMap(); for (Entry entry : minMaxDef.entrySet()) { try { minMaxDefInt.put(entry.getKey(), Integer.parseInt(entry.getValue())); } catch (NumberFormatException e) { logger.warn(entry.getKey() + " attribute in property \"" + id + "\" getId in class \"" + classId + "\" should be an integer value"); } } builder.setType(DecimalParam.class); builder.setMin(minMaxDefInt.get("MIN")); builder.setMax(minMaxDefInt.get("MAX")); builder.setDef(minMaxDefInt.get("DEF")); } else if ("f".equals(type)) { Map minMaxDefDouble = new HashMap(); for (Entry entry : minMaxDef.entrySet()) { try { minMaxDefDouble.put(entry.getKey(), Double.parseDouble(entry.getValue())); } catch (NumberFormatException e) { logger.warn(entry.getKey() + " attribute in property \"" + id + "\" getId in class \"" + classId + "\" should be a double value"); } } builder.setType(FloatParam.class); builder.setMin(minMaxDefDouble.get("MIN")); builder.setMax(minMaxDefDouble.get("MAX")); builder.setDef(minMaxDefDouble.get("DEF")); } else if ("s".equals(type)) { builder.setType(StringParam.class); builder.setDef(minMaxDef.get("DEF")); } else { builder.setType(type); } return builder.build(); } public Map getNeuroClasses() { return Collections.unmodifiableMap(neuroClasses); } public final Registry getRegistry() { return registry; } }