package alien4cloud.tosca.properties.constraints; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import alien4cloud.tosca.normative.IComparablePropertyType; import alien4cloud.tosca.normative.IPropertyType; import alien4cloud.tosca.normative.InvalidPropertyValueException; import alien4cloud.tosca.normative.StringType; import alien4cloud.tosca.properties.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException; /** * Utility class to validate constraints types. */ public final class ConstraintUtil { private ConstraintUtil() { } /** * Validates that the {@link alien4cloud.tosca.normative.IPropertyType} specified is a {@link alien4cloud.tosca.normative.StringType}. * * @param propertyType The property tosca type. * @throws ConstraintValueDoNotMatchPropertyTypeException In case the type is not {@link alien4cloud.tosca.normative.StringType}. */ public static void checkStringType(IPropertyType<?> propertyType) throws ConstraintValueDoNotMatchPropertyTypeException { if (!StringType.NAME.equals(propertyType.getTypeName())) { throw new ConstraintValueDoNotMatchPropertyTypeException("Only string property type is accepted but found <" + propertyType.getTypeName() + ">"); } } /** * Verify that the given tosca type is supported for comparison * * @param propertyType the tosca type to check * @throws ConstraintValueDoNotMatchPropertyTypeException if the property type cannot be compared */ public static void checkComparableType(IPropertyType<?> propertyType) throws ConstraintValueDoNotMatchPropertyTypeException { // The validity of the value is already assured by us with our ToscaType.convert() method // here we just want to check that the constraint is not used on unsupported type as boolean if (!(propertyType instanceof IComparablePropertyType)) { throw new ConstraintValueDoNotMatchPropertyTypeException("Constraint is invalid for property type <" + propertyType.getTypeName() + ">, as it's not comparable"); } } /** * Convert a string value following its type throw exception if it cannot be converted to a comparable * * @param propertyType the type of the property * @param value the value to convert * @return the converted comparable * @throws ConstraintValueDoNotMatchPropertyTypeException if the converted value is not a comparable */ @SuppressWarnings("rawtypes") public static Comparable convertToComparable(IPropertyType<?> propertyType, String value) throws ConstraintValueDoNotMatchPropertyTypeException { if (!(propertyType instanceof IComparablePropertyType)) { throw new ConstraintValueDoNotMatchPropertyTypeException("Constraint is invalid for property type <" + propertyType.getTypeName() + ">, as it's not comparable"); } IComparablePropertyType<?> comparablePropertyType = (IComparablePropertyType) propertyType; try { return comparablePropertyType.parse(value); } catch (InvalidPropertyValueException e) { throw new ConstraintValueDoNotMatchPropertyTypeException("Unable to parse value <" + value + "> of type <" + propertyType.getTypeName() + ">", e); } } public static Object convert(IPropertyType<?> propertyType, String value) throws ConstraintValueDoNotMatchPropertyTypeException { try { return propertyType.parse(value); } catch (InvalidPropertyValueException e) { throw new ConstraintValueDoNotMatchPropertyTypeException("Unable to parse value <" + value + "> of type <" + propertyType.getTypeName() + ">", e); } } @Getter @Setter @NoArgsConstructor public static class ConstraintInformation { private String name; private String path; private Object reference; private String value; private String type; public ConstraintInformation(String name, Object reference, String value, String type) { this.name = name; this.reference = reference; this.value = value; this.type = type; } @Override public String toString() { return "ConstraintInformation{" + "name='" + name + '\'' + ", path='" + path + '\'' + ", reference=" + reference + ", value='" + value + '\'' + ", type='" + type + '\'' + '}'; } } public static ConstraintInformation getConstraintInformation(Object constraint) throws IntrospectionException { PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(constraint.getClass()).getPropertyDescriptors(); PropertyDescriptor firstDescriptor = null; for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (propertyDescriptor.getReadMethod() != null && propertyDescriptor.getWriteMethod() != null) { firstDescriptor = propertyDescriptor; break; } } if (firstDescriptor == null) { throw new IntrospectionException("Cannot find constraint name"); } try { return new ConstraintInformation(firstDescriptor.getName(), firstDescriptor.getReadMethod().invoke(constraint), null, null); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new IntrospectionException("Cannot retrieve constraint reference " + e.getMessage()); } } }