/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG and others.
* 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:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.ui.swt.lnf;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.osgi.service.log.LogService;
import org.eclipse.equinox.log.Logger;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Resource;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.riena.core.Log4r;
import org.eclipse.riena.core.cache.LRUHashMap;
import org.eclipse.riena.core.singleton.SessionSingletonProvider;
import org.eclipse.riena.core.singleton.SingletonProvider;
import org.eclipse.riena.core.util.StringUtils;
import org.eclipse.riena.ui.swt.lnf.rienadefault.RienaDefaultLnf;
import org.eclipse.riena.ui.swt.utils.UIControlsFactory;
/**
* This class updates the properties of the UI controls according the settings of the current Look&Feel.
*/
public final class LnFUpdater {
private static final Map<String, Object> RESOURCE_CACHE = LRUHashMap.createSynchronizedLRUHashMap(200);
private static final Object NULL_RESOURCE = new Object();
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
private static final SingletonProvider<LnFUpdater> LNFU = new SessionSingletonProvider<LnFUpdater>(LnFUpdater.class);
private static final Logger LOGGER = Log4r.getLogger(LnFUpdater.class);
/**
* System property defining if properties of views are updated.
*/
private static final String PROPERTY_RIENA_LNF_UPDATE_VIEW = "riena.lnf.update.view"; //$NON-NLS-1$
private static final String[] ANYWAY_SET_PROPERTIES = new String[] { "background", "foreground" }; //$NON-NLS-1$ //$NON-NLS-2$
private final Composite shellComposite = new Composite(new Shell(), SWT.NONE);
private final Map<Class<? extends Control>, String> simpleNames = new HashMap<Class<? extends Control>, String>();
private final Map<Class<? extends Control>, List<PropertyDescriptor>> controlProperties = new HashMap<Class<? extends Control>, List<PropertyDescriptor>>();
private final Map<String, Map<String, Object>> defaultPropertyValues = new HashMap<String, Map<String, Object>>();
private final List<PropertyDescriptor> emptyDescriptors = Collections.emptyList();
private boolean dirtyLayout;
/**
* @return the {@link LnFUpdater} instance bound to the current session
* @since 3.0
*/
public static LnFUpdater getInstance() {
return LNFU.getInstance();
}
/**
* Clears all cached data like the default property names and control properties. This method is not supposed to be called from Riena at all. It is only
* needed for the Riena-Toolbox to refresh the LnF.
*
* @since 3.0
*/
public void clearCache() {
RESOURCE_CACHE.clear();
defaultPropertyValues.clear();
controlProperties.clear();
simpleNames.clear();
emptyDescriptors.clear();
}
/**
* Do not instantiate directly
*/
private LnFUpdater() {
}
/**
* Updates the properties of all children of the given composite.
*
* @param parent
* composite which children are updated.
* @param updateLayout
* on {@code true} update the layout; otherwise not
*/
public void updateUIControls(final Composite parent, final boolean updateLayout) {
if (checkPropertyUpdateView()) {
setDirtyLayout(false);
updateUIControlsRecursive(parent, false);
} else {
setDirtyLayout(true);
}
if (updateLayout) {
updateLayout(parent);
}
}
/**
* Updates the Color properties of all children of the given composite.
*
* @param parent
* composite which children are updated.
* @since 5.0
*/
public void updateUIControlColors(final Composite parent) {
if (checkPropertyUpdateView()) {
updateUIControlsRecursive(parent, true);
}
}
/**
* Updates the properties of all children of the given composite and updates the layout of the given parent.
*
* @param parent
* composite which children are updated.
*/
public void updateUIControlsAfterBind(final Composite parent) {
updateUIControls(parent, true);
}
/**
* Updates the layout of the given composite only if it's necessary.
*
* @param parent
* composite which children are updated.
*/
private void updateLayout(final Composite parent) {
if (isDirtyLayout()) {
parent.layout(true, true);
setDirtyLayout(false);
}
}
/**
* Updates the properties of all children of the given composite.
*
* @param parent
* composite which children are updated.
*/
private void updateUIControlsRecursive(final Composite parent, final boolean onlyColor) {
final Control[] controls = parent.getChildren();
for (final Control uiControl : controls) {
updateUIControl(uiControl, onlyColor);
if (uiControl instanceof Composite) {
updateUIControlsRecursive((Composite) uiControl, onlyColor);
}
}
}
/**
* Checks the value of the system property "riena.lnf.update.view".
*
* @return value of system property
*/
private boolean checkPropertyUpdateView() {
return Boolean.getBoolean(PROPERTY_RIENA_LNF_UPDATE_VIEW);
}
/**
* Updates the properties of the UI control according to the values of the LnF.
* <p>
* Note: this is very frequently (basically once for each control in the UI) so it is performance sensitive.
*
* @param control
* UI control
*/
public void updateUIControl(final Control control) {
updateUIControl(control, false);
}
/**
* Updates the properties of the UI control according to the values of the LnF.
* <p>
* Note: this is very frequently (basically once for each control in the UI) so it is performance sensitive.
*
* @param control
* UI control
* @param onlyColor
*/
private void updateUIControl(final Control control, final boolean onlyColor) {
if (!checkPropertyUpdateView()) {
return;
}
final int classModifiers = control.getClass().getModifiers();
if (!Modifier.isPublic(classModifiers)) {
return;
}
if (ignoreControl(control)) {
return;
}
if (!checkLnfKeys(control)) {
return;
}
final List<PropertyDescriptor> properties = getProperties(control);
for (final PropertyDescriptor property : properties) {
if (onlyColor) {
if (!(property.getPropertyType() == Color.class)) {
continue;
}
}
final Object newValue = getLnfValue(control, property);
if (newValue == null) {
continue;
}
final boolean anyway = isAnywayProperty(property);
final Object currentValue = getPropertyValue(control, property);
if (anyway && valuesEquals(currentValue, newValue)) {
continue;
}
if (hasNoDefaultValue(control, property, currentValue)) {
continue;
}
try {
final Method setter = property.getWriteMethod();
setter.invoke(control, newValue);
if (!anyway) {
setDirtyLayout(true);
}
} catch (final IllegalArgumentException e) {
LOGGER.log(LogService.LOG_WARNING, getErrorMessage(control, property), e);
} catch (final IllegalAccessException e) {
LOGGER.log(LogService.LOG_WARNING, getErrorMessage(control, property), e);
} catch (final InvocationTargetException e) {
LOGGER.log(LogService.LOG_WARNING, getErrorMessage(control, property), e);
}
}
}
/**
* Checks if a key for the given control exists in the current Look&Feel. If no key for the control exits, checks keys for the (optional) style exits.
*
* @param control
* UI control
* @return {@code true} if latest one key exists; otherwise {@code false}
*/
private boolean checkLnfKeys(final Control control) {
final Class<? extends Control> controlClass = control.getClass();
if (checkLnfClassKeys(controlClass)) {
return true;
}
final RienaDefaultLnf lnf = LnfManager.getLnf();
final String style = (String) control.getData(UIControlsFactory.KEY_LNF_STYLE);
return lnf.containsLnfResourcePrefix(style);
}
/**
* Checks if a key for the given control class exists in the current Look&Feel.
*
* @param controlClass
* class of the UI control
* @return {@code true} if latest one key exists; otherwise {@code false}
*/
@SuppressWarnings("unchecked")
private boolean checkLnfClassKeys(final Class<? extends Control> controlClass) {
final String className = getSimpleClassName(controlClass);
if (className.length() != 0) {
final RienaDefaultLnf lnf = LnfManager.getLnf();
if (lnf.containsLnfResourcePrefix(className)) {
return true;
}
}
final Class<?> superclass = controlClass.getSuperclass();
if (Control.class.isAssignableFrom(superclass)) {
return checkLnfClassKeys((Class<? extends Control>) superclass);
}
return false;
}
/**
* Returns the simple name of a class.<br>
* For anonymous classes the name of the super class is returned.
*
* @param controlClass
* class of the UI control
* @return simple name of the class or empty string if non existent
*/
private String getSimpleClassName(final Class<? extends Control> controlClass) {
String simpleName = simpleNames.get(controlClass);
if (simpleName == null) {
simpleName = getSimpleClassNameBasic(controlClass);
simpleNames.put(controlClass, simpleName);
}
return simpleName;
}
private String getSimpleClassNameBasic(final Class<? extends Control> controlClass) {
String simpleName;
Class<?> clazz = controlClass;
while ((simpleName = clazz.getSimpleName()).length() == 0) {
clazz = clazz.getSuperclass();
if (!Control.class.isAssignableFrom(clazz)) {
return EMPTY_STRING;
}
}
return simpleName;
}
/**
* Compares the default value of the UI control and the current value of the given property.
*
* @param control
* UI control
* @param property
* property
* @return {@code true} if the current value of the property isn't equals the default value; otherwise {@code false}.
*/
private boolean hasNoDefaultValue(final Control control, final PropertyDescriptor property, final Object currentValue) {
final Method getter = property.getReadMethod();
if (getter != null) {
final Object defaultValue = getDefaultPropertyValue(control, property);
return !valuesEquals(defaultValue, currentValue);
}
return false;
}
/**
* Compares two property values. For font or color the <i>description</i> of the resource, {@link FontData} or {@link RGB}, is used for comparison.
*
* @param value1
* first property value
* @param value2
* second property value
* @return {@code true} if the values are equals; otherwise {@code false}
*/
private boolean valuesEquals(final Object value1, final Object value2) {
if (value1 != null) {
final FontData[] fontData1 = getFontData(value1);
final FontData[] fontData2 = getFontData(value2);
if (fontData1 != null && fontData2 != null) {
return Arrays.equals(fontData1, fontData2);
}
final RGB rgb1 = getRgb(value1);
final RGB rgb2 = getRgb(value2);
if ((rgb1 != null) && (rgb2 != null)) {
return rgb1.equals(rgb2);
}
return value1.equals(value2);
}
return true;
}
/**
* Returns the {@link FontData}s of the given value.
*
* @param value
* property value
* @return {@link FontData}s or {@code null} if the value has no
*/
private FontData[] getFontData(final Object value) {
if (value instanceof FontData[]) {
return (FontData[]) value;
}
if (value instanceof Font) {
return ((Font) value).getFontData();
}
return null;
}
/**
* Returns the {@link RGB} of the given value.
*
* @param value
* property value
* @return {@link RGB}s or {@code null} if the value has no
*/
private RGB getRgb(final Object value) {
if (value instanceof RGB) {
return (RGB) value;
}
if (value instanceof Color) {
return ((Color) value).getRGB();
}
return null;
}
/**
* Returns the default value of the given property of the given UI control.
*
* @param control
* UI control
* @param property
* property
* @return default value
*/
private Object getDefaultPropertyValue(final Control control, final PropertyDescriptor property) {
final Class<? extends Control> controlClass = control.getClass();
final String mapKey = createDefaultPropertiesClassKey(controlClass, control.getStyle());
Map<String, Object> defaultValues = defaultPropertyValues.get(mapKey);
if (defaultValues == null) {
final Control defaultControl = createDefaultControl(controlClass, control.getStyle());
if (defaultControl != null) {
final List<PropertyDescriptor> properties = getProperties(control);
defaultValues = new Hashtable<String, Object>(properties.size());
for (final PropertyDescriptor defaultProperty : properties) {
Object value = getPropertyValue(defaultControl, defaultProperty);
value = getResourceData(value);
if (value != null) {
defaultValues.put(defaultProperty.getName(), value);
}
}
defaultControl.dispose();
defaultPropertyValues.put(mapKey, defaultValues);
} else {
LOGGER.log(LogService.LOG_ERROR, "Cannot create an instance of \"" + controlClass.getName() + "\""); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (defaultValues == null) {
return null;
}
return defaultValues.get(property.getName());
}
/**
* Extracts the description of the given resource.
* <p>
* <ul>
* <li>for {@code Font} this method returns {@code FontData}</li>
* <li>for {@code Color} this method returns {@code RGB}</li>
* </ul>
* <p>
* <i>This descriptions can be used to compare resources (also disposed).</i>
*
* @param object
* the resource
* @return description of resource or the resource itself, if no description exists
*/
private Object getResourceData(final Object object) {
if (!(object instanceof Resource)) {
return object;
}
final Resource resource = (Resource) object;
if (resource.isDisposed()) {
return resource;
}
if (resource instanceof Font) {
return ((Font) resource).getFontData();
} else if (resource instanceof Color) {
return ((Color) resource).getRGB();
}
return resource;
}
/**
* Returns the value of the given property of the given UI control.
*
* @param control
* UI control
* @param property
* property
* @return value of the property or {@code null} if the property cannot read.
*/
private Object getPropertyValue(final Control control, final PropertyDescriptor property) {
final Method getter = property.getReadMethod();
if (getter == null) {
return null;
}
try {
return getter.invoke(control);
} catch (final Exception failure) {
// TODO This is a workaround of a nebula "bug"
if (control.getClass().getName().equals("org.eclipse.swt.nebula.widgets.compositetable.CompositeTable")) { //$NON-NLS-1$
return null;
}
final String message = "Cannot get the value of the property \"" + property.getName() + "\" of the class \"" //$NON-NLS-1$ //$NON-NLS-2$
+ control.getClass().getName() + "\"."; //$NON-NLS-1$
LOGGER.log(LogService.LOG_ERROR, message, failure);
return null;
}
}
/**
* Creates the error message for a given class and a given property.
*
* @param control
* the control
* @param property
* property
* @return error message
*/
private String getErrorMessage(final Control control, final PropertyDescriptor property) {
final Class<? extends Control> controlClass = control.getClass();
final StringBuilder sb = new StringBuilder("Cannot update property "); //$NON-NLS-1$
sb.append("\"" + property.getName() + "\""); //$NON-NLS-1$ //$NON-NLS-2$
sb.append(" of the class "); //$NON-NLS-1$
sb.append("\"" + controlClass.getName() + "\""); //$NON-NLS-1$ //$NON-NLS-2$
return sb.toString();
}
/**
* Returns the properties of the class of the given control.<br>
* The properties of the classes are cached. So introspection is only necessary for new classes.
*
* @param control
* the control
* @return properties
*/
private List<PropertyDescriptor> getProperties(final Control control) {
final Class<? extends Control> controlClass = control.getClass();
List<PropertyDescriptor> propertyDescriptors = controlProperties.get(controlClass);
if (propertyDescriptors == null) {
try {
final PropertyDescriptor[] descriptors = Introspector.getBeanInfo(controlClass).getPropertyDescriptors();
propertyDescriptors = new ArrayList<PropertyDescriptor>(descriptors.length);
for (final PropertyDescriptor descriptor : descriptors) {
final Method setter = descriptor.getWriteMethod();
if (setter == null) {
continue;
}
int modifiers = setter.getModifiers();
if (!Modifier.isPublic(modifiers)) {
continue;
}
final Method getter = descriptor.getReadMethod();
if (getter == null) {
continue;
}
modifiers = getter.getModifiers();
if (!Modifier.isPublic(modifiers)) {
continue;
}
if (ignoreProperty(controlClass, descriptor)) {
continue;
}
propertyDescriptors.add(descriptor);
}
} catch (final IntrospectionException e) {
propertyDescriptors = emptyDescriptors;
}
controlProperties.put(controlClass, propertyDescriptors);
}
return propertyDescriptors;
}
/**
* Returns for the given control and the given property the corresponding value of the LnF.
*
* @param control
* the control
* @param property
* description of one property
* @return value of LnF
*/
private Object getLnfValue(final Control control, final PropertyDescriptor property) {
Object lnfValue = getLnfStyleValue(control, property);
if (lnfValue == null) {
final Class<? extends Control> controlClass = control.getClass();
lnfValue = getLnfValue(controlClass, property);
}
return lnfValue;
}
/**
* Returns for the given control class and the given property the corresponding value of the LnF.
* <p>
* This method will use cached values first.
*
* @param controlClass
* class of the control
* @param property
* description of one property
* @return value of LnF
*/
@SuppressWarnings("unchecked")
private Object getLnfValue(final Class<? extends Control> controlClass, final PropertyDescriptor property) {
final String lnfKey = generateLnfKey(controlClass, property);
Object lnfValue = RESOURCE_CACHE.get(lnfKey);
if (lnfValue != null) {
return lnfValue == NULL_RESOURCE ? null : lnfValue;
}
lnfValue = LnfManager.getLnf().getResource(lnfKey);
if (lnfValue == null) {
final Class<?> superclass = controlClass.getSuperclass();
if (Control.class.isAssignableFrom(superclass)) {
lnfValue = getLnfValueInternal((Class<? extends Control>) superclass, property);
}
}
// Store the lnf value for the given controlClass. Since the lookup starts with the most specific class
// and goes upwards the type hierarchy towards more generic types, we store the most specific result only.
// This saves the most time at a later look-up AND allows us to operate with a fairly small cache size.
// This is implemented by invoking getLnfValue for the 1st lookup and the getLnfValueInternal for the
// 2nd - nth levels of the type hierarchy.
RESOURCE_CACHE.put(lnfKey, lnfValue == null ? NULL_RESOURCE : lnfValue);
return lnfValue;
}
/**
* Returns for the given control class and the given property the corresponding value of the LnF.
* <p>
* This method does not use any caching.
*
* @param controlClass
* class of the control
* @param property
* description of one property
* @return value of LnF
*/
@SuppressWarnings("unchecked")
private Object getLnfValueInternal(final Class<? extends Control> controlClass, final PropertyDescriptor property) {
final String lnfKey = generateLnfKey(controlClass, property);
Object lnfValue = LnfManager.getLnf().getResource(lnfKey);
if (lnfValue == null) {
final Class<?> superclass = controlClass.getSuperclass();
if (Control.class.isAssignableFrom(superclass)) {
lnfValue = getLnfValueInternal((Class<? extends Control>) superclass, property);
}
}
return lnfValue;
}
/**
* Generates the LnF key with the given parameters.
*
* @param controlClass
* class of the control
* @param property
* description of one property
* @return LnF key
*/
private String generateLnfKey(final Class<? extends Control> controlClass, final PropertyDescriptor property) {
final String controlName = getSimpleClassName(controlClass);
final StringBuilder lnfKey = new StringBuilder(controlName);
lnfKey.append('.');
lnfKey.append(property.getName());
return lnfKey.toString();
}
/**
* Returns for the given control and the given property the corresponding value of the LnF style.
*
* @param control
* the control with style "attribute"
* @param property
* property
* @return value of Lnf or {@code null} if not style exists
*/
private Object getLnfStyleValue(final Control control, final PropertyDescriptor property) {
final String style = (String) control.getData(UIControlsFactory.KEY_LNF_STYLE);
if (StringUtils.isEmpty(style)) {
return null;
}
final RienaDefaultLnf lnf = LnfManager.getLnf();
final StringBuilder lnfKey = new StringBuilder(style);
lnfKey.append('.');
lnfKey.append(property.getName());
return lnf.getResource(lnfKey.toString());
}
/**
* Creates an instance of the given control class.
*
* @param controlClass
* class of the UI control
* @param style
* the style of widget to construct
* @return instance of UI control or {@code null} if no instance can be created
*/
private Control createDefaultControl(final Class<? extends Control> controlClass, final int style) {
final Composite parent = shellComposite;
// this is the most likely case
Control defaultControl = getControl(controlClass, parent, style);
if (defaultControl != null) {
return defaultControl;
}
// this is the second most likely case
defaultControl = getControl(controlClass, parent);
if (defaultControl != null) {
return defaultControl;
}
// try hard
try {
final Constructor<?>[] constructors = controlClass.getConstructors();
for (final Constructor<?> constructor : constructors) {
final Class<?>[] paramTypes = constructor.getParameterTypes();
final Object[] params = new Object[paramTypes.length];
boolean parentAssigned = false;
boolean styleAssigned = false;
for (int i = 0; i < paramTypes.length; i++) {
if (paramTypes[i].isAssignableFrom(Composite.class) && !parentAssigned) {
params[i] = parent;
parentAssigned = true;
} else if (paramTypes[i].isAssignableFrom(Integer.TYPE) && !styleAssigned) {
params[i] = style;
styleAssigned = true;
} else {
try {
params[i] = paramTypes[i].newInstance();
} catch (final Exception e) {
params[i] = null;
}
}
}
try {
defaultControl = (Control) constructor.newInstance(params);
} catch (final Exception e) {
defaultControl = null;
}
if (defaultControl != null) {
break;
}
}
} catch (final SecurityException e) {
LOGGER.log(LogService.LOG_WARNING, "Exception while creating default control the hard way for " //$NON-NLS-1$
+ controlClass, e);
}
return defaultControl;
}
private Control getControl(final Class<? extends Control> controlClass, final Composite parent, final int style) {
try {
final Constructor<? extends Control> constructor = controlClass.getConstructor(Composite.class, Integer.TYPE);
return constructor.newInstance(parent, style);
} catch (final NoSuchMethodException e) {
return null;
} catch (final Exception e) {
LOGGER.log(LogService.LOG_WARNING, "Exception while creating default control with composite and style for " //$NON-NLS-1$
+ controlClass, e);
return null;
}
}
private Control getControl(final Class<? extends Control> controlClass, final Composite parent) {
try {
final Constructor<? extends Control> constructor = controlClass.getConstructor(Composite.class);
return constructor.newInstance(parent);
} catch (final NoSuchMethodException e) {
return null;
} catch (final Exception e) {
LOGGER.log(LogService.LOG_WARNING, "Exception while creating default control with composite for " //$NON-NLS-1$
+ controlClass, e);
return null;
}
}
private void setDirtyLayout(final boolean dirtyLayout) {
this.dirtyLayout = dirtyLayout;
}
private boolean isDirtyLayout() {
return dirtyLayout;
}
/**
* Returns whether the given property should be ignored for the given control.
* <p>
* Properties of the annotation {@code IgnoreLnFUpdater} should not be changed.
*
* @param controlClass
* class of the UI control
* @param property
* description of the property to check
* @return {@code true} if property should be ignored; otherwise {@code false}
*/
private boolean ignoreProperty(final Class<? extends Control> controlClass, final PropertyDescriptor property) {
return ignoreProperty(controlClass, property.getName());
}
/**
* Returns whether the given control should be ignored.
* <p>
* If the the annotation {@code IgnoreLnFUpdater} has a <i>property name</i> "*", no property of the given control should be changed. So the complete
* control can be ignored.
*
* @param control
* UI control
* @return {@code true} if control should be ignored; otherwise {@code false}
*/
private boolean ignoreControl(final Control control) {
return ignoreProperty(control.getClass(), "*"); //$NON-NLS-1$
}
private boolean ignoreProperty(final Class<?> controlClass, final String property) {
if (!Control.class.isAssignableFrom(controlClass)) {
return false;
}
final IgnoreLnFUpdater ignoreLnfUpdater = controlClass.getAnnotation(IgnoreLnFUpdater.class);
if (ignoreLnfUpdater == null) {
return false;
}
for (final String ignoreProp : ignoreLnfUpdater.value()) {
if (ignoreProp != null) {
if (ignoreProp.equals(property)) {
return true;
}
}
}
return ignoreProperty(controlClass.getSuperclass(), property);
}
private String createDefaultPropertiesClassKey(final Class<? extends Control> controlClass, final int style) {
return controlClass.toString() + "#" + style; //$NON-NLS-1$
}
/**
* Checks, if the value of the given property should set also if the control already has the new value.
*
* @param property
* @return {@code true} set anyway; {@code false} set only if the value are different
*/
private boolean isAnywayProperty(final PropertyDescriptor property) {
final String propName = property.getName();
for (final String anywayPropName : ANYWAY_SET_PROPERTIES) {
if (propName.equals(anywayPropName)) {
return true;
}
}
return false;
}
}