Eliminate quotedString parser construct
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / ir / IRArgument.java
1 /*
2  * Copyright (c) 2020 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.ir;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.collect.ImmutableList;
14 import java.util.Iterator;
15 import java.util.List;
16 import org.eclipse.jdt.annotation.NonNull;
17
18 /**
19  * An argument to a YANG statement, as defined by section 6.1.3 of both
20  * <a href="https://tools.ietf.org/html/rfc6020#section-6.1.3">RFC6020</a> and
21  * <a href="https://tools.ietf.org/html/rfc7950#section-6.1.3">RFC7950</a>. An argument is effectively any old string,
22  * except it can be defined in a number of ways:
23  * <ul>
24  *   <li>it can be a simple unquoted string, or</li>
25  *   <li>it can be a single-quoted string, with its contents being completely preserved, or</li>
26  *   <li>it can be a double-quoted string, which defines some escaping and whitespace-stripping rules, or</li>
27  *   <li>it can be a concatenation of any number of single- or double-quoted strings</li>
28  * </ul>
29  *
30  * <p>
31  * The first three cases as covered by {@link Single} subclass, which exposes appropriate methods to infer how its
32  * string literal is to be interpreted. The last case is handled by {@link Concatenation} subclass, which exposes
33  * the constituent parts as {@link Single} items.
34  *
35  * <p>
36  * Please note that parser implementations producing these argument representations are <b>NOT</b> required to retain
37  * the format of the original definition. They are free to perform quoting and concatenation transformations as long as
38  * they maintain semantic equivalence. As a matter of example, these transformations are explicitly allowed:
39  * <ul>
40  *   <li>elimination of unneeded quotes, for example turning {@code "foo"} into {@code foo}</li>
41  *   <li>transformation of quotes, for example turning {@code "foo\nbar"} into {@code 'foo&#10bar'}</li>
42  *   <li>concatenation processing, for example turning {@code 'foo' + 'bar'} into {@code foobar}</li>
43  * </ul>
44  */
45 @Beta
46 public abstract class IRArgument extends AbstractIRObject {
47     /**
48      * An argument composed of multiple concatenated parts.
49      */
50     public static final class Concatenation extends IRArgument {
51         private final @NonNull ImmutableList<Single> parts;
52
53         Concatenation(final List<Single> parts) {
54             this.parts = ImmutableList.copyOf(parts);
55         }
56
57         /**
58          * Return the argument parts that need to be concatenated.
59          *
60          * @return Argument parts.
61          */
62         public @NonNull List<? extends Single> parts() {
63             return parts;
64         }
65
66         @Override
67         StringBuilder toYangFragment(final StringBuilder sb) {
68             final Iterator<Single> it = parts.iterator();
69             it.next().toYangFragment(sb);
70             while (it.hasNext()) {
71                 it.next().toYangFragment(sb.append(" + "));
72             }
73             return sb;
74         }
75     }
76
77     /**
78      * An argument composed of a single string. This string may need further validation and processing, as it may not
79      * actually conform to the specification as requested by {@code yang-version}.
80      */
81     /*
82      * This is the public footprint which is served by three final subclasses: DoublyQuoted, SingleQuoted, Unquoted.
83      * Those classes must never be exposed, as they are a manifestation of current implementation in StatementFactory.
84      * As noted in the interface contract of IRArgument, we have very much free reign on syntactic transformations,
85      * StatementFactory is just not taking advantage of those at this point.
86      *
87      * The subclasses may very much change, in terms of both naming and function, to support whatever StatementFactory
88      * ends up doing.
89      */
90     public abstract static class Single extends IRArgument {
91         private final @NonNull String string;
92
93         Single(final String string) {
94             this.string = requireNonNull(string);
95         }
96
97         /**
98          * Significant portion of this argument. For unquoted and single-quoted strings this is the unquoted string
99          * literal. For double-quoted strings this is the unquoted string, after whitespace trimming as defined by
100          * RFC6020/RFC7950 section 6.1.3, but before escape substitution.
101          *
102          * @return Significant portion of this argument.
103          */
104         public final @NonNull String string() {
105             return string;
106         }
107
108         /**
109          * Imprecise check if this argument needs further unescape operation (which is version-specific) to arrive at
110          * the literal string value. This is false for unquoted and single-quoted strings, which do not support any sort
111          * of escaping. This may be true for double-quoted strings, as they <b>may</b> need to be further processed in
112          * version-dependent ways to arrive at the correct literal value.
113          *
114          * <p>
115          * This method is allowed to err on the false-positive side -- i.e. it may report any double-quoted string as
116          * needing further processing, even when the actual content could be determined to not need further processing.
117          *
118          * @return False if the value of {@link #string} can be used as-is.
119          */
120         public final boolean needUnescape() {
121             return this instanceof DoubleQuoted;
122         }
123
124         /**
125          * Imprecise check if this argument needs an additional content check for compliance. This is false if the
126          * string was explicitly quoted and therefore cannot contain stray single- or double-quotes, or if the content
127          * has already been checked to not contain them.
128          *
129          * <p>
130          * The content check is needed to ascertain RFC7950 compliance, because RFC6020 allows constructs like
131          * <pre>abc"def</pre> in unquoted strings, while RFC7950 explicitly forbids them.
132          *
133          * <p>
134          * This method is allowed to err on the false-positive side -- i.e. it may report any unquoted string as
135          * needing this check, even when the actual content could be determined to not contain quotes.
136          *
137          * @return True if this argument requires a version-specific check for quote content.
138          */
139         public final boolean needQuoteCheck() {
140             return this instanceof Unquoted;
141         }
142
143         /**
144          * Imprecise check if this argument complies with the {@code identifier} YANG specification.
145          *
146          * <p>
147          * This method is allowed to err on the false-negative side -- i.e. it may report any string as not being
148          * compliant with {@code identifier}, even when the actual content could be determined to be compliant.
149          *
150          * @return True if this argument is known to be directly usable in contexts where YANG requires the use of
151          */
152         public final boolean isValidIdentifier() {
153             return this instanceof Identifier;
154         }
155
156         @Override
157         StringBuilder toYangFragment(final StringBuilder sb) {
158             return sb.append(string);
159         }
160     }
161
162     static final class DoubleQuoted extends Single {
163         DoubleQuoted(final String string) {
164             super(string);
165         }
166
167         @Override
168         StringBuilder toYangFragment(final StringBuilder sb) {
169             // Note this is just an approximation. We do not have enough state knowledge to restore any whitespace we
170             // may have trimmed.
171             return super.toYangFragment(sb.append('"')).append('"');
172         }
173     }
174
175     static final class SingleQuoted extends Single {
176         static final @NonNull SingleQuoted EMPTY = new SingleQuoted("");
177
178         SingleQuoted(final String string) {
179             super(string);
180         }
181
182         @Override
183         StringBuilder toYangFragment(final StringBuilder sb) {
184             return super.toYangFragment(sb.append('\'')).append('\'');
185         }
186     }
187
188     static final class Identifier extends Single {
189         Identifier(final String string) {
190             super(string);
191         }
192     }
193
194     static final class Unquoted extends Single {
195         Unquoted(final String string) {
196             super(string);
197         }
198     }
199
200     IRArgument() {
201         // Hidden on purpose
202     }
203 }