/******************************************************************************* * Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH 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 *******************************************************************************/ package de.gebit.integrity.runner.variables; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.google.inject.Singleton; import de.gebit.integrity.dsl.ConstantEntity; import de.gebit.integrity.dsl.Variable; import de.gebit.integrity.dsl.VariableOrConstantEntity; import de.gebit.integrity.parameter.variables.VariableManager; /** * The simple, default variable manager which keeps variables in a map. * * @author Rene Schneider - initial API and implementation * */ @Singleton public class DefaultVariableManager implements VariableManager { /** * The map used to store variables. */ protected Map<VariableOrConstantEntity, Object> variableMap = new HashMap<VariableOrConstantEntity, Object>(); @Override public Object get(VariableOrConstantEntity anEntity) { return variableMap.get(anEntity); } @Override public boolean isDefined(VariableOrConstantEntity anEntity) { return variableMap.containsKey(anEntity); } @Override public Object get(Variable aVariable) { Object tempObject = get(aVariable.getName()); if (aVariable.getAttribute() != null && tempObject != null) { // The attribute value may be a path to nested objects String[] tempAttributeParts = aVariable.getAttribute().split("\\."); outer: for (String tempAttributePart : tempAttributeParts) { try { for (PropertyDescriptor tempDescriptor : Introspector .getBeanInfo(tempObject.getClass(), Object.class).getPropertyDescriptors()) { if (tempDescriptor.getName().equals(tempAttributePart)) { Method tempReadMethod = tempDescriptor.getReadMethod(); if (tempReadMethod != null && Modifier.isPublic(tempReadMethod.getModifiers())) { try { tempObject = tempReadMethod.invoke(tempObject); if (tempObject == null) { return null; } continue outer; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exc) { throw new RuntimeException("Failed to read attribute '" + aVariable.getAttribute() + "' in bean class " + tempObject.getClass().getName(), exc); } } } } // No matching property was found throw new RuntimeException("Did not find readable attribute '" + aVariable.getAttribute() + "' in bean class " + tempObject.getClass().getName()); } catch (IntrospectionException exc) { throw new RuntimeException("Failed to introspect bean class " + tempObject.getClass().getName(), exc); } } // Now, the target value is found return tempObject; } else { return tempObject; } } @Override public boolean isDefined(Variable aVariable) { Object tempObject = get(aVariable.getName()); if (aVariable.getAttribute() != null && tempObject != null) { // Probe for a runtime exception during resolving of a potentially embedded nested object path try { get(aVariable); } catch (RuntimeException exc) { return false; } return true; } return tempObject != null || variableMap.containsKey(aVariable.getName()); } @Override public void set(VariableOrConstantEntity anEntity, Object aValue) { if (anEntity instanceof ConstantEntity) { if (variableMap.containsKey(anEntity)) { throw new RuntimeException("Illegal attempt to redefine a constant: " + anEntity.getName()); } } variableMap.put(anEntity, aValue); } @Override public void unset(VariableOrConstantEntity anEntity) { variableMap.remove(anEntity); } @Override public Set<Entry<VariableOrConstantEntity, Object>> getAllEntries() { return variableMap.entrySet(); } @Override public void clear(boolean aClearConstantsFlag) { if (aClearConstantsFlag) { variableMap.clear(); } else { // Pick all non-constants out of the map, leave the constants untouched. Iterator<Entry<VariableOrConstantEntity, Object>> tempIterator = variableMap.entrySet().iterator(); while (tempIterator.hasNext()) { if (!(tempIterator.next().getKey() instanceof ConstantEntity)) { tempIterator.remove(); } } } } }