/*!
* 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.engine.classic.wizard.ui.xul.steps;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.MetaAttributeNames;
import org.pentaho.reporting.engine.classic.core.wizard.DataAttributes;
import org.pentaho.reporting.engine.classic.core.wizard.DataSchema;
import org.pentaho.reporting.engine.classic.core.wizard.DataSchemaModel;
import org.pentaho.reporting.engine.classic.core.wizard.DefaultDataAttributeContext;
import org.pentaho.reporting.engine.classic.wizard.model.DefaultDetailFieldDefinition;
import org.pentaho.reporting.engine.classic.wizard.model.DefaultGroupDefinition;
import org.pentaho.reporting.engine.classic.wizard.model.DetailFieldDefinition;
import org.pentaho.reporting.engine.classic.wizard.model.GroupDefinition;
import org.pentaho.reporting.engine.classic.wizard.ui.xul.components.AbstractWizardStep;
import org.pentaho.reporting.engine.classic.wizard.ui.xul.util.FieldWrapper;
import org.pentaho.reporting.engine.classic.wizard.ui.xul.util.SourceFieldDefinition;
import org.pentaho.reporting.engine.classic.wizard.ui.xul.util.XulGroupDefinition;
import org.pentaho.reporting.libraries.base.util.DebugLog;
import org.pentaho.ui.xul.XulDomContainer;
import org.pentaho.ui.xul.XulException;
import org.pentaho.ui.xul.binding.Binding;
import org.pentaho.ui.xul.binding.Binding.Type;
import org.pentaho.ui.xul.binding.BindingConvertor;
import org.pentaho.ui.xul.containers.XulListbox;
import org.pentaho.ui.xul.impl.AbstractXulEventHandler;
public class LayoutStep extends AbstractWizardStep
{
protected class SelectFieldsAndGroupsEventHandler extends AbstractXulEventHandler
{
protected SelectFieldsAndGroupsEventHandler()
{
}
@Override
public String getName()
{
return "layout_controller"; //$NON-NLS-1$
}
private int[] getNewSelections(final int[] oldSelectedRows, final DIRECTION direction)
{
final int offset;
offset = direction == DIRECTION.DOWN ? 1 : -1;
// update the selection to move with the items
final int[] newSelectedRows = new int[oldSelectedRows.length];
for (int i = 0; i < oldSelectedRows.length; i++)
{
newSelectedRows[i] = oldSelectedRows[i] + offset;
}
return newSelectedRows;
}
public void doMoveToGroups()
{
final XulListbox availableList = (XulListbox) getDocument().getElementById(AVAILABLE_COLUMNS_LIST_ID);
final int[] selectedIndices = availableList.getSelectedIndices();
final List<FieldWrapper> groups = getGroupFields();
final DataSchema schema = getEditorModel().getDataSchema().getDataSchema();
for (final int i : selectedIndices)
{
final SourceFieldDefinition group = getSelectableFields().get(i);
final GroupDefinition xulGroup = new DefaultGroupDefinition();
xulGroup.setField(group.getFieldName());
FieldWrapper wrapper = new FieldWrapper(xulGroup, schema);
groups.add(wrapper);
final DefaultGroupDefinition definition = (DefaultGroupDefinition) wrapper.getFieldDefinition();
new XulGroupDefinition(definition, schema);
}
setGroupFields(groups);
}
public void doMoveToDetails()
{
final XulListbox availableList = (XulListbox) getDocument().getElementById(AVAILABLE_COLUMNS_LIST_ID);
final int[] selectedIndices = availableList.getSelectedIndices();
final List<FieldWrapper> details = getDetailFields();
final DataSchema schema = getEditorModel().getDataSchema().getDataSchema();
for (final int i : selectedIndices)
{
final SourceFieldDefinition field = getSelectableFields().get(i);
final DetailFieldDefinition xulField = new DefaultDetailFieldDefinition();
xulField.setField(field.getFieldName());
details.add(new FieldWrapper(xulField, schema));
}
setDetailFields(details);
}
public void doMoveUpSelectedGroupItems()
{
final List<FieldWrapper> groups = getGroupFields();
final XulListbox groupList = (XulListbox) getDocument().getElementById(GROUP_FIELDS_LIST_ID);
final int[] selectedIndices = groupList.getSelectedIndices();
Arrays.sort(selectedIndices);
for (final int selectedRow : selectedIndices)
{
final FieldWrapper group = groups.remove(selectedRow);
groups.add(selectedRow - 1, group);
}
setGroupFields(groups);
// update the selection to move with the items
groupList.setSelectedIndices(getNewSelections(selectedIndices, DIRECTION.UP));
}
public void doMoveDownSelectedGroupItems()
{
final List<FieldWrapper> groups = getGroupFields();
final XulListbox groupList = (XulListbox) getDocument().getElementById(GROUP_FIELDS_LIST_ID);
final int[] selectedIndices = groupList.getSelectedIndices();
Arrays.sort(selectedIndices);
reverseArray(selectedIndices);
for (final int selectedRow : selectedIndices)
{
final FieldWrapper group = groups.remove(selectedRow);
groups.add(selectedRow + 1, group);
}
setGroupFields(groups);
// update the selection to move with the items
groupList.setSelectedIndices(getNewSelections(selectedIndices, DIRECTION.DOWN));
}
public void doRemoveSelectedGroupItems()
{
final List<FieldWrapper> groups = getGroupFields();
final XulListbox groupList = (XulListbox) getDocument().getElementById(GROUP_FIELDS_LIST_ID);
for (int i = groupList.getSelectedIndices().length - 1; i >= 0; i--)
{ // Count from the end back
groups.remove(groupList.getSelectedIndices()[i]);
}
setGroupFields(groups);
groupList.setSelectedIndices(EMPTY_SELECTION); // Clear any selections
}
public void doMoveUpSelectedDetailItems()
{
final List<FieldWrapper> details = getDetailFields();
final XulListbox detailList = (XulListbox) getDocument().getElementById(DETAIL_FIELDS_LIST_ID);
final int[] selectedIndices = detailList.getSelectedIndices();
Arrays.sort(selectedIndices);
for (final int selectedRow : selectedIndices)
{
final FieldWrapper detail = details.remove(selectedRow);
details.add(selectedRow - 1, detail);
}
setDetailFields(details);
// update the selection to move with the items
detailList.setSelectedIndices(getNewSelections(selectedIndices, DIRECTION.UP));
}
public void doMoveDownSelectedDetailItems()
{
final List<FieldWrapper> details = getDetailFields();
final XulListbox detailList = (XulListbox) getDocument().getElementById(DETAIL_FIELDS_LIST_ID);
final int[] selectedIndices = detailList.getSelectedIndices();
Arrays.sort(selectedIndices);
reverseArray(selectedIndices);
for (final int selectedRow : selectedIndices)
{
final FieldWrapper detail = details.remove(selectedRow);
details.add(selectedRow + 1, detail);
}
setDetailFields(details);
// update the selection to move with the items
detailList.setSelectedIndices(getNewSelections(selectedIndices, DIRECTION.DOWN));
}
public void doRemoveSelectedDetailItems()
{
final List<FieldWrapper> details = getDetailFields();
final XulListbox detailList = (XulListbox) getDocument().getElementById(DETAIL_FIELDS_LIST_ID);
for (int i = detailList.getSelectedIndices().length - 1; i >= 0; i--)
{ // Count from the end back
details.remove(detailList.getSelectedIndices()[i]);
}
setDetailFields(details);
detailList.setSelectedIndices(EMPTY_SELECTION); // Clear any selections
}
private void reverseArray(final int[] target)
{
for (int i = 0; i < target.length / 2; i++)
{
final int temp = target[i];
target[i] = target[target.length - i - 1];
target[target.length - i - 1] = temp;
}
}
}
/**
* @author wseyler
*/
private static class ListSelectionToBooleanConverter extends BindingConvertor<int[], Boolean>
{
private ListSelectionToBooleanConverter()
{
}
/* (non-Javadoc)
* @see org.pentaho.ui.xul.binding.BindingConvertor#sourceToTarget(java.lang.Object)
*/
@Override
public Boolean sourceToTarget(final int[] value)
{
return value.length > 0;
}
/* (non-Javadoc)
* @see org.pentaho.ui.xul.binding.BindingConvertor#targetToSource(java.lang.Object)
*/
@Override
public int[] targetToSource(final Boolean value)
{
return null;
}
}
/**
* @author wseyler
*/
private static class MoveUpBindingConverter extends BindingConvertor<int[], Boolean>
{
private MoveUpBindingConverter()
{
}
/* (non-Javadoc)
* @see org.pentaho.ui.xul.binding.BindingConvertor#sourceToTarget(java.lang.Object)
*/
@Override
public Boolean sourceToTarget(final int[] value)
{
Arrays.sort(value);
return value.length > 0 && value[0] > 0;
}
/* (non-Javadoc)
* @see org.pentaho.ui.xul.binding.BindingConvertor#targetToSource(java.lang.Object)
*/
@Override
public int[] targetToSource(final Boolean value)
{
return null;
}
}
/**
* @author wseyler
*/
private static class MoveDownBindingConverter extends BindingConvertor<int[], Boolean>
{
private XulListbox list;
private MoveDownBindingConverter(final XulListbox list)
{
this.list = list;
}
/* (non-Javadoc)
* @see org.pentaho.ui.xul.binding.BindingConvertor#sourceToTarget(java.lang.Object)
*/
@Override
public Boolean sourceToTarget(final int[] value)
{
Arrays.sort(value);
return value.length > 0 && value[value.length - 1] < list.getElements().size() - 1;
}
/* (non-Javadoc)
* @see org.pentaho.ui.xul.binding.BindingConvertor#targetToSource(java.lang.Object)
*/
@Override
public int[] targetToSource(final Boolean value)
{
return null;
}
}
private enum DIRECTION
{
UP, DOWN
}
private static final String LAYOUT_STEP_OVERLAY = "org/pentaho/reporting/engine/classic/wizard/ui/xul/res/layout_step_Overlay.xul"; //$NON-NLS-1$
private static final String ELEMENTS_PROPERTY_NAME = "elements"; //$NON-NLS-1$
private static final String NOT_DISABLED_PROPERTY_NAME = "!disabled"; //$NON-NLS-1$
private static final String SELECTABLE_FIELDS_PROPERTY_NAME = "selectableFields"; //$NON-NLS-1$
private static final String GROUP_FIELDS_PROPERTY_NAME = "groupFields"; //$NON-NLS-1$
private static final String DETAIL_FIELDS_PROPERTY_NAME = "detailFields"; //$NON-NLS-1$
private static final String SELECTED_INDICES_PROPERTY_NAME = "selectedIndices"; //$NON-NLS-1$
protected static final String AVAILABLE_COLUMNS_LIST_ID = "available_columns_list"; //$NON-NLS-1$
protected static final String GROUP_FIELDS_LIST_ID = "group_fields_list"; //$NON-NLS-1$
protected static final String DETAIL_FIELDS_LIST_ID = "detail_fields_list"; //$NON-NLS-1$
private static final String MOVE_TO_GROUPS_BTN_ID = "move_to_groups_btn"; //$NON-NLS-1$
private static final String MOVE_TO_DETAILS_BTN_ID = "move_to_details_btn"; //$NON-NLS-1$
private static final String MOVE_GROUP_UP_BTN_ID = "move_group_up_btn"; //$NON-NLS-1$
private static final String MOVE_GROUP_DOWN_BTN_ID = "move_group_down_btn"; //$NON-NLS-1$
private static final String REMOVE_GROUP_ITEM_BTN_ID = "remove_group_item_btn"; //$NON-NLS-1$
private static final String MOVE_DETAIL_UP_BTN_ID = "move_detail_up_btn"; //$NON-NLS-1$
private static final String MOVE_DETAIL_DOWN_BTN_ID = "move_detail_down_btn"; //$NON-NLS-1$
private static final String REMOVE_DETAIL_ITEM_BTN_ID = "remove_detail_item_btn"; //$NON-NLS-1$
private static final String PREVIEW_BUTTON_ID = "layout_preview_btn"; //$NON-NLS-1$
private Binding previewBinding;
private Binding selectableFieldsBinding;
private Binding groupsBinding;
private Binding detailsBinding;
private ArrayList<SourceFieldDefinition> selectableFields;
protected static final int[] EMPTY_SELECTION = new int[0];
public LayoutStep()
{
selectableFields = new ArrayList<SourceFieldDefinition>();
}
/* (non-Javadoc)
* @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#initialize()
*/
public void setBindings()
{
// Create the binding converters
final ListSelectionToBooleanConverter listToBooleanConverter = new ListSelectionToBooleanConverter();
final MoveUpBindingConverter moveUpBindingConverter = new MoveUpBindingConverter();
// Bindings for the lists
getBindingFactory().setBindingType(Type.BI_DIRECTIONAL);
selectableFieldsBinding = getBindingFactory().createBinding(this, SELECTABLE_FIELDS_PROPERTY_NAME, AVAILABLE_COLUMNS_LIST_ID, ELEMENTS_PROPERTY_NAME);
groupsBinding = getBindingFactory().createBinding(this, GROUP_FIELDS_PROPERTY_NAME, GROUP_FIELDS_LIST_ID, ELEMENTS_PROPERTY_NAME);
detailsBinding = getBindingFactory().createBinding(this, DETAIL_FIELDS_PROPERTY_NAME, DETAIL_FIELDS_LIST_ID, ELEMENTS_PROPERTY_NAME);
// Bindings for the move to fields and groups buttons
getBindingFactory().setBindingType(Type.ONE_WAY);
getBindingFactory().createBinding(AVAILABLE_COLUMNS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_TO_GROUPS_BTN_ID, NOT_DISABLED_PROPERTY_NAME, listToBooleanConverter);
getBindingFactory().createBinding(AVAILABLE_COLUMNS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_TO_DETAILS_BTN_ID, NOT_DISABLED_PROPERTY_NAME, listToBooleanConverter);
// Bindings for the buttons on the groups panel
getBindingFactory().setBindingType(Type.ONE_WAY);
getBindingFactory().createBinding(GROUP_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_GROUP_UP_BTN_ID, NOT_DISABLED_PROPERTY_NAME, moveUpBindingConverter);
getBindingFactory().createBinding(GROUP_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_GROUP_DOWN_BTN_ID, NOT_DISABLED_PROPERTY_NAME, new MoveDownBindingConverter((XulListbox) getDocument().getElementById(GROUP_FIELDS_LIST_ID)));
getBindingFactory().createBinding(GROUP_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, REMOVE_GROUP_ITEM_BTN_ID, NOT_DISABLED_PROPERTY_NAME, listToBooleanConverter);
// Bindings for the buttons on the detail panel
getBindingFactory().setBindingType(Type.ONE_WAY);
getBindingFactory().createBinding(DETAIL_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_DETAIL_UP_BTN_ID, NOT_DISABLED_PROPERTY_NAME, moveUpBindingConverter);
getBindingFactory().createBinding(DETAIL_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_DETAIL_DOWN_BTN_ID, NOT_DISABLED_PROPERTY_NAME, new MoveDownBindingConverter((XulListbox) getDocument().getElementById(DETAIL_FIELDS_LIST_ID)));
getBindingFactory().createBinding(DETAIL_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, REMOVE_DETAIL_ITEM_BTN_ID, NOT_DISABLED_PROPERTY_NAME, listToBooleanConverter);
// Binding for the preview button
getBindingFactory().setBindingType(Type.ONE_WAY);
previewBinding = getBindingFactory().createBinding(this, PREVIEWABLE_PROPERTY_NAME, PREVIEW_BUTTON_ID, NOT_DISABLED_PROPERTY_NAME);
}
public void stepActivating()
{
super.stepActivating();
try
{
previewBinding.fireSourceChanged();
selectableFieldsBinding.fireSourceChanged();
groupsBinding.fireSourceChanged();
detailsBinding.fireSourceChanged();
}
catch (Exception e)
{
if (getDesignTimeContext() != null)
{
getDesignTimeContext().error(e);
}
else
{
DebugLog.log(e);
}
}
if (getEditorModel().isRelationalModel())
{
populateSourceList();
setDetailFields(getDetailFields());
setGroupFields(getGroupFields());
}
else
{
setValid(false);
}
}
private void populateSourceList()
{
final DataSchemaModel dataSchemaModel = getEditorModel().getDataSchema();
final DataSchema dataSchema = dataSchemaModel.getDataSchema();
final String[] names = dataSchema.getNames();
Arrays.sort(names);
final ArrayList<SourceFieldDefinition> fields = new ArrayList<SourceFieldDefinition>();
for (int i = 0; i < names.length; i++)
{
final String fieldName = names[i];
if (fieldName == null)
{
continue;
}
if (isFilteredField(dataSchema, fieldName))
{
continue;
}
final SourceFieldDefinition fieldDefinition = new SourceFieldDefinition(fieldName, dataSchema);
fields.add(fieldDefinition);
}
this.setSelectableFields(fields);
}
private boolean isFilteredField(final DataSchema dataSchema, final String fieldName)
{
final DefaultDataAttributeContext dac = new DefaultDataAttributeContext();
final DataAttributes attributes = dataSchema.getAttributes(fieldName);
final Object source = attributes.getMetaAttribute
(MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.SOURCE, String.class, dac);
if (MetaAttributeNames.Core.SOURCE_VALUE_ENVIRONMENT.equals(source))
{
return true;
}
if (MetaAttributeNames.Core.SOURCE_VALUE_PARAMETER.equals(source))
{
return true;
}
final Object indexColumn = attributes.getMetaAttribute
(MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.INDEXED_COLUMN, Boolean.class, dac);
if (Boolean.TRUE.equals(indexColumn))
{
return true;
}
return false;
}
public List<FieldWrapper> getDetailFields()
{
final ArrayList<FieldWrapper> fields = new ArrayList<FieldWrapper>();
final DataSchema schema = getEditorModel().getDataSchema().getDataSchema();
for (final DetailFieldDefinition field : getEditorModel().getReportSpec().getDetailFieldDefinitions())
{
fields.add(new FieldWrapper(field, schema));
}
return fields;
}
public void setDetailFields(final List<FieldWrapper> detailFields)
{
final List<FieldWrapper> oldFields = getDetailFields();
final DetailFieldDefinition[] fields = new DetailFieldDefinition[detailFields.size()];
for (int i = 0; i < detailFields.size(); i++)
{
final FieldWrapper fieldWrapper = detailFields.get(i);
fields[i] = (DetailFieldDefinition) fieldWrapper.getFieldDefinition();
}
getEditorModel().getReportSpec().setDetailFieldDefinitions(fields);
// If we change the detail fields check and see if the list is populated
// if it is we can enable preview
// this should be refactored to a binding
this.setPreviewable(!detailFields.isEmpty());
this.setFinishable(!detailFields.isEmpty());
this.setValid(!detailFields.isEmpty());
this.firePropertyChange(DETAIL_FIELDS_PROPERTY_NAME, oldFields, detailFields);
}
public List<FieldWrapper> getGroupFields()
{
final ArrayList<FieldWrapper> groups = new ArrayList<FieldWrapper>();
final DataSchema schema = getEditorModel().getDataSchema().getDataSchema();
for (final GroupDefinition group : getEditorModel().getReportSpec().getGroupDefinitions())
{
groups.add(new FieldWrapper(group, schema));
}
return groups;
}
public void setGroupFields(final List<FieldWrapper> groupFields)
{
final List<FieldWrapper> oldGroups = getGroupFields();
final GroupDefinition[] fields = new GroupDefinition[groupFields.size()];
for (int i = 0; i < groupFields.size(); i++)
{
final FieldWrapper fieldWrapper = groupFields.get(i);
fields[i] = (GroupDefinition) fieldWrapper.getFieldDefinition();
}
getEditorModel().getReportSpec().setGroupDefinitions(fields);
this.firePropertyChange(GROUP_FIELDS_PROPERTY_NAME, oldGroups, groupFields);
}
/* (non-Javadoc)
* @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#createPresentationComponent(org.pentaho.ui.xul.XulDomContainer)
*/
public void createPresentationComponent(final XulDomContainer mainWizardContainer) throws XulException
{
super.createPresentationComponent(mainWizardContainer);
mainWizardContainer.loadOverlay(LAYOUT_STEP_OVERLAY);
mainWizardContainer.addEventHandler(new SelectFieldsAndGroupsEventHandler());
}
public ArrayList<SourceFieldDefinition> getSelectableFields()
{
return selectableFields;
}
public void setSelectableFields(final ArrayList<SourceFieldDefinition> selectableFields)
{
if (selectableFields == null)
{
throw new NullPointerException();
}
final ArrayList<SourceFieldDefinition> oldSelectableFields = this.selectableFields;
//noinspection AssignmentToCollectionOrArrayFieldFromParameter
this.selectableFields = selectableFields;
this.firePropertyChange(SELECTABLE_FIELDS_PROPERTY_NAME, oldSelectableFields, this.selectableFields);
}
/* (non-Javadoc)
* @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#getStepName()
*/
public String getStepName()
{
return messages.getString("LAYOUT_STEP.Step_Name"); //$NON-NLS-1$
}
}