/* * <copyright> * Copyright 2010 BBN Technologies * </copyright> */ package com.bbn.openmap.util; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.util.logging.Level; import java.util.logging.Logger; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; /** * Base class for xml parsing * * @author rshapiro */ abstract public class AbstractXmlParser extends DefaultHandler { private final Logger logger = Logger.getLogger("com.bbn.openmap.util.AbstractXmlParser"); private final StringBuilder charactersCollector = new StringBuilder(); private String collectCharactersForElement; /** * Parse an XML resource by url. Exceptions are logged rather than thrown. If * an exception occurs the return value will be false to indicate that fact. * * @param file The file to parse. * @return Whether or not the parse succeeded. */ public boolean parseXmlResource(File file) { Reader reader = null; try { reader = new BufferedReader(new FileReader(file)); return parseXmlResource(file.getPath(), new InputSource(reader)); } catch (FileNotFoundException e) { logger.warning("Failed to open " + file + ":" + e.getMessage()); return false; } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { // best-effort } } } } /** * Parse from the given source. The source will not be closed after the * parse: the caller should do this. * * Exceptions are logged rather than thrown. If an exception occurs the * return value will be false to indicate that fact. * * @param resourceName The resource name to use in warning messages. * @param source The source of the xml input. * @return Whether or not the parse succeeded. */ public boolean parseXmlResource(Object resourceName, InputSource source) { XMLReader reader; source.setEncoding("UTF8"); try { reader = XMLReaderFactory.createXMLReader(); } catch (SAXException e) { if (logger.isLoggable(Level.SEVERE)) { logger.warning("Failed to create reader for " + resourceName + ": " + e.getMessage()); } return false; } boolean status = false; reader.setContentHandler(this); reader.setErrorHandler(this); try { reader.parse(source); status = true; } catch (SAXParseException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Failed to parse " + resourceName + " Line: " + e.getLineNumber() + " Col: " + e.getColumnNumber() + ": " + e); } } catch (SAXException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Failed to parse " + resourceName + e.getMessage()); } } catch (IOException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Failed to parse " + resourceName + ":" + e.getMessage()); } } return status; } /** * Inform the parser that we want to start gathering up the character data * for the current element. This would typically be invoked in * {@link #startElement} with the localName as the parameter. * * Use {@link #getCollectedCharacters}, typically in the corresponding * {@link #endElement}, to retrieve the result. * * @param expectedElement The element whose character data we're gathering. * Caller must pass the same element to * {@link #getCollectedCharacters} to ensure consistency. */ public void collectCharacters(String expectedElement) { collectCharactersForElement = expectedElement; } /** * Get the final string accumulated from all invocations of * {@link #characters} since the last call to {@link #collectCharacters}. * * @param expectedElement The element whose character data we're gathering. * Caller must pass the same element to {@link #collectCharacters} to * ensure consistency. * @return The String accumulated from all intervening calls to * {@link #characters}. * @throws RuntimeException if the expectedElement doesn't match. */ public String getCollectedCharacters(String expectedElement) { if (expectedElement.equals(collectCharactersForElement)) { String result = charactersCollector.toString(); charactersCollector.delete(0, result.length()); collectCharactersForElement = null; return result; } else { throw new RuntimeException("Expected \"" + expectedElement + "\", found \"" + collectCharactersForElement + "\""); } } /** * Handle multiple callbacks per element, which will happen when the data * includes xml escape sequences. Each escape sequence comes in its own * callback. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { if (collectCharactersForElement != null) { charactersCollector.append(ch, start, length); } } }