/* * Copyright (c) 2013 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.ui.functions.core; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; 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.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; 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.swt.widgets.Label; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import de.fhg.igd.slf4jplus.ALogger; import de.fhg.igd.slf4jplus.ALoggerFactory; import eu.esdihumboldt.hale.common.align.extension.function.FunctionUtil; import eu.esdihumboldt.hale.common.align.model.AlignmentUtil; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.Entity; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.ParameterValue; import eu.esdihumboldt.hale.common.align.model.functions.JoinFunction; 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.schema.SchemaSpaceID; import eu.esdihumboldt.hale.common.schema.model.ChildDefinition; import eu.esdihumboldt.hale.common.schema.model.Definition; import eu.esdihumboldt.hale.common.schema.model.DefinitionUtil; import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.common.schema.model.constraint.property.Reference; import eu.esdihumboldt.hale.common.schema.model.constraint.type.HasValueFlag; import eu.esdihumboldt.hale.common.schema.model.constraint.type.PrimaryKey; import eu.esdihumboldt.hale.ui.HaleUI; import eu.esdihumboldt.hale.ui.HaleWizardPage; import eu.esdihumboldt.hale.ui.common.CommonSharedImages; 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.function.generic.pages.AbstractParameterPage; import eu.esdihumboldt.hale.ui.functions.core.internal.CoreFunctionsUIPlugin; import eu.esdihumboldt.hale.ui.service.entity.EntityDefinitionService; import eu.esdihumboldt.hale.ui.service.entity.util.EntityTypeIterableContentProvider; import eu.esdihumboldt.hale.ui.util.viewer.tree.TreePathFilteredTree; import eu.esdihumboldt.hale.ui.util.viewer.tree.TreePathProviderAdapter; /** * Parameter page for the Join type transformation. * * @author Kai Schwierczek */ public class JoinParameterPage extends AbstractParameterPage implements JoinFunction { private static final ALogger log = ALoggerFactory.getLogger(JoinParameterPage.class); private final List<ConditionPage> pages = new ArrayList<ConditionPage>(); private List<TypeEntityDefinition> types = new ArrayList<TypeEntityDefinition>(); private TableViewer table; private Image upIcon; private Image downIcon; private Image equalsIcon; /** * Constructor. */ public JoinParameterPage() { super(FunctionUtil.getTypeFunction(ID, HaleUI.getServiceProvider()), "Please configure the join order"); setPageComplete(false); } /** * @see eu.esdihumboldt.hale.ui.function.generic.pages.ParameterPage#getConfiguration() */ @Override public ListMultimap<String, ParameterValue> getConfiguration() { ListMultimap<String, ParameterValue> result = ArrayListMultimap.create(1, 1); Set<JoinCondition> conditions = new HashSet<>(); for (ConditionPage page : pages) conditions.addAll(page.conditions); JoinParameter param = new JoinParameter(types, conditions); result.put(PARAMETER_JOIN, new ParameterValue(new ComplexValue(param))); return result; } /** * Creates a join parameter for the types up to <code>upToIndex</code>. * * @param upToIndex the type index up to which the parameter should be * created for * @return the current join parameter up to the specified index */ private JoinParameter createJoinParameter(int upToIndex) { Set<JoinCondition> conditions = new HashSet<>(); for (int i = 0; i < upToIndex; i++) conditions.addAll(pages.get(i).conditions); JoinParameter param = new JoinParameter(types.subList(0, upToIndex + 1), conditions); return param; } /** * @see eu.esdihumboldt.hale.ui.HaleWizardPage#createContent(org.eclipse.swt.widgets.Composite) */ @Override protected void createContent(Composite page) { page.setLayout(new GridLayout(2, false)); Label intro = new Label(page, SWT.NONE); intro.setText("Select the join order. Focus will happen on the topmost type."); intro.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, true, false, 2, 1)); table = new TableViewer(page, SWT.V_SCROLL | SWT.BORDER | SWT.SINGLE); table.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2)); table.setLabelProvider(new StyledDefinitionLabelProvider(table)); table.setContentProvider(ArrayContentProvider.getInstance()); table.setInput(types); final Button up = new Button(page, SWT.PUSH); final Button down = new Button(page, SWT.PUSH); upIcon = CoreFunctionsUIPlugin.getImageDescriptor("icons/nav_up.gif").createImage(); up.setImage(upIcon); up.setLayoutData(new GridData(SWT.CENTER, SWT.BEGINNING, false, false)); up.setEnabled(false); up.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { int index = getCurrentSelection(); if (index > 0) { swapTypes(index - 1, index); if (index - 1 == 0) up.setEnabled(false); down.setEnabled(true); } } }); downIcon = CoreFunctionsUIPlugin.getImageDescriptor("icons/nav_down.gif").createImage(); down.setImage(downIcon); down.setLayoutData(new GridData(SWT.CENTER, SWT.BEGINNING, false, false)); down.setEnabled(false); down.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { int index = getCurrentSelection(); if (index + 1 < types.size()) { swapTypes(index, index + 1); if (index + 2 == types.size()) down.setEnabled(false); up.setEnabled(true); } } }); table.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { int index = getCurrentSelection(); if (index < 0) { down.setEnabled(false); up.setEnabled(false); } else { down.setEnabled(index + 1 < types.size()); up.setEnabled(index > 0); } } }); } /** * Swaps the given types, their pages and validates all affected pages * conditions after the change. * * @param lowerTypeIndex the lower type index * @param higherTypeIndex the higher type index */ private void swapTypes(int lowerTypeIndex, int higherTypeIndex) { TypeEntityDefinition lowerType = types.get(lowerTypeIndex); types.set(lowerTypeIndex, types.get(higherTypeIndex)); types.set(higherTypeIndex, lowerType); table.refresh(); // swap pages accordingly int lowerPageIndex = lowerTypeIndex - 1; int higherPageIndex = higherTypeIndex - 1; if (lowerPageIndex != -1) { // swap the page positions together with their types // so it is possible to keep some conditions from before ConditionPage lowerPage = pages.get(lowerPageIndex); ConditionPage higherPage = pages.get(higherPageIndex); lowerPage.typeIndex = higherTypeIndex; pages.set(higherPageIndex, lowerPage); higherPage.typeIndex = lowerTypeIndex; pages.set(lowerPageIndex, higherPage); } // check and refresh pages in between for (ConditionPage page : pages.subList(Math.max(0, lowerPageIndex), higherPageIndex + 1)) page.checkAndRefresh(); getContainer().updateButtons(); } /** * Returns the currently selected index or -1. * * @return the currently selected index or -1 */ private int getCurrentSelection() { ISelection selection = table.getSelection(); if (selection.isEmpty()) return -1; else { IStructuredSelection sel = (IStructuredSelection) selection; TypeEntityDefinition type = (TypeEntityDefinition) sel.getFirstElement(); return types.indexOf(type); } } /** * @see eu.esdihumboldt.hale.ui.HaleWizardPage#onShowPage(boolean) */ @Override protected void onShowPage(boolean firstShow) { super.onShowPage(firstShow); Cell cell = getWizard().getUnfinishedCell(); List<? extends Entity> sourceEntities = cell.getSource().get(JOIN_TYPES); List<TypeEntityDefinition> types = new ArrayList<TypeEntityDefinition>(); Iterator<? extends Entity> iter = sourceEntities.iterator(); while (iter.hasNext()) types.add(AlignmentUtil.getTypeEntity(iter.next().getDefinition())); if (sameTypes(this.types, types)) return; if (containsDuplicateType(types)) { setPageComplete(false); setErrorMessage("The selected source types contain duplicates."); this.types.clear(); table.setInput(null); return; } else { setErrorMessage(null); } JoinParameter initialValue = null; if (firstShow && !getInitialValues().isEmpty()) { initialValue = getInitialValues().get(PARAMETER_JOIN).get(0).as(JoinParameter.class); if (initialValue != null && (initialValue.validate() != null || !sameTypes(types, initialValue.types))) initialValue = null; } for (ConditionPage page : pages) page.dispose(); pages.clear(); if (initialValue != null) { // use ordering of the initial value (needs to be modifiable) types = new ArrayList<>(initialValue.types); } this.types = types; if (table != null) table.setInput(types); for (int i = 1; i < types.size(); i++) { ConditionPage conditionPage = new ConditionPage(i); conditionPage.setWizard(getWizard()); pages.add(conditionPage); } if (initialValue != null) { // add initial conditions for (JoinCondition condition : initialValue.conditions) { TypeEntityDefinition joinType = AlignmentUtil.getTypeEntity(condition.joinProperty); int typeIndex = types.indexOf(joinType); int pageIndex = typeIndex - 1; pages.get(pageIndex).conditions.add(condition); pages.get(pageIndex).updateCompletionStatus(); } } // order is always valid, will trigger updateButtons setPageComplete(true); } /** * Returns whether the two lists contain the same types disregarding their * order. * * @param a the first list * @param b the second list * @return whether the two lists contain the same types disregarding their * order */ private boolean sameTypes(List<TypeEntityDefinition> a, List<TypeEntityDefinition> b) { if (a.size() != b.size()) return false; for (TypeEntityDefinition type : a) if (!b.contains(type)) return false; return true; } /** * Checks whether the given {@link Iterable} of type entity definitions * contains any type definition more than once. * * @param types the entity definitions to check * @return true, if there is a duplicate type definition. */ private boolean containsDuplicateType(Iterable<TypeEntityDefinition> types) { Set<TypeDefinition> typeDefs = new HashSet<>(); for (TypeEntityDefinition type : types) if (typeDefs.contains(type.getDefinition())) return true; else typeDefs.add(type.getDefinition()); return false; } /** * @see org.eclipse.jface.wizard.WizardPage#getNextPage() */ @Override public IWizardPage getNextPage() { if (pages.size() > 0) return pages.get(0); else return null; } /** * @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 (ConditionPage page : pages) if (!page.isPageComplete()) return false; return true; } /** * @see org.eclipse.jface.wizard.WizardPage#canFlipToNextPage() */ @Override public boolean canFlipToNextPage() { // should still be true, even if only this page is completed. return super.isPageComplete() && getNextPage() != null; } @Override public void dispose() { super.dispose(); if (upIcon != null) upIcon.dispose(); if (downIcon != null) downIcon.dispose(); if (equalsIcon != null) equalsIcon.dispose(); } @Override public String getHelpContext() { return "eu.esdihumboldt.hale.common.align.join_order"; } /** * Page for specifying the join conditions of a type */ private class ConditionPage extends HaleWizardPage<AbstractGenericFunctionWizard<?, ?>> { private int typeIndex; private final Set<JoinCondition> conditions = new HashSet<>(); private TreeViewer joinViewer; private Button addButton; private TreeViewer baseViewer; private TableViewer conditionViewer; private Label joinText; private final Map<JoinCondition, Button> removeConditionButtons = new HashMap<>(); protected ConditionPage(int typeIndex) { super("join" + typeIndex, "Join " + types.get(typeIndex).getDefinition().getDisplayName(), null); setDescription("Please select join conditions for type " + types.get(typeIndex).getDefinition().getDisplayName()); setPageComplete(false); this.typeIndex = typeIndex; } @Override public String getHelpContext() { return "eu.esdihumboldt.hale.common.align.join_condition"; } @Override protected void createContent(Composite page) { Composite main = new Composite(page, SWT.NONE); main.setLayout(new GridLayout(3, false)); joinViewer = createTypeViewer(main, Collections.singleton(types.get(typeIndex))); if (equalsIcon == null) equalsIcon = CoreFunctionsUIPlugin.getImageDescriptor("icons/equals.gif") .createImage(); addButton = new Button(main, SWT.PUSH); addButton.setImage(equalsIcon); addButton.setEnabled(false); addButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { addCondition(); super.widgetSelected(e); } }); baseViewer = createTypeViewer(main, types.subList(0, typeIndex)); joinText = new Label(main, SWT.NONE); joinText.setText( "Join type " + types.get(typeIndex).getDefinition().getDisplayName() + " on:"); joinText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); conditionViewer = createConditionViewer(main, conditions); } private TableViewer createConditionViewer(Composite parent, Collection<JoinCondition> input) { parent = new Composite(parent, SWT.NONE); TableColumnLayout layout = new TableColumnLayout(); parent.setLayout(layout); GridData gridData = new GridData(SWT.FILL, SWT.BOTTOM, true, true, 3, 1); gridData.minimumHeight = 80; parent.setLayoutData(gridData); TableViewer viewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); viewer.getTable().setHeaderVisible(true); viewer.getTable().setLinesVisible(true); viewer.setComparator(new DefinitionComparator() { @Override public int compare(Viewer viewer, Object e1, Object e2) { JoinCondition j1 = (JoinCondition) e1; JoinCondition j2 = (JoinCondition) e2; return super.compare(viewer, j1.joinProperty, j2.joinProperty); } }); viewer.setContentProvider(ArrayContentProvider.getInstance()); final DefinitionLabelProvider dlp = new DefinitionLabelProvider(viewer, true, true); viewer.getTable().addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { dlp.dispose(); } }); TableViewerColumn joinTypeColumn = new TableViewerColumn(viewer, SWT.NONE); layout.setColumnData(joinTypeColumn.getColumn(), new ColumnWeightData(1, true)); joinTypeColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { JoinCondition condition = (JoinCondition) element; return dlp.getText(AlignmentUtil.getTypeEntity(condition.joinProperty)); } @Override public Image getImage(Object element) { JoinCondition condition = (JoinCondition) element; return dlp.getImage(AlignmentUtil.getTypeEntity(condition.joinProperty)); } }); TableViewerColumn joinPropertyColumn = new TableViewerColumn(viewer, SWT.NONE); layout.setColumnData(joinPropertyColumn.getColumn(), new ColumnWeightData(2, true)); joinPropertyColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { JoinCondition condition = (JoinCondition) element; return dlp.getText(condition.joinProperty); } @Override public Image getImage(Object element) { JoinCondition condition = (JoinCondition) element; return dlp.getImage(condition.joinProperty); } }); TableViewerColumn equalColumn = new TableViewerColumn(viewer, SWT.NONE); layout.setColumnData(equalColumn.getColumn(), new ColumnWeightData(0, false)); equalColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { return "="; } }); equalColumn.getColumn().setWidth(20); TableViewerColumn baseTypeColumn = new TableViewerColumn(viewer, SWT.NONE); layout.setColumnData(baseTypeColumn.getColumn(), new ColumnWeightData(1, true)); baseTypeColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { JoinCondition condition = (JoinCondition) element; return dlp.getText(AlignmentUtil.getTypeEntity(condition.baseProperty)); } @Override public Image getImage(Object element) { JoinCondition condition = (JoinCondition) element; return dlp.getImage(AlignmentUtil.getTypeEntity(condition.baseProperty)); } }); TableViewerColumn basePropertyColumn = new TableViewerColumn(viewer, SWT.NONE); layout.setColumnData(basePropertyColumn.getColumn(), new ColumnWeightData(2, true)); basePropertyColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { JoinCondition condition = (JoinCondition) element; return dlp.getText(condition.baseProperty); } @Override public Image getImage(Object element) { JoinCondition condition = (JoinCondition) element; return dlp.getImage(condition.baseProperty); } }); TableViewerColumn deleteConditionColumn = new TableViewerColumn(viewer, SWT.NONE); layout.setColumnData(deleteConditionColumn.getColumn(), new ColumnWeightData(0, false)); deleteConditionColumn.setLabelProvider(new ColumnLabelProvider() { @Override public void update(ViewerCell cell) { TableItem item = (TableItem) cell.getItem(); final JoinCondition condition = (JoinCondition) cell.getElement(); Button button; if (removeConditionButtons.containsKey(condition)) button = removeConditionButtons.get(condition); else { button = new Button((Composite) cell.getViewerRow().getControl(), SWT.NONE); button.setImage(CommonSharedImages.getImageRegistry() .get(CommonSharedImages.IMG_REMOVE)); removeConditionButtons.put(condition, button); button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { removeCondition(condition, true); } }); } TableEditor editor = new TableEditor(item.getParent()); editor.grabHorizontal = true; editor.grabVertical = true; editor.setEditor(button, item, cell.getColumnIndex()); editor.layout(); } }); deleteConditionColumn.getColumn().setWidth(20); viewer.setInput(input); return viewer; } private TreeViewer createTypeViewer(Composite parent, Collection<TypeEntityDefinition> input) { 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); 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.SOURCE))); viewer.setLabelProvider(new StyledDefinitionLabelProvider(viewer)); viewer.setInput(input); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { updateButtonStatus(); } }); return viewer; } /** * Called if the ordering of pages changed, maybe even this page's type * changed. */ private void checkAndRefresh() { String typeDisplayName = types.get(typeIndex).getDefinition().getDisplayName(); setTitle("Join " + typeDisplayName); setDescription("Please select join conditions for type " + typeDisplayName); // may be called before the page control was created if (getControl() == null) return; joinText.setText("Join type " + typeDisplayName + " on:"); joinViewer.setInput(Collections.singleton(types.get(typeIndex))); joinViewer.refresh(); List<TypeEntityDefinition> baseTypes = types.subList(0, typeIndex); baseViewer.setInput(baseTypes); baseViewer.refresh(); for (JoinCondition condition : new ArrayList<>(conditions)) { TypeEntityDefinition joinType = AlignmentUtil.getTypeEntity(condition.joinProperty); TypeEntityDefinition baseType = AlignmentUtil.getTypeEntity(condition.baseProperty); if (!joinType.equals(types.get(typeIndex)) || !baseTypes.contains(baseType)) removeCondition(condition, false); } conditionViewer.refresh(); updateCompletionStatus(); } /** * Removes the specified condition and disposes of the remove button. * * @param condition the condition to remove * @param refresh whether to refresh the viewer and update completion * status or not */ private void removeCondition(JoinCondition condition, boolean refresh) { conditions.remove(condition); removeConditionButtons.get(condition).dispose(); removeConditionButtons.remove(condition); if (refresh) { conditionViewer.refresh(); updateCompletionStatus(); } } private void updateButtonStatus() { ISelection leftSelection = joinViewer.getSelection(); ISelection rightSelection = baseViewer.getSelection(); boolean enable = false; if (!leftSelection.isEmpty() && !rightSelection.isEmpty()) { Object leftElem = ((IStructuredSelection) leftSelection).getFirstElement(); Object rightElem = ((IStructuredSelection) rightSelection).getFirstElement(); EntityDefinition leftDef = (EntityDefinition) leftElem; EntityDefinition rightDef = (EntityDefinition) rightElem; if (leftDef instanceof PropertyEntityDefinition && rightDef instanceof PropertyEntityDefinition) { PropertyEntityDefinition leftProp = (PropertyEntityDefinition) leftDef; PropertyEntityDefinition rightProp = (PropertyEntityDefinition) rightDef; if (leftProp.getDefinition().getPropertyType().getConstraint(HasValueFlag.class) .isEnabled() && rightProp.getDefinition().getPropertyType() .getConstraint(HasValueFlag.class).isEnabled()) enable = true; } } addButton.setEnabled(enable); } private void addCondition() { // can only be called if selection is valid ISelection joinSelection = joinViewer.getSelection(); ISelection baseSelection = baseViewer.getSelection(); Object joinElem = ((IStructuredSelection) joinSelection).getFirstElement(); Object baseElem = ((IStructuredSelection) baseSelection).getFirstElement(); PropertyEntityDefinition joinDef = (PropertyEntityDefinition) joinElem; PropertyEntityDefinition baseDef = (PropertyEntityDefinition) baseElem; JoinCondition condition = new JoinCondition(baseDef, joinDef); if (!conditions.contains(condition)) { conditions.add(condition); conditionViewer.refresh(); } updateCompletionStatus(); } private void updateCompletionStatus() { boolean pageComplete = false; if (conditions.isEmpty()) setErrorMessage("Add at least one condition."); else { // Previous pages are valid, this page has at least one // condition. // If it has more than one, we need to check the dependency // chain. // For this purpose use the validate method of JoinParameter... if (conditions.size() > 1 && createJoinParameter(typeIndex).validate() != null) setErrorMessage( "Conditions depend on different types which do not depend on each other."); else { pageComplete = true; setErrorMessage(null); } } // after any condition changes also update following pages... // they can become invalid due to the removal of conditions, so it // has to happen here, and not onShowPage for (ConditionPage page : pages.subList(typeIndex, pages.size())) page.updateCompletionStatus(); // this call after the others to update the buttons, too setPageComplete(pageComplete); } @Override protected void onShowPage(boolean firstShow) { super.onShowPage(firstShow); // no initial data -> check Reference constraints if (conditions.isEmpty()) { boolean added = false; TypeEntityDefinition joinTypeEntity = types.get(typeIndex); List<TypeDefinition> baseTypes = new ArrayList<>(typeIndex); for (int i = 0; i < typeIndex; i++) baseTypes.add(types.get(i).getDefinition()); added |= addReferenceConditions(joinTypeEntity, types.subList(0, typeIndex), false); for (TypeEntityDefinition baseTypeEntity : types.subList(0, typeIndex)) added |= addReferenceConditions(baseTypeEntity, Collections.singletonList(joinTypeEntity), true); if (added) { conditionViewer.refresh(); updateCompletionStatus(); } } } private boolean addReferenceConditions(TypeEntityDefinition joinType, List<TypeEntityDefinition> baseTypes, boolean invert) { int size = baseTypes.size(); List<TypeDefinition> baseTypeDefs = new ArrayList<>(size); for (int i = 0; i < size; i++) baseTypeDefs.add(baseTypes.get(i).getDefinition()); boolean added = false; // only check direct children (->database) Collection<? extends ChildDefinition<?>> children = DefinitionUtil .getAllChildren(joinType.getDefinition()); // check all direct children for (ChildDefinition<?> child : children) { // only properties if (child.asProperty() == null) continue; PropertyDefinition property = child.asProperty(); Reference ref = property.getConstraint(Reference.class); Collection<? extends TypeDefinition> referencedTypes = ref.getReferencedTypes(); // only if they reference something if (referencedTypes == null || referencedTypes.isEmpty()) continue; // check all references for (TypeDefinition referencedType : referencedTypes) { int index = baseTypeDefs.indexOf(referencedType); // only those which occur in the base types if (index == -1) continue; PropertyEntityDefinition baseDef = getPrimaryKeyEntity(baseTypes.get(index)); // only those with a property primary key if (baseDef == null) continue; // create property entity definitions PropertyEntityDefinition joinDef = (PropertyEntityDefinition) AlignmentUtil .getChild(joinType, property.getName()); if (invert) { PropertyEntityDefinition tmp = joinDef; joinDef = baseDef; baseDef = tmp; } // add join condition conditions.add(new JoinCondition(baseDef, joinDef)); added = true; } } return added; } private List<ChildDefinition<?>> getPrimaryKeyPath(TypeDefinition type) { PrimaryKey key = type.getConstraint(PrimaryKey.class); List<ChildDefinition<?>> path = new ArrayList<>(); Definition<?> parent = type; List<QName> namePath = key.getPrimaryKeyPath(); for (QName name : namePath) { ChildDefinition<?> child = DefinitionUtil.getChild(parent, name); if (child == null) { log.error("Primary key property path could not be resolved in type " + type.getName()); return null; } path.add(child); parent = child; } if (key.hasPrimaryKey() && path.get(path.size() - 1) instanceof PropertyDefinition) return path; else return null; } private PropertyEntityDefinition getPrimaryKeyEntity(TypeEntityDefinition type) { List<ChildDefinition<?>> primaryKeyPath = getPrimaryKeyPath(type.getDefinition()); if (primaryKeyPath != null) return (PropertyEntityDefinition) AlignmentUtil.createEntityFromDefinitions( type.getType(), primaryKeyPath, type.getSchemaSpace(), type.getFilter()); else return null; } @Override public IWizardPage getNextPage() { int pageIndex = typeIndex - 1; if (pageIndex + 1 < pages.size()) return pages.get(pageIndex + 1); else return JoinParameterPage.super.getNextPage(); } } }