1
2
3
4
5
6 package dev.metaschema.core.model.constraint.impl;
7
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.regex.Pattern;
11
12 import dev.metaschema.core.datatype.markup.MarkupLine;
13 import dev.metaschema.core.datatype.markup.MarkupMultiline;
14 import dev.metaschema.core.metapath.DynamicContext;
15 import dev.metaschema.core.metapath.IMetapathExpression;
16 import dev.metaschema.core.metapath.InvalidMetapathGrammarException;
17 import dev.metaschema.core.metapath.MetapathException;
18 import dev.metaschema.core.metapath.item.node.INodeItem;
19 import dev.metaschema.core.model.IAttributable;
20 import dev.metaschema.core.model.ISource;
21 import dev.metaschema.core.model.constraint.ConstraintInitializationException;
22 import dev.metaschema.core.model.constraint.ConstraintValidationException;
23 import dev.metaschema.core.model.constraint.IConfigurableMessageConstraint;
24 import dev.metaschema.core.model.constraint.IConstraint;
25 import dev.metaschema.core.util.ObjectUtils;
26 import dev.metaschema.core.util.StringUtils;
27 import edu.umd.cs.findbugs.annotations.NonNull;
28 import edu.umd.cs.findbugs.annotations.Nullable;
29
30
31
32
33
34
35
36 public abstract class AbstractConfigurableMessageConstraint
37 extends AbstractConstraint
38 implements IConfigurableMessageConstraint {
39 @NonNull
40 private static final Pattern METAPATH_VALUE_TEMPLATE_PATTERN
41 = ObjectUtils.notNull(Pattern.compile("(?<!\\\\)(\\{\\s*((?:(?:\\\\})|[^}])*)\\s*\\})"));
42
43 @Nullable
44 private final String message;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 protected AbstractConfigurableMessageConstraint(
70 @Nullable String id,
71 @Nullable String formalName,
72 @Nullable MarkupLine description,
73 @NonNull ISource source,
74 @NonNull Level level,
75 @NonNull IMetapathExpression target,
76 @NonNull Map<IAttributable.Key, Set<String>> properties,
77 @Nullable String message,
78 @Nullable MarkupMultiline remarks) {
79 super(id, formalName, description, source, level, target, properties, remarks);
80 this.message = message;
81 }
82
83 @Override
84 public String getMessage() {
85 return message;
86 }
87
88 @Override
89 public String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext context)
90 throws ConstraintValidationException {
91 String message = getMessage();
92 if (message == null) {
93 throw new ConstraintInitializationException(
94 String.format("A custom message is not defined in the constraint %s in %s.",
95 IConstraint.getConstraintIdentity(this),
96 getSource().getLocationHint()));
97 }
98 try {
99 return ObjectUtils.notNull(StringUtils.replaceTokens(message, METAPATH_VALUE_TEMPLATE_PATTERN, match -> {
100 String metapath = ObjectUtils.notNull(match.group(2));
101 IMetapathExpression expr = IMetapathExpression.compile(
102 metapath,
103
104
105 getSource().getStaticContext());
106 return expr.evaluateAs(
107 item,
108 IMetapathExpression.ResultType.STRING,
109
110
111 context);
112 }).toString());
113 } catch (InvalidMetapathGrammarException ex) {
114 throw new ConstraintValidationException(
115 String.format("Unable to compile a message replacement expression in constraint '%s'. %s",
116 IConstraint.getConstraintIdentity(this),
117 ex.getLocalizedMessage()),
118 ex);
119 } catch (MetapathException ex) {
120 throw new ConstraintValidationException(
121 String.format("Unable to evaluate a message replacement expression in constraint '%s'. %s",
122 IConstraint.getConstraintIdentity(this),
123 ex.getLocalizedMessage()),
124 ex);
125 }
126 }
127 }