com.anrisoftware.prefdialog.core.AbstractFieldComponent Maven / Gradle / Ivy
/*
* Copyright 2012-2016 Erwin Müller
*
* This file is part of prefdialog-core.
*
* prefdialog-core 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.
*
* prefdialog-core 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 General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with prefdialog-core. If not, see .
*/
package com.anrisoftware.prefdialog.core;
import static java.lang.String.format;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.io.Serializable;
import java.lang.reflect.AccessibleObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import javax.inject.Inject;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.ToolTipManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.anrisoftware.globalpom.mnemonic.Mnemonic;
import com.anrisoftware.globalpom.mnemonic.MnemonicFactory;
import com.anrisoftware.globalpom.reflection.annotations.AnnotationAccess;
import com.anrisoftware.globalpom.reflection.annotations.AnnotationAccessFactory;
import com.anrisoftware.globalpom.reflection.beans.BeanAccess;
import com.anrisoftware.globalpom.reflection.beans.BeanAccessFactory;
import com.anrisoftware.globalpom.reflection.beans.BeanFactory;
import com.anrisoftware.globalpom.textposition.TextPosition;
import com.anrisoftware.prefdialog.fields.FieldComponent;
import com.anrisoftware.resources.images.api.IconSize;
import com.anrisoftware.resources.images.api.Images;
import com.anrisoftware.resources.texts.api.Texts;
/**
* Sets the component and sets the enumerated attributes of the component.
*
* For the tool-tip to be set the component of this field must be of class
* {@link JComponent}.
*
*
* - name
* - title
* - show title flag
* - mnemonic
* - tool-tip
* - invalidText
* - read-only flag
* - width
* - height
* - title position
* - icon
* - show icon flag
* - icon size
* - locale
* - order
* - value
*
*
* @param
* the type of the component of this field. Must be of class
* {@link Component}.
*
* @author Erwin Mueller, [email protected]
* @since 1.0
*/
@SuppressWarnings("serial")
public abstract class AbstractFieldComponent
implements FieldComponent, Serializable {
private static final Class ANNOTATION_CLASS = com.anrisoftware.prefdialog.annotations.FieldComponent.class;
private static final String TITLE_ELEMENT = "title";
private static final String SHOW_TITLE_ELEMENT = "showTitle";
private static final String MNEMONIC_ELEMENT = "mnemonic";
private static final String TOOLTIP_ELEMENT = "toolTip";
private static final String READ_ONLY_ELEMENT = "readOnly";
private static final String WIDTH_ELEMENT = "width";
private static final String HEIGHT_ELEMENT = "height";
private static final String TITLE_POSITION_ELEMENT = "titlePosition";
private static final String ICON_ELEMENT = "icon";
private static final String ICON_SIZE_ELEMENT = "iconSize";
private static final String LOCALE_ELEMENT = "locale";
private static final String INVALID_TEXT_ELEMENT = "invalidText";
private static final String ORDER_ELEMENT = "order";
private final Object parentObject;
private final String fieldName;
private final List> childFields;
private transient VetoableChangeSupport vetoableSupport;
private transient PropertyChangeSupport propertySupport;
private transient MnemonicFactory mnemonicFactory;
private transient AnnotationAccess annotationAccess;
private transient BeanAccess beanAccess;
private transient BeanFactory beanFactory;
private AbstractFieldComponentLogger log;
private ComponentType component;
private String titleResource;
private String title;
private boolean showTitle;
private String mnemonicResource;
private Integer mnemonic;
private String toolTipResource;
private String toolTip;
private String invalidTextResource;
private String invalidText;
private boolean showToolTip;
private boolean readOnly;
private Number width;
private Number height;
private TextPosition titlePosition;
private String iconResource;
private Icon icon;
private IconSize iconSize;
private Locale locale;
private Texts texts;
private Images images;
private Object originalValue;
private Object value;
private Object oldValue;
private String name;
private int order;
private int mnemonicIndex;
/**
* Sets the component of this field.
*
* @param component
* the {@link Component}.
*
* @param parentObject
* the parent object of this field.
*
* @param fieldName
* the name of the field in the parent object.
*/
protected AbstractFieldComponent(ComponentType component,
Object parentObject, String fieldName) {
this.component = component;
this.parentObject = parentObject;
this.fieldName = fieldName;
this.childFields = new ArrayList>();
this.mnemonic = null;
this.mnemonicIndex = -1;
this.vetoableSupport = new VetoableChangeSupport(this);
this.propertySupport = new PropertyChangeSupport(this);
}
/**
* Setups the field component.
*/
@Inject
void setupAbstractFieldComponent(AbstractFieldComponentLogger logger,
BeanAccessFactory beanAccessFactory, BeanFactory beanFactory,
AnnotationAccessFactory annotationAccessFactory,
MnemonicFactory mnemonicFactory) {
this.beanAccess = createBeanAccess(beanAccessFactory);
this.annotationAccess = createAnnotationAccess(annotationAccessFactory);
this.beanFactory = beanFactory;
this.log = logger;
this.mnemonicFactory = mnemonicFactory;
setupField();
}
private AnnotationAccess createAnnotationAccess(
AnnotationAccessFactory annotationAccessFactory) {
return annotationAccessFactory.create(ANNOTATION_CLASS,
beanAccess.getGettterObject());
}
private BeanAccess createBeanAccess(BeanAccessFactory beanAccessFactory) {
return beanAccessFactory.create(fieldName, parentObject);
}
/**
* Returns the annotation access to access the elements of an annotation.
*
* @return the {@link AnnotationAccess}.
*/
protected AnnotationAccess getAnnotationAccess() {
return annotationAccess;
}
/**
* Returns the bean access to access the fields of a object.
*
* @return the {@link BeanAccess}.
*/
protected BeanAccess getBeanAccess() {
return beanAccess;
}
/**
* Returns the bean factory to create beans.
*
* @return the {@link BeanFactory}.
*/
protected BeanFactory getBeanFactory() {
return beanFactory;
}
private void setupField() {
setupName();
setupTitle();
setupShowTitle();
setupMnemonic();
setupToolTip();
setupInvalidText();
setupReadOnly();
setupWidth();
setupHeight();
setupTitlePosition();
setupIcon();
setupIconSize();
setupLocale();
setupOrder();
setupValue();
}
private void setupName() {
String name = fieldName;
setName(name);
}
private void setupTitle() {
String title = annotationAccess.getValue(TITLE_ELEMENT);
title = isEmpty(title) ? fieldName : title;
setTitle(title);
}
private void setupShowTitle() {
boolean show = annotationAccess.getValue(SHOW_TITLE_ELEMENT);
setShowTitle(show);
}
private void setupMnemonic() {
String string = annotationAccess.getValue(MNEMONIC_ELEMENT);
string = isEmpty(string) ? format("%s_mnemonic", fieldName) : string;
setMnemonicString(string);
}
private void setupToolTip() {
String text = annotationAccess.getValue(TOOLTIP_ELEMENT);
setToolTipText(text);
}
private void setupInvalidText() {
String text = annotationAccess.getValue(INVALID_TEXT_ELEMENT);
setInvalidText(text);
}
private void setupReadOnly() {
boolean readOnly = annotationAccess.getValue(READ_ONLY_ELEMENT);
setEnabled(!readOnly);
}
private void setupWidth() {
double width = annotationAccess.getValue(WIDTH_ELEMENT);
setWidth(width);
}
private void setupHeight() {
double height = annotationAccess.getValue(HEIGHT_ELEMENT);
setHeight(height);
}
private void setupTitlePosition() {
TextPosition position = annotationAccess
.getValue(TITLE_POSITION_ELEMENT);
setTitlePosition(position);
}
private void setupIcon() {
String name = annotationAccess.getValue(ICON_ELEMENT);
setIconResource(name);
}
private void setupIconSize() {
IconSize size = annotationAccess.getValue(ICON_SIZE_ELEMENT);
setIconSize(size);
}
private void setupLocale() {
String localeName = annotationAccess.getValue(LOCALE_ELEMENT);
Locale locale = isEmpty(localeName) ? Locale.getDefault() : new Locale(
localeName);
setLocale(locale);
}
private void setupOrder() {
int order = annotationAccess.getValue(ORDER_ELEMENT);
setOrder(order);
}
private void setupValue() {
Object value = beanAccess.getValue();
if (value == null) {
value = beanFactory.create(beanAccess.getType());
}
this.originalValue = value;
try {
setValue(value);
} catch (PropertyVetoException e) {
throw log.errorSetupValue(this, e, value);
}
}
/**
* Returns the field or getter method of this component.
*
* @return the {@link AccessibleObject}.
*/
public AccessibleObject getAccessibleObject() {
return beanAccess.getGettterObject();
}
/**
* Returns the parent object of this field.
*
* @return the parent {@link Object}.
*/
protected Object getParentObject() {
return parentObject;
}
/**
* Returns the field name.
*
* @return the {@link String} field name.
*/
protected String getFieldName() {
return fieldName;
}
@Override
public void setName(String name) {
log.checkName(this, name);
this.name = name;
component.setName(name);
log.nameSet(this, name);
}
@Override
public String getName() {
return name;
}
@Override
public void setTitle(String title) {
titleResource = title;
this.title = title;
updateTitleResource();
log.titleSet(this, title);
}
@Override
public String getTitle() {
return title;
}
@Override
public void setShowTitle(boolean show) {
this.showTitle = show;
log.showTitleSet(this, show);
}
@Override
public boolean isShowTitle() {
return showTitle;
}
@Override
public void setMnemonicString(String string) {
this.mnemonicResource = string;
Mnemonic mnemonic = mnemonicFactory.create(string);
Integer code = mnemonic(mnemonic);
if (code != null) {
setMnemonic(code);
setMnemonicIndex(mnemonic.getMnemonicIndex());
} else {
updateMnemonicResource();
}
log.mnemonicSet(this, string);
}
private Integer mnemonic(Mnemonic mnemonic) {
return mnemonic.isValid() ? mnemonic.getMnemonic() : null;
}
@Override
public void setMnemonic(int mnemonics) {
this.mnemonic = mnemonics;
}
@Override
public Integer getMnemonic() {
return mnemonic;
}
@Override
public void setMnemonicIndex(int index) {
this.mnemonicIndex = index;
}
@Override
public int getMnemonicIndex() {
return mnemonicIndex;
}
@Override
public void setToolTipText(String text) {
text = StringUtils.isEmpty(text) ? null : text;
toolTipResource = text;
toolTip = text;
updateToolTipResource();
setupToolTipText();
}
private void setupToolTipText() {
if (component instanceof JComponent) {
JComponent jcomponent = (JComponent) component;
jcomponent.setToolTipText(toolTip);
}
}
@Override
public String getToolTipText() {
return toolTip;
}
@Override
public void setInvalidText(String text) {
text = StringUtils.isEmpty(text) ? null : text;
invalidTextResource = text;
invalidText = text;
updateInvalidTextResource();
}
@Override
public String getInvalidText() {
return invalidText;
}
@Override
public void setShowToolTip(boolean show) {
if (show && !showToolTip) {
showToolTip();
} else if (!show && showToolTip) {
hideToolTip();
}
this.showToolTip = show;
log.showToolTipSet(this, show);
}
private void showToolTip() {
int id = 0;
long when = 0;
int modifiers = 0;
int x = 0;
int y = 0;
int clickCount = 0;
ToolTipManager.sharedInstance().mouseMoved(
new MouseEvent(component, id, when, modifiers, x, y,
clickCount, false));
}
private void hideToolTip() {
int id = 0;
long when = 0;
int modifiers = 0;
int x = 0;
int y = 0;
int clickCount = 0;
ToolTipManager.sharedInstance().mouseExited(
new MouseEvent(component, id, when, modifiers, x, y,
clickCount, false));
}
@Override
public void setEnabled(boolean enabled) {
component.setEnabled(enabled);
this.readOnly = !enabled;
for (FieldComponent> field : getFields()) {
field.setEnabled(enabled);
}
log.enabledSet(this, enabled);
}
@Override
public boolean isEnabled() {
return !readOnly;
}
@Override
public void setWidth(Number width) {
log.checkWidth(this, width);
this.width = width;
log.widthSet(this, width);
}
@Override
public Number getWidth() {
return width;
}
@Override
public void setHeight(Number height) {
log.checkHeight(this, height);
this.height = height;
log.heightSet(this, height);
}
@Override
public Number getHeight() {
return height;
}
@Override
public void setTitlePosition(TextPosition position) {
this.titlePosition = position;
log.titlePositionSet(this, position);
}
@Override
public TextPosition getTitlePosition() {
return titlePosition;
}
@Override
public void setIconResource(String name) {
this.iconResource = name;
if (isEmpty(iconResource)) {
icon = null;
} else {
updateIconResources();
}
log.iconResourceSet(this, name);
}
@Override
public void setIcon(Icon icon) {
iconResource = null;
this.icon = icon;
log.iconSet(this, icon);
}
@Override
public Icon getIcon() {
return icon;
}
@Override
public void setIconSize(IconSize size) {
this.iconSize = size;
updateIconResources();
for (FieldComponent> field : childFields) {
field.setIconSize(size);
}
log.iconSizeSet(this, iconSize);
}
@Override
public IconSize getIconSize() {
return iconSize;
}
@Override
public void setLocale(Locale locale) {
log.checkLocale(this, locale);
this.locale = locale;
component.setLocale(locale);
for (FieldComponent> field : childFields) {
field.setLocale(locale);
}
updateTextsResources();
updateIconResources();
log.localeSet(this, locale);
}
@Override
public Locale getLocale() {
return locale;
}
@Override
public void setOrder(int order) {
this.order = order;
log.orderSet(this, order);
}
@Override
public int getOrder() {
return order;
}
/**
* Sets the original value of the field. That is the value that is restored.
*
* @param value
* the original value {@link Object}.
*/
protected void setOriginalValue(Object value) {
this.originalValue = value;
}
/**
* Returns the original value of the field. That is the value that is
* restored.
*
* @return the original value {@link Object}.
*/
protected Object getOriginalValue() {
return originalValue;
}
@Override
public void setValue(Object value) throws PropertyVetoException {
if (this.value == value) {
return;
}
trySetValue(value);
vetoValueChanged(value);
changeValue(value);
fireValueChanged(value);
log.valueSet(this, value);
}
private void fireValueChanged(Object value) {
propertySupport.firePropertyChange(VALUE_PROPERTY, null, value);
}
private void vetoValueChanged(Object value) throws PropertyVetoException {
try {
vetoableSupport.fireVetoableChange(VALUE_PROPERTY, null, value);
} catch (PropertyVetoException e) {
trySetValue(oldValue);
throw e;
}
}
/**
* Sets the specified value as the new value without modifying the bean
* object field.
*
* @param value
* the {@link Object} value.
*/
protected void changeValue(Object value) {
oldValue = this.value;
this.value = value;
}
/**
* Sets the value to the bean object field.
*
* @param value
* the {@link Object} value.
*
* @throws PropertyVetoException
* if the value is unacceptable by the bean.
*/
protected void trySetValue(Object value) throws PropertyVetoException {
try {
beanAccess.setValue(value);
} catch (Exception e) {
throw log.errorTrySetValue(this, e, value, VALUE_PROPERTY);
}
}
@Override
public Object getValue() {
return value;
}
/**
* Returns the old value of this field. That is the value that was
* previously set.
*
* @return the old value {@link Object}.
*/
public Object getOldValue() {
return oldValue;
}
@Override
public void setImages(Images images) {
log.checkImagesResource(this, images);
this.images = images;
updateIconResources();
for (FieldComponent> child : childFields) {
child.setImages(images);
}
}
@Override
public Images getImages() {
return images;
}
private void updateIconResources() {
if (isEmpty(iconResource) || images == null) {
return;
}
icon = new ImageIcon(images.getResource(iconResource, getLocale(),
iconSize).getImage());
}
@Override
public void setTexts(Texts texts) {
log.checkTextsResource(this, texts);
this.texts = texts;
updateTextsResources();
for (FieldComponent> child : childFields) {
child.setTexts(texts);
}
}
@Override
public Texts getTexts() {
return texts;
}
private void updateTextsResources() {
updateTitleResource();
updateToolTipResource();
updateInvalidTextResource();
updateMnemonicResource();
}
private void updateToolTipResource() {
if (haveTextResource(toolTipResource)) {
toolTip = getTextResource(toolTipResource, toolTip);
setupToolTipText();
}
}
private void updateTitleResource() {
if (haveTextResource(titleResource)) {
title = getTextResource(titleResource, title);
}
}
private void updateInvalidTextResource() {
if (haveTextResource(invalidTextResource)) {
invalidText = getTextResource(invalidTextResource, invalidText);
}
}
private void updateMnemonicResource() {
if (haveTextResource(mnemonicResource)) {
String string = getTextResource(mnemonicResource, null);
if (string != null) {
setMnemonic(mnemonicFactory.create(string).getMnemonic());
setMnemonicIndex(mnemonicFactory.create(string)
.getMnemonicIndex());
}
}
}
/**
* Tests if the text resource is available.
*
* @param name
* the {@link String} name of the text resource.
*
* @return {@code true} if the name is not {@code null} or empty and the
* texts resources are available, {@code false} otherwise.
*/
protected boolean haveTextResource(String name) {
return !isEmpty(name) && texts != null;
}
/**
* Returns the text resource with the specified name and the current locale.
*
* @param name
* the {@link String} resource name.
*
* @param defaultText
* the default text for the resource.
*
*
* @return the {@link String} text resource or the default text if the text
* resource could not be found.
*
* @see #getLocale()
*/
protected String getTextResource(String name, String defaultText) {
try {
return texts.getResource(name, getLocale()).getText();
} catch (MissingResourceException e) {
log.textResourceMissing(this, name);
return defaultText;
}
}
/**
* Tests if the image resource is available.
*
* @param name
* the {@link String} name of the image resource.
*
* @return {@code true} if the name is not {@code null} or empty and the
* texts resources are available, {@code false} otherwise.
*/
protected boolean haveImageResource(String name) {
return !isEmpty(name) && images != null;
}
/**
* Returns the icon resource with the specified name and the current locale.
*
* @param name
* the {@link String} resource name.
*
* @param size
* the {@link IconSize} size of the icon.
*
* @param defaultText
* the default {@link Icon} icon for the resource.
*
* @return the {@link Icon} icon resource or the default icon if the image
* resource could not be found.
*
* @see #getLocale()
*/
protected Icon getIconResource(String name, IconSize size, Icon defaultIcon) {
try {
return new ImageIcon(images.getResource(name, getLocale(), size)
.getImage());
} catch (MissingResourceException e) {
log.iconResourceMissing(this, name);
return defaultIcon;
}
}
@Override
public void restoreInput() throws PropertyVetoException {
for (FieldComponent> component : childFields) {
component.restoreInput();
}
setValue(originalValue);
log.restoredInput(this);
}
@Override
public void setComponent(ComponentType component) {
log.checkComponent(this, component);
this.component = component;
setName(name);
setupToolTipText();
setEnabled(!readOnly);
if (locale != null) {
setLocale(locale);
}
}
@Override
public ComponentType getComponent() {
return component;
}
@Override
public Component getAWTComponent() {
return component;
}
@Override
public void addField(FieldComponent> field) {
log.checkField(this, field);
childFields.add(field);
Collections.sort(childFields, new FieldComponentComparator());
}
@Override
public > T findField(
String name) {
if (getName().equals(name)) {
return asField(this);
} else {
return findFieldRecursive(name);
}
}
@Override
public FieldComponent> getField(int index) {
return childFields.get(index);
}
private > T findFieldRecursive(
String name) {
T field = null;
for (FieldComponent> component : childFields) {
if (component.getName().equals(name)) {
field = asField(component);
} else {
field = component.findField(name);
}
if (field != null) {
return field;
}
}
return field;
}
@SuppressWarnings("unchecked")
private > T asField(
FieldComponent> component) {
return (T) component;
}
@Override
public int getFieldsCount() {
return childFields.size();
}
@Override
public Iterable> getFields() {
return new ArrayList>(childFields);
}
@Override
public void requestFocus() {
component.requestFocusInWindow();
}
@Override
public void addVetoableChangeListener(VetoableChangeListener listener) {
vetoableSupport.addVetoableChangeListener(listener);
for (FieldComponent> field : childFields) {
field.addVetoableChangeListener(listener);
}
}
@Override
public void removeVetoableChangeListener(VetoableChangeListener listener) {
vetoableSupport.removeVetoableChangeListener(listener);
for (FieldComponent> field : childFields) {
field.removeVetoableChangeListener(listener);
}
}
@Override
public void addVetoableChangeListener(String propertyName,
VetoableChangeListener listener) {
vetoableSupport.addVetoableChangeListener(propertyName, listener);
for (FieldComponent> field : childFields) {
field.addVetoableChangeListener(propertyName, listener);
}
}
@Override
public void removeVetoableChangeListener(String propertyName,
VetoableChangeListener listener) {
vetoableSupport.removeVetoableChangeListener(propertyName, listener);
for (FieldComponent> field : childFields) {
field.removeVetoableChangeListener(propertyName, listener);
}
}
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupport.addPropertyChangeListener(listener);
for (FieldComponent> field : childFields) {
field.addPropertyChangeListener(listener);
}
}
@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertySupport.removePropertyChangeListener(listener);
for (FieldComponent> field : childFields) {
field.removePropertyChangeListener(listener);
}
}
@Override
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
propertySupport.addPropertyChangeListener(propertyName, listener);
for (FieldComponent> field : childFields) {
field.addPropertyChangeListener(propertyName, listener);
}
}
@Override
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
propertySupport.removePropertyChangeListener(propertyName, listener);
for (FieldComponent> field : childFields) {
field.removePropertyChangeListener(propertyName, listener);
}
}
@Override
public String toString() {
return new ToStringBuilder(this).append("name", getName()).toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy