/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program 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. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.libraries.css.parser; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.libraries.base.config.Configuration; import org.pentaho.reporting.libraries.base.util.ObjectUtilities; import org.pentaho.reporting.libraries.css.LibCssBoot; import org.pentaho.reporting.libraries.css.model.CSSDeclarationRule; import org.pentaho.reporting.libraries.css.model.StyleKey; import org.pentaho.reporting.libraries.css.model.StyleKeyRegistry; import org.pentaho.reporting.libraries.css.values.CSSAttrFunction; import org.pentaho.reporting.libraries.css.values.CSSCompoundAttrFunction; import org.pentaho.reporting.libraries.css.values.CSSConstant; import org.pentaho.reporting.libraries.css.values.CSSFunctionValue; import org.pentaho.reporting.libraries.css.values.CSSInheritValue; import org.pentaho.reporting.libraries.css.values.CSSNumericType; import org.pentaho.reporting.libraries.css.values.CSSNumericValue; import org.pentaho.reporting.libraries.css.values.CSSStringType; import org.pentaho.reporting.libraries.css.values.CSSStringValue; import org.pentaho.reporting.libraries.css.values.CSSValue; import org.w3c.css.sac.LexicalUnit; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Creation-Date: 25.11.2005, 17:43:38 * * @author Thomas Morgner */ public class CSSValueFactory { private static final CSSValue[] EMPTY_PARAMETERS = new CSSValue[ 0 ]; private static final Log logger = LogFactory.getLog( CSSValueFactory.class ); public static final String SIMPLE_PREFIX = "org.pentaho.reporting.libraries.css.parser.handlers."; public static final String COMPOUND_PREFIX = "org.pentaho.reporting.libraries.css.parser.compoundhandlers."; private HashMap handlers; private HashMap compoundHandlers; private StyleKeyRegistry registry; public CSSValueFactory( StyleKeyRegistry registry ) { if ( registry == null ) { throw new NullPointerException(); } this.registry = registry; this.handlers = new HashMap(); this.compoundHandlers = new HashMap(); this.registerDefaults(); } public void registerDefaults() { final Configuration config = LibCssBoot.getInstance().getGlobalConfig(); Iterator sit = config.findPropertyKeys( SIMPLE_PREFIX ); while ( sit.hasNext() ) { final String key = (String) sit.next(); final String name = key.substring( SIMPLE_PREFIX.length() ).toLowerCase(); final String c = config.getConfigProperty( key ); Object module = ObjectUtilities.loadAndInstantiate( c, CSSValueFactory.class, CSSValueReadHandler.class ); if ( module instanceof CSSValueReadHandler ) { handlers.put( name, module ); } else { logger.warn( "Invalid module implementation: [" + c + "] for style key [" + name + ']' ); } } Iterator cit = config.findPropertyKeys( COMPOUND_PREFIX ); while ( cit.hasNext() ) { final String key = (String) cit.next(); final String name = key.substring( COMPOUND_PREFIX.length() ).toLowerCase(); final String c = config.getConfigProperty( key ); Object module = ObjectUtilities.loadAndInstantiate( c, CSSValueFactory.class, CSSCompoundValueReadHandler.class ); if ( module instanceof CSSCompoundValueReadHandler ) { compoundHandlers.put( name, module ); } } } private CSSValue createValue( StyleKey key, LexicalUnit value ) { final CSSValueReadHandler module = (CSSValueReadHandler) handlers.get( key.getName() ); if ( module == null ) { // || module instanceof CSSCompoundValueReadHandler // Compund handler are more important than simple handlers .. return null; } return module.createValue( key, value ); } public static CSSAttrFunction parseAttrFunction( LexicalUnit unit ) { if ( unit.getLexicalUnitType() != LexicalUnit.SAC_ATTR ) { return null; } final String attrName = unit.getStringValue().trim(); final String[] name = StyleSheetParserUtil.parseNamespaceIdent( attrName ); return new CSSAttrFunction( name[ 0 ], name[ 1 ] ); } public static boolean isFunctionValue( LexicalUnit unit ) { final short lexicalUnitType = unit.getLexicalUnitType(); return ( lexicalUnitType == LexicalUnit.SAC_FUNCTION || lexicalUnitType == LexicalUnit.SAC_COUNTER_FUNCTION || lexicalUnitType == LexicalUnit.SAC_COUNTERS_FUNCTION || lexicalUnitType == LexicalUnit.SAC_RGBCOLOR || lexicalUnitType == LexicalUnit.SAC_RECT_FUNCTION ); } private static CSSAttrFunction parseComplexAttrFn( LexicalUnit parameters ) { if ( parameters == null ) { return null; } final String attrName = parameters.getStringValue().trim(); final String[] name = StyleSheetParserUtil.parseNamespaceIdent( attrName ); final LexicalUnit afterComma = parseComma( parameters ); if ( afterComma == null ) { return new CSSAttrFunction( name[ 0 ], name[ 1 ] ); } final String attrType = parseAttributeType( afterComma ); if ( attrType == null ) { return new CSSAttrFunction( name[ 0 ], name[ 1 ] ); } else { return new CSSAttrFunction( name[ 0 ], name[ 1 ], attrType ); } } public static CSSFunctionValue parseFunction( LexicalUnit unit ) { if ( isFunctionValue( unit ) == false ) { return null; } LexicalUnit parameters = unit.getParameters(); String functionName = unit.getFunctionName(); if ( parameters == null ) { // no-parameter function include the date() function... return new CSSFunctionValue( functionName, EMPTY_PARAMETERS ); } if ( "attr".equalsIgnoreCase( functionName ) ) { return parseComplexAttrFn( unit.getParameters() ); } if ( "color".equalsIgnoreCase( functionName ) ) { // for some strange reason, flute translates "rgb" functions into "color" functions which // are not even mentioned in the CSS specs. We therefore translate it back into RGB. functionName = "rgb"; } final ArrayList contentList = new ArrayList(); while ( parameters != null ) { if ( parameters.getLexicalUnitType() == LexicalUnit.SAC_IDENT ) { contentList.add( new CSSConstant( parameters.getStringValue() ) ); } else if ( parameters.getLexicalUnitType() == LexicalUnit.SAC_STRING_VALUE ) { contentList.add( new CSSStringValue( CSSStringType.STRING, parameters.getStringValue() ) ); } else if ( CSSValueFactory.isNumericValue( parameters ) ) { final CSSNumericValue numericValue = CSSValueFactory.createNumericValue( parameters ); if ( numericValue == null ) { return null; } contentList.add( numericValue ); } else if ( CSSValueFactory.isLengthValue( parameters ) ) { final CSSNumericValue lengthValue = CSSValueFactory.createLengthValue( parameters ); if ( lengthValue == null ) { return null; } contentList.add( lengthValue ); } else if ( parameters.getLexicalUnitType() == LexicalUnit.SAC_ATTR ) { final CSSAttrFunction attrFn = CSSValueFactory.parseAttrFunction( parameters ); if ( attrFn == null ) { return null; } contentList.add( attrFn ); } else if ( parameters.getLexicalUnitType() == LexicalUnit.SAC_URI ) { final CSSStringValue uriValue = CSSValueFactory.createUriValue( parameters ); if ( uriValue == null ) { return null; } contentList.add( uriValue ); } else if ( isFunctionValue( parameters ) ) { final CSSFunctionValue functionValue = parseFunction( parameters ); if ( functionValue == null ) { return null; } contentList.add( functionValue ); } else { // parse error: Something we do not understand ... return null; } parameters = CSSValueFactory.parseComma( parameters ); } final CSSValue[] paramVals = (CSSValue[]) contentList.toArray( new CSSValue[ contentList.size() ] ); return new CSSFunctionValue( functionName, paramVals ); } private static String parseAttributeType( LexicalUnit unit ) { if ( unit == null ) { return null; } if ( unit.getLexicalUnitType() == LexicalUnit.SAC_IDENT ) { return unit.getStringValue(); } return null; } private void setCompundInheritValue( String name, CSSDeclarationRule rule, boolean important ) { CSSCompoundValueReadHandler handler = (CSSCompoundValueReadHandler) compoundHandlers.get( name ); if ( handler == null ) { logger.warn( "Got no key for inherited value: " + name ); return; } StyleKey[] keys = handler.getAffectedKeys(); for ( int i = 0; i < keys.length; i++ ) { StyleKey key = keys[ i ]; rule.setPropertyValue( key, CSSInheritValue.getInstance(), important ); } } private void setCompundAttrValue( String name, CSSAttrFunction attr, CSSDeclarationRule rule, boolean important ) { final CSSCompoundValueReadHandler handler = (CSSCompoundValueReadHandler) compoundHandlers.get( name ); if ( handler == null ) { logger.warn( "Got no key for compound attr function: " + name ); return; } StyleKey[] keys = handler.getAffectedKeys(); for ( int i = 0; i < keys.length; i++ ) { StyleKey key = keys[ i ]; final CSSCompoundAttrFunction cattr = new CSSCompoundAttrFunction ( name, attr.getNamespace(), attr.getName(), attr.getValueType() ); rule.setPropertyValue( key, cattr, important ); } } public void parseValue( CSSDeclarationRule rule, String name, LexicalUnit value, boolean important ) throws CSSParserFactoryException { if ( rule == null ) { throw new NullPointerException( "Rule given is null." ); } final String normalizedName = name.toLowerCase(); final StyleKey key = registry.findKeyByName( normalizedName ); if ( value.getLexicalUnitType() == LexicalUnit.SAC_INHERIT ) { if ( key == null ) { setCompundInheritValue( normalizedName, rule, important ); return; } rule.setPropertyValue( key, CSSInheritValue.getInstance(), important ); return; } if ( value.getLexicalUnitType() == LexicalUnit.SAC_ATTR ) { final CSSAttrFunction attrFn = parseAttrFunction( value ); // ATTR function. if ( attrFn != null ) { if ( key == null ) { // Log.warn("Got no key for attribute-function " + normalizedName); setCompundAttrValue( normalizedName, attrFn, rule, important ); return; } rule.setPropertyValue( key, attrFn, important ); } return; } else if ( isFunctionValue( value ) && "attr".equals( value.getFunctionName() ) ) { // ATTR function (extended version). if ( key == null ) { logger.warn( "Got no key for attribute-function " + normalizedName ); return; } final CSSAttrFunction attrFn = parseComplexAttrFn( value.getParameters() ); if ( attrFn != null ) { rule.setPropertyValue( key, attrFn, important ); } return; } if ( key != null ) { CSSValue cssValue = createValue( key, value ); if ( cssValue != null ) { rule.setPropertyValue( key, cssValue, important ); //Log.debug ("Got value " + key.getName() + " = " + cssValue + "(" + cssValue.getClass() + ") - (important = // " + important + ")"); return; } } final CSSCompoundValueReadHandler module = (CSSCompoundValueReadHandler) compoundHandlers.get( normalizedName ); if ( module == null ) { if ( key == null ) { logger.info( "Unknown style-key: Neither compound handlers nor single-value handers are registered for " + normalizedName ); return; } logger.warn( "Unparsable value: Got no valid result for " + normalizedName + " (" + value + ')' ); return; // ignore this rule .. } Map map = module.createValues( value ); if ( map == null ) { return; } Iterator iterator = map.entrySet().iterator(); while ( iterator.hasNext() ) { Map.Entry entry = (Map.Entry) iterator.next(); StyleKey entryKey = (StyleKey) entry.getKey(); CSSValue mapCssValue = (CSSValue) entry.getValue(); rule.setPropertyValue( entryKey, mapCssValue, important ); //Log.debug ("Got value " + entryKey.getName() + " = " + mapCssValue + "(" + mapCssValue.getClass() + ") - // (important = " + important + ")"); } } public static CSSStringValue createUriValue( LexicalUnit value ) { if ( value.getLexicalUnitType() != LexicalUnit.SAC_URI ) { return null; } final String uri = value.getStringValue(); return new CSSStringValue( CSSStringType.URI, uri ); } public static boolean isNumericValue( LexicalUnit value ) { final short lexicalUnitType = value.getLexicalUnitType(); if ( lexicalUnitType == LexicalUnit.SAC_INTEGER ) { return true; } else if ( lexicalUnitType == LexicalUnit.SAC_REAL ) { return true; } return false; } public static CSSNumericValue createNumericValue( LexicalUnit value ) { final short lexicalUnitType = value.getLexicalUnitType(); if ( lexicalUnitType == LexicalUnit.SAC_INTEGER ) { return CSSNumericValue.createValue( CSSNumericType.NUMBER, value.getIntegerValue() ); } if ( lexicalUnitType == LexicalUnit.SAC_REAL ) { return CSSNumericValue.createValue( CSSNumericType.NUMBER, value.getFloatValue() ); } return null; } public static boolean isLengthValue( LexicalUnit value ) { final short lexicalUnitType = value.getLexicalUnitType(); return lexicalUnitType >= LexicalUnit.SAC_EM && lexicalUnitType <= LexicalUnit.SAC_PICA; // if (lexicalUnitType == LexicalUnit.SAC_EM) // { // return true; // } // else if (lexicalUnitType == LexicalUnit.SAC_EX) // { // return true; // } // else if (lexicalUnitType == LexicalUnit.SAC_PIXEL) // { // return true; // } // else if (lexicalUnitType == LexicalUnit.SAC_INCH) // { // return true; // } // else if (lexicalUnitType == LexicalUnit.SAC_CENTIMETER) // { // return true; // } // else if (lexicalUnitType == LexicalUnit.SAC_MILLIMETER) // { // return true; // } // else if (lexicalUnitType == LexicalUnit.SAC_PICA) // { // return true; // } // else if (lexicalUnitType == LexicalUnit.SAC_POINT) // { // return true; // } // return false; } public static CSSNumericValue createLengthValue( LexicalUnit value ) { final short lexicalUnitType = value.getLexicalUnitType(); if ( lexicalUnitType == LexicalUnit.SAC_INTEGER ) { if ( value.getFloatValue() != 0 ) { return null; } return CSSNumericValue.createValue( CSSNumericType.PT, 0 ); } if ( lexicalUnitType == LexicalUnit.SAC_EM ) { return CSSNumericValue.createValue( CSSNumericType.EM, value.getFloatValue() ); } else if ( lexicalUnitType == LexicalUnit.SAC_EX ) { return CSSNumericValue.createValue( CSSNumericType.EX, value.getFloatValue() ); } else if ( lexicalUnitType == LexicalUnit.SAC_PIXEL ) { return CSSNumericValue.createValue( CSSNumericType.PX, value.getFloatValue() ); } else if ( lexicalUnitType == LexicalUnit.SAC_INCH ) { return CSSNumericValue.createValue( CSSNumericType.INCH, value.getFloatValue() ); } else if ( lexicalUnitType == LexicalUnit.SAC_CENTIMETER ) { return CSSNumericValue.createValue( CSSNumericType.CM, value.getFloatValue() ); } else if ( lexicalUnitType == LexicalUnit.SAC_MILLIMETER ) { return CSSNumericValue.createValue( CSSNumericType.MM, value.getFloatValue() ); } else if ( lexicalUnitType == LexicalUnit.SAC_PICA ) { return CSSNumericValue.createValue( CSSNumericType.PC, value.getFloatValue() ); } else if ( lexicalUnitType == LexicalUnit.SAC_POINT ) { return CSSNumericValue.createValue( CSSNumericType.PT, value.getFloatValue() ); } return null; } public static LexicalUnit parseComma( final LexicalUnit value ) { if ( value == null ) { return null; } LexicalUnit maybeComma = value.getNextLexicalUnit(); if ( maybeComma == null ) { return null; } if ( maybeComma.getLexicalUnitType() == LexicalUnit.SAC_OPERATOR_COMMA ) { return maybeComma.getNextLexicalUnit(); } return null; } }