/*
* 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:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.io.wfs.ui.types;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.xml.namespace.QName;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ICheckStateProvider;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import eu.esdihumboldt.hale.io.wfs.ui.capabilities.AbstractWFSCapabilitiesPage;
import eu.esdihumboldt.hale.ui.util.wizard.ConfigurationWizard;
import eu.esdihumboldt.hale.ui.util.wizard.ConfigurationWizardPage;
/**
* Page for specifying the capabilities URL (and loading the capabilities).
*
* @author Simon Templer
* @param <T> the configuration object type
*/
public abstract class AbstractFeatureTypesPage<T> extends ConfigurationWizardPage<T> {
private CheckboxTreeViewer viewer;
private ICheckStateProvider checkStateProvider;
private final Set<QName> selected = new HashSet<>();
private FeatureTypeTreeContentProvider contentProvider;
private final AbstractWFSCapabilitiesPage<T> capabilitiesPage;
private String lastCapUrl;
/**
* Constructor
*
* @param wizard the parent wizard
* @param capabilitiesPage the page querying the WFS capabilities
* @param message the page message
*/
public AbstractFeatureTypesPage(ConfigurationWizard<? extends T> wizard,
AbstractWFSCapabilitiesPage<T> capabilitiesPage, String message) {
super(wizard, "wfsFeatureTypes");
setTitle("WFS Feature types");
setMessage(message);
this.capabilitiesPage = capabilitiesPage;
}
@Override
protected void createContent(Composite parent) {
Composite page = new Composite(parent, SWT.NONE);
page.setLayout(new GridLayout(1, false));
// create filtered tree
PatternFilter patternFilter = new PatternFilter();
patternFilter.setIncludeLeadingWildcard(true);
FilteredTree tree = new FilteredTree(page, SWT.BORDER | SWT.CHECK | SWT.H_SCROLL
| SWT.V_SCROLL, patternFilter, true) {
@Override
protected TreeViewer doCreateTreeViewer(Composite parent, int style) {
return new CheckboxTreeViewer(parent, style);
}
};
// configure viewer
viewer = (CheckboxTreeViewer) tree.getViewer();
contentProvider = new FeatureTypeTreeContentProvider();
viewer.setContentProvider(contentProvider);
// viewer.setComparator(new DefinitionComparator());
viewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
Object doubleClicked = selection.getFirstElement();
if (doubleClicked instanceof String)
viewer.setExpandedState(doubleClicked, !viewer.getExpandedState(doubleClicked));
else {
boolean newState = !checkStateProvider.isChecked(doubleClicked);
viewer.setChecked(doubleClicked, newState);
checkStateOfTypeChanged((QName) doubleClicked, newState);
}
}
});
viewer.setLabelProvider(new FeatureTypeTreeLabelProvider());
// because elements filtered by FilteredTree lose their checked state:
checkStateProvider = new ICheckStateProvider() {
@Override
public boolean isGrayed(Object element) {
if (element instanceof String) {
Object[] children = contentProvider.getChildren(element);
boolean containsChecked = false;
boolean containsUnchecked = false;
for (Object child : children) {
if (isChecked(child))
containsChecked = true;
else
containsUnchecked = true;
if (containsChecked && containsUnchecked)
return true;
}
}
return false;
}
@Override
public boolean isChecked(Object element) {
if (element instanceof String) {
for (Object child : contentProvider.getChildren(element))
if (isChecked(child))
return true;
return false;
}
return selected.contains(element);
}
};
viewer.setCheckStateProvider(checkStateProvider);
viewer.addCheckStateListener(new ICheckStateListener() {
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
if (event.getElement() instanceof String) {
// update children
viewer.setGrayed(event.getElement(), false);
for (Object child : contentProvider.getChildren(event.getElement())) {
if (checkStateProvider.isChecked(child) != event.getChecked()) {
viewer.setChecked(child, event.getChecked());
checkStateOfTypeChanged((QName) child, event.getChecked());
}
}
viewer.setGrayed(event.getElement(),
checkStateProvider.isGrayed(event.getElement()));
// only two levels, no need to update any parents or
// children's children
}
else {
checkStateOfTypeChanged((QName) event.getElement(), event.getChecked());
}
}
});
setControl(page);
// initial update
updateState(selected);
}
@Override
protected void onShowPage() {
// set input to types from capabilities
Set<QName> types = capabilitiesPage.getCapabilities().getFeatureTypes().keySet();
types = filterTypes(types);
viewer.setInput(types);
String capUrl = capabilitiesPage.getCapabilitiesURL();
if (!Objects.equals(capUrl, lastCapUrl)) {
// capabilities changed
selected.clear();
selected.addAll(initialSelection(types));
for (QName sel : selected) {
viewer.expandToLevel(sel, 0);
updateParent(sel);
}
updateState(selected);
}
else {
// capabilities stayed the same
if (selected.retainAll(types)) {
updateState(selected);
}
}
lastCapUrl = capUrl;
}
/**
* Filter the types provided via the capabilities.
*
* @param types the feature type names
* @return the feature type names to display
*/
protected Set<QName> filterTypes(Set<QName> types) {
return types;
}
/**
* Determine the initial selection given the list of types.
*
* @param types the feature types names as stated in the capabilities
* @return the collection of names to be initially selected
*/
protected Collection<? extends QName> initialSelection(Set<QName> types) {
return Collections.emptySet();
}
private void checkStateOfTypeChanged(QName type, boolean checked) {
if (checked)
selected.add(type);
else
selected.remove(type);
updateParent(type);
updateState(selected);
}
private void updateParent(QName type) {
if (contentProvider != null && checkStateProvider != null) {
Object parent = contentProvider.getParent(type);
viewer.setGrayed(parent, checkStateProvider.isGrayed(parent));
viewer.setChecked(parent, checkStateProvider.isChecked(parent));
}
}
/**
* Update page state based on the selection.
*
* @param selected the selected types
*/
protected abstract void updateState(Set<QName> selected);
@Override
public boolean updateConfiguration(T configuration) {
return updateConfiguration(configuration, selected);
}
/**
* Update the configuration object based on the type selection.
*
* @param configuration the configuration object to update
* @param selected the selected types
* @return if the configuration could be successfully updated
*/
protected abstract boolean updateConfiguration(T configuration, Set<QName> selected);
}