/*
* 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.views.data.internal.filter;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.PlatformUI;
import eu.esdihumboldt.hale.common.instance.model.DataSet;
import eu.esdihumboldt.hale.common.instance.model.Filter;
import eu.esdihumboldt.hale.common.instance.model.Instance;
import eu.esdihumboldt.hale.common.instance.model.InstanceCollection;
import eu.esdihumboldt.hale.common.instance.model.InstanceMetadata;
import eu.esdihumboldt.hale.common.instance.model.MetaFilter;
import eu.esdihumboldt.hale.common.instance.model.ResourceIterator;
import eu.esdihumboldt.hale.common.schema.SchemaSpaceID;
import eu.esdihumboldt.hale.common.schema.model.Schema;
import eu.esdihumboldt.hale.common.schema.model.TypeDefinition;
import eu.esdihumboldt.hale.ui.common.definition.viewer.DefinitionComparator;
import eu.esdihumboldt.hale.ui.common.definition.viewer.DefinitionLabelProvider;
import eu.esdihumboldt.hale.ui.filter.TypeFilterField;
import eu.esdihumboldt.hale.ui.filter.cql.CQLFilterField;
import eu.esdihumboldt.hale.ui.service.instance.InstanceService;
import eu.esdihumboldt.hale.ui.service.instance.InstanceServiceAdapter;
import eu.esdihumboldt.hale.ui.service.instance.InstanceServiceListener;
import eu.esdihumboldt.hale.ui.service.schema.SchemaService;
import eu.esdihumboldt.hale.ui.service.schema.SchemaServiceListener;
import eu.esdihumboldt.hale.ui.views.data.internal.DataViewPlugin;
import eu.esdihumboldt.hale.ui.views.data.internal.Messages;
/**
* Selects filtered features
*
* @author Simon Templer
* @partner 01 / Fraunhofer Institute for Computer Graphics Research
*/
public class InstanceServiceSelector implements InstanceSelector {
// private static final ALogger log = ALoggerFactory.getLogger(InstanceServiceSelector.class);
/**
* Instance selector control
*/
private class InstanceSelectorControl extends Composite {
private final ComboViewer schemaSpaces;
private final ComboViewer typeDefinitions;
private final ComboViewer count;
final CQLFilterField filterField;
private Iterable<Instance> selection;
/*
* XXX There seems to be a minor (memory leak) with selectedType not
* being reset when the project is cleared, though this should be OK, as
* it will be replaced, when a schema is loaded.
*/
private TypeDefinition selectedType;
private final Image refreshImage;
private final SchemaServiceListener schemaListener;
private final InstanceServiceListener instanceListener;
/**
* @see Composite#Composite(Composite, int)
*/
public InstanceSelectorControl(Composite parent, int style) {
super(parent, style);
refreshImage = DataViewPlugin.getImageDescriptor("icons/refresh.gif").createImage(); //$NON-NLS-1$
GridLayout layout = new GridLayout((spaceID == null) ? (4) : (3), false);
layout.marginHeight = 2;
layout.marginWidth = 3;
setLayout(layout);
// schema type selector
if (spaceID == null) {
schemaSpaces = new ComboViewer(this, SWT.READ_ONLY);
schemaSpaces.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof SchemaSpaceID) {
switch ((SchemaSpaceID) element) {
case SOURCE:
return Messages.InstanceServiceFeatureSelector_SourceReturnText;
case TARGET:
return Messages.InstanceServiceFeatureSelector_TargetReturnText;
default:
return Messages.InstanceServiceFeatureSelector_defaultReturnText;
}
}
else {
return super.getText(element);
}
}
});
schemaSpaces.setContentProvider(ArrayContentProvider.getInstance());
schemaSpaces.setInput(new Object[] { SchemaSpaceID.SOURCE, SchemaSpaceID.TARGET });
schemaSpaces.setSelection(new StructuredSelection(SchemaSpaceID.SOURCE));
}
else {
schemaSpaces = null;
}
// feature type selector
typeDefinitions = new ComboViewer(this, SWT.READ_ONLY);
typeDefinitions.setContentProvider(ArrayContentProvider.getInstance());
typeDefinitions.setComparator(new DefinitionComparator());
typeDefinitions.setLabelProvider(new DefinitionLabelProvider(null));
typeDefinitions.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
updateSelection();
}
});
// filter field
filterField = new CQLFilterField((selectedType == null) ? (null) : (selectedType), this,
SWT.NONE, spaceID);
filterField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
filterField.addListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(TypeFilterField.PROPERTY_FILTER)) {
updateSelection();
}
}
});
// refresh button
/*
* XXX disabled for now - Button refresh = new Button(this,
* SWT.PUSH); refresh.setImage(refreshImage);
* refresh.setToolTipText("Refresh"); refresh.setLayoutData(new
* GridData(SWT.CENTER, SWT.CENTER, false, false));
* refresh.addSelectionListener(new SelectionAdapter() {
*
* @Override public void widgetSelected(SelectionEvent e) {
* updateSelection(); }
*
* });
*/
// max count selector
count = new ComboViewer(this, SWT.READ_ONLY);
count.setContentProvider(ArrayContentProvider.getInstance());
count.setInput(new Integer[] { Integer.valueOf(1), Integer.valueOf(2),
Integer.valueOf(3), Integer.valueOf(4), Integer.valueOf(5) });
count.setSelection(new StructuredSelection(Integer.valueOf(2)));
count.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
updateSelection();
}
});
updateTypesSelection();
if (schemaSpaces != null) {
schemaSpaces.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
updateTypesSelection();
}
});
}
// service listeners
SchemaService ss = PlatformUI.getWorkbench().getService(SchemaService.class);
ss.addSchemaServiceListener(schemaListener = new SchemaServiceListener() {
@Override
public void schemaAdded(SchemaSpaceID spaceID, Schema schema) {
final Display display = PlatformUI.getWorkbench().getDisplay();
display.syncExec(new Runnable() {
@Override
public void run() {
updateTypesSelection();
}
});
}
@Override
public void schemasCleared(SchemaSpaceID spaceID) {
final Display display = PlatformUI.getWorkbench().getDisplay();
display.syncExec(new Runnable() {
@Override
public void run() {
updateTypesSelection();
}
});
}
@Override
public void mappableTypesChanged(SchemaSpaceID spaceID,
Collection<? extends TypeDefinition> types) {
final Display display = PlatformUI.getWorkbench().getDisplay();
display.syncExec(new Runnable() {
@Override
public void run() {
updateTypesSelection();
}
});
}
});
InstanceService is = PlatformUI.getWorkbench().getService(InstanceService.class);
is.addListener(instanceListener = new InstanceServiceAdapter() {
@Override
public void datasetChanged(DataSet dataSet) {
final Display display = PlatformUI.getWorkbench().getDisplay();
display.syncExec(new Runnable() {
@Override
public void run() {
updateTypesSelection();
}
});
}
});
}
/**
* Update the feature types selection
*/
protected void updateTypesSelection() {
SchemaSpaceID space = getSchemaSpace();
TypeDefinition lastSelected = null;
ISelection lastSelection = typeDefinitions.getSelection();
if (!lastSelection.isEmpty() && lastSelection instanceof IStructuredSelection) {
lastSelected = (TypeDefinition) ((IStructuredSelection) lastSelection)
.getFirstElement();
}
DataSet dataset = (space == SchemaSpaceID.SOURCE) ? (DataSet.SOURCE)
: (DataSet.TRANSFORMED);
InstanceService is = PlatformUI.getWorkbench().getService(InstanceService.class);
// get instance types
List<TypeDefinition> filteredTypes = new ArrayList<TypeDefinition>(
is.getInstanceTypes(dataset));
if (filteredTypes.isEmpty()) {
// if there are no instances present, show all types
SchemaService ss = PlatformUI.getWorkbench().getService(SchemaService.class);
filteredTypes = new ArrayList<TypeDefinition>(
ss.getSchemas(space).getMappingRelevantTypes());
}
typeDefinitions.setInput(filteredTypes);
// select the previously selected type if possible
TypeDefinition typeToSelect = (filteredTypes.contains(lastSelected)) ? (lastSelected)
: (null);
// fallback selection
if (typeToSelect == null && !filteredTypes.isEmpty()) {
typeToSelect = filteredTypes.iterator().next();
}
if (typeToSelect != null) {
typeDefinitions.setSelection(new StructuredSelection(typeToSelect));
}
boolean enabled = !filteredTypes.isEmpty();
typeDefinitions.getControl().setEnabled(enabled);
count.getControl().setEnabled(enabled);
layout(true, true);
updateSelection();
}
/**
* Get the selected schema type
*
* @return the selected schema type
*/
private SchemaSpaceID getSchemaSpace() {
if (spaceID != null) {
return spaceID;
}
else {
return (SchemaSpaceID) ((IStructuredSelection) schemaSpaces.getSelection())
.getFirstElement();
}
}
/**
* Update the selection
*/
protected void updateSelection() {
if (!typeDefinitions.getSelection().isEmpty()) {
TypeDefinition type = (TypeDefinition) ((IStructuredSelection) typeDefinitions
.getSelection()).getFirstElement();
filterField.setType(type);
SchemaSpaceID space = getSchemaSpace();
Integer max = (Integer) ((IStructuredSelection) count.getSelection())
.getFirstElement();
InstanceService is = PlatformUI.getWorkbench().getService(InstanceService.class);
List<Instance> instanceList = new ArrayList<Instance>();
DataSet dataset = (space == SchemaSpaceID.SOURCE) ? (DataSet.SOURCE)
: (DataSet.TRANSFORMED);
Filter filter = null;
String filterExpression = filterField.getFilterExpression();
/*
* Custom filter handling.
*
* FIXME Ultimately this should be done by the filter field
* instead, which should be able to handle all kinds of
* registered filters (e.g. also Groovy).
*/
if (filterExpression.startsWith("id:")) {
// XXX meta ID "hack"
String metaFilter = filterExpression.substring("id:".length());
String[] values = metaFilter.split(",");
filter = new MetaFilter(type, InstanceMetadata.METADATA_ID,
new HashSet<>(Arrays.asList(values)));
}
else if (filterExpression.startsWith("source:")) {
// XXX meta source ID "hack"
String metaFilter = filterExpression.substring("source:".length());
String[] values = metaFilter.split(",");
filter = new MetaFilter(type, InstanceMetadata.METADATA_SOURCEID,
new HashSet<>(Arrays.asList(values)));
}
else {
filter = filterField.getFilter();
}
InstanceCollection instances = is.getInstances(dataset);
if (filter != null) {
instances = instances.select(filter);
}
ResourceIterator<Instance> it = instances.iterator();
try {
int num = 0;
while (it.hasNext() && num < max) {
Instance instance = it.next();
if (instance.getDefinition().equals(type)) {
instanceList.add(instance);
num++;
}
}
} finally {
it.close();
}
selection = instanceList;
selectedType = type;
}
else {
selection = null;
selectedType = null;
filterField.setType(null);
}
for (InstanceSelectionListener listener : listeners) {
listener.selectionChanged(selectedType, selection);
}
}
/**
* @see Widget#dispose()
*/
@Override
public void dispose() {
SchemaService ss = PlatformUI.getWorkbench().getService(SchemaService.class);
InstanceService is = PlatformUI.getWorkbench().getService(InstanceService.class);
ss.removeSchemaServiceListener(schemaListener);
is.removeListener(instanceListener);
refreshImage.dispose();
listeners.clear();
super.dispose();
}
}
private final Set<InstanceSelectionListener> listeners = new HashSet<InstanceSelectionListener>();
private InstanceSelectorControl current;
private final SchemaSpaceID spaceID;
/**
* Create an instance selector
*
* @param spaceID the fixed schema space ID or <code>null</code> to allow
* selecting the schema space
*/
public InstanceServiceSelector(SchemaSpaceID spaceID) {
super();
this.spaceID = spaceID;
}
/**
* @see InstanceSelector#addSelectionListener(InstanceSelectionListener)
*/
@Override
public void addSelectionListener(InstanceSelectionListener listener) {
listeners.add(listener);
if (current != null && !current.isDisposed()) {
listener.selectionChanged(current.selectedType, current.selection);
}
}
/**
* @see InstanceSelector#removeSelectionListener(InstanceSelectionListener)
*/
@Override
public void removeSelectionListener(InstanceSelectionListener listener) {
listeners.remove(listener);
}
/**
* @see InstanceSelector#createControl(Composite)
*/
@Override
public Control createControl(Composite parent) {
current = new InstanceSelectorControl(parent, SWT.NONE);
return current;
}
}