1
2
3
4
5
6 package gov.nist.secauto.metaschema.core.metapath;
7
8 import gov.nist.secauto.metaschema.core.metapath.antlr.FailingErrorListener;
9 import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10;
10 import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10Lexer;
11 import gov.nist.secauto.metaschema.core.metapath.antlr.ParseTreePrinter;
12 import gov.nist.secauto.metaschema.core.metapath.cst.BuildCSTVisitor;
13 import gov.nist.secauto.metaschema.core.metapath.cst.CSTPrinter;
14 import gov.nist.secauto.metaschema.core.metapath.cst.path.ContextItem;
15 import gov.nist.secauto.metaschema.core.metapath.item.IItem;
16 import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
17 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
18
19 import org.antlr.v4.runtime.CharStreams;
20 import org.antlr.v4.runtime.CommonTokenStream;
21 import org.antlr.v4.runtime.DefaultErrorStrategy;
22 import org.antlr.v4.runtime.Parser;
23 import org.antlr.v4.runtime.misc.ParseCancellationException;
24 import org.antlr.v4.runtime.tree.ParseTree;
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27
28 import java.io.ByteArrayOutputStream;
29 import java.io.IOException;
30 import java.io.PrintStream;
31 import java.nio.charset.StandardCharsets;
32
33 import edu.umd.cs.findbugs.annotations.NonNull;
34 import edu.umd.cs.findbugs.annotations.Nullable;
35
36
37
38
39 @SuppressWarnings({
40 "PMD.CouplingBetweenObjects"
41 })
42 class MetapathExpression implements IMetapathExpression {
43
44
45
46
47 @NonNull
48 public static final MetapathExpression CONTEXT_NODE
49 = new MetapathExpression(".", ContextItem.instance(), StaticContext.instance());
50 private static final Logger LOGGER = LogManager.getLogger(MetapathExpression.class);
51
52 @NonNull
53 private final String path;
54 @NonNull
55 private final IExpression expression;
56 @NonNull
57 private final StaticContext staticContext;
58
59
60
61
62
63
64
65
66
67
68
69
70 @NonNull
71 public static MetapathExpression compile(@NonNull String path, @NonNull StaticContext context) {
72 @NonNull
73 MetapathExpression retval;
74 if (".".equals(path)) {
75 retval = CONTEXT_NODE;
76 } else {
77 try {
78 Metapath10 parser = newParser(path);
79 ParseTree tree = ObjectUtils.notNull(parser.metapath());
80 logAst(tree);
81 IExpression expr = new BuildCSTVisitor(context).visit(tree);
82 logCst(expr);
83 retval = new MetapathExpression(path, expr, context);
84 } catch (StaticMetapathException ex) {
85 String message = ex.getMessageText();
86 throw new StaticMetapathException(
87 ex.getCode(),
88 String.format("Unable to compile path '%s'.%s", path, message == null ? "" : " " + message),
89 ex);
90 } catch (MetapathException | ParseCancellationException ex) {
91 String msg = String.format("Unable to compile Metapath '%s'", path);
92 LOGGER.atError().withThrowable(ex).log(msg);
93 throw new StaticMetapathException(StaticMetapathException.INVALID_PATH_GRAMMAR, msg, ex);
94 }
95 }
96 return retval;
97 }
98
99 private static void logCst(@NonNull IExpression expr) {
100 if (LOGGER.isDebugEnabled()) {
101 LOGGER.atDebug().log(String.format("Metapath CST:%n%s", CSTPrinter.toString(expr)));
102 }
103 }
104
105 private static void logAst(@NonNull ParseTree tree) {
106 if (LOGGER.isDebugEnabled()) {
107 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
108 try (PrintStream ps = new PrintStream(os, true, StandardCharsets.UTF_8)) {
109 ParseTreePrinter printer = new ParseTreePrinter(ps);
110 printer.print(tree, Metapath10.ruleNames);
111 ps.flush();
112 }
113 LOGGER.atDebug().log(String.format("Metapath AST:%n%s", os.toString(StandardCharsets.UTF_8)));
114 } catch (IOException ex) {
115 LOGGER.atError().withThrowable(ex).log("An unexpected error occurred while closing the steam.");
116 }
117 }
118 }
119
120 @NonNull
121 private static Metapath10 newParser(@NonNull String path) {
122 Metapath10Lexer lexer = new Metapath10Lexer(CharStreams.fromString(path));
123 lexer.removeErrorListeners();
124 lexer.addErrorListener(new FailingErrorListener());
125
126 CommonTokenStream tokens = new CommonTokenStream(lexer);
127 Metapath10 parser = new Metapath10(tokens);
128 parser.removeErrorListeners();
129 parser.addErrorListener(new FailingErrorListener());
130 parser.setErrorHandler(new DefaultErrorStrategy() {
131
132 @Override
133 public void sync(Parser recognizer) {
134
135 }
136 });
137 return parser;
138 }
139
140
141
142
143
144
145
146
147
148
149
150 protected MetapathExpression(
151 @NonNull String path,
152 @NonNull IExpression expr,
153 @NonNull StaticContext staticContext) {
154 this.path = path;
155 this.expression = expr;
156 this.staticContext = staticContext;
157 }
158
159 @Override
160 public String getPath() {
161 return path;
162 }
163
164
165
166
167
168
169 @NonNull
170 protected IExpression getCSTNode() {
171 return expression;
172 }
173
174 @Override
175 public StaticContext getStaticContext() {
176 return staticContext;
177 }
178
179 @Override
180 public String toString() {
181 return getPath();
182 }
183
184 @Override
185 @NonNull
186 public <T extends IItem> ISequence<T> evaluate(
187 @Nullable IItem focus,
188 @NonNull DynamicContext dynamicContext) {
189 try {
190 return ObjectUtils.asType(getCSTNode().accept(dynamicContext, ISequence.of(focus)).reusable());
191 } catch (MetapathException ex) {
192 throw new MetapathException(
193 String.format("An error occurred while evaluating the expression '%s'. %s",
194 getPath(),
195 ex.getLocalizedMessage()),
196 ex);
197 }
198 }
199
200 @FunctionalInterface
201 interface ConversionFunction {
202 @Nullable
203 Object convert(@NonNull ISequence<?> sequence);
204 }
205
206 }