/*******************************************************************************
* Copyright (c) 2004 Red Hat, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Keith Seitz <keiths@redhat.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.oprofile.core.opxml;
import java.util.HashMap;
import java.util.Stack;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.checkevent.CheckEventsProcessor;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.info.OpInfoProcessor;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.modeldata.ModelDataProcessor;
import org.eclipse.linuxtools.internal.oprofile.core.opxml.sessions.SessionsProcessor;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
/**
* The SAX handler class that is used to parse the output of opxml.
* @see org.eclipse.linuxtools.internal.oprofile.core.opxml.OpxmlRunner
*/
public class OprofileSAXHandler extends DefaultHandler {
// The only allowed instance of this class
private static OprofileSAXHandler instance = null;
// A Map of all the XML processors for opxml
private static HashMap<String,Class<?>> processors = new HashMap<>();
// The current processor being used to parse the document
private XMLProcessor processor = null;
private Object callData;
/* A stack of XML processors. This allows processors to invoke sub-processors
for handling nested tags more efficiently. */
private Stack<XMLProcessor> processorStack = new Stack<>();
// Introduced for fix of Eclipse BZ#343025
// As per SAX spec, SAX parsers are allowed to split character data into as many chunks as
// they please, and they can split the text at whichever boundaries they want. In order to
// handle this properly, it is needed to accumulate the text returned in each call
// until it recieves a callback that isn't characters.
private StringBuffer charactersBuffer;
// A convenience class for specifying XMLProcessors
private static class ProcessorItem {
public String tagName;
public Class<?> handlerClass;
public ProcessorItem(String name, Class<?> cls) {
tagName = name;
handlerClass = cls;
}
}
// The list of all "root" XML tags and their handler classes
private static final ProcessorItem[] handlerList = {
new ProcessorItem(OpxmlConstants.INFO_TAG, OpInfoProcessor.class),
new ProcessorItem(OpxmlConstants.CHECKEVENTS_TAG, CheckEventsProcessor.class),
new ProcessorItem(OpxmlConstants.MODELDATA_TAG, ModelDataProcessor.class),
new ProcessorItem(OpxmlConstants.SESSIONS_TAG, SessionsProcessor.class)
};
/**
* Returns an instance of the handler. This must be used to access the parser!
* @return a handler instance
*/
public static OprofileSAXHandler getInstance(Object callData) {
if (instance == null) {
instance = new OprofileSAXHandler();
// Initialize processor map
for (int i = 0; i < handlerList.length; ++i) {
processors.put(handlerList[i].tagName, handlerList[i].handlerClass);
}
}
// Set calldata into handler
instance.setCallData (callData);
return instance;
}
/**
* Sets the calldata for the processor.
* @param callData the calldata to pass to the processor
*/
private void setCallData(Object callData) {
this.callData = callData;
}
/**
* Returns the processor for a given request type.
* @param type the name of the processor
* @return the requested processor or null
*/
private static XMLProcessor getProcessor(String type) {
XMLProcessor processor = null;
Class<?> handlerClass = processors.get(type);
if (handlerClass != null) {
try {
processor = (XMLProcessor) handlerClass.newInstance();
} catch (InstantiationException|IllegalAccessException e) {
}
}
return processor;
}
@Override
public void startDocument() {
// Reset processor
processor = null;
}
@Override
public void endDocument() {
}
@Override
public void startElement(String uri, String lName, String qName, Attributes attrs) {
if (processor == null) {
// Get processor for this event type
processor = getProcessor(qName);
processor.reset(callData);
}
// If we already have a processor, so let it deal with this new element.
// Allow the processor to deal with it's own tag as well: this way it can
// grab attributes from it.
processor.startElement(qName, attrs, callData);
// Clean up the characters buffer
charactersBuffer = new StringBuffer();
}
@Override
public void endElement(String uri, String name, String qName) {
// Set the accumulated characters
processor.characters(charactersBuffer.toString(), callData);
processor.endElement(qName, callData);
}
@Override
public void characters(char ch[], int start, int length) {
// Ignore characters which are only whitespace
String str = new String(ch, start, length).trim();
if (str.length() > 0 && processor != null)
// Append the character to the buffer.
charactersBuffer.append(str);
}
/**
* Pushes the current XMLProcessor onto the stack and installs the given
* processor as the document's parser/handler.
* @param proc the processor to continue parsing the document
*/
public void push(XMLProcessor proc) {
processorStack.add(processor);
processor = proc;
processor.reset(callData);
}
/**
* Removes the current XMLProcessor and installs the previous processor.
* NOTE: This assumes that endElement caused the pop, so it calls endElement in
* the parent processor.
* @param tag the XML tag to pass to the parent processor
*/
public void pop(String tag) {
processor = processorStack.pop();
processor.endElement(tag, callData);
}
}