PropertyUtils.java

package org.osate.pluginsupport.properties;

import java.util.Optional;
import java.util.OptionalDouble;
import java.util.function.Function;

import org.osate.aadl2.NamedElement;

/**
 * @since 7.0
 */
public final class PropertyUtils {
	private PropertyUtils() {
		// Utility class
	}

	/**
	 * Given an optional {@link Scalable} value, return an optional that scales the value to the given unit.
	 *
	 * @param <U> The enumeration type of the unit.
	 * @param value The optional {@link Scalable} value
	 * @param unit The unit to scale the value to.
	 * @return An {@link Optional} containing the scaled value if the original value exists; {@link Optional#empty()} otherwise.
	 */
	public static <U extends Enum<U> & GeneratedUnits<U>> Optional<Double> scale(
			final Optional<? extends Scalable<U>> value,
			final U unit) {
		return value.map(rwu -> rwu.getValue(unit));
	}

	/**
	 * Get the value of a property association whose type is {@link IntegerWithUnits} or {@link RealWithUnits}, and
	 * scale the value to the given unit.
	 *
	 * @param <U> The enumeration type of the unit.
	 * @param getProperty The handle of a generated property getter function.
	 * @param ne The element to get the property association from.
	 * @param unit The unit to scale the associated property value to.
	 * @return An {@link Optional} containing the scaled property value if the property association exists; {@link Optional#empty()} otherwise.
	 */
	public static <U extends Enum<U> & GeneratedUnits<U>> Optional<Double> getScaled(
			final Function<NamedElement, Optional<? extends Scalable<U>>> getProperty, final NamedElement ne, final U unit) {
		return scale(getProperty.apply(ne), unit);
	}

	/**
	 * Given a {@link IntegerRangeWithUnits} or {@link RealRangeWithUnits}, use a unitless {@link RealRange} to return the range scaled to the given unit.
	 *
	 * @param <U> The enumeration type of the unit.
	 * @param rangeWithUnits The range with units value to scale.
	 * @param unit The unit to scale the associated property value to.
	 * @return A {@link RealRange} containing the scaled range.  If the optional delta is present in the original,
	 * it is scaled, otherwise the delta is equal to {@link OptionalDouble#empty()}.
	 */
	public static <U extends Enum<U> & GeneratedUnits<U>> RealRange scaleRange(
			final RangeWithUnits<U, ? extends Scalable<U>> rangeWithUnits, final U unit) {
		final double min = rangeWithUnits.getMinimum().getValue(unit);
		final double max = rangeWithUnits.getMaximum().getValue(unit);
		final OptionalDouble delta = rangeWithUnits.getDelta().map(d -> OptionalDouble.of(d.getValue(unit)))
				.orElse(OptionalDouble.empty());
		return new RealRange(min, max, delta);
	}

	/**
	 * Given an Optional {@link IntegerRangeWithUnits} or {@link RealRangeWithUnits}, return an optional that may contain a unitless {@link RealRange} to return the range scaled to the given unit.
	 *
	 * @param <U> The enumeration type of the unit.
	 * @param rangeWithUnits The optional range with units value to scale.
	 * @param unit The unit to scale the associated property value to.
	 * @return An optional {@link RealRange} containing the scaled range.  If the optional delta is present in the original,
	 * it is scaled, otherwise the delta is equal to {@link OptionalDouble#empty()}.  If the original optional is empty, then the returned
	 * optional is empty.
	 */
	public static <U extends Enum<U> & GeneratedUnits<U>> Optional<RealRange> scaleRange(
			final Optional<? extends RangeWithUnits<U, ? extends Scalable<U>>> rangeWithUnits, final U unit) {
		return rangeWithUnits.map(range -> scaleRange(range, unit));
	}

	/**
	 * Get the value of a property association whose type is {@link IntegerRangeWithUnits} or {@link RealRangeWithUnits}, and
	 * scale the range to the given unit using {@link #scaleRange(RangeWithUnits, Enum)}.
	 *
	 * @param <U> The enumeration type of the unit.
	 * @param getProperty The handle of a generated property getter function.
	 * @param ne The element to get the property association from.
	 * @param unit The unit to scale the associated property value to.
	 * @return An {@link Optional} containing the scaled range property value if the property association exists; {@link Optional#empty()} otherwise.
	 */
	public static <U extends Enum<U> & GeneratedUnits<U>> Optional<RealRange> getScaledRange(
			final Function<NamedElement, Optional<? extends RangeWithUnits<U, ? extends Scalable<U>>>> getProperty,
			final NamedElement ne, final U unit) {
		return scaleRange(getProperty.apply(ne), unit);
	}

	/**
	 * Returns whether the Optional value equals the given value.
	 *
	 * @param <V> The type of the property value.
	 * @param optValue The optional to test
	 * @param value The value to test against.
	 * @param orElse The value return if the optional has no value.
	 *
	 * @return {@code true} if the optional value exists and the value {@link #equals(Object)} {@code value};
	 * the value of {@code orElse} if the optional does not exist.
	 */
	public static <V> boolean equals(final Optional<V> optValue, final V value, final boolean orElse) {
		return optValue.map(x -> x.equals(value)).orElse(orElse);
	}

	/**
	 * Returns whether the property value associated with the given element, if present, equals the given value.
	 *
	 * @param <V> The type of the property value.
	 * @param getProperty The handle of a generated property getter function.
	 * @param ne The element to get the property association from.
	 * @param value The value to test against.
	 *
	 * @return {@code true} if the property association exists and the value {@link #equals(Object)} {@code value}.
	 * If the property association doesn't exist then the value {@code false} is returned.
	 */
	public static <V> boolean propertyEquals(final Function<NamedElement, Optional<V>> getProperty,
			final NamedElement ne,
			final V value) {
		return equals(getProperty.apply(ne), value, false);
	}

}