/** * Copyright (C) 2015 Valkyrie RCP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.valkyriercp.application.config.support; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.NoSuchMessageException; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.valkyriercp.application.config.ApplicationConfig; import org.valkyriercp.application.config.ApplicationObjectConfigurer; import org.valkyriercp.command.config.CommandButtonIconInfo; import org.valkyriercp.command.config.CommandButtonLabelInfo; import org.valkyriercp.command.config.CommandIconConfigurable; import org.valkyriercp.command.config.CommandLabelConfigurable; import org.valkyriercp.core.*; import org.valkyriercp.core.support.LabelInfo; import org.valkyriercp.image.IconSource; import org.valkyriercp.image.ImageSource; import org.valkyriercp.image.NoSuchImageResourceException; import org.valkyriercp.image.config.IconConfigurable; import org.valkyriercp.image.config.ImageConfigurable; import org.valkyriercp.security.SecurityController; import org.valkyriercp.security.SecurityControllerManager; import javax.swing.*; import java.awt.*; import java.util.Locale; /** * The default implementation of the {@link ApplicationObjectConfigurer} * interface. * * This class makes use of several application services in order to determine * the property values to be applied to objects being configured. For example, * some string properties will be retrieved from the application's message * resource bundle using a {@link org.springframework.context.MessageSource}. To configure an object with * images and icons, an {@link ImageSource} and {@link IconSource} respectively * will be used. Subclasses can modify this behaviour by overriding the * {@link #configure(Object, String)} method but it may be more convenient to * override some of the various methods that deal specificly with objects that * implement certain 'configurable' interfaces, such as * {@link LabelConfigurable} or {@link TitleConfigurable}. See the javadoc of * the {@link #configure(Object, String)} method for more details. * * * @author Keith Donald * @author Kevin Stembridge */ public class DefaultApplicationObjectConfigurer implements ApplicationObjectConfigurer /*, BeanPostProcessor */ { /** * The key fragment used to retrieve the <i>pressed</i> icon for a given * object. */ public static final String PRESSED_ICON_KEY = "pressedIcon"; /** * The key fragment used to retrieve the <i>disabled</i> icon for a given * object. */ public static final String DISABLED_ICON_KEY = "disabledIcon"; /** * The key fragment used to retrieve the <i>rollover</i> icon for a given * object. */ public static final String ROLLOVER_ICON_KEY = "rolloverIcon"; /** * The key fragment used to retrieve the <i>selected</i> icon for a given * object. */ public static final String SELECTED_ICON_KEY = "selectedIcon"; /** The key fragment used to retrieve the icon for a given object. */ public static final String ICON_KEY = "icon"; /** The key fragment used to retrieve the image for a given object. */ public static final String IMAGE_KEY = "image"; /** The key fragment used to retrieve the description for a given object. */ public static final String DESCRIPTION_KEY = "description"; /** The key fragment used to retrieve the caption for a given object. */ public static final String CAPTION_KEY = "caption"; /** The key fragment used to retrieve the title for a given object. */ public static final String TITLE_KEY = "title"; /** The key fragment used to retrieve the label for a given object. */ public static final String LABEL_KEY = "label"; /** Class logger, available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); private boolean loadOptionalIcons = true; private MessageSource messageSource; private ImageSource imageSource; private IconSource iconSource; private SecurityControllerManager securityControllerManager; @Autowired private ApplicationConfig applicationConfig; /** * Creates a new {@code DefaultApplicationObjectConfigurer} that will obtain * required services from the application services locator. */ public DefaultApplicationObjectConfigurer() { // do nothing } /** * Creates a new {@code DefaultApplicationObjectConfigurer} that will use * the given message source. Other application services will be retrieved * using the application services locator. * * @param messageSource The message source. May be null. */ public DefaultApplicationObjectConfigurer(MessageSource messageSource) { this(messageSource, null, null, null); } /** * Creates a new {@code DefaultApplicationObjectConfigurer} that will use * the given message and image sources. Other application services will be * retrieved using the application services locator. * * @param messageSource The message source. May be null. * @param imageSource The image source. May be null. */ public DefaultApplicationObjectConfigurer(MessageSource messageSource, ImageSource imageSource) { this(messageSource, imageSource, null, null); } /** * Creates a new {@code DefaultApplicationObjectConfigurer} that will use * the given message, image and icon sources. If any of these services are * null, they will be retrieved using the application services locator. * * @param messageSource The message source. May be null. * @param imageSource The image source. May be null. * @param iconSource The icon source. May be null. * @param securityControllerManager The security controller manager. May be * null. */ public DefaultApplicationObjectConfigurer(MessageSource messageSource, ImageSource imageSource, IconSource iconSource, SecurityControllerManager securityControllerManager) { this.messageSource = messageSource; this.imageSource = imageSource; this.iconSource = iconSource; this.securityControllerManager = securityControllerManager; } /** * Sets the flag that determines if optional icons will be loaded for any * {@link CommandIconConfigurable} objects that are configured by this * instance. The default is true. * * @param loadOptionalIcons The flag to load optional options. */ public void setLoadOptionalIcons(boolean loadOptionalIcons) { this.loadOptionalIcons = loadOptionalIcons; } /** * Returns this instance's message source. If a source was not provided at * construction, it will be retrieved by the application services locator. * * @return The message source, never null. * */ protected MessageSource getMessageSource() { if (messageSource == null) { messageSource = applicationConfig.messageSource(); } return messageSource; } /** * Returns this instance's icon source. If a source was not provided at * construction, it will be retrieved by the application services locator. * * @return The icon source, never null. */ protected IconSource getIconSource() { if (iconSource == null) { iconSource = applicationConfig.iconSource(); } return iconSource; } /** * Returns this instance's image source. If a source was not provided at * construction, it will be retrieved by the application services locator. * * @return The image source, never null. */ protected ImageSource getImageSource() { if (imageSource == null) { imageSource = applicationConfig.imageSource(); } return imageSource; } /** * Returns this instance's security controller manager. If the security * manager was not provided at construction, it will be retrieved by the * application services locator. * * @return The security controller manager, never null. */ protected SecurityControllerManager getSecurityControllerManager() { if (securityControllerManager == null) { securityControllerManager = applicationConfig.securityControllerManager(); } return securityControllerManager; } /** * Configures the given object according to the interfaces that it * implements. * * <p> * This implementation forwards the object to the following overridable * methods in the order listed. Subclasses can use these methods as hooks to * modify the default configuration behaviour without having to override * this method entirely. * * <ul> * <li>{@link #configureTitle(TitleConfigurable, String)}</li> * <li>{@link #configureLabel(LabelConfigurable, String)}</li> * <li>{@link #configureCommandLabel(CommandLabelConfigurable, String)}</li> * <li>{@link #configureDescription(DescriptionConfigurable, String)}</li> * <li>{@link #configureImage(ImageConfigurable, String)}</li> * <li>{@link #configureIcon(IconConfigurable, String)}</li> * <li>{@link #configureCommandIcons(CommandIconConfigurable, String)}</li> * <li>{@link #configureSecurityController(Secured, String)}</li> * </ul> * </p> * * @param object The object to be configured. May be null. * @param objectName The name for the object, expected to be unique within * the application. If {@code object} is not null, then {@code objectName} * must also be non-null. * * @throws IllegalArgumentException if {@code object} is not null, but * {@code objectName} is null. * */ public void configure(Object object, String objectName) { if (object == null) { logger.debug("object to be configured is null"); return; } Assert.notNull(objectName, "objectName"); if (object instanceof TitleConfigurable) { configureTitle((TitleConfigurable) object, objectName); } if (object instanceof LabelConfigurable) { configureLabel((LabelConfigurable) object, objectName); } if (object instanceof ColorConfigurable) { configureColor((ColorConfigurable)object, objectName); } if (object instanceof CommandLabelConfigurable) { configureCommandLabel((CommandLabelConfigurable) object, objectName); } if (object instanceof DescriptionConfigurable) { configureDescription((DescriptionConfigurable) object, objectName); } if (object instanceof ImageConfigurable) { configureImage((ImageConfigurable) object, objectName); } if (object instanceof IconConfigurable) { configureIcon((IconConfigurable) object, objectName); } if (object instanceof CommandIconConfigurable) { configureCommandIcons((CommandIconConfigurable) object, objectName); } if (object instanceof Secured) { configureSecurityController((Secured) object, objectName); } } /** * Sets the title of the given object. The title is loaded from this * instance's {@link MessageSource} using a message code in the format * * <pre> * <objectName>.title * </pre> * * @param configurable The object to be configured. Must not be null. * @param objectName The name of the configurable object, unique within the * application. Must not be null. * * @throws IllegalArgumentException if either argument is null. */ protected void configureTitle(TitleConfigurable configurable, String objectName) { Assert.notNull(configurable, "configurable"); Assert.notNull(objectName, "objectName"); String title = loadMessage(objectName + "." + TITLE_KEY); if (StringUtils.hasText(title)) { configurable.setTitle(title); } } /** * Sets the {@link LabelInfo} of the given object. The label info is created * after loading the encoded label string from this instance's * {@link MessageSource} using a message code in the format * * <pre> * <objectName>.label * </pre> * * @param configurable The object to be configured. Must not be null. * @param objectName The name of the configurable object, unique within the * application. Must not be null. * * @throws IllegalArgumentException if either argument is null. */ protected void configureLabel(LabelConfigurable configurable, String objectName) { Assert.notNull(configurable, "configurable"); Assert.notNull(objectName, "objectName"); String labelStr = loadMessage(objectName + "." + LABEL_KEY); if (StringUtils.hasText(labelStr)) { LabelInfo labelInfo = LabelInfo.valueOf(labelStr); configurable.setLabelInfo(labelInfo); } } /** * Sets the foreground and background colours of the given object. * Use the following message codes: * * <pre> * <objectName>.foreground * <objectName>.background * </pre> * * @param configurable The object to be configured. Must not be null. * @param objectName The name of the configurable object, unique within the * application. Must not be null. * * @throws IllegalArgumentException if either argument is null. */ protected void configureColor(ColorConfigurable configurable, String objectName) { Assert.notNull(configurable, "configurable"); Assert.notNull(objectName, "objectName"); Color color = loadColor(objectName + ".foreground"); if (color != null) configurable.setForeground(color); color = loadColor(objectName + ".background"); if (color != null) configurable.setBackground(color); } /** * Sets the {@link CommandButtonLabelInfo} of the given object. The label * info is created after loading the encoded label string from this * instance's {@link MessageSource} using a message code in the format * * <pre> * <objectName>.label * </pre> * * @param configurable The object to be configured. Must not be null. * @param objectName The name of the configurable object, unique within the * application. Must not be null. * * @throws IllegalArgumentException if either argument is null. */ protected void configureCommandLabel(CommandLabelConfigurable configurable, String objectName) { Assert.notNull(configurable, "configurable"); Assert.notNull(objectName, "objectName"); String labelStr = loadMessage(objectName + "." + LABEL_KEY); if (StringUtils.hasText(labelStr)) { CommandButtonLabelInfo labelInfo = CommandButtonLabelInfo.valueOf(labelStr); configurable.setLabelInfo(labelInfo); } } /** * Sets the description and caption of the given object. These values are * loaded from this instance's {@link MessageSource} using message codes in * the format * * <pre> * <objectName>.description * </pre> * * and * * <pre> * <objectName>.caption * </pre> * * respectively. * * @param configurable The object to be configured. Must not be null. * @param objectName The name of the configurable object, unique within the * application. Must not be null. * * @throws IllegalArgumentException if either argument is null. */ protected void configureDescription(DescriptionConfigurable configurable, String objectName) { Assert.notNull(configurable, "configurable"); Assert.notNull(objectName, "objectName"); String caption = loadMessage(objectName + "." + CAPTION_KEY); if (StringUtils.hasText(caption)) { configurable.setCaption(caption); } String description = loadMessage(objectName + "." + DESCRIPTION_KEY); if (StringUtils.hasText(description)) { configurable.setDescription(description); } } /** * Sets the image of the given object. The image is loaded from this * instance's {@link ImageSource} using a key in the format * * <pre> * <objectName>.image * </pre> * * If the image source cannot find an image under that key, the object's * image will be set to null. * * @param configurable The object to be configured. Must not be null. * @param objectName The name of the configurable object, unique within the * application. Must not be null. * * @throws IllegalArgumentException if either argument is null. */ protected void configureImage(ImageConfigurable configurable, String objectName) { Assert.notNull(configurable, "configurable"); Assert.notNull(objectName, "objectName"); Image image = loadImage(objectName, IMAGE_KEY); if (image != null) { configurable.setImage(image); } } /** * Sets the icon of the given object. The icon is loaded from this * instance's {@link IconSource} using a key in the format * * <pre> * <objectName>.icon * </pre> * * If the icon source cannot find an icon under that key, the object's icon * will be set to null. * * @param configurable The object to be configured. Must not be null. * @param objectName The name of the configurable object, unique within the * application. Must not be null. * * @throws IllegalArgumentException if either argument is null. */ protected void configureIcon(IconConfigurable configurable, String objectName) { Assert.notNull(configurable, "configurable"); Assert.notNull(objectName, "objectName"); Icon icon = loadIcon(objectName, ICON_KEY); if (icon != null) { configurable.setIcon(icon); } } /** * Sets the icons of the given object. * * <p> * The icons are loaded from this instance's {@link IconSource}. using a * key in the format * </p> * * <pre> * <objectName>.someIconType * </pre> * * <p> * The keys used to retrieve large icons from the icon source are created by * concatenating the given {@code objectName} with a dot (.), the text * 'large' and then an icon type like so: * </p> * * <pre> * <myObjectName>.large.someIconType * </pre> * * <p> * If the icon source cannot find an icon under that key, the object's icon * will be set to null. * </p> * * <p> * If the {@code loadOptionalIcons} flag is set to true (it is by default) * all the following icon types will be used. If the flag is false, only the * first will be used: * </p> * * <ul> * <li>{@value #ICON_KEY}</li> * <li>{@value #SELECTED_ICON_KEY}</li> * <li>{@value #ROLLOVER_ICON_KEY}</li> * <li>{@value #DISABLED_ICON_KEY}</li> * <li>{@value #PRESSED_ICON_KEY}</li> * </ul> * * @param configurable The object to be configured. Must not be null. * @param objectName The name of the configurable object, unique within the * application. Must not be null. * * @throws IllegalArgumentException if either argument is null. */ protected void configureCommandIcons(CommandIconConfigurable configurable, String objectName) { Assert.notNull(configurable, "configurable"); Assert.notNull(objectName, "objectName"); setIconInfo(configurable, objectName, false); setIconInfo(configurable, objectName, true); } /** * Associates the given object with a security controller if it implements * the {@link Secured} interface. * @param controllable The object to be configured. * @param objectName The name (id) of the object. * @throws org.springframework.beans.BeansException if a referenced security controller is not found * or is of the wrong type */ protected void configureSecurityController(Secured controllable, String objectName) { Assert.notNull(controllable, "controllable"); Assert.notNull(objectName, "objectName"); String controllerId = controllable.getSecurityControllerId(); if (controllerId != null) { // Find the referenced controller. if (logger.isDebugEnabled()) { logger.debug("Lookup SecurityController with id [" + controllerId + "]"); } SecurityController controller = getSecurityControllerManager().getSecurityController(controllerId); // And add the object to the controlled object set if (controller != null) { if (logger.isDebugEnabled()) { logger.debug("configuring SecurityControllable [" + objectName + "]; security controller id='" + controllerId + "'"); } controller.addControlledObject(controllable); } else { if (logger.isDebugEnabled()) { logger.debug("configuring SecurityControllable [" + objectName + "]; no security controller for id='" + controllerId + "'"); } } } else { if (logger.isDebugEnabled()) { logger.debug("configuring SecurityControllable [" + objectName + "]; no security controller Id specified"); } } } protected void setIconInfo(CommandIconConfigurable object, String objectName, boolean large) { Assert.notNull(object, "object"); Assert.notNull(objectName, "objectName"); Icon icon = loadIcon(objectName, ICON_KEY, large); if (icon == null) { return; } CommandButtonIconInfo iconInfo; if (loadOptionalIcons) { Icon selectedIcon = loadIcon(objectName, SELECTED_ICON_KEY, large); Icon rolloverIcon = loadIcon(objectName, ROLLOVER_ICON_KEY, large); Icon disabledIcon = loadIcon(objectName, DISABLED_ICON_KEY, large); Icon pressedIcon = loadIcon(objectName, PRESSED_ICON_KEY, large); iconInfo = new CommandButtonIconInfo(icon, selectedIcon, rolloverIcon, disabledIcon, pressedIcon); } else { iconInfo = new CommandButtonIconInfo(icon); } if (large) { object.setLargeIconInfo(iconInfo); } else { object.setIconInfo(iconInfo); } } /** * Configures the given object. * @see #configure(Object, String) */ // public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // configure(bean, beanName); // return bean; // } /** * Attempts to load a {@link Color} by decoding the message that is found by * looking up the given colorKey. Decoding is done by by * {@link Color#decode(String)}. * * @param colorKey The message code used to retrieve the colour code. * @return the decoded {@link Color} or <code>null</code> if no colour could * be decoded/found. */ private Color loadColor(String colorKey) { String colorCode = loadMessage(colorKey); if (colorCode == null) { return null; } try { return Color.decode(colorCode); } catch (NumberFormatException nfe) { if (logger.isWarnEnabled()) { logger.warn("Could not parse a valid Color from code [" + colorCode + "]. Ignoring and returning null."); } return null; } } /** * Attempts to load the message corresponding to the given message code * using this instance's {@link MessageSource} and locale. * * @param messageCode The message code that will be used to retrieve the * message. Must not be null. * @return The message for the given code, or null if the message code could * not be found. * * @throws IllegalArgumentException if {@code messageCode} is null. */ private String loadMessage(String messageCode) { Assert.notNull(messageCode, "messageCode"); if (logger.isDebugEnabled()) { logger.debug("Resolving label with code '" + messageCode + "'"); } try { return getMessageSource().getMessage(messageCode, null, getLocale()); } catch (NoSuchMessageException e) { if (logger.isInfoEnabled()) { logger.info("The message source is unable to find message code [" + messageCode + "]. Ignoring and returning null."); } return null; } } /** * Returns the system default locale. * * @return The system default locale, never null. */ protected Locale getLocale() { return Locale.getDefault(); } private Icon loadIcon(String objectName, String iconType) { return loadIcon(objectName, iconType, false); } private Icon loadIcon(String objectName, String iconType, boolean large) { String key = objectName + (large ? ".large." : ".") + iconType; return getIconSource().getIcon(key); } private Image loadImage(String objectName, String imageType) { String key = objectName + "." + imageType; try { if (logger.isDebugEnabled()) { logger.debug("Resolving optional image with code '" + key + "'"); } return getImageSource().getImage(key); } catch (NoSuchImageResourceException e) { if (logger.isInfoEnabled()) { logger.info("Labelable object's image '" + key + "' does not exist in image bundle; continuing..."); } return null; } } /** * A default implemenation, performing no operation. */ // public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // return bean; // } }