/*!
* 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 java.util.HashMap;
import java.util.Iterator;
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 org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
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);
}