/******************************************************************************* * Copyright (c) 2007, 2009 Innoopract Informationssysteme GmbH. * 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: * Innoopract Informationssysteme GmbH - initial API and implementation * EclipseSource - ongoing development ******************************************************************************/ package org.eclipse.rwt.internal.theme; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import javax.xml.parsers.*; import org.eclipse.rwt.internal.service.ServletLog; import org.w3c.dom.*; import org.xml.sax.*; /** * Reader for theme definition files. These are the "*.theme.xml" files * that define themeable properties of a certain widget. */ public final class ThemeDefinitionReader { private static final String ELEM_ROOT = "theme"; private static final String ELEM_ELEMENT = "element"; private static final String ELEM_PROPERTY = "property"; private static final String ELEM_STYLE = "style"; private static final String ELEM_STATE = "state"; private static final String ATTR_NAME = "name"; private static final String THEME_DEF_SCHEMA = "themedef.xsd"; private static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; private static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; private final InputStream inputStream; private final String fileName; private final Collection cssElements; /** * An instance of this class reads theme definitions from an XML resource. * * @param inputStream input stream from a theme definition XML * @param fileName the file name to refer to in (error) messages */ public ThemeDefinitionReader( final InputStream inputStream, final String fileName ) { if( inputStream == null ) { throw new NullPointerException( "null argument" ); } this.inputStream = inputStream; this.fileName = fileName; this.cssElements = new ArrayList(); } /** * Reads a theme definition from the specified stream. The stream is kept open * after reading. * * @throws IOException if a I/O error occurs * @throws SAXException if a parse error occurs */ public void read() throws SAXException, IOException { Document document; document = parseThemeDefinition( inputStream ); Node root = document.getElementsByTagName( ELEM_ROOT ).item( 0 ); NodeList childNodes = root.getChildNodes(); for( int i = 0; i < childNodes.getLength(); i++ ) { Node node = childNodes.item( i ); if( node.getNodeType() == Node.ELEMENT_NODE ) { if( ELEM_ELEMENT.equals( node.getNodeName() ) ) { readElement( node ); } } } } /** * Returns the CSS element names in the definition. */ public IThemeCssElement[] getThemeCssElements() { IThemeCssElement[] result = new IThemeCssElement[ cssElements.size() ]; cssElements.toArray( result ); return result; } private void readElement( final Node node ) { String name = getAttributeValue( node, ATTR_NAME ); ThemeCssElement themeElement = new ThemeCssElement( name ); cssElements.add( themeElement ); NodeList childNodes = node.getChildNodes(); for( int i = 0; i < childNodes.getLength(); i++ ) { Node childNode = childNodes.item( i ); if( childNode.getNodeType() == Node.ELEMENT_NODE ) { if( ELEM_ELEMENT.equals( childNode.getNodeName() ) ) { readElement( childNode ); } else if( ELEM_PROPERTY.equals( childNode.getNodeName() ) ) { themeElement.addProperty( getAttributeValue( childNode, ATTR_NAME ) ); } else if( ELEM_STYLE.equals( childNode.getNodeName() ) ) { themeElement.addStyle( getAttributeValue( childNode, ATTR_NAME ) ); } else if( ELEM_STATE.equals( childNode.getNodeName() ) ) { themeElement.addState( getAttributeValue( childNode, ATTR_NAME ) ); } } } } private Document parseThemeDefinition( final InputStream is ) throws SAXException, IOException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware( true ); ClassLoader loader = ThemeDefinitionReader.class.getClassLoader(); final URL schema = loader.getResource( THEME_DEF_SCHEMA ); factory.setValidating( schema != null ); try { factory.setAttribute( JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA ); } catch( final IllegalArgumentException iae ) { // XML-Processing does not support JAXP 1.2 or greater factory.setNamespaceAware( false ); factory.setValidating( false ); } DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); } catch( final ParserConfigurationException e ) { String message = "Failed to initialize parser for theme definition files"; throw new RuntimeException( message, e ); } // builder.setEntityResolver( new EntityResolver() { // public InputSource resolveEntity( final String publicID, // final String systemID ) // throws IOException, SAXException // { // InputSource result = null; // if( schema != null && systemID.endsWith( THEME_DEF_SCHEMA ) ) { // URLConnection connection = schema.openConnection(); // connection.setUseCaches( false ); // result = new InputSource( connection.getInputStream() ); // } // return result; // } // } ); builder.setErrorHandler( new ThemeDefinitionErrorHandler() ); return builder.parse( is ); } private static String getAttributeValue( final Node node, final String name ) { String result = null; NamedNodeMap attributes = node.getAttributes(); if( attributes != null ) { Node namedItem = attributes.getNamedItem( name ); if( namedItem != null ) { result = namedItem.getNodeValue(); } } return result; } private class ThemeDefinitionErrorHandler implements ErrorHandler { public void error( final SAXParseException spe ) throws SAXException { String msg = "Error parsing theme definition " + getPosition( spe ) + ":"; ServletLog.log( msg, null ); ServletLog.log( spe.getMessage(), null ); } public void fatalError( final SAXParseException spe ) throws SAXException { String msg = "Fatal error parsing theme definition " + getPosition( spe ) + ":"; ServletLog.log( msg, null ); ServletLog.log( spe.getMessage(), null ); } public void warning( final SAXParseException spe ) throws SAXException { String msg = "Warning parsing theme definition " + getPosition( spe ) + ":"; ServletLog.log( msg, null ); ServletLog.log( spe.getMessage(), null ); } private String getPosition( final SAXParseException spe ) { String result = "in file '" + fileName + "' at line " + spe.getLineNumber() + ", col " + spe.getColumnNumber(); return result; } } }