/* * Copyright (C) 2003-2004 Red Hat Inc. 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 * */ package com.arsdigita.xml; import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.xml.formatters.DateTimeFormatter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import org.apache.log4j.Logger; /** * Provides a set of helper methods for dealing with XML, * including file parsing & object -> string serialization */ public class XML { private static final Logger s_log = Logger.getLogger(XML.class); // private static XMLConfig s_config; private static Map s_formatters = new HashMap(); static { s_log.debug("Static initalizer starting..."); s_formatters.put(Date.class, new DateTimeFormatter()); s_log.debug("Static initalizer finished."); } private XML() {} /** * Retrieves the current configuration public static XMLConfig getConfig() { if (s_config == null) { s_config = new XMLConfig(); s_config.load(); } return s_config; } */ /** * Registers a formatter for serializing objects of a * class to a String suitable for XML output. */ public static void registerFormatter(Class klass, Formatter formatter) { s_formatters.put(klass, formatter); } /** * Unregisters a formatter against a class. */ public static void unregisterFormatter(Class klass) { s_formatters.remove(klass); } /** * Gets a directly registered formatter for a class. * @param klass the class to find a formatter for * @return the formatter, or null if non is registered */ public static Formatter getFormatter(Class klass) { return (Formatter)s_formatters.get(klass); } /** * Looks for the best matching formatter. * @param klass the class to find a formatter for * @return the formatter, or null if non is registered */ public static Formatter findFormatter(Class klass) { Formatter formatter = null; while (formatter == null && klass != null) { formatter = getFormatter(klass); klass = klass.getSuperclass(); } return formatter; } /** * Converts an object to a String using the closest * matching registered Formatter implementation. Looks * for a formatter registered against the object's * class first, then its superclass, etc. If no formatter * is found, uses the toString() method */ public static String format(Object value) { if (value == null) { return null; } if (value instanceof String) { return (String)value; } Formatter formatter = findFormatter(value.getClass()); if (formatter == null) { if (s_log.isDebugEnabled()) { s_log.debug("No formatter for " + value.getClass()); } return value.toString(); } if (s_log.isDebugEnabled()) { s_log.debug("Processing " + value.getClass() + " with " + formatter.getClass()); } return formatter.format(value); } /** * Processes an XML file with the default SAX Parser, with * namespace processing, schema validation & DTD validation * enabled. * * @param path the XML file relative to the webapp root * @param handler the content handler */ public static final void parseResource(String path, DefaultHandler handler) { if (s_log.isDebugEnabled()) { s_log.debug("Processing resource " + path + " with " + handler.getClass()); } if (path.startsWith("/")) { path = path.substring(1); } ClassLoader cload = Thread.currentThread().getContextClassLoader(); InputStream stream = cload.getResourceAsStream(path); if (stream == null) { throw new IllegalArgumentException("no such resource: " + path); } parse(stream, handler); } /** * Processes an XML file with the default SAX Parser, with * namespace processing, schema validation & DTD validation * enabled. * * @param source the xml input stream * @param handler the content handler */ public static final void parse(InputStream source, DefaultHandler handler) { if (s_log.isDebugEnabled()) { s_log.debug("Processing stream " + source + " with " + handler.getClass()); } try { // ToDo (pboy): We should use // SAXParserFactory.newInstance(String clName, ClassLoader clLoader) // instead to achieve independence of a JVM wide acceptable // configuration (affecting all CCM instances which may run in a // container). // Requires additional modifications in c.ad.util.xml.XML SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setFeature("http://xml.org/sax/features/namespaces", true); SAXParser parser = spf.newSAXParser(); parser.parse(source, handler); } catch (ParserConfigurationException e) { throw new UncheckedWrapperException("error parsing stream", e); } catch (SAXException e) { if (e.getException() != null) { throw new UncheckedWrapperException("error parsing stream", e.getException()); } else { throw new UncheckedWrapperException("error parsing stream", e); } } catch (IOException e) { throw new UncheckedWrapperException("error parsing stream", e); } } /** * This visitor is called by {@link #traverse(Element, int, XML.Action)}. **/ public interface Action { void apply(Element elem, int level); } /** * Prints the skeleton structure of the element to the supplied print * writer. **/ public static void toSkeleton(final Element element, final PrintWriter writer) { XML.traverse(element, 0, new Action() { public void apply(Element elem, int level) { final String padding = " "; for (int ii=0; ii