/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.gui.utils.common.configuration; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.views.properties.IPropertyDescriptor; import org.eclipse.ui.views.properties.PropertyDescriptor; /** * The {@link ITreeContentProvider} for the {@link ConfigurationViewer}. * * @author Christian Weiss */ public class ConfigurationViewerContentProvider implements ITreeContentProvider { /** The {@link ElementFactory} to create elements with. */ private final ElementFactory elementFactory = new ElementFactory(); /** The root element of the configuration tree. */ private Element root; /** The {@link Viewer}s this content provider is providing content for. */ private final Set<StructuredViewer> viewers = new HashSet<StructuredViewer>(); /** * Adds a {@link Viewer} to the set of instances to be notified upon model changes. * * @param viewer the {@link Viewer} */ public void addViewer(final StructuredViewer viewer) { viewers.add(viewer); } /** * Removes a {@link Viewer} from the set of instances to be notified upon model changes. * * @param viewer the {@link Viewer} */ public void removeViewer(final StructuredViewer viewer) { viewers.remove(viewer); } /** * Gets the elements. * * @param inputElement the input element * @return the elements {@inheritDoc} * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) */ @Override public Object[] getElements(final Object inputElement) { final PropertyDescriptor descriptor = new PropertyDescriptor("<root>", "<root>"); root = elementFactory.createElement(null, descriptor, inputElement); root.setOverrideValue(inputElement); return getChildren(root); } /** * Gets the parent. * * @param element the element * @return the parent {@inheritDoc} * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) */ @Override public Object getParent(final Object element) { final Element treeElement = (Element) element; return treeElement.getParent(); } /** * Checks for children. * * @param element the element * @return true, if successful {@inheritDoc} * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) */ @Override public boolean hasChildren(final Object element) { final Element treeElement = (Element) element; if (treeElement instanceof Leaf) { return false; } else { return ((Node) treeElement).hasChildren(); } } /** * Gets the children. * * @param parentElement the parent element * @return the children {@inheritDoc} * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) */ @Override public Object[] getChildren(final Object parentElement) { final Element treeElement = (Element) parentElement; if (treeElement instanceof Leaf) { return new Object[0]; } else { return ((Node) treeElement).getChildren(); } } /** * Dispose. * * {@inheritDoc} * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ @Override public void dispose() { // nothing to do } /** * Input changed. * * @param viewer the viewer * @param oldInput the old input * @param newInput the new input {@inheritDoc} * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, * java.lang.Object, java.lang.Object) */ @Override public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { } /** * Fire element changed. * * @param element the element */ private void fireElementChanged(final Element element) { for (final StructuredViewer viewer : viewers) { try { viewer.refresh(element); } catch (RuntimeException e) { e = null; } } } /** * An element in the tree. * * @author Christian Weiss */ /* default */abstract class Element { /** The parent. */ private final Node parent; /** The descriptor. */ private final IPropertyDescriptor descriptor; /** The override value. */ private Object overrideValue; /** * Instantiates a new element. * * @param parent the parent * @param descriptor the descriptor */ private Element(final Node parent, final IPropertyDescriptor descriptor) { this.parent = parent; this.descriptor = descriptor; } /** * Sets the override value. * * @param overrideValue the override value */ public void setOverrideValue(final Object overrideValue) { this.overrideValue = overrideValue; } /** * Initializes this {@link Element}. * */ protected void initialize() { if (parent == null) { return; } PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Element element = Element.this; fireElementChanged(element); } }; try { final Object bean = parent.getValue(); final Method addMethod = bean.getClass().getMethod("addPropertyChangeListener", String.class, PropertyChangeListener.class); addMethod.invoke(bean, descriptor.getId().toString(), listener); } catch (SecurityException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { e = null; } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } } /** * Returns the parent. * * @return the parent */ public Node getParent() { return parent; } /** * Returns the display name. * * @return the display name */ public String getDisplayName() { return descriptor.getDisplayName(); } /** * Returns the display value. * * @return the display value */ public String getDisplayValue() { return descriptor.getLabelProvider().getText(getValue()); } /** * Returns the description. * * @return the description */ public String getDescription() { return descriptor.getDescription(); } /** * Returns the {@link IPropertyDescriptor}. * * @return the {@link IPropertyDescriptor} */ public IPropertyDescriptor getPropertyDescriptor() { return descriptor; } /** * Returns the value. * * @return the value */ protected Object getValue() { if (overrideValue != null) { return overrideValue; } return parent.getValue(descriptor.getId()); } /** * Sets the value. * * @param value the new value */ protected void setValue(final Object value) { if (overrideValue != null) { throw new UnsupportedOperationException(); } parent.setValue(descriptor.getId(), value); } /** * Creates the property editor. * * @param editorParent the editor parent * @return the cell editor */ public CellEditor createPropertyEditor(final Composite editorParent) { return descriptor.createPropertyEditor(editorParent); } } /** * An element of the tree having no sub-elements, usually a simple property (scalar value). */ /* package */final class Leaf extends Element { /** * Instantiates a new leaf. * * @param parent the parent * @param descriptor the descriptor */ private Leaf(final Node parent, final IPropertyDescriptor descriptor) { super(parent, descriptor); } } /** * An element of the tree having sub-elements, usually a complex property (composite value). */ /* package */final class Node extends Element { /** The source. */ private final IConfigurationSource source; /** * Instantiates a new node. * * @param parent the parent * @param descriptor the descriptor * @param source the source */ private Node(final Node parent, final IPropertyDescriptor descriptor, final IConfigurationSource source) { super(parent, descriptor); this.source = source; } /** * Gets the display value. * * @return the display value {@inheritDoc} * @see de.rcenvironment.core.gui.utils.common.configuration.ConfigurationViewerContentProvider.Element#getDisplayValue() */ @Override public String getDisplayValue() { final Object editableValue = source.getEditableValue(); if (editableValue instanceof String) { return editableValue.toString(); } else { return super.getDisplayValue(); } } /** * Returns the value of the property with the given id. * * @param id the id * @return the value */ private Object getValue(final Object id) { return source.getPropertyValue(id); } /** * Sets the value of the property with the given id to the given value. * * @param id the id * @param value the value */ private void setValue(final Object id, final Object value) { source.setPropertyValue(id, value); } /** * Checks for children. * * @return true, if successful */ public boolean hasChildren() { return true; } /** * Returns the children. * * @return the children */ public Element[] getChildren() { final List<Element> children = new LinkedList<Element>(); final IPropertyDescriptor[] descriptors = source .getConfigurationPropertyDescriptors(); for (final IPropertyDescriptor descriptor : descriptors) { final Element child; Object value = source .getPropertyValue(descriptor.getId()); // wrap arrays if (value != null && value.getClass().getComponentType() != null) { value = new ArraySource<Object>() { @Override public Object[] getValue() { return (Object[]) source.getPropertyValue(descriptor.getId()); } @Override public void setValue(Object[] value) { source.setPropertyValue(descriptor.getId(), value); } }; } child = elementFactory.createElement(this, descriptor, value); if (child == null) { continue; } children.add(child); } Collections.sort(children, new Comparator<Element>() { @Override public int compare(Element o1, Element o2) { return o1.getDisplayName().toLowerCase() .compareTo(o2.getDisplayName().toLowerCase()); } }); return children.toArray(new Element[0]); } } /** * A factory to create {@link Element}s based on {@link IPropertyDescriptor}s of configuration * properties. */ protected final class ElementFactory { private ElementFactory() {} /** * Creates a new Element object. * * @param parent the parent * @param descriptor the descriptor * @param value the value * @return the element */ private Element createElement(final Node parent, final IPropertyDescriptor descriptor, final Object value) { final Element result; if (descriptor.getClass() != PropertyDescriptor.class) { result = new Leaf(parent, descriptor); } else { final IConfigurationSource source = (IConfigurationSource) AdapterManager .getInstance().getAdapter(value, IConfigurationSource.class); if (source == null) { result = new Leaf(parent, descriptor); } else { result = new Node(parent, descriptor, source); } } result.initialize(); return result; } } /** * The Interface ArraySource. * * @param <T> the generic type */ public interface ArraySource<T> { /** * Gets the value. * * @return the value */ T[] getValue(); /** * Sets the value. * * @param value the new value */ void setValue(T[] value); } }