AlarmNotification - How to parse expressions in a property

can someone please give me a hint how to parse expressions in a NotificationProfileProperty?
I figured out how to get the raw value of the property from the NotificationContext, but now have no idea how to parse this raw message to resolve the referenced properties.
In the javadocs there’s a class AlarmEventCollectionExpressionParseContext that seems to be related, but there’s no description of the implementation.
A little example would be great.

Thank you,

This is how the email profile evaluates its subject and message properties as an example:

		private String evaluate(String expressionString, List<AlarmEvent> alarmEvents) {
			if (StringUtils.isBlank(expressionString)) {
				return "";

			AlarmEventCollectionExpressionParseContext ctx = new AlarmEventCollectionExpressionParseContext(
					new FallbackPropertyResolver(context.getAlarmManager().getPropertyResolver()), alarmEvents);
			ExpressionParseContext parseContext = new LegacyAlertPropParseContext(new FormattedExpressionParseContext(ctx));

			String evaluated = expressionString;

			try {
				QualifiedValue value = parser.parse(ctx.expandCollectionReferences(expressionString), parseContext)
				if (value.getQuality().isGood()) {
					evaluated = TypeUtilities.toString(value.getValue());
			} catch (Exception e) {
				log.error(String.format("Error parsing expression \"%s\".", expressionString), e);

			return evaluated;

Pretty straightforward… right? right? eh…

Thank you, that helped. Now that i understand the concept of nested parse contexts and the use of the Parser class it makes sense.
The LegacyAlertPropParseContext must be a non-api class, so i simply left it away.

[quote]Pretty straightforward… right? right? eh…[/quote]Yes, that’s what i always tell people when i got no time for documentation: That’s totally straight forward, you don’t need documentation.
Works perfect until ten years later, when i’m called to repair a machine and wonder what ### wrote that ### code. Normally i realize after some minutes that it was me :wink:.

You can make your own parse context. We extended ExpressionParseContext as follows:

public class MyExpressionParseContext implements ExpressionParseContext {

	public Expression createBoundExpression(String path)
			throws RuntimeException {
		TagPath tagPath;
		try {
			tagPath = TagPathParser.parse(path);
			BoundTagExpression exp = new BoundTagExpression(tagPath);
			exp.setTagListenerDelegate(new com.inductiveautomation.ignition.common.expressions.TagListener(
					tagPath, null, DataQuality.UNKNOWN));
			return exp;
		} catch (IOException e) {
			return null;

	public FunctionFactory getFunctionFactory() {
		return DefaultFunctionFactory.getSharedInstance();


Note that DefaultFunctionFactory.getSharedInstance() is deprecated. Possible replace that with new ContextDelegateFunctionFactory(context)?