/*!
* 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.resolver.impl;
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.dom.DefaultLayoutStyle;
import org.pentaho.reporting.libraries.css.dom.DocumentContext;
import org.pentaho.reporting.libraries.css.dom.LayoutElement;
import org.pentaho.reporting.libraries.css.dom.LayoutStyle;
import org.pentaho.reporting.libraries.css.keys.content.ContentStyleKeys;
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.model.StyleRule;
import org.pentaho.reporting.libraries.css.model.StyleSheet;
import org.pentaho.reporting.libraries.css.namespace.NamespaceCollection;
import org.pentaho.reporting.libraries.css.resolver.FunctionEvaluationException;
import org.pentaho.reporting.libraries.css.resolver.StyleResolver;
import org.pentaho.reporting.libraries.css.resolver.function.StyleValueFunction;
import org.pentaho.reporting.libraries.css.values.CSSFunctionValue;
import org.pentaho.reporting.libraries.css.values.CSSValue;
import org.pentaho.reporting.libraries.css.values.CSSValueList;
import org.pentaho.reporting.libraries.css.values.CSSValuePair;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import java.util.HashMap;
import java.util.Iterator;
public abstract class AbstractStyleResolver implements StyleResolver {
private static final String INITIAL_CSS_PREFIX = "org.pentaho.reporting.libraries.css.styles.initial";
private static final String CSS_VALUE_FUNCTIONS_PREFIX =
"org.pentaho.reporting.libraries.css.styles.value-functions.";
private static final Log logger = LogFactory.getLog( AbstractStyleResolver.class );
private LayoutStyle initialStyle;
private DocumentContext documentContext;
private NamespaceCollection namespaces;
private StyleKey[] keys;
private HashMap functions;
protected AbstractStyleResolver() {
functions = new HashMap();
}
public void initialize( final DocumentContext documentContext ) {
this.documentContext = documentContext;
this.namespaces = documentContext.getNamespaces();
initializeFunctions();
}
private void initializeFunctions() {
final Configuration config = LibCssBoot.getInstance().getGlobalConfig();
Iterator it = config.findPropertyKeys( CSS_VALUE_FUNCTIONS_PREFIX );
while ( it.hasNext() ) {
final String key = (String) it.next();
final String value = config.getConfigProperty( key );
final String name = key.substring( CSS_VALUE_FUNCTIONS_PREFIX.length() ).toLowerCase();
final Object o =
ObjectUtilities.loadAndInstantiate( value, AbstractStyleResolver.class, StyleValueFunction.class );
if ( o != null ) {
functions.put( name, o );
}
}
}
protected void loadInitialStyle( DocumentContext context ) {
this.initialStyle = new DefaultLayoutStyle();
try {
final ResourceManager manager = documentContext.getResourceManager();
// Get the configuration file properties for the initial css load
final Configuration config = LibCssBoot.getInstance().getGlobalConfig();
Iterator it = config.findPropertyKeys( INITIAL_CSS_PREFIX );
while ( it.hasNext() ) {
// Get the next initial set of styles to add to the global list of initial styles
final String key = (String) it.next();
final String value = config.getConfigProperty( key );
// Load the style information
final Resource resource = manager.createDirectly( value, StyleSheet.class );
final StyleSheet initialStyleSheet = (StyleSheet) resource.getResource();
// Add to the master list
final int rc = initialStyleSheet.getRuleCount();
for ( int i = 0; i < rc; i++ ) {
final StyleRule rule = initialStyleSheet.getRule( i );
if ( rule instanceof CSSDeclarationRule ) {
final CSSDeclarationRule drule = (CSSDeclarationRule) rule;
copyStyleInformation( initialStyle, drule, null );
}
}
}
} catch ( Exception e ) {
logger.error( "Initial-StyleSheet could not be parsed. This is a FATAL error.", e );
throw new IllegalStateException( "Initial-StyleSheet could not be parsed. This is a FATAL error." );
}
}
protected void copyStyleInformation
( final LayoutStyle target, final CSSDeclarationRule rule, final LayoutElement element ) {
try {
final StyleRule parentRule = rule.getParentRule();
if ( parentRule instanceof CSSDeclarationRule ) {
copyStyleInformation( target, (CSSDeclarationRule) parentRule, element );
}
if ( element == null ) {
final StyleKey[] propertyKeys = rule.getPropertyKeysAsArray();
for ( int i = 0; i < propertyKeys.length; i++ ) {
final StyleKey key = propertyKeys[ i ];
final CSSValue propertyCSSValue = rule.getPropertyCSSValue( key );
if ( propertyCSSValue != null ) {
target.setValue( key, propertyCSSValue );
}
}
return;
}
final StyleKey[] propertyKeys = rule.getPropertyKeysAsArray();
final CSSValue[] values = rule.getStyleValues();
for ( int i = 0; i < values.length; i++ ) {
final StyleKey key = propertyKeys[ i ];
final CSSValue value = rule.getPropertyCSSValue( key );
if ( ContentStyleKeys.CONTENT.equals( key ) ||
ContentStyleKeys.STRING_DEFINE.equals( key ) ||
ContentStyleKeys.STRING_SET.equals( key ) ) {
// dont resolve that one ..
values[ i ] = value;
} else {
values[ i ] = resolveValue( value, element );
}
}
for ( int i = 0; i < values.length; i++ ) {
final StyleKey key = propertyKeys[ i ];
final CSSValue value = values[ i ];
if ( value != null ) {
target.setValue( key, value );
}
}
} catch ( FunctionEvaluationException e ) {
// something went terribly wrong
// Log.debug("Skipping rule, as resolving failed.");
}
}
protected CSSValue resolveValue( final CSSValue value, final LayoutElement element )
throws FunctionEvaluationException
{
if ( element == null ) {
return value;
}
if ( value == null ) {
return null;
}
if ( containsResolveableFunction( value ) == false ) {
return value;
}
if ( value instanceof CSSFunctionValue ) {
// thats plain and simple - resolve it directly.
final CSSFunctionValue functionValue = (CSSFunctionValue) value;
final String name = functionValue.getFunctionName().toLowerCase();
final StyleValueFunction o = (StyleValueFunction) functions.get( name );
if ( o == null ) {
throw new FunctionEvaluationException( "No such function registered: " + name );
}
return o.evaluate( documentContext, element, (CSSFunctionValue) value );
} else if ( value instanceof CSSValueList ) {
final CSSValueList list = (CSSValueList) value;
final int length = list.getLength();
final CSSValue[] retValus = new CSSValue[ length ];
for ( int i = 0; i < length; i++ ) {
final CSSValue item = list.getItem( i );
retValus[ i ] = resolveValue( item, element );
}
return new CSSValueList( retValus );
} else if ( value instanceof CSSValuePair ) {
final CSSValuePair pair = (CSSValuePair) value;
return new CSSValuePair
( resolveValue( pair.getFirstValue(), element ),
resolveValue( pair.getSecondValue(), element ) );
} else {
return value;
}
}
protected boolean containsResolveableFunction( final CSSValue value ) {
if ( value == null ) {
return false;
}
if ( value instanceof CSSFunctionValue ) {
return true;
}
if ( value instanceof CSSValueList ) {
final CSSValueList list = (CSSValueList) value;
final int length = list.getLength();
for ( int i = 0; i < length; i++ ) {
final CSSValue item = list.getItem( i );
if ( containsResolveableFunction( item ) ) {
return true;
}
}
return false;
}
if ( value instanceof CSSValuePair ) {
final CSSValuePair pair = (CSSValuePair) value;
if ( containsResolveableFunction( pair.getFirstValue() ) ) {
return true;
}
if ( containsResolveableFunction( pair.getSecondValue() ) ) {
return true;
}
return false;
}
return false;
}
public LayoutStyle getInitialStyle() {
return initialStyle;
}
protected DocumentContext getDocumentContext() {
return documentContext;
}
protected StyleKey[] getKeys() {
if ( keys == null ) {
keys = StyleKeyRegistry.getRegistry().getKeys();
}
return keys;
}
protected NamespaceCollection getNamespaces() {
return namespaces;
}
protected abstract void resolveOutOfContext( LayoutElement element );
}