/*!
* 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.actions.report.EditParametersAction;
import org.pentaho.reporting.designer.core.actions.report.EditQueryAction;
import org.pentaho.reporting.designer.core.editor.ReportDataChangeListener;
import org.pentaho.reporting.designer.core.editor.ReportDocumentContext;
import org.pentaho.reporting.designer.core.model.selection.DocumentContextSelectionModel;
import org.pentaho.reporting.designer.core.settings.SettingsListener;
import org.pentaho.reporting.designer.core.settings.WorkspaceSettings;
import org.pentaho.reporting.designer.core.util.dnd.FieldDescriptionTransferable;
import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel;
import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.CompoundDataFactory;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ParameterMapping;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.SubReport;
import org.pentaho.reporting.engine.classic.core.designtime.AttributeChange;
import org.pentaho.reporting.engine.classic.core.designtime.AttributeExpressionChange;
import org.pentaho.reporting.engine.classic.core.designtime.StyleChange;
import org.pentaho.reporting.engine.classic.core.designtime.StyleExpressionChange;
import org.pentaho.reporting.engine.classic.core.designtime.SubReportParameterChange;
import org.pentaho.reporting.engine.classic.core.event.ReportModelEvent;
import org.pentaho.reporting.engine.classic.core.event.ReportModelListener;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterDefinitionEntry;
import org.pentaho.reporting.engine.classic.core.parameters.ReportParameterDefinition;
import org.pentaho.reporting.libraries.base.util.DebugLog;
import javax.swing.*;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.awt.datatransfer.Transferable;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class DataReportTree extends AbstractReportTree {
private class ReportUpdateHandler implements ReportModelListener, ReportDataChangeListener {
private ReportUpdateHandler() {
}
public void dataModelChanged( final ReportDocumentContext context ) {
final AbstractReportDataTreeModel realModel = getDataTreeModel();
if ( realModel == null ) {
return;
}
DebugLog.log( "Data Changed, Update tree" );
realModel.fireQueryChanged( renderContext.getReportDefinition().getQuery() );
}
public void nodeChanged( final ReportModelEvent event ) {
final AbstractReportDataTreeModel realModel = getDataTreeModel();
if ( realModel == null ) {
return;
}
try {
if ( event.isNodeDeleteEvent() ) {
handleNodeRemoved( event, realModel );
}
if ( event.isNodeAddedEvent() ) {
handleNodeAdded( event, realModel );
}
if ( event.getType() == ReportModelEvent.NODE_PROPERTIES_CHANGED ) {
if ( event.getElement() == renderContext.getReportDefinition() ) {
final Object eventParameter = event.getParameter();
if ( eventParameter instanceof AttributeExpressionChange ||
eventParameter instanceof StyleChange ||
eventParameter instanceof StyleExpressionChange ) {
// these things have no effect on the data ..
return;
}
if ( eventParameter instanceof AttributeChange ) {
final AttributeChange attributeChange = (AttributeChange) eventParameter;
if ( AttributeNames.Internal.NAMESPACE.equals( attributeChange.getNamespace() ) ||
AttributeNames.Internal.QUERY.equals( attributeChange.getName() ) ) {
realModel.fireQueryChanged( attributeChange.getOldValue() );
realModel.fireQueryChanged( attributeChange.getNewValue() );
}
return;
}
if ( eventParameter instanceof Expression ||
eventParameter instanceof ReportParameterDefinition ) {
realModel.fireTreeNodeChanged( eventParameter );
} else if ( eventParameter instanceof SubReportParameterChange ) {
if ( realModel instanceof SubReportDataTreeModel ) {
final SubReportDataTreeModel subReportDataTreeModel = (SubReportDataTreeModel) realModel;
realModel.fireTreeStructureChanged( subReportDataTreeModel.getReportParametersNode() );
}
} else {
realModel.fireTreeDataChanged();
}
} else {
realModel.fireTreeNodeChanged( event.getElement() );
}
}
} finally {
restoreState();
expandAfterDataSourceEdit( event );
}
}
private void handleNodeRemoved( final ReportModelEvent event, final AbstractReportDataTreeModel realModel ) {
if ( event.getElement() == renderContext.getReportDefinition() ) {
final Object eventParam = event.getParameter();
if ( eventParam instanceof Expression ) {
final Expression expression = (Expression) eventParam;
realModel.fireExpressionRemoved( expression );
} else if ( eventParam instanceof ParameterDefinitionEntry ) {
final ParameterDefinitionEntry parameter = (ParameterDefinitionEntry) eventParam;
if ( realModel instanceof MasterReportDataTreeModel ) {
final MasterReportDataTreeModel masterModel = (MasterReportDataTreeModel) realModel;
masterModel.fireParameterRemoved( parameter );
}
} else if ( eventParam instanceof DataFactory ) {
realModel.fireTreeStructureChanged( realModel.getDataFactoryElement() );
} else {
realModel.fireTreeDataChanged();
}
}
}
private void handleNodeAdded( final ReportModelEvent event, final AbstractReportDataTreeModel realModel ) {
if ( event.getElement() == renderContext.getReportDefinition() ) {
final Object eventParam = event.getParameter();
if ( eventParam instanceof Expression ) {
final Expression expression = (Expression) eventParam;
realModel.fireExpressionAdded( expression );
} else if ( eventParam instanceof ParameterDefinitionEntry ) {
final ParameterDefinitionEntry parameter = (ParameterDefinitionEntry) eventParam;
if ( realModel instanceof MasterReportDataTreeModel ) {
final MasterReportDataTreeModel masterModel = (MasterReportDataTreeModel) realModel;
masterModel.fireParameterAdded( parameter );
}
} else if ( eventParam instanceof DataFactory ) {
realModel.fireTreeStructureChanged( realModel.getDataFactoryElement() );
} else {
realModel.fireTreeDataChanged();
}
}
}
private void expandAfterDataSourceEdit( final ReportModelEvent event ) {
final Object element = event.getElement();
if ( event.isNodeStructureChanged() == false ) {
return;
}
if ( element instanceof AbstractReportDefinition == false ) {
return;
}
if ( treeModel instanceof AbstractReportDataTreeModel == false ) {
return;
}
final AbstractReportDataTreeModel dataTreeModel = (AbstractReportDataTreeModel) treeModel;
final Object parameter = event.getParameter();
if ( parameter instanceof DataFactory ) {
SwingUtilities.invokeLater( new ExpandDataFactoryNodesTask( dataTreeModel ) );
} else if ( parameter instanceof Expression ) {
SwingUtilities.invokeLater( new ExpandExpressionNodesTask( dataTreeModel ) );
} else if ( parameter instanceof ReportParameterDefinition ) {
SwingUtilities.invokeLater( new ExpandParameterDataSourceTask( dataTreeModel ) );
}
}
}
private class SettingsChangeHandler implements SettingsListener {
private boolean showIndexColumns;
private SettingsChangeHandler() {
showIndexColumns = WorkspaceSettings.getInstance().isShowIndexColumns();
}
public void settingsChanged() {
// revalidate the data model ..
if ( showIndexColumns != WorkspaceSettings.getInstance().isShowIndexColumns() ) {
showIndexColumns = WorkspaceSettings.getInstance().isShowIndexColumns();
final TreeModel model = getModel();
if ( model instanceof AbstractReportDataTreeModel ) {
final AbstractReportDataTreeModel realModel = (AbstractReportDataTreeModel) model;
realModel.fireTreeDataChanged();
restoreState();
}
}
invalidateLayoutCache();
}
}
private class ExpandDataFactoryNodesTask implements Runnable {
private AbstractReportDataTreeModel treeModel;
private ExpandDataFactoryNodesTask( final AbstractReportDataTreeModel treeModel ) {
this.treeModel = treeModel;
}
public void run() {
if ( getModel() != treeModel ) {
return;
}
final CompoundDataFactory compoundDataFactory = treeModel.getDataFactoryElement();
final int size = compoundDataFactory.size();
for ( int i = 0; i < size; i++ ) {
final DataFactory df = compoundDataFactory.getReference( i );
final TreePath path = treeModel.getPathForNode( df );
if ( path == null ) {
return;
}
expandPath( path );
final int count = treeModel.getChildCount( df );
for ( int x = 0; x < count; x++ ) {
final Object child = treeModel.getChild( df, x );
if ( child == null ) {
continue;
}
final TreePath childPath = path.pathByAddingChild( child );
expandPath( childPath );
}
}
}
}
private class ExpandExpressionNodesTask implements Runnable {
private AbstractReportDataTreeModel treeModel;
private ExpandExpressionNodesTask( final AbstractReportDataTreeModel treeModel ) {
this.treeModel = treeModel;
}
public void run() {
if ( getModel() != treeModel ) {
return;
}
final AbstractReportDataTreeModel dataTreeModel = treeModel;
expandPath( new TreePath( new Object[] { dataTreeModel.getRoot(), dataTreeModel.getReportFunctionNode() } ) );
}
}
private class ExpandParameterDataSourceTask implements Runnable {
private AbstractReportDataTreeModel treeModel;
private ExpandParameterDataSourceTask( final AbstractReportDataTreeModel treeModel ) {
this.treeModel = treeModel;
}
public void run() {
if ( getModel() != treeModel ) {
return;
}
if ( treeModel instanceof MasterReportDataTreeModel ) {
final MasterReportDataTreeModel dataTreeModel = (MasterReportDataTreeModel) treeModel;
expandPath( new TreePath( new Object[] { dataTreeModel.getRoot(), dataTreeModel.getReportParametersNode() } ) );
}
if ( treeModel instanceof SubReportDataTreeModel ) {
final SubReportDataTreeModel dataTreeModel = (SubReportDataTreeModel) treeModel;
expandPath( new TreePath( new Object[] { dataTreeModel.getRoot(), dataTreeModel.getReportParametersNode() } ) );
}
}
}
private class ExpandEnvironmentDataSourceTask implements Runnable {
private AbstractReportDataTreeModel treeModel;
private ExpandEnvironmentDataSourceTask( final AbstractReportDataTreeModel treeModel ) {
this.treeModel = treeModel;
}
public void run() {
if ( getModel() != treeModel ) {
return;
}
expandPath( new TreePath( new Object[] { treeModel.getRoot(), treeModel.getReportEnvironmentDataRow() } ) );
}
}
private class ColumnTransferHandler extends TransferHandler {
/**
* Creates a <code>Transferable</code> to use as the source for a data transfer. Returns the representation of the
* data to be transferred, or <code>null</code> if the component's property is <code>null</code>
*
* @param c the component holding the data to be transferred; this argument is provided to enable sharing of
* <code>TransferHandler</code>s by multiple components
* @return the representation of the data to be transferred, or <code>null</code> if the property associated with
* <code>c</code> is <code>null</code>
*/
protected Transferable createTransferable( final JComponent c ) {
if ( c != DataReportTree.this ) {
return null;
}
final Object node = getSelectionPath().getLastPathComponent();
if ( node instanceof ReportFieldNode ) {
final ReportFieldNode field = (ReportFieldNode) node;
return new FieldDescriptionTransferable( field.getFieldName() );
}
if ( node instanceof ParameterMapping ) {
final Object o = getSelectionPath().getParentPath().getLastPathComponent();
if ( o instanceof SubReportParametersNode.ImportParametersNode ) {
final ParameterMapping field = (ParameterMapping) node;
return new FieldDescriptionTransferable( field.getAlias() );
}
}
if ( node instanceof ParameterDefinitionEntry ) {
final ParameterDefinitionEntry field = (ParameterDefinitionEntry) node;
return new FieldDescriptionTransferable( field.getName() );
}
if ( node instanceof Expression ) {
final Expression expression = (Expression) node;
if ( expression.getName() != null ) {
return new FieldDescriptionTransferable( expression.getName() );
}
}
return null;
}
public int getSourceActions( final JComponent c ) {
return COPY;
}
}
private class EditQueryDoubleClickHandler extends MouseAdapter {
private EditQueryDoubleClickHandler() {
}
public void mouseClicked( final MouseEvent e ) {
if ( e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1 ) {
final TreePath selectionPath = getLeadSelectionPath();
if ( selectionPath == null ) {
return;
}
try {
final Object selection = selectionPath.getLastPathComponent();
if ( selection instanceof ParameterDefinitionEntry ) {
final ParameterDefinitionEntry parameterDefinitionEntry = (ParameterDefinitionEntry) selection;
EditParametersAction
.performEditMasterReportParameters( getReportDesignerContext(), parameterDefinitionEntry );
return;
}
if ( selection instanceof ParameterMapping ||
selection instanceof SubReportParametersNode ) {
EditParametersAction.performEditSubReportParameters( getReportDesignerContext() );
return;
}
if ( selection instanceof DataFactory ) {
final EditQueryAction action = new EditQueryAction();
action.setReportDesignerContext( getReportDesignerContext() );
action.performEdit( (DataFactory) selection, null );
return;
}
if ( selection instanceof ReportQueryNode ) {
final ReportQueryNode queryNode = (ReportQueryNode) selection;
if ( queryNode.isAllowEdit() == false ) {
return;
}
final EditQueryAction action = new EditQueryAction();
action.setReportDesignerContext( getReportDesignerContext() );
action.performEdit( queryNode.getDataFactory(), queryNode.getQueryName() );
e.consume();
}
} catch ( ReportDataFactoryException e1 ) {
UncaughtExceptionsModel.getInstance().addException( e1 );
}
}
}
}
private ReportDocumentContext renderContext;
private ReportUpdateHandler updateHandler;
@SuppressWarnings( "FieldCanBeLocal" )
private SettingsChangeHandler settingsChangeHandler;
public DataReportTree() {
updateHandler = new ReportUpdateHandler();
setCellRenderer( new StructureTreeCellRenderer() );
setTransferHandler( new ColumnTransferHandler() );
setDragEnabled( true );
setEditable( false );
setRootVisible( false );
addMouseListener( new EditQueryDoubleClickHandler() );
settingsChangeHandler = new SettingsChangeHandler();
WorkspaceSettings.getInstance().addSettingsListener( settingsChangeHandler );
}
public ReportDocumentContext getRenderContext() {
return renderContext;
}
protected TreePath getPathForNode( final Object node ) {
if ( getDataTreeModel() == null ) {
return null;
}
return getDataTreeModel().getPathForNode( node );
}
public void setRenderContext( final ReportDocumentContext renderContext ) {
if ( this.renderContext != null ) {
this.renderContext.getSelectionModel().removeReportSelectionListener( getSelectionHandler() );
this.renderContext.getReportDefinition().removeReportModelListener( updateHandler );
this.renderContext.removeReportDataChangeListener( updateHandler );
}
this.renderContext = renderContext;
if ( this.renderContext != null ) {
this.renderContext.getSelectionModel().addReportSelectionListener( getSelectionHandler() );
this.renderContext.getReportDefinition().addReportModelListener( updateHandler );
this.renderContext.addReportDataChangeListener( updateHandler );
}
updateFromRenderContext();
restoreState();
}
protected void updateFromRenderContext() {
try {
setUpdateFromExternalSource( true );
if ( this.renderContext == null ) {
setModel( EMPTY_MODEL );
return;
}
final AbstractReportDefinition report = this.renderContext.getReportDefinition();
final AbstractReportDataTreeModel model;
if ( report instanceof MasterReport ) {
model = new MasterReportDataTreeModel( renderContext );
} else if ( report instanceof SubReport ) {
model = new SubReportDataTreeModel( renderContext );
} else {
setModel( EMPTY_MODEL );
return;
}
setModel( model );
final DocumentContextSelectionModel selectionModel = renderContext.getSelectionModel();
final Object[] selectedElements = selectionModel.getSelectedElements();
final ArrayList<TreePath> selectionPaths = new ArrayList<TreePath>();
for ( int i = 0; i < selectedElements.length; i++ ) {
final Object o = selectedElements[ i ];
final TreePath path = model.getPathForNode( o );
if ( path != null ) {
selectionPaths.add( path );
}
}
getSelectionModel().setSelectionPaths( selectionPaths.toArray( new TreePath[ selectionPaths.size() ] ) );
SwingUtilities.invokeLater( new ExpandDataFactoryNodesTask( model ) );
SwingUtilities.invokeLater( new ExpandExpressionNodesTask( model ) );
SwingUtilities.invokeLater( new ExpandParameterDataSourceTask( model ) );
SwingUtilities.invokeLater( new ExpandEnvironmentDataSourceTask( model ) );
} finally {
setUpdateFromExternalSource( false );
}
}
private AbstractReportDataTreeModel getDataTreeModel() {
final TreeModel model = getModel();
if ( model instanceof AbstractReportDataTreeModel ) {
return (AbstractReportDataTreeModel) model;
}
return null;
}
}