/******************************************************************************* * Copyright (c) 2008, 2015 Innoopract Informationssysteme GmbH and others. * 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.rap.rwt.internal.theme.css; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import org.eclipse.rap.rwt.apache.batik.css.parser.ParseException; import org.eclipse.rap.rwt.internal.theme.ThemeManagerException; import org.eclipse.rap.rwt.internal.util.ParamCheck; import org.eclipse.rap.rwt.service.ResourceLoader; import org.w3c.css.sac.CSSException; import org.w3c.css.sac.CSSParseException; import org.w3c.css.sac.ErrorHandler; import org.w3c.css.sac.InputSource; import org.w3c.css.sac.Parser; public class CssFileReader { private static final String CSS_ENCODING = "UTF-8"; private final List<CSSException> problems; private final Parser parser; CssFileReader() { try { parser = new org.eclipse.rap.rwt.apache.batik.css.parser.Parser(); } catch( Exception e ) { throw new RuntimeException( "Failed to instantiate CSS parser", e ); } problems = new ArrayList<>(); } /** * Reads a style sheet from a file. The loader is used to load the file and * resources referenced in the CSS. */ public static StyleSheet readStyleSheet( String fileName, ResourceLoader loader ) throws IOException { ParamCheck.notNull( fileName, "fileName" ); InputStream inputStream = loader.getResourceAsStream( fileName ); if( inputStream == null ) { throw new IllegalArgumentException( "Could not open resource " + fileName ); } return parseStyleSheet( inputStream, fileName, loader ); } /** * Reads a style sheet from an input stream. The fileName is only used for * error messages. The loader is used to load resources referenced in the CSS. */ public static StyleSheet readStyleSheet( InputStream inputStream, String fileName, ResourceLoader loader ) throws IOException { ParamCheck.notNull( inputStream, "inputStream" ); ParamCheck.notNull( fileName, "fileName" ); return parseStyleSheet( inputStream, fileName, loader ); } private static StyleSheet parseStyleSheet( InputStream inputStream, String fileName, ResourceLoader loader ) throws IOException { StyleSheet styleSheet; try { CssFileReader reader = new CssFileReader(); styleSheet = reader.parse( inputStream, fileName, loader ); } catch( CSSException e ) { throw new ThemeManagerException( "Failed parsing CSS file", e ); } finally { inputStream.close(); } return styleSheet; } StyleSheet parse( InputStream inputStream, String uri, ResourceLoader loader ) throws CSSException, IOException { InputSource source = new InputSource(); source.setByteStream( inputStream ); source.setEncoding( CSS_ENCODING ); source.setURI( uri ); parser.setConditionFactory( new ConditionFactoryImpl( this ) ); parser.setSelectorFactory( new SelectorFactoryImpl( this ) ); DocumentHandlerImpl documentHandler = new DocumentHandlerImpl( this, loader ); parser.setDocumentHandler( documentHandler ); parser.setErrorHandler( new ErrorHandlerImpl( this ) ); // TODO [rst] Batik parser throws ParseException try { parser.parseStyleSheet( source ); } catch( ParseException e ) { throw new CSSException( e ); } return documentHandler.getStyleSheet(); } CSSException[] getProblems() { CSSException[] result = new CSSException[ problems.size() ]; problems.toArray( result ); return result; } void addProblem( CSSException exception ) { // TODO [rst] Logging instead of sysout System.err.println( exception ); problems.add( exception ); } private static class ErrorHandlerImpl implements ErrorHandler { private final List<CSSException> problems; public ErrorHandlerImpl( CssFileReader reader ) { problems = reader.problems; } // TODO [rst] decent logging instead of sysout @Override public void warning( CSSParseException exception ) throws CSSException { String problem = createProblemDescription( "WARNING: ", exception ); System.err.println( problem ); problems.add( exception ); } @Override public void error( CSSParseException exception ) throws CSSException { String problem = createProblemDescription( "ERROR: ", exception ); System.err.println( problem ); problems.add( exception ); } @Override public void fatalError( CSSParseException exception ) throws CSSException { String problem = createProblemDescription( "FATAL ERROR: ", exception ); System.err.println( problem ); problems.add( exception ); throw exception; } private static String createProblemDescription( String type, CSSParseException exception ) { String pattern = "{0}: {1} in {2} at pos [{3}:{4}]"; Object[] arguments = { type, exception.getMessage(), exception.getURI(), String.valueOf( exception.getLineNumber() ), String.valueOf( exception.getColumnNumber() ) }; return MessageFormat.format( pattern, arguments ); } } }