/*!
* 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.function;
import org.pentaho.reporting.libraries.css.dom.DocumentContext;
import org.pentaho.reporting.libraries.css.dom.LayoutElement;
import org.pentaho.reporting.libraries.css.resolver.FunctionEvaluationException;
import org.pentaho.reporting.libraries.css.values.CSSFunctionValue;
import org.pentaho.reporting.libraries.css.values.CSSNumericType;
import org.pentaho.reporting.libraries.css.values.CSSNumericValue;
import org.pentaho.reporting.libraries.css.values.CSSResourceValue;
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.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceKeyCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
/**
* Creation-Date: 04.07.2006, 14:30:10
*
* @author Thomas Morgner
*/
public class FunctionUtilities {
private static CSSNumericType[] KNOWN_TYPES = {
CSSNumericType.PERCENTAGE,
CSSNumericType.EM,
CSSNumericType.EX,
CSSNumericType.CM,
CSSNumericType.MM,
CSSNumericType.INCH,
CSSNumericType.PT,
CSSNumericType.PC,
CSSNumericType.DEG,
CSSNumericType.PX
};
private FunctionUtilities() {
}
public static CSSResourceValue loadResource( final DocumentContext process,
final Object value )
throws FunctionEvaluationException {
final Class[] supportedResourceTypes = process.getSupportedResourceTypes();
if ( supportedResourceTypes.length == 0 ) {
throw new FunctionEvaluationException
( "Failed to create URI: Resource loading failed as the output " +
"target does not support any resource types." );
}
return loadResource( process, value, supportedResourceTypes );
}
public static CSSResourceValue loadResource( final DocumentContext process,
final Object value,
final Class[] type )
throws FunctionEvaluationException {
// ok, this is going to be expensive. Kids, you dont wanna try this at home ...
final ResourceManager manager = process.getResourceManager();
final ResourceKey baseKey = process.getContextKey();
try {
final ResourceKey key;
if ( value instanceof ResourceKey ) {
key = (ResourceKey) value;
} else if ( baseKey == null ) {
key = manager.createKey( value );
} else if ( value instanceof String ) {
key = manager.deriveKey( baseKey, (String) value );
} else {
throw new FunctionEvaluationException
( "Failed to create URI: Resource loading failed: Key not derivable" );
}
final Resource res = manager.create( key, baseKey, type );
return new CSSResourceValue( res );
} catch ( Exception e ) {
throw new FunctionEvaluationException
( "Failed to create URI: Resource loading failed: " + e.getMessage(), e );
}
}
public static CSSValue parseValue( final DocumentContext process,
final String text ) {
final CSSNumericValue val = convertToNumber( text );
if ( val != null ) {
return val;
}
// next step: That may be expensive, but we search for URLs ..
try {
return loadResource( process, text );
} catch ( FunctionEvaluationException e ) {
// ignore, it was just an attempt ...
}
return new CSSStringValue( CSSStringType.STRING, text );
}
public static CSSNumericValue parseNumberValue( final String text, final String type )
throws FunctionEvaluationException {
final CSSNumericValue val = convertToNumber( text, getUnitType( type ) );
if ( val != null ) {
return val;
}
throw new FunctionEvaluationException( "Unable to convert to number." );
}
public static CSSNumericValue parseNumberValue( final String text )
throws FunctionEvaluationException {
final CSSNumericValue val = convertToNumber( text );
if ( val != null ) {
return val;
}
throw new FunctionEvaluationException( "Unable to convert to number." );
}
private static CSSNumericValue convertToNumber( final String stringValue ) {
final String txt = stringValue.trim();
CSSNumericType type = null;
for ( int i = 0; i < KNOWN_TYPES.length; i++ ) {
final CSSNumericType numericType = KNOWN_TYPES[ i ];
if ( txt.endsWith( numericType.getType() ) ) {
type = numericType;
}
}
if ( type == null ) {
type = CSSNumericType.NUMBER;
}
final String number = txt.substring
( 0, txt.length() - type.getType().length() ).trim();
return convertToNumber( number, type );
}
private static CSSNumericValue convertToNumber( final String stringValue,
CSSNumericType type ) {
if ( type == null ) {
type = CSSNumericType.NUMBER;
}
try {
final String number = stringValue.trim();
final double nVal = Double.parseDouble( number );
return CSSNumericValue.createValue( type, nVal );
} catch ( Exception e ) {
return null;
}
}
public static CSSNumericType getUnitType( final String typeText ) {
if ( typeText == null ) {
return CSSNumericType.NUMBER;
}
final String txt = typeText.trim();
for ( int i = 0; i < KNOWN_TYPES.length; i++ ) {
final CSSNumericType numericType = KNOWN_TYPES[ i ];
if ( txt.equalsIgnoreCase( numericType.getType() ) ) {
return numericType;
}
}
return CSSNumericType.NUMBER;
}
public static String resolveString( final DocumentContext layoutProcess,
final LayoutElement layoutElement,
final CSSValue value )
throws FunctionEvaluationException {
final CSSValue notAFunctionAnymore = resolveParameter( layoutProcess, layoutElement, value );
if ( notAFunctionAnymore instanceof CSSStringValue ) {
final CSSStringValue strVal = (CSSStringValue) notAFunctionAnymore;
return strVal.getValue();
}
// Falling back to the Value itself ..
final String retval = notAFunctionAnymore.getCSSText();
if ( retval == null ) {
throw new FunctionEvaluationException
( "Value " + notAFunctionAnymore + " is invalid" );
}
return retval;
}
public static CSSValue resolveParameter( final DocumentContext layoutProcess,
final LayoutElement layoutElement,
final CSSValue value )
throws FunctionEvaluationException {
if ( value instanceof CSSFunctionValue == false ) {
return value;
}
final CSSFunctionValue functionValue = (CSSFunctionValue) value;
final StyleValueFunction function =
FunctionFactory.getInstance().getStyleFunction
( functionValue.getFunctionName() );
if ( function == null ) {
throw new FunctionEvaluationException
( "Unsupported Function: " + functionValue );
}
return function.evaluate
( layoutProcess, layoutElement, functionValue );
}
public static ResourceKey createURI( final String uri, final DocumentContext layoutProcess ) {
try {
final ResourceKey base = layoutProcess.getContextKey();
final ResourceManager resourceManager =
layoutProcess.getResourceManager();
if ( base != null ) {
try {
return resourceManager.deriveKey( base, uri );
} catch ( ResourceKeyCreationException ex ) {
// ignore ..
}
}
return resourceManager.createKey( uri );
} catch ( Exception e ) {
return null;
}
}
}