/*******************************************************************************
* Copyright (c) 2013 BREDEX GmbH.
* 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:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.rc.common.util;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.eclipse.jubula.rc.common.adaptable.AdapterFactoryRegistry;
import org.eclipse.jubula.rc.common.adaptable.IPropertyValue;
import org.eclipse.jubula.rc.common.exception.RobotException;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author BREDEX GmbH
* @created 16.10.2013
*/
public class PropertyUtil {
/** invalid XML character */
public static final char[] INVALID_XML_CHARS = { 0 };
/** the logger */
private static Logger log = LoggerFactory.getLogger(PropertyUtil.class);
/** Constructor */
private PropertyUtil() {
//empty
}
/**
* @param object
* the object to introspect
* @param propertyName
* the properties name to retrieve
* @return the property or <code>null</code> if not found
* @throws RobotException
* in case of problems
*/
@SuppressWarnings("unchecked")
public static String getPropertyValue(Object object, String propertyName)
throws RobotException {
String propertyValue = StringConstants.EMPTY;
Validate.notNull(object, "Tested component must not be null"); //$NON-NLS-1$
try {
ArrayList<String> path =
new ArrayList<String>(Arrays.asList(
propertyName.split(StringConstants.SLASH)));
final Object prop;
if (path.size() > 0) {
prop = getPropertyByPathOrMethod(object, path, 0);
} else {
prop = null;
}
if (prop != null) {
// Check if an adapter exists
IPropertyValue propertyValueAdapter =
((IPropertyValue) AdapterFactoryRegistry
.getInstance().getAdapter(
IPropertyValue.class, prop));
if (propertyValueAdapter != null) {
propertyValue = propertyValueAdapter
.getStringRepresentation(prop);
} else {
propertyValue = String.valueOf(prop);
}
} else {
propertyValue = String.valueOf(prop);
}
} catch (IllegalAccessException e) {
throw new RobotException(e);
} catch (InvocationTargetException e) {
throw new RobotException(e);
} catch (NoSuchMethodException e) {
throw new RobotException(e);
}
if (StringUtils.containsAny(propertyValue, INVALID_XML_CHARS)) {
for (Character c : INVALID_XML_CHARS) {
propertyValue = StringUtils.remove(propertyValue, c);
}
}
return propertyValue;
}
/**
* Either retrieves the value of the last property of the path or
* invokes the method with the last name in the path of the return value
* of the forelast property.
* @param object the return value of the last recursion step
* @param path the path of the nested properties / method name
* @param index the current recursion's index
* @return the value of the nested property or the return value of the
* invoked method
*
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
private static Object getPropertyByPathOrMethod(Object object,
ArrayList<String> path, int index) throws IllegalAccessException,
InvocationTargetException, NoSuchMethodException {
String name = path.get(index);
if (index >= 0 && index < (path.size() - 1)) {
if (name.endsWith(StringConstants.PARENTHESES_PAIR)) {
return getPropertyByPathOrMethod(
MethodUtils
.invokeExactMethod(object,
name.replace(
StringConstants.LEFT_PARENTHESIS
+ StringConstants.RIGHT_PARENTHESIS,
StringConstants.EMPTY
),
null
),
path, index + 1);
}
return getPropertyByPathOrMethod(PropertyUtils
.getProperty(object, name), path, index + 1);
} else if (index >= 0 && index == (path.size() - 1)) {
if (name.endsWith(StringConstants.PARENTHESES_PAIR)) {
return MethodUtils.invokeExactMethod(object,
name.replace(
StringConstants.PARENTHESES_PAIR,
StringConstants.EMPTY
),
null);
}
return PropertyUtils.getProperty(object, path.get(index));
} else {
return null;
}
}
/**
* Returns a sorted map consisting of the bean properties of a component
*
* @param currComp
* the component
* @return the sorted map of properties
*/
public static Map<String, String> getMapOfComponentProperties(
final Object currComp) {
PropertyDescriptor[] propertyDescriptors = PropertyUtils
.getPropertyDescriptors(currComp);
Map<String, String> componentProperties = new TreeMap<String, String>();
for (int i = 0; i < propertyDescriptors.length; i++) {
PropertyDescriptor pd = propertyDescriptors[i];
String propertyName = pd.getName();
try {
Method readMethod = pd.getReadMethod();
if (readMethod != null) {
componentProperties.put(propertyName,
getPropertyValue(currComp, propertyName));
} else {
componentProperties.put(propertyName,
"This property is not readable"); //$NON-NLS-1$
}
} catch (Exception e) {
log.warn(
"Property " + propertyName + " of " //$NON-NLS-1$ //$NON-NLS-2$
+ currComp.toString()
+ " caused an exception while being read.", e); //$NON-NLS-1$
componentProperties.put(propertyName, "---Error---"); //$NON-NLS-1$
}
}
return componentProperties;
}
}