/* * Copyright (c) 2012 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: * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.ui.functions.core; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.jface.fieldassist.IContentProposal; import org.eclipse.jface.fieldassist.IContentProposalProvider; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import eu.esdihumboldt.hale.common.align.extension.function.FunctionParameterDefinition; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.ChildContext; 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.ui.HaleWizardPage; 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.ParameterPage; import eu.esdihumboldt.hale.ui.service.project.ProjectVariablesContentProposalProvider; /** * Base parameter page for parameter pages that contain a listing of source * types which can be put together to a target value. * * @param <T> the type of the text field/editor * * @author Kai Schwierczek */ public abstract class SourceListParameterPage<T> extends HaleWizardPage<AbstractGenericFunctionWizard<?, ?>> implements ParameterPage, IContentProposalProvider { private String initialValue = ""; private T textField; private final List<EntityDefinition> variables = new ArrayList<EntityDefinition>(); private TableViewer varTable; private final List<IContentProposalProvider> additionalContentProposalProviders = new ArrayList<>(); private final ProjectVariablesContentProposalProvider projectVariablesProposalsProvider = new ProjectVariablesContentProposalProvider(); /** * @see HaleWizardPage#HaleWizardPage(String, String, ImageDescriptor) */ protected SourceListParameterPage(String pageName, String title, ImageDescriptor titleImage) { super(pageName, title, titleImage); } /** * @see HaleWizardPage#HaleWizardPage(String) */ protected SourceListParameterPage(String pageName) { super(pageName); } /** * Should return the parameter which should be configured using all source * properties. * * @return the parameter name */ protected abstract String getParameterName(); /** * Should return the name of the source property which should be used. * * @return the source property name */ protected abstract String getSourcePropertyName(); /** * Subclasses can configure the text field to for example add some * validation mechanism. * * @param textField the text field to configure */ protected void configure(T textField) { // default: do nothing } /** * @return the list of {@link EntityDefinition}s */ protected List<EntityDefinition> getVariables() { return variables; } /** * Adds an {@link IContentProposalProvider} to this parameter page that may * contribute to the content assistance. * * @param provider provider to add */ protected void addContentProposalProvider(IContentProposalProvider provider) { this.additionalContentProposalProviders.add(provider); } /** * This gets called for all variables.<br> * Subclasses can change how they are displayed here.<br> * The default format is like "part1.part2.name". * * @param variable the variable * @return the modified name */ protected String getVariableName(EntityDefinition variable) { if (variable.getPropertyPath() != null && !variable.getPropertyPath().isEmpty()) { List<String> names = new ArrayList<String>(); for (ChildContext context : variable.getPropertyPath()) { names.add(context.getChild().getName().getLocalPart()); } String longName = Joiner.on('.').join(names); return longName; } else return variable.getDefinition().getDisplayName(); } /** * This gets called, when the user chose other source properties. * * @param variables the new source properties */ protected void sourcePropertiesChanged(Iterable<EntityDefinition> variables) { // do nothing by default } /** * @see ParameterPage#setParameter(Set, ListMultimap) */ @Override public void setParameter(Set<FunctionParameterDefinition> params, ListMultimap<String, ParameterValue> initialValues) { for (FunctionParameterDefinition param : params) { if (param.getName().equals(getParameterName())) { String description = param.getDescription(); if (description != null) { setMessage(description); } String displayName = param.getDisplayName(); if (displayName != null) { setTitle(displayName); } break; } } if (initialValues != null) { List<ParameterValue> initialData = initialValues.get(getParameterName()); if (initialData.size() > 0) initialValue = initialData.get(0).as(String.class); } } /** * @see ParameterPage#getConfiguration() */ @Override public ListMultimap<String, ParameterValue> getConfiguration() { ListMultimap<String, ParameterValue> params = ArrayListMultimap.create(); params.put(getParameterName(), new ParameterValue(getText(textField))); return params; } /** * @see HaleWizardPage#onShowPage(boolean) */ @Override protected void onShowPage(boolean firstShow) { Cell cell = getWizard().getUnfinishedCell(); // update variables as they could have changed variables.clear(); List<? extends Entity> sourceEntities = cell.getSource().get(getSourcePropertyName()); for (Entity entity : sourceEntities) { variables.add(entity.getDefinition()); } Map<EntityDefinition, String> varsAndNames = determineDefaultVariableNames(variables); varTable.setInput(varsAndNames.entrySet()); // Update project variables content provider projectVariablesProposalsProvider.reload(); // inform subclasses sourcePropertiesChanged(varsAndNames.keySet()); ((Composite) getControl()).layout(); } /** * Determine the variable names from the corresponding entity definitions.<br> * <br> * The default implementation uses * {@link #getVariableName(EntityDefinition)} to determine the name for each * variable independently. * * @param variables the variables * @return the variables associated to the variable names to use */ protected Map<EntityDefinition, String> determineDefaultVariableNames( List<EntityDefinition> variables) { Map<EntityDefinition, String> result = new LinkedHashMap<EntityDefinition, String>(); for (EntityDefinition var : variables) { result.put(var, getVariableName(var)); } return result; } /** * @see HaleWizardPage#createContent(Composite) */ @Override protected void createContent(Composite page) { GridLayout layout = GridLayoutFactory.swtDefaults().create(); // Add margin to leave space for control decorations layout.marginLeft = 5; layout.marginRight = 5; page.setLayout(layout); // input field textField = createAndLayoutTextField(page); // let subclasses for example add validation configure(textField); addContentProposalProvider(projectVariablesProposalsProvider); setText(textField, initialValue); // variables Label label = new Label(page, SWT.NONE); label.setText("Available variables (double click to insert)"); label.setLayoutData(GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER) .indent(4, 12).create()); // variables table Composite tableComposite = new Composite(page, SWT.NONE); tableComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, false) .hint(SWT.DEFAULT, 100).create()); TableColumnLayout columnLayout = new TableColumnLayout(); tableComposite.setLayout(columnLayout); varTable = new TableViewer(tableComposite, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION); varTable.getTable().setHeaderVisible(true); TableViewerColumn entityColumn = new TableViewerColumn(varTable, SWT.NONE); columnLayout.setColumnData(entityColumn.getColumn(), new ColumnWeightData(2, true)); entityColumn.getColumn().setText("Entity"); varTable.setContentProvider(ArrayContentProvider.getInstance()); varTable.setLabelProvider(new StyledDefinitionLabelProvider(varTable) { @Override protected Object extractElement(Object element) { if (element instanceof Entry) { return ((Entry<?, ?>) element).getKey(); } return super.extractElement(element); } }); varTable.getTable().addMouseListener(new MouseAdapter() { /** * @see MouseAdapter#mouseDoubleClick(MouseEvent) */ @Override public void mouseDoubleClick(MouseEvent e) { ISelection sel = varTable.getSelection(); if (!sel.isEmpty() && sel instanceof IStructuredSelection) { Object selected = ((IStructuredSelection) sel).getFirstElement(); if (selected instanceof Entry) { selected = ((Entry<?, ?>) selected).getValue(); } insertTextAtCurrentPos(textField, selected.toString()); } } }); // variable name column TableViewerColumn varColumn = new TableViewerColumn(varTable, SWT.NONE); columnLayout.setColumnData(varColumn.getColumn(), new ColumnWeightData(1, true)); varColumn.getColumn().setText("Variable"); varColumn.setLabelProvider(new CellLabelProvider() { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); if (element instanceof Entry) { element = ((Entry<?, ?>) element).getValue(); } cell.setText(element.toString()); } }); } /** * Set the text of the text field to the given value. * * @param textField the text field * @param value the value to set as text */ protected abstract void setText(T textField, String value); /** * Get the current text of the text field. * * @param textField the text field * @return the current text of the text field */ protected abstract String getText(T textField); /** * Insert a given text at the current position of the given text field. * * @param textField the text field * @param insert the text to insert */ protected abstract void insertTextAtCurrentPos(T textField, String insert); /** * Create and text field and layout it. * * @param parent the parent composite, it has a one-column grid layout * * @return the created text field */ protected abstract T createAndLayoutTextField(Composite parent); /** * Get the text editor/field. * * @return the text field */ protected T getTextField() { return textField; } /** * @see org.eclipse.jface.fieldassist.IContentProposalProvider#getProposals(java.lang.String, * int) */ @Override public IContentProposal[] getProposals(final String contents, final int position) { final List<IContentProposal> proposals = new ArrayList<>(); variables.forEach(var -> proposals.add(createContentProposal(contents, position, var))); additionalContentProposalProviders.forEach(provider -> proposals .addAll(Arrays.asList(provider.getProposals(contents, position)))); return proposals.toArray(new IContentProposal[proposals.size()]); } /** * @param contents current contents of the input field * @param position current position inside the input field * @param entity entity to create {@link IContentProposal} for * @return {@link IContentProposal} for the given {@link EntityDefinition} */ protected IContentProposal createContentProposal(String contents, int position, EntityDefinition entity) { return new IContentProposal() { @Override public String getLabel() { return entity.getDefinition().getDisplayName(); } @Override public String getDescription() { return null; } @Override public int getCursorPosition() { return getContent().length(); } @Override public String getContent() { return getVariableName(entity); } }; } }