/* * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved. * * This file is part of the Jspresso framework. * * Jspresso is free software: you can redistribute it and/or modify * it 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. * * Jspresso is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Jspresso. If not, see <http://www.gnu.org/licenses/>. */ package org.jspresso.framework.binding; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Map; import gnu.trove.map.hash.THashMap; import org.jspresso.framework.util.event.IItemSelectable; import org.jspresso.framework.util.event.IItemSelectionListener; import org.jspresso.framework.util.event.ItemSelectionEvent; import org.jspresso.framework.util.event.ItemSelectionSupport; import org.jspresso.framework.util.event.ValueChangeEvent; import org.jspresso.framework.util.gui.Icon; import org.jspresso.framework.util.gui.IconProvider; /** * This is a simple connector implementation which allows the management of child * connectors. It can be used for straight view connector implementation. * * @author Vincent Vandenschrick */ public abstract class AbstractCompositeValueConnector extends AbstractValueConnector implements IRenderableCompositeValueConnector { private Map<String, IValueConnector> childConnectors; private Collection<String> childConnectorKeys; private String displayDescription; private Icon displayIcon; private String displayValue; private IconProvider iconImageURLProvider; private ItemSelectionSupport itemSelectionSupport; private String renderingChildConnectorId; private Object selectedItem; private boolean trackingChildrenSelection; /** * Constructs a new {@code AbstractCompositeValueConnector}. * * @param id * the connector identifier */ public AbstractCompositeValueConnector(String id) { super(id); trackingChildrenSelection = false; initChildStructureIfNecessary(); } private void initChildStructureIfNecessary() { if (childConnectors == null) { childConnectors = new THashMap<>(); childConnectorKeys = new ArrayList<>(); } } /** * {@inheritDoc} */ @Override public boolean areChildrenReadable() { return isReadable(); } /** * {@inheritDoc} */ @Override public AbstractCompositeValueConnector clone() { return clone(getId()); } /** * {@inheritDoc} */ @Override public AbstractCompositeValueConnector clone(String newConnectorId) { AbstractCompositeValueConnector clonedConnector = (AbstractCompositeValueConnector) super .clone(newConnectorId); clonedConnector.childConnectors = null; clonedConnector.childConnectorKeys = null; clonedConnector.itemSelectionSupport = null; for (String connectorKey : getChildConnectorKeys()) { clonedConnector.addChildConnector(connectorKey, getChildConnector(connectorKey).clone()); } return clonedConnector; } /** * {@inheritDoc} */ @Override public IValueConnector getChildConnector(String connectorKey) { if (childConnectors == null) { return null; } return childConnectors.get(connectorKey); } /** * {@inheritDoc} */ @Override public int getChildConnectorCount() { if (childConnectorKeys == null) { return 0; } return childConnectorKeys.size(); } /** * {@inheritDoc} */ @Override public Collection<String> getChildConnectorKeys() { if (childConnectorKeys == null) { return Collections.emptyList(); } return new ArrayList<>(childConnectorKeys); } /** * Gets the displayDescription. * * @return the displayDescription. */ @Override public String getDisplayDescription() { return displayDescription; } /** * Gets the static icon or uses the icon provider to compute it based on the * connector value. * <p> * {@inheritDoc} */ @Override public Icon getDisplayIcon() { Icon icon; if (iconImageURLProvider != null) { icon = iconImageURLProvider.getIconForObject(getConnectorValue()); } else { icon = displayIcon; } return icon; } /** * Gets the static display value or uses the rendering connector to compute * the string representation. * <p> * {@inheritDoc} */ @Override public String getDisplayValue() { IValueConnector renderingConnector = getRenderingConnector(); if (renderingConnector != null) { if (renderingConnector.getConnectorValue() != null) { return renderingConnector.getConnectorValue().toString(); } return ""; } // if (displayValue != null) { // return displayValue; // } // Object value = getConnectorValue(); // if (value != null) { // return value.toString(); // } return displayValue; } /** * {@inheritDoc} */ @Override public IValueConnector getRenderingConnector() { if (renderingChildConnectorId != null) { return getChildConnector(renderingChildConnectorId); } return null; } /** * {@inheritDoc} */ @Override public void readabilityChange() { super.writabilityChange(); for (String key : getChildConnectorKeys()) { getChildConnector(key).readabilityChange(); } } /** * Sets the displayDescription. * * @param displayDescription * the displayDescription to set. */ public void setDisplayDescription(String displayDescription) { this.displayDescription = displayDescription; } /** * Sets the static displayIcon. * * @param displayIcon * the displayIcon to set. */ public void setDisplayIcon(Icon displayIcon) { this.displayIcon = displayIcon; } /** * Sets the static displayValue. * * @param displayValue * the displayValue to set. */ public void setDisplayValue(String displayValue) { this.displayValue = displayValue; } /** * Sets the iconImageURLProvider. * * @param iconImageURLProvider * the iconImageURLProvider to set. */ public void setIconImageURLProvider(IconProvider iconImageURLProvider) { this.iconImageURLProvider = iconImageURLProvider; } /** * Sets the renderingChildConnectorId. * * @param renderingChildConnectorId * the renderingChildConnectorId to set. */ public void setRenderingChildConnectorId(String renderingChildConnectorId) { this.renderingChildConnectorId = renderingChildConnectorId; } /** * Uses the value to compute the string representation. * <p> * {@inheritDoc} */ @Override public String toString() { return getDisplayValue(); } /** * {@inheritDoc} */ @Override public void writabilityChange() { super.writabilityChange(); for (String key : getChildConnectorKeys()) { getChildConnector(key).writabilityChange(); } } /** * Adds a new child connector to this composite. The key used as storage key * is the child connector id. * * @param childConnector * the added connector. */ @Override public final void addChildConnector(IValueConnector childConnector) { addChildConnector(childConnector.getId(), childConnector); } /** * Adds a new child connector using a specified storage key. * * @param storageKey * the key to use to store the child connector. It may be different * from its id. * @param childConnector * the connector to be added as composite. */ @Override public void addChildConnector(String storageKey, IValueConnector childConnector) { initChildStructureIfNecessary(); childConnectors.put(storageKey, childConnector); childConnector.setParentConnector(this); childConnectorKeys.add(storageKey); } /** * Utility implementation to factorize method support. This should only be * used by subclasses which implement {@code IConnectorSelector}. * * @param listener * the listener to add. */ protected void implAddConnectorSelectionListener( IItemSelectionListener listener) { if (itemSelectionSupport == null) { itemSelectionSupport = new ItemSelectionSupport(); } itemSelectionSupport.addItemSelectionListener(listener); } /** * Utility implementation to factorize method support. This should only be * used by subclasses which implement {@code IConnectorSelector}. * * @param selectedConnector * the newly selected connector or null. */ protected void implFireSelectedConnectorChange( IValueConnector selectedConnector) { implFireSelectedItemChange(new ItemSelectionEvent(this, selectedConnector)); } /** * Utility implementation to factorize method support. This should only be * used by subclasses which implement {@code IItemSelectable}. * * @param evt * the item selection event to propagate. */ protected void implFireSelectedItemChange(ItemSelectionEvent evt) { selectedItem = evt.getSelectedItem(); if (itemSelectionSupport != null && (evt.getSource() == this || isTrackingChildrenSelection())) { itemSelectionSupport.fireSelectedConnectorChange(evt); } IValueConnector parentConnector = getParentConnector(); while (parentConnector != null && !(parentConnector instanceof IItemSelectable)) { parentConnector = parentConnector.getParentConnector(); } if (parentConnector != null) { ((IItemSelectable) parentConnector).fireSelectedItemChange(evt); } } /** * Utility implementation to factorize method support. This should only be * used by subclasses which implement {@code IItemSelectable}. * * @return the selected item. */ protected Object implGetSelectedItem() { return selectedItem; } /** * Utility implementation to factorize method support. This should only be * used by subclasses which implement {@code IConnectorSelector}. * * @param listener * the listener to remove. */ protected void implRemoveConnectorSelectionListener( IItemSelectionListener listener) { if (itemSelectionSupport != null) { itemSelectionSupport.removeConnectorSelectionListener(listener); } } /** * Utility implementation to factorize method support. This should only be * used by subclasses which implement {@code IConnectorSelector}. * * @param tracksChildren * the trackingChildrenSelection to set. */ protected void implSetTracksChildrenSelection(boolean tracksChildren) { this.trackingChildrenSelection = tracksChildren; } /** * Gets the trackingChildrenSelection. * * @return the trackingChildrenSelection. */ protected boolean isTrackingChildrenSelection() { return trackingChildrenSelection; } /** * {@inheritDoc} */ @Override public void removeChildConnector(String storageKey) { if (childConnectors != null) { childConnectors.remove(storageKey); childConnectorKeys.remove(storageKey); } } /** * {@inheritDoc} */ @Override protected void propagateRollback() { Object badValue = getConnectorValue(); super.propagateRollback(); if (badValue == null) { IValueConnector renderingConnector = getRenderingConnector(); if (renderingConnector instanceof AbstractValueConnector) { ((AbstractValueConnector) renderingConnector) .fireValueChange(new ValueChangeEvent(renderingConnector, null, renderingConnector.getConnectorValue())); } } } /** * {@inheritDoc} */ @Override public void recycle(IMvcBinder mvcBinder) { super.recycle(mvcBinder); // Keep displayIconImageUrl since it is set statically at creation time. So // it does not support recycling. // displayIconImageUrl = null; displayDescription = null; displayValue = null; } }