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