package com.cosylab.cdb.jdal;
/*******************************************************************************
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration)
* and Cosylab 2002, All rights reserved
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
*
*
* @author dragan
*
* To change this generated comment edit the template variable "typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import alma.cdbErrType.wrappers.AcsJCDBRecordDoesNotExistEx;
public class XMLHandler extends DefaultHandler {
private static boolean m_toString;
private final Logger logger;
//
public XMLTreeNode m_rootNode = null;
protected XMLTreeNode m_parent;
// private StringBuffer m_arrayContent = new StringBuffer(64);
private int elementID = 0;
//
// if we are already write down xml header
private boolean headerEmited = false;
// used in hierarchy build when we don't want to close element before we emit all its subnodes
private boolean autoCloseStartingElement = true;
private boolean firstElement = true;
// elements as they are in xmls i.e. 'TestPowerSupplyACS'
private ArrayList startElements = null;
// logical names as found in hierarchy i.e. 'TEST_PS_1'
private ArrayList elementNames = null;
private String firstElementName = null;
// will be used to store xmlns prefixes
private ArrayList prefixes = new ArrayList();
// should we mark arrays
private int markArrays = 1;
// public fields for content and eventually error
public String m_errorString = null;
public StringBuffer m_xmlString = new StringBuffer(256);
// ctor
public XMLHandler(boolean toString, Logger logger) {
this.logger = logger;
m_toString = toString;
}
public void startDocument() throws SAXException {
if (m_toString && !headerEmited) {
m_xmlString.append("<?xml version='1.0' encoding='ISO-8859-1'?>\n");
headerEmited = true;
}
firstElement = true;
}
/** Start element. */
public void startElement(String uri, String local, String raw, Attributes attrs)
throws SAXException {
boolean inArray = false;
if(!autoCloseStartingElement && firstElement) {
if(startElements == null) {
startElements = new ArrayList();
elementNames = new ArrayList();
}
startElements.add(raw);
if(firstElementName == null || raw.equals("fault-family")) // alarm system fix to allow "component#property"
firstElementName = raw;
elementNames.add(firstElementName);
raw = firstElementName;
firstElementName = null;
firstElement = false;
}
if (!m_toString) {
XMLTreeNode pNode;
if (m_rootNode == null) {
m_rootNode = new XMLTreeNode(null);
m_rootNode.m_name = raw;
m_parent = m_rootNode;
}
else{
pNode = new XMLTreeNode(m_parent);
if (attrs.getLength() > 0 && (raw.equals("_") || raw.endsWith(":_"))) {
// in this case we will replace the element name with Name name, otherwise its first attribute is taken
if(raw.indexOf(':') >= 0) pNode.m_nameSpace = raw.substring(0,raw.indexOf(':'));
raw = attrs.getValue("Name");
if (raw == null){
inArray = true; // special case when array is found
raw = attrs.getValue(0);
if( markArrays > 0 ) { // ad an array marker
pNode.setArrayNode();
/* if(markArrays == 2) { // add node to the array - used by diff check where we must distinguish between original array elements and merged ones
XMLTreeNode arrNode = (XMLTreeNode)m_parent.m_subNodesMap.get(XMLTreeNode.ARRAY_MARKER);
arrNode.m_subNodesMap.put(String.valueOf(arrNode.m_subNodesMap.size()), pNode);
}
*/
}
}else{
if( markArrays > 0 ){
if(raw.equals("*")) pNode.setDynamicNode();
else pNode.setMapNode();
}
}
}
String mapKey;
if (m_parent.m_subNodesMap.get(raw) != null) {
elementID++;
pNode.m_name = raw;
mapKey = raw + elementID;
}
else {
pNode.m_name = mapKey = raw;
}
m_parent.m_subNodesMap.put(mapKey, pNode);
m_parent = pNode;
}
}
else{
if(elementNames == null) elementNames = new ArrayList();
m_xmlString.append('<');
if (attrs.getLength() > 0 && (raw.equals("_") || raw.endsWith(":_"))) {
// in this case we will replace the element name with Name name
//String mapName = attrs.getValue("Name");
//if(mapName != null && !mapName.equals("*")){
// m_xmlString.append(mapName);
// elementNames.add(mapName);
// }else{
m_xmlString.append(raw); elementNames.add(raw);//}
}else{
m_xmlString.append(raw);
elementNames.add(raw);
}
}
// add prefixes if any only if we are wiritng XML otherwise (like in DAO) it is not needed
if(prefixes.size() > 0) {
for (int i = 0; i < prefixes.size(); i++) {
if (m_toString) {
m_xmlString.append(prefixes.get(i));
}
else {
String prefix = (String)prefixes.get(i);
String name = prefix.substring(0, prefix.indexOf('='));
String value = prefix.substring(prefix.indexOf('=')+1);
m_parent.m_fieldMap.put(name, value);
}
}
prefixes.clear();
}
if (attrs != null) {
for (int i = 0; i < attrs.getLength(); i++) {
if (m_toString) {
m_xmlString.append(' ').append(attrs.getQName(i)).append("=\"").append(attrs.getValue(i)).append("\"");
}
else {
m_parent.m_fieldMap.put(attrs.getQName(i), attrs.getValue(i));
}
}
}
if (m_toString)
m_xmlString.append('>');
}
/** End element. */
public void endElement(String uri, String local, String raw) throws SAXException {
if(!autoCloseStartingElement) {
String elem = (String)startElements.get(startElements.size()-1);
if(elem.equals(raw))
return; // will close it later on explicit call
}
if (m_toString) {
String elem = (String)elementNames.remove(elementNames.size()-1);
m_xmlString.append("</").append(elem).append('>');
}
else {
/* if (m_parent.m_isArray) {
String nolastDelim = m_arrayContent.substring(0,m_arrayContent.length()-1); // remove last delimiter
m_parent.m_parent.m_fieldMap.put(m_parent.m_name, nolastDelim);
// since we added array content as field then there is no need any more for array node itself
m_parent.m_parent.m_subNodesMap.remove(m_parent.m_name);
m_arrayContent.delete(0, m_arrayContent.length());
}
*/ m_parent = m_parent.m_parent;
}
}
public void characters(char buf[], int offset, int len) throws SAXException {
if (m_toString) {
// maybe we should normalize first
m_xmlString.append(buf, offset, len);
}
}
/* (non-Javadoc)
* @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
*/
public void startPrefixMapping(String prefix, String uri) throws SAXException {
//if(!firstElement)
// return; // use just mappings for first element. Other elements also have prefixes at least from first element but they are not written inside XML so we will ignore them
if(prefix.length()>0)
prefixes.add(" xmlns:"+prefix+"=\""+uri+"\"");
else
prefixes.add(" xmlns=\""+uri+"\"");
}
public void error(SAXParseException e) {
m_errorString = "Line=" + e.getLineNumber() + ", Column="+e.getColumnNumber()+": " + e.getMessage();
logger.log(Level.FINE, "XML parser error: ", e);
}
/**
* Should we automatically close ending node
* @param b
*/
public void setAutoCloseStartingElement(boolean b) {
autoCloseStartingElement = b;
}
/**
*
*/
public void closeElement() throws SAXException {
if(autoCloseStartingElement)
return;
/*String elem = (String)*/startElements.remove(startElements.size()-1);
if(startElements.size() == 0)
startElements.add("dummy_elem");
String name = (String)elementNames.remove(elementNames.size()-1);
endElement(null, null, name);
}
/**
* @param string
*/
public void setFirstElement(String string) {
firstElementName = string;
}
/**
* @param markArrays
*/
public void setMarkArrays(int mode) {
this.markArrays = mode;
}
public XMLHandler getChild(String curl)
throws AcsJCDBRecordDoesNotExistEx{
XMLTreeNode tnFather = m_rootNode;
XMLTreeNode tnChild = null;
String strFirst = "";
String strLast = curl;
String strPast ="";
do{
strFirst = strLast.substring(0, strLast.indexOf('/'));
strLast = strLast.substring(strLast.indexOf('/')+1);
/*Iterator i = tnFather.getNodesMap().keySet().iterator();
while(i.hasNext()){
String s = (String)i.next();
}
*/
tnChild = tnFather.getNodesMap().get(strFirst);
strPast+="/"+strFirst;
if(tnChild == null){
AcsJCDBRecordDoesNotExistEx recordDoesNotExist =
new AcsJCDBRecordDoesNotExistEx();
recordDoesNotExist.setCurl(strPast);
throw recordDoesNotExist;
}
tnFather = tnChild;
}while(!strLast.equals(""));
XMLHandler a = new XMLHandler(false, logger);
a.m_rootNode = tnChild;
return a;
}
public String toString(boolean withMapNames){
if(m_toString)
return m_xmlString.toString();
else{
if(m_rootNode == null) return null;
else return m_rootNode.toString(withMapNames);
}
}
}