/******************************************************************************* * Copyright (c) 2011 The Board of Trustees of the Leland Stanford Junior University * as Operator of the SLAC National Accelerator Laboratory. * Copyright (c) 2011 Brookhaven National Laboratory. * EPICS archiver appliance is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. *******************************************************************************/ package org.epics.archiverappliance.config.ChannelArchiver; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.LinkedList; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * Ability to import a ChannelArchiver engine config file. * Support for PVNames, periods and scan/monitor so far. * @author mshankar * */ public class EngineConfigParser extends DefaultHandler { /** * Main import method. Pass in an input stream (perhaps from an HTTP POST) and get a list of PVConfigs. * @param is InputStream * @return PVConfig The list of PVconfigs * @throws Exception   */ public static LinkedList<PVConfig> importEngineConfig(InputStream is) throws Exception { SAXParserFactory sfac = SAXParserFactory.newInstance(); sfac.setNamespaceAware(false); sfac.setValidating(false); SAXParser parser = sfac.newSAXParser(); EngineConfigParser econfig = new EngineConfigParser(); parser.parse(is, econfig); return econfig.pvConfigs; } LinkedList<PVConfig> pvConfigs = new LinkedList<PVConfig>(); boolean inChannel = false; boolean inElement = true; StringWriter getThresholdBuf = null; StringWriter nameBuf = null; StringWriter periodBuf = null; String name = null; String period = null; boolean monitor = true; // According to the channel archiver manual, this defaults to 20 seconds. int getThreshold = 20; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if(qName.equals("channel")) { inChannel = true; } else if (inChannel && qName.equals("name")) { nameBuf = new StringWriter(); } else if (inChannel && qName.equals("period")) { periodBuf = new StringWriter(); } else if (inChannel && qName.equals("monitor")) { monitor = true; } else if (inChannel && qName.equals("scan")) { monitor = false; } else if(qName.equals("get_threshold")) { getThresholdBuf = new StringWriter(); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if(qName.equals("channel")) { inChannel = false; float samplingperiod = Float.parseFloat(period); pvConfigs.add(new PVConfig(name, samplingperiod, monitor)); name = null; period = null; monitor = false; } else if (inChannel && qName.equals("name") && nameBuf != null) { name = nameBuf.toString(); nameBuf = null; } else if (inChannel && qName.equals("period") && periodBuf != null) { period = periodBuf.toString(); periodBuf = null; } else if(qName.equals("get_threshold") && getThresholdBuf != null) { getThreshold = Integer.parseInt(getThresholdBuf.toString()); getThresholdBuf = null; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if(inChannel && nameBuf != null) { nameBuf.append(new String(ch, start, length)); } else if(inChannel && periodBuf != null) { periodBuf.append(new String(ch, start, length)); } else if(getThresholdBuf != null) { getThresholdBuf.append(new String(ch, start, length)); } } @Override public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { if(systemId.endsWith("engineconfig.dtd")) { ByteArrayInputStream bis = new ByteArrayInputStream(channelArchiverEngineConfigDTD.getBytes()); return new InputSource(bis); } throw new IOException("Cannot resolve " + systemId); } private static String channelArchiverEngineConfigDTD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!-- DTD for the ArchiveEngine Configuration -->\n" + "<!-- Note that we do not allow empty configurations: -->\n" + "<!-- Each config. must contain at least one group, -->\n" + "<!-- and each group must contain at least 1 channel. -->\n" + "<!ELEMENT engineconfig ((write_period|get_threshold|\n" + " file_size|ignored_future|\n" + " buffer_reserve|\n" + " max_repeat_count|disconnect)*,\n" + " group+)>\n" + "<!ELEMENT group (name,channel+)>\n" + "<!ELEMENT channel (name,period,(scan|monitor),disable?)>\n" + "<!ELEMENT write_period (#PCDATA)><!-- int seconds -->\n" + "<!ELEMENT get_threshold (#PCDATA)><!-- int seconds -->\n" + "<!ELEMENT file_size (#PCDATA)><!-- MB -->\n" + "<!ELEMENT ignored_future (#PCDATA)><!-- double hours -->\n" + "<!ELEMENT buffer_reserve (#PCDATA)><!-- int times -->\n" + "<!ELEMENT max_repeat_count (#PCDATA)><!-- int times -->\n" + "<!ELEMENT disconnect EMPTY>\n" + "<!ELEMENT name (#PCDATA)>\n" + "<!ELEMENT period (#PCDATA)><!-- double seconds -->\n" + "<!ELEMENT scan EMPTY>\n" + "<!ELEMENT monitor EMPTY>\n" + "<!ELEMENT disable EMPTY>\n"; }