diff -r fe49e33862e2 -r 04b7640f6fb5 themeinstaller/source/src/com/nokia/tools/themeinstaller/cssparser/CSSHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themeinstaller/source/src/com/nokia/tools/themeinstaller/cssparser/CSSHandler.java Wed Sep 01 12:32:13 2010 +0100 @@ -0,0 +1,588 @@ +/* +* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: This class gets notified during the CSS file parsing + * +*/ + + +package com.nokia.tools.themeinstaller.cssparser; + +import java.awt.Color; +import java.io.File; +import java.io.FileReader; +import java.io.Reader; +import java.util.HashMap; +import java.util.Vector; + +import org.w3c.css.sac.CSSException; +import org.w3c.css.sac.DocumentHandler; +import org.w3c.css.sac.InputSource; +import org.w3c.css.sac.LexicalUnit; +import org.w3c.css.sac.Parser; +import org.w3c.css.sac.SACMediaList; +import org.w3c.css.sac.SelectorList; +import org.w3c.css.sac.helpers.ParserFactory; +import org.w3c.dom.Document; + +import com.nokia.tools.themeinstaller.logger.LogWriter; + +/** + * The Class CSSHandler. Informs application of basic parsing events. This + * should be registered with CSS Parser with setDocumentHandler method. + * + * Handler gets notified during the CSS parsing: + * 1. startSelector(), when new selector is noticed + * 2. property(), when new property is noticed + * 3. lexicalValue() is used to calculate the values of the parsed property + * + * If you are using CSS import with paths relative to main CSS, you need to + * set the path of main CSS using setImportDirectory( String aImportDirectory ) + */ + +public class CSSHandler implements DocumentHandler + { + + /** The style map for style property. */ + private HashMap iStyleMap; + + /** List of parsed rules. */ + private Vector iRuleList; + + /** The DOM document. */ + private Document iDocument; + + /** Color resolver for resolving colors in string form */ + private ColorResolver iColorResolver; + + /** The main CSS directory for finding imported CSS File's relative path. */ + private String iImportDirectory; + + /** The Constant FUNCTION_HSL. */ + public static final String FUNCTION_HSL = "hsl"; + + /** The Constant COMMA. */ + public static final String COMMA = ","; + + /** The Constant CHAR_APOSTROPHE. */ + private static final char CHAR_APOSTROPHE = '"'; + + /** The Constant CHAR_SPACE. */ + private static final char CHAR_SPACE = ' '; + + /** The Constant CHAR_COLON. */ + private static final char CHAR_COLON = ':'; + + /** The Constant SEPARATOR. */ + public static final String SEPARATOR = "|"; + + /** The Constant FORWARD_SLASH. */ + public static final String FORWARD_SLASH = "/"; + + /** The Constant BACKWARD_SLASH. */ + public static final String BACKWARD_SLASH = "\\"; + + /** The CSS to DOM Processor for applying changes to DOM. */ + private CSSDOMProcessor iCSSDOMProcessor; + + /** + * Instantiates a new CSS handler. + */ + public CSSHandler() + { + super(); + iCSSDOMProcessor = new CSSDOMProcessor(); + iStyleMap = new HashMap(); + iRuleList = new Vector(); + iColorResolver = new ColorResolver(); + } + + /** + * Instantiates a new CSS handler. This one is used for handling imported + * CSS files + * + * @param aHandler The handler to retrieve previous rule list and DOM document + * from + */ + private CSSHandler( CSSHandler aHandler ) + { + super(); + iCSSDOMProcessor = new CSSDOMProcessor(); + iStyleMap = new HashMap(); + + iRuleList = aHandler.iRuleList; + iDocument = aHandler.iDocument; + iColorResolver = new ColorResolver(); + } + + /** + * Sets the document. + * + * @param aDocument The new document + */ + public void setDocument( Document aDocument ) + { + iDocument = aDocument; + } + + /** + * Gets the document. + * + * @return the document + */ + public Document getDocument() + { + return iDocument; + } + + /** + * Sets the directory of main CSS. + * + * @param aImportDirectory The directory of main CSS + */ + public void setImportDirectory( String aImportDirectory ) + { + iImportDirectory = aImportDirectory; + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#startDocument(org.w3c.css.sac.InputSource) + */ + public void startDocument( InputSource arg0 ) throws CSSException + { + iRuleList = new Vector(); + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#startSelector(org.w3c.css.sac.SelectorList) + */ + public void startSelector( SelectorList aSelectorList ) throws CSSException + { + iStyleMap = new HashMap(); + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#endSelector(org.w3c.css.sac.SelectorList) + */ + public void endSelector( SelectorList aSelectors ) throws CSSException + { + if ( iStyleMap.size() != 0 ) + { + for ( int i = 0; i < aSelectors.getLength(); i++ ) + { +// System.out.println("CSSHandler Selectors --> "+aSelectors.item(i)); + CSSRule rule = new CSSRule( aSelectors.item( i ), iStyleMap ); + iRuleList.add( rule ); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#property(java.lang.String, + * org.w3c.css.sac.LexicalUnit, boolean) + */ + public void property( String aName, LexicalUnit aLexicalUnit, + boolean aImportant ) throws CSSException + { + Vector propertyValues = new Vector(); + lexicalValue( aLexicalUnit, propertyValues ); + iStyleMap.put( aName, new CSSStyleProperty( propertyValues, aImportant ) ); + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#endDocument(org.w3c.css.sac.InputSource) + */ + public void endDocument( InputSource aInputSource ) throws CSSException + { + if ( iDocument == null ) + { + throw new IllegalStateException( "CSSHandler has null DOM Document" ); + } + + iCSSDOMProcessor.applyRulesToDom( iDocument, iRuleList ); + } + + + /** + * Finds out the property values and adds them into a Vector + * aPropertyValues. Property types that are taken into account are + * from Symbian side: ...\Xuikon\Dom\src\xndompropertyvalue.cpp + * + * @param aLexicalUnit Value + * @param aPropertyValues Vector for Property values + */ + public void lexicalValue( LexicalUnit aLexicalUnit, + Vector aPropertyValues ) + { + while ( aLexicalUnit != null ) + { + switch ( aLexicalUnit.getLexicalUnitType() ) + { + case LexicalUnit.SAC_OPERATOR_COMMA: + break; + // Allowed cases for integer value + case LexicalUnit.SAC_INTEGER: + aPropertyValues + .add( new CSSPropertyValue( + LexicalUnit.SAC_INTEGER, Integer + .toString( aLexicalUnit + .getIntegerValue() ) ) ); + break; + // Allowed cases for real value + // Fallthrough + case LexicalUnit.SAC_REAL: + case LexicalUnit.SAC_DIMENSION: + case LexicalUnit.SAC_EM: + case LexicalUnit.SAC_EX: + case LexicalUnit.SAC_INCH: + case LexicalUnit.SAC_PIXEL: + case LexicalUnit.SAC_CENTIMETER: + case LexicalUnit.SAC_MILLIMETER: + case LexicalUnit.SAC_POINT: + case LexicalUnit.SAC_PICA: + case LexicalUnit.SAC_PERCENTAGE: + case LexicalUnit.SAC_DEGREE: + case LexicalUnit.SAC_GRADIAN: + case LexicalUnit.SAC_RADIAN: + case LexicalUnit.SAC_MILLISECOND: + case LexicalUnit.SAC_SECOND: + case LexicalUnit.SAC_HERTZ: + case LexicalUnit.SAC_KILOHERTZ: + aPropertyValues.add( new CSSPropertyValue( aLexicalUnit + .getLexicalUnitType(), Float.toString( aLexicalUnit + .getFloatValue() ) ) ); + break; + // RGB Values + case LexicalUnit.SAC_RGBCOLOR: + //Using StringBuffer to store values of RGB property + //in order to handle values (R,G and B) as one value + StringBuffer sb = new StringBuffer(); + colorValue( aLexicalUnit.getParameters(), sb ); + aPropertyValues.add( new CSSPropertyValue( aLexicalUnit + .getLexicalUnitType(), sb.toString() ) ); + break; + // Allowed cases for string values + // Fallthrough + case LexicalUnit.SAC_URI: + case LexicalUnit.SAC_IDENT: + case LexicalUnit.SAC_STRING_VALUE: + case LexicalUnit.SAC_ATTR: + // Color value as string + if ( iColorResolver.get( aLexicalUnit.getStringValue() ) != null ) + { + Color color = ( Color ) iColorResolver.get( aLexicalUnit + .getStringValue() ); + aPropertyValues.add( new CSSPropertyValue( + LexicalUnit.SAC_RGBCOLOR, + LexicalUnit.SAC_INTEGER + SEPARATOR + + color.getRed() + COMMA + + LexicalUnit.SAC_INTEGER + SEPARATOR + + color.getGreen() + COMMA + + LexicalUnit.SAC_INTEGER + SEPARATOR + + color.getBlue() ) ); + } + // Space + else if ( aLexicalUnit.getStringValue() + .indexOf( CHAR_SPACE ) != -1 ) + { + aPropertyValues.add( new CSSPropertyValue( aLexicalUnit + .getLexicalUnitType(), CHAR_APOSTROPHE + + aLexicalUnit.getStringValue() + + CHAR_APOSTROPHE ) ); + } + // Text + else + { + aPropertyValues.add( new CSSPropertyValue( aLexicalUnit + .getLexicalUnitType(), aLexicalUnit + .getStringValue() ) ); + } + break; + // In case value is set as "inherit" already in CSS -file + // This sets it to DOM as Ident value that is "inherit" + case LexicalUnit.SAC_INHERIT: + aPropertyValues.add( new CSSPropertyValue( + LexicalUnit.SAC_IDENT, + CSSDOMProcessor.STRING_INHERIT ) ); + break; + case LexicalUnit.SAC_FUNCTION: + String functionName = aLexicalUnit.getFunctionName(); + if ( functionName.equals( FUNCTION_HSL ) ) + { + Color color = colorFromHsl( aLexicalUnit + .getParameters() ); + aPropertyValues.add( new CSSPropertyValue( + LexicalUnit.SAC_RGBCOLOR, + LexicalUnit.SAC_INTEGER + SEPARATOR + + color.getRed() + COMMA + + LexicalUnit.SAC_INTEGER + SEPARATOR + + color.getGreen() + COMMA + + LexicalUnit.SAC_INTEGER + SEPARATOR + + color.getBlue() ) ); + + } + else + { + throw new IllegalStateException( + "Unknown CSS Function : " + functionName ); + } + break; + default: + break; + } + aLexicalUnit = aLexicalUnit.getNextLexicalUnit(); + } + } + + /** + * Parses HSL Function using ColorResolver. + * + * @param aLexicalUnit Parameters containing color values + * + * @return the color + */ + private Color colorFromHsl( LexicalUnit aLexicalUnit ) + { + float h = 0; + float s = 0; + float l = 0; + try + { + h = ( float ) aLexicalUnit.getIntegerValue(); + aLexicalUnit = aLexicalUnit.getNextLexicalUnit() + .getNextLexicalUnit(); + s = aLexicalUnit.getFloatValue(); + aLexicalUnit = aLexicalUnit.getNextLexicalUnit() + .getNextLexicalUnit(); + l = aLexicalUnit.getFloatValue(); + } + catch ( RuntimeException e ) + { + throw new IllegalStateException( "Illegal HSL Color values" ); + } + if ( aLexicalUnit.getNextLexicalUnit() != null ) + { + throw new IllegalStateException( "Too many parameters in HSL color" ); + } + return iColorResolver.hslToRgb( h, s, l ); + } + + /** + * Finds out the property values for RGB color and adds them into a + * StringBuffer aProperty. + * + * @param aLexicalUnit Parameters containing color values + * @param aProperty StringBuffer for storing property information + */ + public void colorValue( LexicalUnit aLexicalUnit, StringBuffer aProperty ) + { + while ( aLexicalUnit != null ) + { + switch ( aLexicalUnit.getLexicalUnitType() ) + { + // Operator "," + case LexicalUnit.SAC_OPERATOR_COMMA: + aProperty.append( COMMA ); + break; + case LexicalUnit.SAC_INTEGER: + aProperty.append( aLexicalUnit.getLexicalUnitType() ); + aProperty.append( SEPARATOR ); + aProperty.append( aLexicalUnit.getIntegerValue() ); + break; + case LexicalUnit.SAC_PERCENTAGE: + aProperty.append( LexicalUnit.SAC_INTEGER ); + aProperty.append( SEPARATOR ); + aProperty.append( iColorResolver + .getColorValueFromPercentage( aLexicalUnit + .getFloatValue() ) ); + break; + default: + break; + } + aLexicalUnit = aLexicalUnit.getNextLexicalUnit(); + } + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#startFontFace () + */ + public void startFontFace() throws CSSException + { + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#startPage(java.lang.String, + * java.lang.String) + */ + public void startPage( String arg0, String arg1 ) throws CSSException + { + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#endFontFace () + */ + public void endFontFace() throws CSSException + { + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#endPage(java.lang.String, + * java.lang.String) + */ + public void endPage( String arg0, String arg1 ) throws CSSException + { + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#comment (java.lang.String) + */ + public void comment( String arg0 ) throws CSSException + { + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#endMedia (org.w3c.css.sac.SACMediaList) + */ + public void endMedia( SACMediaList arg0 ) throws CSSException + { + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#ignorableAtRule (java.lang.String) + */ + public void ignorableAtRule( String arg0 ) throws CSSException + { + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#importStyle (java.lang.String, + * org.w3c.css.sac.SACMediaList, java.lang.String) + */ + public void importStyle( String aCSSFileName, SACMediaList aMedia, + String aNameSpaceUri ) throws CSSException + { + try + { + CSSHandler handler = new CSSHandler( this ); + ParserFactory factory = new ParserFactory(); + + Parser parser = factory.makeParser(); + parser.setDocumentHandler( handler ); + Reader r = null; + + File cssImport = new File( aCSSFileName ); + if ( isAbsolutePath( aCSSFileName ) && cssImport.exists() ) + { + // The imported CSS file is given with absolute path + r = new FileReader( aCSSFileName ); + LogWriter.getInstance().logInfo( + "Imported CSS : " + cssImport.getAbsolutePath() ); + } + else if ( !isAbsolutePath( aCSSFileName ) + && iImportDirectory != null ) + { + // If the given imported CSS File is not found, + // try to locate it using path relative to main CSS + File relativeCSS = new File( iImportDirectory + File.separator + + aCSSFileName ); + r = new FileReader( relativeCSS ); + LogWriter.getInstance().logInfo( + "Imported CSS : " + relativeCSS.getAbsolutePath() ); + } + else + { + throw new IllegalStateException( + "Can't resolve imported CSS File: " + aCSSFileName ); + } + + InputSource is = new InputSource( r ); + parser.parseStyleSheet( is ); + } + catch ( Exception e ) + { + throw new CSSException( e.getMessage() ); + } + } + + /** + * Checks if path is absolute path. + * Path is absolute if it starts with '/', '\' + * or is of form [drive]:[path] + * @param aFilePath the a file path + * + * @return true, if is absolute path + */ + private boolean isAbsolutePath( String aFilePath ) + { + if ( aFilePath.startsWith( FORWARD_SLASH ) ) + { + return true; + } + if ( aFilePath.startsWith( BACKWARD_SLASH ) ) + { + return true; + } + if ( aFilePath.length() > 1 && aFilePath.charAt( 1 ) == CHAR_COLON ) + { + return true; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#namespaceDeclaration(java.lang.String, + * java.lang.String) + */ + public void namespaceDeclaration( String arg0, String arg1 ) + throws CSSException + { + } + + /* + * (non-Javadoc) + * + * @see org.w3c.css.sac.DocumentHandler#startMedia(org.w3c.css.sac.SACMediaList) + */ + public void startMedia( SACMediaList arg0 ) throws CSSException + { + } + + }