/* * Copyright (c) 2015 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.io.appschema.ui; import static eu.esdihumboldt.hale.io.appschema.writer.AppSchemaMappingUtils.getJoinParameter; import static eu.esdihumboldt.hale.io.appschema.writer.AppSchemaMappingUtils.getSortedJoinConditions; import static eu.esdihumboldt.hale.io.appschema.writer.AppSchemaMappingUtils.getTargetType; import static eu.esdihumboldt.hale.io.appschema.writer.AppSchemaMappingUtils.isNested; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.UUID; import org.eclipse.jface.dialogs.IPageChangingListener; import org.eclipse.jface.dialogs.PageChangingEvent; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.wizard.IWizardContainer; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; import eu.esdihumboldt.hale.common.align.model.Alignment; import eu.esdihumboldt.hale.common.align.model.AlignmentUtil; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.ChildContext; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.functions.join.JoinParameter; import eu.esdihumboldt.hale.common.align.model.functions.join.JoinParameter.JoinCondition; import eu.esdihumboldt.hale.common.align.model.impl.PropertyEntityDefinition; import eu.esdihumboldt.hale.common.align.model.impl.TypeEntityDefinition; import eu.esdihumboldt.hale.common.core.io.impl.ComplexValue; import eu.esdihumboldt.hale.common.core.io.project.model.IOConfiguration; import eu.esdihumboldt.hale.common.schema.SchemaSpaceID; import eu.esdihumboldt.hale.common.schema.model.SchemaSpace; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.io.appschema.AppSchemaIO; import eu.esdihumboldt.hale.io.appschema.model.ChainConfiguration; import eu.esdihumboldt.hale.io.appschema.model.FeatureChaining; import eu.esdihumboldt.hale.io.appschema.writer.AbstractAppSchemaConfigurator; import eu.esdihumboldt.hale.io.appschema.writer.AppSchemaMappingUtils; import eu.esdihumboldt.hale.ui.HaleUI; import eu.esdihumboldt.hale.ui.HaleWizardPage; import eu.esdihumboldt.hale.ui.common.definition.viewer.DefinitionComparator; import eu.esdihumboldt.hale.ui.common.definition.viewer.DefinitionLabelProvider; import eu.esdihumboldt.hale.ui.common.definition.viewer.SchemaPatternFilter; import eu.esdihumboldt.hale.ui.common.definition.viewer.StyledDefinitionLabelProvider; import eu.esdihumboldt.hale.ui.function.generic.AbstractGenericFunctionWizard; import eu.esdihumboldt.hale.ui.io.config.AbstractConfigurationPage; import eu.esdihumboldt.hale.ui.service.align.AlignmentService; import eu.esdihumboldt.hale.ui.service.entity.EntityDefinitionService; import eu.esdihumboldt.hale.ui.service.entity.util.EntityTypeIterableContentProvider; import eu.esdihumboldt.hale.ui.service.schema.SchemaService; import eu.esdihumboldt.hale.ui.util.viewer.tree.TreePathFilteredTree; import eu.esdihumboldt.hale.ui.util.viewer.tree.TreePathProviderAdapter; /** * Configuration page for feature chaining settings. * * <p> * This page is actually a stub whose only responsibility is to create an * adequate number of {@link ChainPage} child pages. * </p> * * @author Stefano Costa, GeoSolutions */ public class FeatureChainingConfigurationPage extends AbstractConfigurationPage<AbstractAppSchemaConfigurator, AppSchemaAlignmentExportWizard> { private final List<ChainPage> pages = new ArrayList<ChainPage>(); private final FeatureChaining featureChaining = new FeatureChaining(); private IPageChangingListener changeListener; private boolean goingBack = false; /** * Default constructor. */ public FeatureChainingConfigurationPage() { super("feature.chaining.conf"); setTitle("Configure feature chaining"); setPageComplete(false); } /** * @see eu.esdihumboldt.hale.ui.HaleWizardPage#dispose() */ @Override public void dispose() { if (changeListener != null) { IWizardContainer container = getContainer(); if (container instanceof WizardDialog) { ((WizardDialog) container).removePageChangingListener(changeListener); } } super.dispose(); } /** * @see eu.esdihumboldt.hale.ui.io.config.AbstractConfigurationPage#enable() */ @Override public void enable() { // nothing to do... yet? } /** * @see eu.esdihumboldt.hale.ui.io.config.AbstractConfigurationPage#disable() */ @Override public void disable() { // nothing to do... yet? } /** * @see eu.esdihumboldt.hale.ui.io.IOWizardPage#loadPreSelection(eu.esdihumboldt.hale.common.core.io.project.model.IOConfiguration) */ @Override public void loadPreSelection(IOConfiguration conf) { // TODO: I fear this method is never called... super.loadPreSelection(conf); FeatureChaining previousConf = conf.getProviderConfiguration() .get(AppSchemaIO.PARAM_CHAINING).as(FeatureChaining.class); if (previousConf != null && previousConf.getJoins().size() > 0) { featureChaining.replaceJoins(previousConf.getJoins()); } } /** * @see eu.esdihumboldt.hale.ui.io.IOWizardPage#updateConfiguration(eu.esdihumboldt.hale.common.core.io.IOProvider) */ @Override public boolean updateConfiguration(AbstractAppSchemaConfigurator provider) { provider.setParameter(AppSchemaIO.PARAM_CHAINING, new ComplexValue(featureChaining)); return true; } /** * @see eu.esdihumboldt.hale.ui.HaleWizardPage#onShowPage(boolean) */ @Override protected void onShowPage(boolean firstShow) { super.onShowPage(firstShow); if (firstShow) { for (ChainPage page : pages) page.dispose(); pages.clear(); AlignmentService alignmentService = HaleUI.getServiceProvider() .getService(AlignmentService.class); Alignment alignment = alignmentService.getAlignment(); int pageIdx = 0; Collection<? extends Cell> typeCells = alignment.getActiveTypeCells(); for (Cell typeCell : typeCells) { if (AppSchemaMappingUtils.isJoin(typeCell)) { JoinParameter joinParameter = getJoinParameter(typeCell); List<JoinCondition> conditions = getSortedJoinConditions(joinParameter); TypeEntityDefinition joinTarget = getTargetType(typeCell).getDefinition(); for (int i = 0; i < joinParameter.types.size() - 1; i++) { ChainPage chainPage = new ChainPage(pageIdx, typeCell.getId(), i, joinParameter.types, conditions, joinTarget); chainPage.setWizard(getWizard()); pages.add(chainPage); pageIdx++; } } } } setPageComplete(true); if (!goingBack) { getContainer().showPage(getNextPage()); } else { getContainer().showPage(getPreviousPage()); } } /** * @see org.eclipse.jface.wizard.WizardPage#getNextPage() */ @Override public IWizardPage getNextPage() { if (pages.size() > 0) return pages.get(0); else return super.getNextPage(); } /** * @see org.eclipse.jface.wizard.WizardPage#isPageComplete() */ @Override public boolean isPageComplete() { // may only return true, if this page with all sub pages is complete if (!super.isPageComplete()) return false; else for (ChainPage page : pages) if (!page.isPageComplete()) return false; return true; } /** * @see eu.esdihumboldt.hale.ui.HaleWizardPage#createContent(org.eclipse.swt.widgets.Composite) */ @Override protected void createContent(Composite page) { IWizardContainer container = getContainer(); if (container instanceof WizardDialog) { changeListener = new IPageChangingListener() { @Override public void handlePageChanging(PageChangingEvent event) { Object currentPage = event.getCurrentPage(); Object targetPage = event.getTargetPage(); if ((currentPage instanceof ChainPage || currentPage instanceof AppSchemaDataStoreConfigurationPage) && targetPage instanceof FeatureChainingConfigurationPage) { goingBack = true; } else if (currentPage instanceof IncludeSchemaConfigurationPage && targetPage instanceof FeatureChainingConfigurationPage) { goingBack = false; } } }; WizardDialog dialog = (WizardDialog) container; dialog.addPageChangingListener(changeListener); } else { changeListener = null; } } private class ChainPage extends HaleWizardPage<AbstractGenericFunctionWizard<?, ?>> { private final List<TypeEntityDefinition> joinTypes; private final TypeEntityDefinition joinTarget; private final int pageIdx; private final String joinCellId; private final int chainIdx; private final TypeEntityDefinition containerTypeSource; private final TypeEntityDefinition nestedTypeSource; private EntityDefinition containerTypeTarget; private PropertyEntityDefinition nestedTypeTarget; private final JoinCondition joinCondition; private final String message; private boolean uniqueMapping = false; // UI private TableViewer joinTypesViewer; private Button checkUniqueMapping; protected ChainPage(int pageIdx, String joinCellId, int chainIdx, List<TypeEntityDefinition> joinTypes, List<JoinCondition> joinConditions, TypeEntityDefinition joinTarget) { super("join-" + joinCellId + "; chain-" + chainIdx, "Chain " + AlignmentUtil.getTypeEntity(joinConditions.get(chainIdx).baseProperty) .getDefinition().getDisplayName() + " and " + AlignmentUtil.getTypeEntity(joinConditions.get(chainIdx).joinProperty) .getDefinition().getDisplayName(), null); this.joinCondition = joinConditions.get(chainIdx); this.message = "Please select target for nested source type " + AlignmentUtil .getTypeEntity(joinCondition.joinProperty).getDefinition().getDisplayName(); this.joinTypes = joinTypes; this.joinTarget = joinTarget; this.pageIdx = pageIdx; this.joinCellId = joinCellId; this.chainIdx = chainIdx; this.containerTypeSource = AlignmentUtil .getTypeEntity(joinConditions.get(chainIdx).baseProperty); this.nestedTypeSource = AlignmentUtil .getTypeEntity(joinConditions.get(chainIdx).joinProperty); setPageComplete(false); setMessage(); } private void setMessage() { if (!isPageComplete()) { setMessage(this.message, ERROR); } else { setMessage(""); } } /** * @see eu.esdihumboldt.hale.ui.HaleWizardPage#onShowPage(boolean) */ @Override protected void onShowPage(boolean firstShow) { super.onShowPage(firstShow); // can't reliably get the previous chain configuration from current // chain configuration, because the latter may not exist yet int previousChainIndex = joinTypes.indexOf(containerTypeSource) - 1; ChainConfiguration previousChainConf = (previousChainIndex >= 0) ? featureChaining.getChain(joinCellId, previousChainIndex) : null; ChainConfiguration chainConf = featureChaining.getChain(joinCellId, chainIdx); // set nested type target if (chainConf != null) { nestedTypeTarget = chainConf.getNestedTypeTarget(); uniqueMapping = (chainConf.getMappingName() != null && !chainConf.getMappingName().isEmpty()); checkUniqueMapping.setSelection(uniqueMapping); } else { nestedTypeTarget = null; uniqueMapping = false; checkUniqueMapping.setSelection(uniqueMapping); } // set container type target if (previousChainConf != null) { containerTypeTarget = previousChainConf.getNestedTypeTarget(); } else { containerTypeTarget = joinTarget; } joinTypesViewer.refresh(); } @Override public IWizardPage getNextPage() { if (pageIdx + 1 < pages.size()) return pages.get(pageIdx + 1); else return FeatureChainingConfigurationPage.super.getNextPage(); } /** * @see eu.esdihumboldt.hale.ui.HaleWizardPage#createContent(org.eclipse.swt.widgets.Composite) */ @Override protected void createContent(Composite page) { Composite main = new Composite(page, SWT.NONE); main.setLayout(new GridLayout(3, false)); joinTypesViewer = createJoinTypesViewer(main); createTargetTypeViewer(main); checkUniqueMapping = new Button(main, SWT.CHECK); checkUniqueMapping.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1)); checkUniqueMapping.setText("Generate unique mapping for nested target type"); checkUniqueMapping.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { updateConfiguration(); } }); } private TableViewer createJoinTypesViewer(Composite parent) { Composite tableParent = new Composite(parent, SWT.NONE); TableColumnLayout layout = new TableColumnLayout(); tableParent.setLayout(layout); GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 2); gridData.minimumHeight = 150; tableParent.setLayoutData(gridData); final TableViewer tableViewer = new TableViewer(tableParent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); tableViewer.getControl() .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 2)); tableViewer.setContentProvider(ArrayContentProvider.getInstance()); tableViewer.getTable().setHeaderVisible(true); tableViewer.getTable().setLinesVisible(true); // disable selection on table viewer tableViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent event) { if (!event.getSelection().isEmpty()) { tableViewer.setSelection(StructuredSelection.EMPTY); } } }); final DefinitionLabelProvider dlp = new DefinitionLabelProvider(tableViewer, true, true); TableViewerColumn typeColumn = new TableViewerColumn(tableViewer, SWT.NONE); layout.setColumnData(typeColumn.getColumn(), new ColumnWeightData(1, true)); typeColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { TypeEntityDefinition typeEntityDef = (TypeEntityDefinition) element; return dlp.getText(typeEntityDef); } @Override public Image getImage(Object element) { TypeEntityDefinition typeEntityDef = (TypeEntityDefinition) element; return dlp.getImage(typeEntityDef); } }); typeColumn.getColumn().setText("Source Type"); TableViewerColumn roleColumn = new TableViewerColumn(tableViewer, SWT.NONE); layout.setColumnData(roleColumn.getColumn(), new ColumnWeightData(2, true)); roleColumn.setLabelProvider(new RoleLabelProvider()); roleColumn.getColumn().setText("Role"); TableViewerColumn conditionColumn = new TableViewerColumn(tableViewer, SWT.NONE); layout.setColumnData(conditionColumn.getColumn(), new ColumnWeightData(2, true)); conditionColumn.setLabelProvider(new ConditionLabelProvider()); conditionColumn.getColumn().setText("Join Property"); TableViewerColumn targetColumn = new TableViewerColumn(tableViewer, SWT.NONE); layout.setColumnData(targetColumn.getColumn(), new ColumnWeightData(2, true)); targetColumn.setLabelProvider(new TargetLabelProvider()); targetColumn.getColumn().setText("Target Type"); tableViewer .setInput(new TypeEntityDefinition[] { containerTypeSource, nestedTypeSource }); return tableViewer; } private TreeViewer createTargetTypeViewer(Composite parent) { PatternFilter patternFilter = new SchemaPatternFilter(); patternFilter.setIncludeLeadingWildcard(true); final FilteredTree filteredTree = new TreePathFilteredTree(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER, patternFilter, true); GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 2); gridData.minimumHeight = 160; gridData.minimumWidth = 140; filteredTree.setLayoutData(gridData); TreeViewer viewer = filteredTree.getViewer(); viewer.setComparator(new DefinitionComparator()); EntityDefinitionService eds = PlatformUI.getWorkbench() .getService(EntityDefinitionService.class); viewer.setContentProvider(new TreePathProviderAdapter( new EntityTypeIterableContentProvider(eds, SchemaSpaceID.TARGET))); viewer.setLabelProvider(new StyledDefinitionLabelProvider(viewer)); viewer.setInput(Collections.singletonList(joinTarget)); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { TreeSelection selection = (TreeSelection) event.getSelection(); if (!selection.isEmpty() && selection.getFirstElement() instanceof PropertyEntityDefinition) { PropertyEntityDefinition selectedProperty = (PropertyEntityDefinition) selection .getFirstElement(); TypeDefinition selectedPropertyType = selectedProperty.getDefinition() .getPropertyType(); List<ChildContext> selectedPropertyPath = selectedProperty .getPropertyPath(); SchemaService schemaService = HaleUI.getServiceProvider() .getService(SchemaService.class); SchemaSpace targetSchema = schemaService.getSchemas(SchemaSpaceID.TARGET); List<ChildContext> containerPath = containerTypeTarget.getPropertyPath(); if (targetSchema.getMappingRelevantTypes().contains(selectedPropertyType) && isNested(containerPath, selectedPropertyPath)) { nestedTypeTarget = selectedProperty; setPageComplete(true); joinTypesViewer.refresh(); updateConfiguration(); return; } } nestedTypeTarget = null; setPageComplete(false); joinTypesViewer.refresh(); updateConfiguration(); } }); return viewer; } private void updateConfiguration() { ChainConfiguration conf = featureChaining.getChain(joinCellId, chainIdx); if (conf == null) { conf = new ChainConfiguration(); featureChaining.putChain(joinCellId, chainIdx, conf); } conf.setChainIndex(chainIdx); int containerTypeIdx = joinTypes.indexOf(containerTypeSource); // if container type is the first element in the list, no previous // chain exists conf.setPrevChainIndex(containerTypeIdx - 1); conf.setNestedTypeTarget(nestedTypeTarget); uniqueMapping = checkUniqueMapping.getSelection(); if (uniqueMapping) { conf.setMappingName(UUID.randomUUID().toString()); } else { conf.setMappingName(null); } setMessage(); } private class RoleLabelProvider extends ColumnLabelProvider { @Override public String getText(Object element) { TypeEntityDefinition typeEntityDef = (TypeEntityDefinition) element; return (typeEntityDef.equals(containerTypeSource)) ? "CONTAINER" : "NESTED"; } } private class ConditionLabelProvider extends ColumnLabelProvider { @Override public String getText(Object element) { TypeEntityDefinition typeEntityDef = (TypeEntityDefinition) element; PropertyEntityDefinition property = (typeEntityDef.equals(containerTypeSource)) ? joinCondition.baseProperty : joinCondition.joinProperty; return property.getDefinition().getDisplayName(); } } private class TargetLabelProvider extends StyledDefinitionLabelProvider { /** * Suppresses mandatory and cardinality attributes display */ public TargetLabelProvider() { super(new DefinitionLabelProvider(null, false, true), true); } /** * @see eu.esdihumboldt.hale.ui.common.definition.viewer.StyledDefinitionLabelProvider#extractElement(java.lang.Object) */ @Override protected Object extractElement(Object element) { TypeEntityDefinition typeEntityDef = (TypeEntityDefinition) element; return (typeEntityDef.equals(containerTypeSource)) ? containerTypeTarget : nestedTypeTarget; } } } }