/*
* 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.layout.elements;
import java.beans.PropertyEditor;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.ReportAttributeMap;
import org.pentaho.reporting.engine.classic.core.ReportElement;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.metadata.AttributeMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.ElementMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.ElementType;
import org.pentaho.reporting.engine.classic.core.metadata.ElementTypeRegistry;
import org.pentaho.reporting.engine.classic.core.modules.parser.base.ClassicEngineFactoryParameters;
import org.pentaho.reporting.engine.classic.core.modules.parser.base.common.StyleExpressionHandler;
import org.pentaho.reporting.engine.classic.core.modules.parser.bundle.BundleNamespaces;
import org.pentaho.reporting.engine.classic.core.modules.parser.bundle.layout.ElementReadHandler;
import org.pentaho.reporting.engine.classic.core.modules.parser.bundle.layout.ElementStyleReadHandler;
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.ConverterRegistry;
import org.pentaho.reporting.engine.classic.core.util.beans.ValueConverter;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.resourceloader.ResourceData;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceKeyCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.reporting.libraries.xmlns.parser.AbstractXmlReadHandler;
import org.pentaho.reporting.libraries.xmlns.parser.ParseException;
import org.pentaho.reporting.libraries.xmlns.parser.XmlReadHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
public abstract class AbstractElementReadHandler extends AbstractXmlReadHandler implements ElementReadHandler {
private static final Log logger = LogFactory.getLog( AbstractElementReadHandler.class );
private Element element;
private ElementMetaData metaData;
private ArrayList<StyleExpressionHandler> styleExpressions;
private ArrayList<AttributeExpressionReadHandler> attributeExpressions;
private ArrayList<BulkAttributeReadHandler> bulkattributes;
private ArrayList<BulkExpressionReadHandler> bulkexpressions;
protected AbstractElementReadHandler() {
styleExpressions = new ArrayList<StyleExpressionHandler>();
attributeExpressions = new ArrayList<AttributeExpressionReadHandler>();
bulkattributes = new ArrayList<BulkAttributeReadHandler>();
bulkexpressions = new ArrayList<BulkExpressionReadHandler>();
}
protected AbstractElementReadHandler( final ElementType elementType ) throws ParseException {
this();
initialize( elementType );
}
protected void autoInit() throws ParseException {
String tagName = getTagName();
String uri = getUri();
ElementMetaData elementType = ElementTypeRegistry.getInstance().getElementType( tagName );
if ( ObjectUtilities.equal( uri, elementType.getNamespace() ) == false ) {
throw new ParseException( "Metadata not registered, and auto-registration does not match namespace" );
}
this.metaData = elementType;
this.element = createElement();
}
protected void initialize( final ElementType elementType ) throws ParseException {
metaData = elementType.getMetaData();
element = createElement();
}
protected Element createElement() throws ParseException {
try {
final ElementType elementTypeObj = metaData.create();
return (Element) elementTypeObj.create();
} catch ( InstantiationException e ) {
// This should not happen at this point, as there is no way to instantiate the class if the
// element is not there. But it could happen if the element is not registered, which indicates
// a user error (Engine not booted).
throw new ParseException( "Unable to instantiate element for type '" + metaData.getName() + '"' );
}
}
/**
* Starts parsing.
*
* @param attrs
* the attributes.
* @throws SAXException
* if there is a parsing error.
*/
protected void startParsing( final Attributes attrs ) throws SAXException {
final ReportElement element = getElement();
if ( element == null ) {
throw new IllegalStateException( "Failed at " + getClass() );
}
final int length = attrs.getLength();
for ( int i = 0; i < length; i++ ) {
if ( "xmlns".equals( attrs.getQName( i ) ) || attrs.getQName( i ).startsWith( "xmlns:" ) ) {
// workaround for buggy parsers
continue;
}
final String name = attrs.getLocalName( i );
if ( name.indexOf( ':' ) > -1 ) {
// attribute with ':' are not valid and indicate a namespace definition or so
continue;
}
final String namespace = attrs.getURI( i );
final String attributeValue = attrs.getValue( i );
setAttributeValue( element, namespace, name, attributeValue, ReportAttributeMap.EMPTY_MAP );
}
}
private void setAttributeValue( final ReportElement element, final String namespace, final String name,
final String attributeValue, final ReportAttributeMap attributes ) throws ParseException {
final AttributeMetaData attributeMetaData = metaData.getAttributeDescription( namespace, name );
if ( attributeMetaData == null || attributeValue == null ) {
element.setAttribute( namespace, name, attributeValue );
return;
}
if ( attributeMetaData.isTransient() ) {
return;
}
if ( isFiltered( attributeMetaData ) ) {
return;
}
if ( ElementMetaData.VALUEROLE_RESOURCE.equals( attributeMetaData.getValueRole() ) ) {
try {
final Object type = attributes.getAttribute( AttributeNames.Core.NAMESPACE, "resource-type" );
if ( "url".equals( type ) ) {
element.setAttribute( namespace, name, new URL( attributeValue ) );
return;
}
if ( "file".equals( type ) ) {
element.setAttribute( namespace, name, new File( attributeValue ) );
return;
}
if ( "local-ref".equals( type ) ) {
element.setAttribute( namespace, name, attributeValue );
return;
}
if ( "resource-key".equals( type ) ) {
final ResourceManager resourceManager = getRootHandler().getResourceManager();
final ResourceKey key = getRootHandler().getContext();
final ResourceKey parent = key.getParent();
final ResourceKey valueKey = resourceManager.deserialize( parent, attributeValue );
// make local ..
final ResourceKey resourceKey = localizeKey( resourceManager, valueKey );
element.setAttribute( namespace, name, resourceKey );
return;
}
element.setAttribute( namespace, name, attributeValue );
return;
} catch ( MalformedURLException e ) {
throw new ParseException( "Failed to parse URL value", e );
} catch ( ResourceKeyCreationException e ) {
throw new ParseException( "Failed to parse resource-key value", e );
}
}
final Class type = attributeMetaData.getTargetType();
if ( String.class.equals( type ) ) {
element.setAttribute( namespace, name, attributeValue );
} else {
try {
final PropertyEditor propertyEditor = attributeMetaData.getEditor();
if ( propertyEditor != null ) {
propertyEditor.setAsText( attributeValue );
element.setAttribute( namespace, name, propertyEditor.getValue() );
} else {
final ConverterRegistry instance = ConverterRegistry.getInstance();
final ValueConverter valueConverter = instance.getValueConverter( type );
if ( valueConverter != null ) {
final Object o = ConverterRegistry.toPropertyValue( attributeValue, type );
element.setAttribute( namespace, name, o );
} else if ( String.class.isAssignableFrom( type ) ) {
// the attribute would allow raw-string values, so copy the element ..
element.setAttribute( namespace, name, attributeValue );
}
}
} catch ( BeanException e ) {
// ignore.
AbstractElementReadHandler.logger.warn( "Attribute '" + namespace + '|' + name
+ "' is not convertible with the bean-methods " + getLocator() );
}
}
}
protected boolean isFiltered( final AttributeMetaData attributeMetaData ) {
if ( AttributeNames.Core.NAMESPACE.equals( attributeMetaData.getNameSpace() ) ) {
if ( AttributeNames.Core.ELEMENT_TYPE.equals( attributeMetaData.getName() ) ) {
return true;
}
}
return false;
}
private ResourceKey localizeKey( final ResourceManager resourceManager, final ResourceKey valueKey ) {
final Object object = valueKey.getFactoryParameters().get( ClassicEngineFactoryParameters.EMBED );
if ( "false".equals( object ) ) {
return valueKey;
}
if ( "org.pentaho.reporting.libraries.docbundle.bundleloader.RepositoryResourceBundleLoader".equals( valueKey
.getSchema() ) == false
&& object == null ) {
return valueKey;
}
try {
final ResourceData resourceData = resourceManager.load( valueKey );
final byte[] resource = resourceData.getResource( resourceManager );
return resourceManager.createKey( resource, valueKey.getFactoryParameters() );
} catch ( ResourceException e ) {
if ( logger.isDebugEnabled() ) {
logger.info( "Unable to normalize embedded resource-key, using ordinary key-object instead.", e );
} else {
logger.info( "Unable to normalize embedded resource-key, using ordinary key-object instead." );
}
}
return valueKey;
}
/**
* Returns the handler for a child element.
*
* @param uri
* the URI of the namespace of the current element.
* @param tagName
* the tag name.
* @param atts
* the attributes.
* @return the handler or null, if the tagname is invalid.
* @throws SAXException
* if there is a parsing error.
*/
protected XmlReadHandler getHandlerForChild( final String uri, final String tagName, final Attributes atts )
throws SAXException {
if ( BundleNamespaces.LAYOUT.equals( uri ) ) {
if ( "attribute-expression".equals( tagName ) ) {
final AttributeExpressionReadHandler readHandler = new AttributeExpressionReadHandler();
attributeExpressions.add( readHandler );
return readHandler;
} else if ( "style-expression".equals( tagName ) ) {
final StyleExpressionHandler readHandler = new StyleExpressionHandler();
styleExpressions.add( readHandler );
return readHandler;
} else if ( "expression".equals( tagName ) ) {
final BulkExpressionReadHandler readHandler = new BulkExpressionReadHandler();
bulkexpressions.add( readHandler );
return readHandler;
} else if ( "attribute".equals( tagName ) ) {
String namespace = atts.getValue( getUri(), "namespace" );
String attrName = atts.getValue( getUri(), "name" );
final BulkAttributeReadHandler readHandler = new BulkAttributeReadHandler( namespace, attrName );
bulkattributes.add( readHandler );
return readHandler;
}
}
if ( BundleNamespaces.STYLE.equals( uri ) ) {
if ( "element-style".equals( tagName ) ) {
return new ElementStyleReadHandler( getElement().getStyle() );
}
}
if ( metaData.getAttributeDescription( uri, tagName ) != null ) {
final BulkAttributeReadHandler readHandler = new BulkAttributeReadHandler( uri, tagName );
bulkattributes.add( readHandler );
return readHandler;
}
return null;
}
/**
* Done parsing.
*
* @throws SAXException
* if there is a parsing error.
*/
protected void doneParsing() throws SAXException {
for ( int i = 0; i < styleExpressions.size(); i++ ) {
final StyleExpressionHandler handler = styleExpressions.get( i );
final StyleKey key = handler.getKey();
if ( handler.getKey() != null ) {
final Expression expression = handler.getExpression();
element.setStyleExpression( key, expression );
}
}
for ( int i = 0; i < attributeExpressions.size(); i++ ) {
final AttributeExpressionReadHandler handler = attributeExpressions.get( i );
final Expression expression = handler.getExpression();
element.setAttributeExpression( handler.getNamespace(), handler.getName(), expression );
}
for ( int i = 0; i < bulkattributes.size(); i++ ) {
final BulkAttributeReadHandler attributeReadHandler = bulkattributes.get( i );
setAttributeValue( element, attributeReadHandler.getNamespace(), attributeReadHandler.getName(),
attributeReadHandler.getResult(), attributeReadHandler.getAttributes() );
}
for ( int i = 0; i < bulkexpressions.size(); i++ ) {
final BulkExpressionReadHandler expressionReadHandler = bulkexpressions.get( i );
element.setAttribute( expressionReadHandler.getAttributeNameSpace(), expressionReadHandler.getAttributeName(),
expressionReadHandler.getObject() );
}
}
/**
* Returns the object for this element or null, if this element does not create an object.
*
* @return the object.
* @throws SAXException
* if an parser error occured.
*/
public Object getObject() throws SAXException {
return getElement();
}
public Element getElement() {
return element;
}
}