/* * 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) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.engine.classic.core.modules.parser.bundle.writer; import java.io.IOException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition; import org.pentaho.reporting.engine.classic.core.function.Expression; import org.pentaho.reporting.engine.classic.core.function.ExpressionCollection; import org.pentaho.reporting.engine.classic.core.function.FormulaExpression; import org.pentaho.reporting.engine.classic.core.function.FormulaFunction; import org.pentaho.reporting.engine.classic.core.metadata.ExpressionMetaData; import org.pentaho.reporting.engine.classic.core.metadata.ExpressionPropertyMetaData; import org.pentaho.reporting.engine.classic.core.metadata.ExpressionRegistry; import org.pentaho.reporting.engine.classic.core.metadata.ResourceReference; import org.pentaho.reporting.engine.classic.core.modules.parser.base.common.DefaultExpressionPropertyWriteHandler; import org.pentaho.reporting.engine.classic.core.modules.parser.base.common.ExpressionPropertyWriteHandler; import org.pentaho.reporting.engine.classic.core.modules.parser.bundle.BundleNamespaces; import org.pentaho.reporting.engine.classic.core.modules.parser.ext.ExtParserModule; import org.pentaho.reporting.engine.classic.core.style.StyleKey; import org.pentaho.reporting.engine.classic.core.util.beans.BeanException; import org.pentaho.reporting.engine.classic.core.util.beans.BeanUtility; import org.pentaho.reporting.libraries.base.util.IOUtils; import org.pentaho.reporting.libraries.base.util.ObjectUtilities; import org.pentaho.reporting.libraries.base.util.StringUtils; import org.pentaho.reporting.libraries.docbundle.BundleUtilities; import org.pentaho.reporting.libraries.docbundle.WriteableDocumentBundle; import org.pentaho.reporting.libraries.resourceloader.ResourceKey; import org.pentaho.reporting.libraries.resourceloader.ResourceManager; import org.pentaho.reporting.libraries.xmlns.common.AttributeList; import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter; import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport; public class ExpressionWriterUtility { private static final Log logger = LogFactory.getLog( ExpressionWriterUtility.class ); private ExpressionWriterUtility() { } public static boolean isElementLayoutExpressionActive( final BundleWriterState state ) { if ( state == null ) { throw new NullPointerException(); } final ExpressionCollection exp = state.getReport().getExpressions(); final ExpressionRegistry registry = ExpressionRegistry.getInstance(); for ( int i = 0; i < exp.size(); i++ ) { final Expression expression = exp.getExpression( i ); if ( registry.isExpressionRegistered( expression.getClass().getName() ) == false ) { continue; } final ExpressionMetaData emd = registry.getExpressionMetaData( expression.getClass().getName() ); if ( emd.isElementLayoutProcessor() ) { return true; } } return false; } public static void writeElementLayoutExpressions( final WriteableDocumentBundle bundle, final BundleWriterState state, final XmlWriter writer ) throws IOException, BundleWriterException { if ( state == null ) { throw new NullPointerException(); } if ( writer == null ) { throw new NullPointerException(); } if ( bundle == null ) { throw new NullPointerException(); } final ExpressionCollection exp = state.getReport().getExpressions(); final ExpressionRegistry registry = ExpressionRegistry.getInstance(); for ( int i = 0; i < exp.size(); i++ ) { final Expression expression = exp.getExpression( i ); if ( registry.isExpressionRegistered( expression.getClass().getName() ) == false ) { continue; } final ExpressionMetaData emd = registry.getExpressionMetaData( expression.getClass().getName() ); if ( emd.isElementLayoutProcessor() ) { writeExpression( bundle, state, expression, writer, BundleNamespaces.LAYOUT, "expression" ); // NON-NLS } } } public static boolean isGlobalLayoutExpressionActive( final BundleWriterState state ) { if ( state == null ) { throw new NullPointerException(); } final ExpressionCollection exp = state.getReport().getExpressions(); final ExpressionRegistry registry = ExpressionRegistry.getInstance(); for ( int i = 0; i < exp.size(); i++ ) { final Expression expression = exp.getExpression( i ); if ( registry.isExpressionRegistered( expression.getClass().getName() ) == false ) { continue; } final ExpressionMetaData emd = registry.getExpressionMetaData( expression.getClass().getName() ); if ( emd.isGlobalLayoutProcessor() ) { return true; } } return false; } public static void writeGlobalLayoutExpressions( final WriteableDocumentBundle bundle, final BundleWriterState state, final XmlWriter writer ) throws IOException, BundleWriterException { if ( state == null ) { throw new NullPointerException(); } if ( writer == null ) { throw new NullPointerException(); } if ( bundle == null ) { throw new NullPointerException(); } final ExpressionCollection exp = state.getReport().getExpressions(); final ExpressionRegistry registry = ExpressionRegistry.getInstance(); for ( int i = 0; i < exp.size(); i++ ) { final Expression expression = exp.getExpression( i ); if ( registry.isExpressionRegistered( expression.getClass().getName() ) == false ) { continue; } final ExpressionMetaData emd = registry.getExpressionMetaData( expression.getClass().getName() ); if ( emd.isGlobalLayoutProcessor() ) { writeExpression( bundle, state, expression, writer, BundleNamespaces.LAYOUT, "expression" ); // NON-NLS } } } public static void writeDataExpressions( final WriteableDocumentBundle bundle, final BundleWriterState state, final XmlWriter writer ) throws IOException, BundleWriterException { if ( state == null ) { throw new NullPointerException(); } if ( writer == null ) { throw new NullPointerException(); } if ( bundle == null ) { throw new NullPointerException(); } final ExpressionCollection exp = state.getReport().getExpressions(); final ExpressionRegistry registry = ExpressionRegistry.getInstance(); for ( int i = 0; i < exp.size(); i++ ) { final Expression expression = exp.getExpression( i ); if ( registry.isExpressionRegistered( expression.getClass().getName() ) == false ) { continue; } final ExpressionMetaData emd = registry.getExpressionMetaData( expression.getClass().getName() ); if ( emd.isGlobalLayoutProcessor() || emd.isElementLayoutProcessor() ) { continue; } writeExpression( bundle, state, expression, writer, BundleNamespaces.DATADEFINITION, "expression" ); // NON-NLS } } public static void writeExpression( final WriteableDocumentBundle bundle, final BundleWriterState state, final Expression expression, final XmlWriter writer, final String namespaceUri, final String expressionTag ) throws IOException, BundleWriterException { if ( state == null ) { throw new NullPointerException(); } if ( bundle == null ) { throw new NullPointerException(); } if ( expression == null ) { throw new NullPointerException(); } if ( writer == null ) { throw new NullPointerException(); } if ( namespaceUri == null ) { throw new NullPointerException(); } if ( expressionTag == null ) { throw new NullPointerException(); } final AttributeList expressionAttrList = new AttributeList(); if ( expression.getName() != null ) { expressionAttrList.setAttribute( namespaceUri, "name", expression.getName() ); } if ( expression.getDependencyLevel() > 0 ) { expressionAttrList.setAttribute( namespaceUri, "deplevel", // NON-NLS String.valueOf( expression.getDependencyLevel() ) ); } writeExpressionCore( bundle, state, expression, writer, namespaceUri, expressionTag, expressionAttrList ); } public static void writeStyleExpression( final WriteableDocumentBundle bundle, final BundleWriterState state, final Expression expression, final XmlWriter writer, final StyleKey styleKey, final String namespaceUri, final String expressionTag ) throws IOException, BundleWriterException { if ( bundle == null ) { throw new NullPointerException(); } if ( state == null ) { throw new NullPointerException(); } if ( expression == null ) { throw new NullPointerException(); } if ( writer == null ) { throw new NullPointerException(); } if ( styleKey == null ) { throw new NullPointerException(); } if ( namespaceUri == null ) { throw new NullPointerException(); } if ( expressionTag == null ) { throw new NullPointerException(); } final AttributeList expressionAttrList = new AttributeList(); expressionAttrList.setAttribute( namespaceUri, "style-key", styleKey.getName() ); // NON-NLS writeExpressionCore( bundle, state, expression, writer, namespaceUri, expressionTag, expressionAttrList ); } public static void writeExpressionCore( final WriteableDocumentBundle bundle, final BundleWriterState state, final Expression expression, final XmlWriter writer, final String namespaceUri, final String expressionTag, final AttributeList expressionAttrList ) throws IOException, BundleWriterException { if ( bundle == null ) { throw new NullPointerException(); } if ( state == null ) { throw new NullPointerException(); } if ( expression == null ) { throw new NullPointerException(); } if ( writer == null ) { throw new NullPointerException(); } if ( namespaceUri == null ) { throw new NullPointerException(); } if ( expressionTag == null ) { throw new NullPointerException(); } if ( expressionAttrList == null ) { throw new NullPointerException(); } if ( expression instanceof FormulaExpression ) { final FormulaExpression fe = (FormulaExpression) expression; if ( StringUtils.isEmpty( fe.getFormula() ) ) { return; } expressionAttrList.setAttribute( namespaceUri, "formula", fe.getFormula() ); // NON-NLS writer.writeTag( namespaceUri, expressionTag, expressionAttrList, XmlWriterSupport.CLOSE ); return; } if ( expression instanceof FormulaFunction ) { final FormulaFunction fe = (FormulaFunction) expression; if ( StringUtils.isEmpty( fe.getFormula() ) ) { return; } expressionAttrList.setAttribute( namespaceUri, "formula", fe.getFormula() ); // NON-NLS expressionAttrList.setAttribute( namespaceUri, "initial", fe.getInitial() ); // NON-NLS writer.writeTag( namespaceUri, expressionTag, expressionAttrList, XmlWriterSupport.CLOSE ); return; } try { final String expressionId = expression.getClass().getName(); expressionAttrList.setAttribute( namespaceUri, "class", expressionId ); final ExpressionMetaData emd; if ( ExpressionRegistry.getInstance().isExpressionRegistered( expressionId ) ) { emd = ExpressionRegistry.getInstance().getExpressionMetaData( expressionId ); } else { emd = null; } if ( emd != null ) { final BeanUtility bu = new BeanUtility( expression ); final ExpressionPropertyMetaData[] expressionProperties = emd.getPropertyDescriptions(); boolean propertiesOpen = false; for ( int i = 0; i < expressionProperties.length; i++ ) { final ExpressionPropertyMetaData metaData = expressionProperties[i]; final String propertyName = metaData.getName(); if ( isFilteredProperty( propertyName ) ) { continue; } if ( metaData.isComputed() ) { continue; } if ( propertiesOpen == false ) { writer.writeTag( namespaceUri, expressionTag, expressionAttrList, XmlWriterSupport.OPEN ); writer.writeTag( namespaceUri, "properties", XmlWriterSupport.OPEN ); // NON-NLS propertiesOpen = true; } copyStaticResources( bundle, state, expression, bu, expressionProperties ); writeExpressionParameter( bundle, state, writer, expression, bu, propertyName, namespaceUri ); } if ( propertiesOpen ) { writer.writeCloseTag(); writer.writeCloseTag(); } else { writer.writeTag( namespaceUri, expressionTag, expressionAttrList, XmlWriterSupport.CLOSE ); } } else { // the classic way, in case the expression does not provide any meta-data. This is // in the code for legacy reasons, as there are many expression implementations out there // that do not yet provide meta-data descriptions .. final BeanUtility beanUtility = new BeanUtility( expression ); final String[] propertyNames = beanUtility.getProperties(); for ( int i = 0; i < propertyNames.length; i++ ) { final String key = propertyNames[i]; // filter some of the standard properties. These are system-properties // and are set elsewhere if ( isFilteredProperty( key ) ) { continue; } writeExpressionParameter( bundle, state, writer, expression, beanUtility, key, namespaceUri ); } } } catch ( IOException ioe ) { throw ioe; } catch ( Exception e ) { throw new BundleWriterException( "Unable to extract or write properties.", e ); } } private static boolean isFilteredProperty( final String name ) { if ( name == null ) { throw new NullPointerException(); } if ( "name".equals( name ) ) { // NON-NLS return true; } if ( "dependencyLevel".equals( name ) ) { // NON-NLS return true; } if ( "runtime".equals( name ) ) { // NON-NLS return true; } if ( "active".equals( name ) ) { // NON-NLS return true; } if ( "preserve".equals( name ) ) { // NON-NLS return true; } return false; } /** * Writes the parameters for an expression or function. * * @param propertyName * the name of the properties that should be written. * @param namespaceUri * the namespace that should be used when writing elements. * @param writer * the xml writer. * @param beanUtility * the bean utility containing the expression bean. * @throws IOException * if an IO error occurs. * @throws BeanException * if a bean error occured. */ private static void writeExpressionParameter( final WriteableDocumentBundle bundle, final BundleWriterState state, final XmlWriter writer, final Expression e, final BeanUtility beanUtility, final String propertyName, final String namespaceUri ) throws IOException, BeanException { // filter some of the standard properties. These are system-properties // and are set elsewhere final ExpressionPropertyWriteHandler writeHandler = createWriteHandler( e, propertyName ); if ( writeHandler instanceof BundleExpressionPropertyWriteHandler ) { BundleExpressionPropertyWriteHandler bw = ( BundleExpressionPropertyWriteHandler ) writeHandler; bw.initBundleContext( bundle, state ); } writeHandler.writeExpressionParameter( writer, beanUtility, propertyName, namespaceUri ); } private static ExpressionPropertyWriteHandler createWriteHandler( Expression e, String key ) { try { final ExpressionMetaData expressionMetaData = ExpressionRegistry.getInstance().getExpressionMetaData( e.getClass().getName() ); final ExpressionPropertyMetaData propertyDescription = expressionMetaData.getPropertyDescription( key ); final Class<? extends ExpressionPropertyWriteHandler> writeHandlerRef = propertyDescription.getPropertyWriteHandler(); if ( writeHandlerRef != null ) { return writeHandlerRef.newInstance(); } } catch ( Exception ex ) { logger.info( "No valid property metadata defined for " + e.getClass() + " on property " + key ); } return new DefaultExpressionPropertyWriteHandler(); } public static void copyStaticResources( final WriteableDocumentBundle bundle, final BundleWriterState state, final Expression expression, final BeanUtility beanUtility, final ExpressionPropertyMetaData[] datas ) throws BundleWriterException, BeanException { if ( bundle == null ) { throw new NullPointerException(); } if ( state == null ) { throw new NullPointerException(); } if ( expression == null ) { throw new NullPointerException(); } if ( beanUtility == null ) { throw new NullPointerException(); } if ( datas == null ) { throw new NullPointerException(); } final AbstractReportDefinition report = state.getReport(); final ResourceKey contentBase = report.getContentBase(); if ( contentBase == null ) { // treat all resources as linked resources .. return; } final ResourceKey defSource = report.getDefinitionSource(); if ( defSource == null ) { // treat all resources as linked resources .. return; } if ( ObjectUtilities.equal( contentBase.getParent(), defSource.getParent() ) == false ) { // treat all resources as linked resources .. return; } final Object contentBasePathRaw = contentBase.getIdentifier(); if ( contentBasePathRaw instanceof String == false ) { return; } final String contentBasePath = String.valueOf( contentBasePathRaw ); final ResourceManager resourceManager = state.getMasterReport().getResourceManager(); for ( int i = 0; i < datas.length; i++ ) { final ExpressionPropertyMetaData attributeMetaData = datas[i]; final Object attValue = beanUtility.getProperty( attributeMetaData.getName() ); if ( attValue == null ) { continue; } final ResourceReference[] referencedResources = attributeMetaData.getReferencedResources( expression, attValue, report, resourceManager ); for ( int j = 0; j < referencedResources.length; j++ ) { final ResourceReference reference = referencedResources[j]; if ( reference.isLinked() ) { continue; } final ResourceKey path = reference.getPath(); final Object identifier = path.getIdentifier(); if ( identifier instanceof String == false ) { continue; } final String identifierString = String.valueOf( identifier ); final String relativePath = IOUtils.getInstance().createRelativePath( identifierString, contentBasePath ); try { BundleUtilities.copyInto( bundle, relativePath, path, resourceManager ); } catch ( Exception e ) { throw new BundleWriterException( "Failed to copy content from key " + path, e ); } } } } }