Add static ArgumentUtils XPath integration
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / XPathSupport.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
9
10 import java.util.Iterator;
11 import java.util.ServiceLoader;
12 import javax.xml.xpath.XPathExpressionException;
13 import org.eclipse.jdt.annotation.NonNullByDefault;
14 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
15 import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
16 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
17 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
18 import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
19 import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory;
20 import org.slf4j.Logger;
21
22 @NonNullByDefault
23 abstract class XPathSupport {
24     private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(XPathSupport.class);
25
26     private static final XPathSupport INSTANCE;
27
28     static {
29         final Iterator<YangXPathParserFactory> it = ServiceLoader.load(YangXPathParserFactory.class).iterator();
30         if (!it.hasNext()) {
31             LOG.warn("Failed to find XPath parser factory, no XPath validation will be performed");
32             INSTANCE = new Noop();
33         } else {
34             INSTANCE = new XPathImpl(it.next());
35         }
36     }
37
38     static RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String xpath) {
39         return INSTANCE.parseXPath(xpath, ctx);
40     }
41
42     abstract RevisionAwareXPath parseXPath(String xpath, StmtContext<?, ?, ?> ctx);
43
44     private static final class Noop extends XPathSupport {
45         @Override
46         RevisionAwareXPath parseXPath(final String xpath, final StmtContext<?, ?, ?> ctx) {
47             return new RevisionAwareXPathImpl(xpath, ArgumentUtils.isAbsoluteXPath(xpath));
48         }
49     }
50
51     private static final class XPathImpl extends XPathSupport {
52         private final YangXPathParserFactory factory;
53
54         XPathImpl(final YangXPathParserFactory factory) {
55             this.factory = factory;
56         }
57
58         @Override
59         RevisionAwareXPath parseXPath(final String xpath, final StmtContext<?, ?, ?> ctx) {
60             final boolean isAbsolute = ArgumentUtils.isAbsoluteXPath(xpath);
61             final YangXPathParser.QualifiedBound parser = factory.newParser(new StmtNamespaceContext(ctx));
62             final QualifiedBound parsed;
63             try {
64                 parsed = parser.parseExpression(xpath);
65             } catch (XPathExpressionException e) {
66                 LOG.warn("Argument \"{}\" is not valid XPath string at \"{}\"", xpath,
67                     ctx.getStatementSourceReference(), e);
68                 return new RevisionAwareXPathImpl(xpath, isAbsolute);
69             }
70
71             if (ctx.getRootVersion().compareTo(parsed.getYangVersion()) < 0) {
72                 LOG.warn("{} features required in {} context to parse expression '{}' [at {}]",
73                     parsed.getYangVersion().getReference(), ctx.getRootVersion().getReference(), xpath,
74                     ctx.getStatementSourceReference());
75             }
76             return new WithExpressionImpl(xpath, isAbsolute, parsed);
77         }
78     }
79 }