/*! * 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.designer.core.editor.structuretree; import org.pentaho.reporting.designer.core.editor.ReportDocumentContext; import org.pentaho.reporting.designer.core.model.DataSchemaUtility; import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition; import org.pentaho.reporting.engine.classic.core.CompoundDataFactory; import org.pentaho.reporting.engine.classic.core.DataFactory; import org.pentaho.reporting.engine.classic.core.MetaAttributeNames; import org.pentaho.reporting.engine.classic.core.ReportEnvironmentDataRow; import org.pentaho.reporting.engine.classic.core.designtime.DesignTimeUtil; 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.wizard.ContextAwareDataSchemaModel; import org.pentaho.reporting.engine.classic.core.wizard.DataAttributes; import org.pentaho.reporting.libraries.base.util.ObjectUtilities; import javax.swing.event.EventListenerList; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import java.util.ArrayList; public abstract class AbstractReportDataTreeModel implements TreeModel { private EventListenerList eventListenerList; private ReportFunctionNode reportFunctionNode; private ReportDocumentContext context; private ReportEnvironmentDataRow reportEnvironmentDataRow; private Expression[] cachedExpressions; protected AbstractReportDataTreeModel( final ReportDocumentContext context ) { if ( context == null ) { throw new NullPointerException(); } this.context = context; this.reportEnvironmentDataRow = new ReportEnvironmentDataRow( context.getContextRoot().getReportEnvironment() ); this.eventListenerList = new EventListenerList(); this.reportFunctionNode = new ReportFunctionNode(); refreshExpressionsCache(); } protected AbstractReportDefinition getReportDefinition() { return this.context.getReportDefinition(); } public ReportEnvironmentDataRow getReportEnvironmentDataRow() { return reportEnvironmentDataRow; } protected ReportDocumentContext getContext() { return context; } public ReportFunctionNode getReportFunctionNode() { return reportFunctionNode; } protected CompoundDataFactory getDataFactoryElement() { return (CompoundDataFactory) context.getReportDefinition().getDataFactory(); } protected ExpressionCollection getExpressions() { return context.getReportDefinition().getExpressions(); } public Object getChild( final Object parent, final int index ) { if ( parent == getDataFactoryElement() ) { final CompoundDataFactory dataFactoryElement = getDataFactoryElement(); return dataFactoryElement.getReference( index ); } if ( parent == reportEnvironmentDataRow ) { final String[] columnNames = reportEnvironmentDataRow.getColumnNames(); final String name = columnNames[ index ]; final Class targetClass = reportEnvironmentDataRow.isArray( name ) ? Object[].class : Object.class; return new ReportFieldNode( getContext(), name, targetClass ); } if ( parent == reportFunctionNode ) { return getExpressions().getExpression( index ); } if ( parent instanceof DataFactory ) { final DataFactory dataFactory = (DataFactory) parent; final String[] queryNames = dataFactory.getQueryNames(); return new ReportQueryNode( dataFactory, queryNames[ index ], true ); } if ( parent instanceof ReportQueryNode ) { final ReportQueryNode queryNode = (ReportQueryNode) parent; if ( isSelectedDataSource( queryNode ) ) { final String[] names = getDataFactoryColumns(); final String name = names[ index ]; final ContextAwareDataSchemaModel model = context.getReportDataSchemaModel(); final DataAttributes attributes = model.getDataSchema().getAttributes( name ); final Class type = (Class) attributes.getMetaAttribute ( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.TYPE, Class.class, model.getDataAttributeContext() ); final DataFactory dataFactory = queryNode.getDataFactory(); return new ReportFieldNode( context, dataFactory, name, type ); } throw new IndexOutOfBoundsException(); } return null; } private boolean isSelectedDataSource( final ReportQueryNode queryNode ) { AbstractReportDefinition reportDefinition = context.getReportDefinition(); return DesignTimeUtil .isSelectedDataSource( reportDefinition, queryNode.getDataFactory(), queryNode.getQueryName() ); } private String[] getDataFactoryColumns() { final ContextAwareDataSchemaModel model = context.getReportDataSchemaModel(); final String[] columnNames = model.getColumnNames(); final ArrayList<String> targetCols = new ArrayList<String>( columnNames.length ); for ( int i = 0; i < columnNames.length; i++ ) { final String columnName = columnNames[ i ]; final DataAttributes attributes = model.getDataSchema().getAttributes( columnName ); if ( attributes == null ) { // if in doubt, then do not add. continue; } if ( DataSchemaUtility.isFiltered( attributes, model.getDataAttributeContext() ) ) { continue; } if ( "table".equals( attributes.getMetaAttribute ( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.SOURCE, String.class, model.getDataAttributeContext() ) ) ) { targetCols.add( columnName ); } } return targetCols.toArray( new String[ targetCols.size() ] ); } public int getChildCount( final Object parent ) { if ( parent == reportEnvironmentDataRow ) { return reportEnvironmentDataRow.getColumnNames().length; } if ( parent == getDataFactoryElement() ) { final CompoundDataFactory dataFactoryElement = getDataFactoryElement(); return dataFactoryElement.size(); } if ( parent == reportFunctionNode ) { return getExpressions().size(); } if ( parent instanceof DataFactory ) { // a DataFactory is a leaf if it has no columns defined final DataFactory df = (DataFactory) parent; return df.getQueryNames().length; } if ( parent instanceof ReportQueryNode ) { final ReportQueryNode queryNode = (ReportQueryNode) parent; if ( isSelectedDataSource( queryNode ) ) { return getDataFactoryColumns().length; } return 0; } return 0; } public boolean isLeaf( final Object node ) { if ( node == getDataFactoryElement() ) { return false; } if ( node instanceof DataFactory ) { final DataFactory dataFactory = (DataFactory) node; return dataFactory.getQueryNames().length == 0; } if ( node instanceof ReportQueryNode ) { final ReportQueryNode queryNode = (ReportQueryNode) node; if ( isSelectedDataSource( queryNode ) ) { return false; } return true; } if ( node instanceof Expression ) { return true; } if ( node instanceof ReportFieldNode ) { return true; } return false; } public int getIndexOfChild( final Object parent, final Object child ) { if ( parent == reportEnvironmentDataRow ) { if ( child instanceof ReportFieldNode == false ) { return -1; } final ReportFieldNode fieldNode = (ReportFieldNode) child; final String[] columnNames = reportEnvironmentDataRow.getColumnNames(); for ( int i = 0; i < columnNames.length; i++ ) { final String columnName = columnNames[ i ]; if ( columnName.equals( fieldNode.getFieldName() ) ) { return i; } } return -1; } if ( parent == getDataFactoryElement() ) { final CompoundDataFactory dataFactoryElement = getDataFactoryElement(); for ( int i = 0; i < dataFactoryElement.size(); i++ ) { final DataFactory dataFactory = dataFactoryElement.getReference( i ); if ( dataFactory == child ) { return i; } } return -1; } if ( parent instanceof DataFactory ) { if ( child instanceof ReportQueryNode == false ) { return -1; } final ReportQueryNode rfn = (ReportQueryNode) child; if ( rfn.getDataFactory() != parent ) { return -1; } final String[] queryNames = rfn.getDataFactory().getQueryNames(); return indexOf( queryNames, rfn.getQueryName() ); } if ( parent instanceof ReportQueryNode ) { final ReportQueryNode queryNode = (ReportQueryNode) parent; if ( child instanceof ReportFieldNode == false ) { return -1; } final ReportFieldNode node = (ReportFieldNode) child; if ( node.getSource() != queryNode.getDataFactory() ) { return -1; } if ( isSelectedDataSource( queryNode ) == false ) { return -1; } return indexOf( getDataFactoryColumns(), node.getFieldName() ); } if ( parent == reportFunctionNode ) { final ExpressionCollection expressionCollection = getExpressions(); for ( int i = 0; i < expressionCollection.size(); i++ ) { final Expression dataFactory = expressionCollection.getExpression( i ); if ( dataFactory == child ) { return i; } } return -1; } return -1; } protected int indexOf( final String[] array, final String key ) { for ( int i = 0; i < array.length; i++ ) { final String value = array[ i ]; if ( ObjectUtilities.equal( key, value ) ) { return i; } } return -1; } public void valueForPathChanged( final TreePath path, final Object newValue ) { // wont happen, we are not editable } public void fireTreeDataChanged() { final TreeModelListener[] treeModelListeners = getListeners(); final TreeModelEvent treeEvent = new TreeModelEvent( this, new TreePath( getRoot() ) ); // for (int i = treeModelListeners.length - 1; i >= 0; i -= 1) for ( int i = 0; i < treeModelListeners.length; i++ ) { final TreeModelListener listener = treeModelListeners[ i ]; listener.treeStructureChanged( treeEvent ); } refreshExpressionsCache(); } private void refreshExpressionsCache() { this.cachedExpressions = getReportDefinition().getExpressions().getExpressions(); } protected TreeModelListener[] getListeners() { return eventListenerList.getListeners( TreeModelListener.class ); } public void addTreeModelListener( final TreeModelListener l ) { if ( l == null ) { throw new NullPointerException(); } eventListenerList.add( TreeModelListener.class, l ); } public void removeTreeModelListener( final TreeModelListener l ) { if ( l == null ) { throw new NullPointerException(); } eventListenerList.remove( TreeModelListener.class, l ); } public void fireTreeNodeChanged( final Object element ) { TreePath path = getPathForNode( element ); if ( path == null ) { // if we cannot come up with a sensible path, we will take the root and hope the best path = new TreePath( getRoot() ); } final TreeModelListener[] treeModelListeners = getListeners(); final TreeModelEvent treeEvent = new TreeModelEvent( this, path ); for ( int i = treeModelListeners.length - 1; i >= 0; i -= 1 ) { final TreeModelListener listener = treeModelListeners[ i ]; listener.treeNodesChanged( treeEvent ); } } public void fireTreeStructureChanged( final Object element ) { TreePath path = getPathForNode( element ); if ( path == null ) { // if we cannot come up with a sensible path, we will take the root and hope the best path = new TreePath( getRoot() ); } final TreeModelListener[] treeModelListeners = getListeners(); final TreeModelEvent treeEvent = new TreeModelEvent( this, path ); for ( int i = treeModelListeners.length - 1; i >= 0; i -= 1 ) { final TreeModelListener listener = treeModelListeners[ i ]; listener.treeStructureChanged( treeEvent ); } refreshExpressionsCache(); } public void fireQueryChanged( final Object query ) { if ( query == null ) { return; } final ArrayList<ReportQueryNode> nodes = new ArrayList<ReportQueryNode>(); findAllQueryNodes( query, nodes, getDataFactoryElement() ); final TreeModelListener[] treeModelListeners = getListeners(); for ( int n = 0; n < nodes.size(); n++ ) { final ReportQueryNode queryNode = nodes.get( n ); final TreePath path = getPathForNode( queryNode.getDataFactory() ); final TreePath queryPath = path.pathByAddingChild( queryNode ); final TreeModelEvent treeEvent = new TreeModelEvent( this, queryPath ); for ( int i = treeModelListeners.length - 1; i >= 0; i -= 1 ) { final TreeModelListener listener = treeModelListeners[ i ]; listener.treeStructureChanged( treeEvent ); } } } protected void findAllQueryNodes( final Object query, final ArrayList<ReportQueryNode> nodes, final Object element ) { final int childCount = getChildCount( element ); for ( int i = 0; i < childCount; i += 1 ) { final Object child = getChild( element, i ); if ( child instanceof ReportQueryNode ) { final ReportQueryNode queryNode = (ReportQueryNode) child; if ( query.equals( queryNode.getQueryName() ) ) { nodes.add( queryNode ); } } if ( isLeaf( child ) == false ) { findAllQueryNodes( query, nodes, child ); } } } public void fireExpressionAdded( final Expression parameter ) { final TreePath pathForNode = new TreePath( new Object[] { getRoot(), getReportFunctionNode() } ); final TreeModelListener[] treeModelListeners = getListeners(); final int index = getIndexOfChild( getReportFunctionNode(), parameter ); if ( index == -1 ) { return; } final TreeModelEvent treeEvent = new TreeModelEvent( this, pathForNode, new int[] { index }, new Object[] { parameter } ); for ( int i = treeModelListeners.length - 1; i >= 0; i -= 1 ) { final TreeModelListener listener = treeModelListeners[ i ]; listener.treeNodesInserted( treeEvent ); } refreshExpressionsCache(); } public void fireExpressionRemoved( final Expression parameter ) { final TreePath pathForNode = new TreePath( new Object[] { getRoot(), getReportFunctionNode() } ); final TreeModelListener[] treeModelListeners = getListeners(); final int index = findExpressionInCache( parameter ); if ( index == -1 ) { return; } final TreeModelEvent treeEvent = new TreeModelEvent( this, pathForNode, new int[] { index }, new Object[] { parameter } ); for ( int i = treeModelListeners.length - 1; i >= 0; i -= 1 ) { final TreeModelListener listener = treeModelListeners[ i ]; listener.treeNodesRemoved( treeEvent ); } refreshExpressionsCache(); } private int findExpressionInCache( final Expression parameter ) { if ( cachedExpressions == null ) { return -1; } for ( int i = 0; i < cachedExpressions.length; i++ ) { Expression cachedExpression = cachedExpressions[ i ]; if ( cachedExpression == parameter ) { return i; } } return -1; } public TreePath getPathForNode( final Object node ) { if ( node == getRoot() ) { return new TreePath( new Object[] { getRoot() } ); } if ( node instanceof Expression ) { final ReportFunctionNode functions = getReportFunctionNode(); if ( getIndexOfChild( functions, node ) < 0 ) { return null; } return new TreePath( new Object[] { getRoot(), functions, node } ); } if ( node instanceof DataFactory ) { final DataFactory dataFactoryElement = getDataFactoryElement(); if ( getIndexOfChild( dataFactoryElement, node ) < 0 ) { return null; } return new TreePath( new Object[] { getRoot(), dataFactoryElement, node } ); } if ( node == getDataFactoryElement() ) { return new TreePath( new Object[] { getRoot(), getDataFactoryElement() } ); } return null; } }