Revert "Move SchemaNodeIdentifier to yang-common"
[yangtools.git] / parser / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / ArgumentUtils.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies, 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 com.google.common.annotations.Beta;
11 import com.google.common.base.Splitter;
12 import java.math.BigDecimal;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.regex.Pattern;
16 import org.checkerframework.checker.regex.qual.Regex;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
19 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
20 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Descendant;
21 import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
22 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
23 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
24 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
25
26 /**
27  * Utility class for dealing with arguments encountered by StatementSupport classes.
28  */
29 @Beta
30 public final class ArgumentUtils {
31     public static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
32     public static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
33
34     @Regex
35     private static final String PATH_ABS_STR = "/[^/].*";
36     private static final Pattern PATH_ABS = Pattern.compile(PATH_ABS_STR);
37     private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings().trimResults();
38
39     // these objects are to compare whether range has MAX or MIN value
40     // none of these values should appear as Yang number according to spec so they are safe to use
41     private static final BigDecimal YANG_MIN_NUM = BigDecimal.valueOf(-Double.MAX_VALUE);
42     private static final BigDecimal YANG_MAX_NUM = BigDecimal.valueOf(Double.MAX_VALUE);
43
44     private ArgumentUtils() {
45         // Hidden on purpose
46     }
47
48     public static int compareNumbers(final Number n1, final Number n2) {
49         final BigDecimal num1 = yangConstraintToBigDecimal(n1);
50         final BigDecimal num2 = yangConstraintToBigDecimal(n2);
51         return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
52     }
53
54     public static boolean isAbsoluteXPath(final String path) {
55         return PATH_ABS.matcher(path).matches();
56     }
57
58     public static Absolute parseAbsoluteSchemaNodeIdentifier(final StmtContext<?, ?, ?> ctx, final String str) {
59         // FIXME: this does accept check for a leading slash
60         return Absolute.of(parseNodeIdentifiers(ctx, str));
61     }
62
63     public static Descendant parseDescendantSchemaNodeIdentifier(final StmtContext<?, ?, ?> ctx, final String str) {
64         // FIXME: this does accept a leading slash
65         return Descendant.of(parseNodeIdentifiers(ctx, str));
66     }
67
68     public static SchemaNodeIdentifier nodeIdentifierFromPath(final StmtContext<?, ?, ?> ctx, final String path) {
69         final List<QName> qnames = parseNodeIdentifiers(ctx, path);
70         return PATH_ABS.matcher(path).matches() ? Absolute.of(qnames) : Descendant.of(qnames);
71     }
72
73     @SuppressWarnings("checkstyle:illegalCatch")
74     private static  List<QName> parseNodeIdentifiers(final StmtContext<?, ?, ?> ctx, final String path) {
75         // FIXME: is the path trimming really necessary??
76         final List<QName> qnames = new ArrayList<>();
77         for (final String nodeName : SLASH_SPLITTER.split(trimSingleLastSlashFromXPath(path))) {
78             try {
79                 qnames.add(StmtContextUtils.parseNodeIdentifier(ctx, nodeName));
80             } catch (final RuntimeException e) {
81                 throw new SourceException(ctx, e, "Failed to parse node '%s' in path '%s'", nodeName, path);
82             }
83         }
84
85         SourceException.throwIf(qnames.isEmpty(), ctx, "Schema node identifier must not be empty");
86         return qnames;
87     }
88
89     private static String trimSingleLastSlashFromXPath(final String path) {
90         return path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
91     }
92
93     private static BigDecimal yangConstraintToBigDecimal(final Number number) {
94         if (UnresolvedNumber.max().equals(number)) {
95             return YANG_MAX_NUM;
96         }
97         if (UnresolvedNumber.min().equals(number)) {
98             return YANG_MIN_NUM;
99         }
100
101         return new BigDecimal(number.toString());
102     }
103 }