/*******************************************************************************
* Copyright (c) 2008 Dennis Schenk, Peter Siska.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Dennis Schenk - initial implementation
* Peter Siska - initial implementation
*******************************************************************************/
package ch.unibe.iam.scg.archie.ui;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.eclipse.swt.SWT;
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.Label;
import ch.elexis.core.ui.util.Log;
import ch.unibe.iam.scg.archie.ArchieActivator;
import ch.unibe.iam.scg.archie.annotations.GetProperty;
import ch.unibe.iam.scg.archie.annotations.SetProperty;
import ch.unibe.iam.scg.archie.model.AbstractDataProvider;
import ch.unibe.iam.scg.archie.model.RegexValidation;
import ch.unibe.iam.scg.archie.ui.widgets.AbstractWidget;
import ch.unibe.iam.scg.archie.ui.widgets.CheckboxWidget;
import ch.unibe.iam.scg.archie.ui.widgets.ComboWidget;
import ch.unibe.iam.scg.archie.ui.widgets.DateWidget;
import ch.unibe.iam.scg.archie.ui.widgets.NumericWidget;
import ch.unibe.iam.scg.archie.ui.widgets.TextWidget;
import ch.unibe.iam.scg.archie.ui.widgets.WidgetTypes;
import ch.unibe.iam.scg.archie.utils.ProviderHelper;
/**
* <p>
* A composite panel which contains all the parameter fields for a data
* provider. The parameters of a provider are set accordingly. Parameter fields
* and their content are determined at runtime through annotations.
* </p>
*
* $Id: ParametersPanel.java 764 2009-07-24 11:20:03Z peschehimself $
*
* @author Peter Siska
* @author Dennis Schenk
* @version $Rev: 764 $
*/
public class ParametersPanel extends Composite {
/**
* Map containing all text fields an their name. Used to feed the query with
* the user input.
*/
private Map<String, AbstractWidget> widgetMap;
/**
* Map containing the getter method names and their default values. We need
* this because of the control decorations that have to be set after the
* text fields have been created.
*/
private Map<String, Object> defaultValuesMap;
/**
* The query which is selected at the moment and will be configured
* according to the user input in this panel.
*/
private AbstractDataProvider provider;
/**
* @param parent
* @param style
*/
public ParametersPanel(final Composite parent, final int style) {
super(parent, style);
// define layout
GridLayout layout = new GridLayout();
layout.numColumns = 1;
layout.marginWidth = 0;
this.setLayout(layout);
}
/**
* @param provider
*/
public void updateParameterList(AbstractDataProvider provider) {
this.provider = provider; // the query which was selected.
// clear this composite
for (Control child : this.getChildren()) {
child.dispose();
}
// initialize an empty field map
this.widgetMap = new TreeMap<String, AbstractWidget>();
this.defaultValuesMap = new HashMap<String, Object>();
// populate again
this.createWidgets();
// re-show everything
this.layout();
// adjust label widths
// NOTE: Probably not so efficient, maybe refactor the abstract
// composites to only hold the text input fields and put all labels and
// abstract fields into one layout?
this.adjustLabelWidths();
// set default widget values
this.setDefaultValues();
}
/**
* Adjusts the width of the labels left to the text fields in widgets.
*/
private void adjustLabelWidths() {
// calculate max label width
int maxWidth = 0;
for (AbstractWidget field : this.widgetMap.values()) {
Label label = field.getLabel();
int width = label.getBounds().width;
maxWidth = (width > maxWidth) ? width : maxWidth;
}
// set all labels to that max width
for (AbstractWidget field : this.widgetMap.values()) {
Label label = field.getLabel();
GridData data = new GridData();
data.widthHint = maxWidth;
label.setLayoutData(data);
}
this.layout();
}
/**
* Sets the default values of the widgets.
*/
private void setDefaultValues() {
for (Entry<String, Object> name : this.defaultValuesMap.entrySet()) {
this.widgetMap.get(name.getKey()).setValue(name.getValue());
}
}
/**
* Create Parameter Fields. This method creates all the widgets based on the
* annotations of the currently selected provider.
*/
private void createWidgets() {
// get all getters
for (Method method : ProviderHelper.getGetterMethods(this.provider, true)) {
GetProperty getter = method.getAnnotation(GetProperty.class);
RegexValidation regex = null; // can be null
if (!getter.validationRegex().equals("") && !getter.validationMessage().equals("")) {
regex = new RegexValidation(getter.validationRegex(), getter.validationMessage());
}
// create the appropriate text field
AbstractWidget widget = this.createWidget(this, getter.name(), getter.widgetType(), regex,
getter.vendorClass());
// set a description if not empty
if (!getter.description().equals("")) {
widget.setDescription(getter.description());
}
/* ****************************************************************
* Get string array and set the items if we have a Combo, that is:
* ***************************************************************
*
* - if there are any items - if the widget is our combo widget - if
* the widget is not a custom vendor widget
*/
if (getter.items().length > 0 && widget instanceof ComboWidget && getter.widgetType() != WidgetTypes.VENDOR) {
((ComboWidget) widget).setItems(getter.items());
}
// put field and label title in the map
this.widgetMap.put(getter.name(), widget);
// store widget default values in a map
this.defaultValuesMap.put(getter.name(), ProviderHelper.getValue(method, this.provider));
}
}
/**
* Updates the provider parameters according to the user input in the fields
* in this panel.
*
* @throws Exception
*/
public void updateProviderParameters() throws Exception {
this.setProviderData();
}
/**
* Checks the validity of all input fields in this composite.
*
* @return true if all fields are valid, false else.
*/
public boolean allFieldsValid() {
for (Map.Entry<String, AbstractWidget> entry : this.widgetMap.entrySet()) {
if (!entry.getValue().isValid()) {
return false;
}
}
return true;
}
/**
* Enables or disables all controls in the <code>fieldMap</code> belonging
* to this <code>ParameterPanel</code>
*
* @param enabled
* true to enable, false to disable
*/
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (this.widgetMap != null) {
for (Map.Entry<String, AbstractWidget> entry : this.widgetMap.entrySet()) {
entry.getValue().setEnabled(enabled);
}
}
}
/**
* Creates a single widget based on the <code>fieldType</code> parameter
* with given labels and values as well as a validation regex.
*
* @param parent
* Composite container for the widget.
* @param label
* Label for the widget.
* @param widgetType
* Type of the widget to create.
* @param regex
* A regex validation object.
* @param vendorClass
* Class of a vendor specific widget implementation.
* @return An <code>AbstractWidget</code> object.
*/
private AbstractWidget createWidget(final Composite parent, final String label, WidgetTypes widgetType,
final RegexValidation regex, Class<?> vendorClass) {
switch (widgetType) {
case TEXT_DATE:
return new DateWidget(parent, SWT.NONE, label, regex);
case TEXT_NUMERIC:
return new NumericWidget(parent, SWT.NONE, label, regex);
case BUTTON_CHECKBOX:
return new CheckboxWidget(parent, SWT.NONE, label, regex);
case COMBO:
return new ComboWidget(parent, SWT.NONE, label, regex);
case VENDOR: // Vendor specific / custom widgets
return this.createVendorWidget(parent, label, widgetType, regex, vendorClass);
case TEXT:
default: // Text widget returned by default.
return new TextWidget(parent, SWT.NONE, label, regex);
}
}
/** Sets all fields via the meta model in the given query. */
private void setProviderData() throws Exception {
assert (this.provider != null);
this.setData(ProviderHelper.getSetterMethods(this.provider, true));
}
/**
* @param setterList
* @throws Exception
*/
private void setData(ArrayList<Method> setterList) throws Exception {
for (Method method : setterList) {
SetProperty setter = method.getAnnotation(SetProperty.class);
AbstractWidget field = this.widgetMap.get(setter.name());
Object value = field.getValue();
ProviderHelper.setValue(this.provider, method, value);
}
}
/**
* Creates a vendor widget object based on the given class. This method is
* used to instantiate custom widgets.
*
* @param parent
* Composite container for the widget.
* @param label
* Label for the widget.
* @param widgetType
* Type of the widget to create.
* @param regex
* A regex validation object.
* @param vendorClass
* Class of a vendor specific widget implementation.
* @return An <code>AbstractWidget</code> object.
* @return A vendor widget object, null else.
*/
@SuppressWarnings("unchecked")
private AbstractWidget createVendorWidget(final Composite parent, final String label, WidgetTypes widgetType,
final RegexValidation regex, Class<?> vendorClass) {
AbstractWidget widget = null;
Class<AbstractWidget> abstractWidgetClass = (Class<AbstractWidget>) vendorClass;
try {
widget = abstractWidgetClass.getConstructor(
new Class[] { Composite.class, int.class, String.class, RegexValidation.class }).newInstance(
parent, SWT.NONE, label, regex);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
// Log error as FATAL
if (widget == null) {
ArchieActivator.LOG.log(
"Could not create custom vendor widget. Widget class was: [" + vendorClass.getName() + "]",
Log.FATALS);
}
return widget;
}
}