1 | /******************************************************************************* |
---|---|
2 | * Copyright (c) 2000, 2019 IBM Corporation and others. |
3 | * |
4 | * This program and the accompanying materials |
5 | * are made available under the terms of the Eclipse Public License 2.0 |
6 | * which accompanies this distribution, and is available at |
7 | * https://www.eclipse.org/legal/epl-2.0/ |
8 | * |
9 | * SPDX-License-Identifier: EPL-2.0 |
10 | * |
11 | * Contributors: |
12 | * IBM Corporation - initial API and implementation |
13 | *******************************************************************************/ |
14 | |
15 | package org.eclipse.jdt.core.dom; |
16 | |
17 | import java.util.ArrayList; |
18 | import java.util.List; |
19 | |
20 | import org.eclipse.jdt.core.compiler.InvalidInputException; |
21 | import org.eclipse.jdt.internal.compiler.parser.Scanner; |
22 | import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; |
23 | import org.eclipse.jdt.internal.compiler.util.Util; |
24 | |
25 | /** |
26 | * String literal nodes. |
27 | * |
28 | * @since 2.0 |
29 | * @noinstantiate This class is not intended to be instantiated by clients. |
30 | */ |
31 | @SuppressWarnings("rawtypes") |
32 | public class StringLiteral extends Expression { |
33 | |
34 | /** |
35 | * The "escapedValue" structural property of this node type (type: {@link String}). |
36 | * @since 3.0 |
37 | */ |
38 | public static final SimplePropertyDescriptor ESCAPED_VALUE_PROPERTY = |
39 | new SimplePropertyDescriptor(StringLiteral.class, "escapedValue", String.class, MANDATORY); //$NON-NLS-1$ |
40 | |
41 | /** |
42 | * A list of property descriptors (element type: |
43 | * {@link StructuralPropertyDescriptor}), |
44 | * or null if uninitialized. |
45 | */ |
46 | private static final List PROPERTY_DESCRIPTORS; |
47 | |
48 | static { |
49 | List propertyList = new ArrayList(2); |
50 | createPropertyList(StringLiteral.class, propertyList); |
51 | addProperty(ESCAPED_VALUE_PROPERTY, propertyList); |
52 | PROPERTY_DESCRIPTORS = reapPropertyList(propertyList); |
53 | } |
54 | |
55 | /** |
56 | * Returns a list of structural property descriptors for this node type. |
57 | * Clients must not modify the result. |
58 | * |
59 | * @param apiLevel the API level; one of the |
60 | * <code>AST.JLS*</code> constants |
61 | |
62 | * @return a list of property descriptors (element type: |
63 | * {@link StructuralPropertyDescriptor}) |
64 | * @since 3.0 |
65 | */ |
66 | public static List propertyDescriptors(int apiLevel) { |
67 | return PROPERTY_DESCRIPTORS; |
68 | } |
69 | |
70 | /** |
71 | * The literal string, including quotes and escapes; defaults to the |
72 | * literal for the empty string. |
73 | */ |
74 | private String escapedValue = "\"\"";//$NON-NLS-1$ |
75 | |
76 | /** |
77 | * Creates a new unparented string literal node owned by the given AST. |
78 | * By default, the string literal denotes the empty string. |
79 | * <p> |
80 | * N.B. This constructor is package-private. |
81 | * </p> |
82 | * |
83 | * @param ast the AST that is to own this node |
84 | */ |
85 | StringLiteral(AST ast) { |
86 | super(ast); |
87 | } |
88 | |
89 | @Override |
90 | final List internalStructuralPropertiesForType(int apiLevel) { |
91 | return propertyDescriptors(apiLevel); |
92 | } |
93 | |
94 | @Override |
95 | final Object internalGetSetObjectProperty(SimplePropertyDescriptor property, boolean get, Object value) { |
96 | if (property == ESCAPED_VALUE_PROPERTY) { |
97 | if (get) { |
98 | return getEscapedValue(); |
99 | } else { |
100 | setEscapedValue((String) value); |
101 | return null; |
102 | } |
103 | } |
104 | // allow default implementation to flag the error |
105 | return super.internalGetSetObjectProperty(property, get, value); |
106 | } |
107 | |
108 | @Override |
109 | final int getNodeType0() { |
110 | return STRING_LITERAL; |
111 | } |
112 | |
113 | @Override |
114 | ASTNode clone0(AST target) { |
115 | StringLiteral result = new StringLiteral(target); |
116 | result.setSourceRange(getStartPosition(), getLength()); |
117 | result.setEscapedValue(getEscapedValue()); |
118 | return result; |
119 | } |
120 | |
121 | @Override |
122 | final boolean subtreeMatch0(ASTMatcher matcher, Object other) { |
123 | // dispatch to correct overloaded match method |
124 | return matcher.match(this, other); |
125 | } |
126 | |
127 | @Override |
128 | void accept0(ASTVisitor visitor) { |
129 | visitor.visit(this); |
130 | visitor.endVisit(this); |
131 | } |
132 | |
133 | /** |
134 | * Returns the string value of this literal node to the given string |
135 | * literal token. The token is the sequence of characters that would appear |
136 | * in the source program, including enclosing double quotes and embedded |
137 | * escapes. |
138 | * |
139 | * @return the string literal token, including enclosing double |
140 | * quotes and embedded escapes |
141 | */ |
142 | public String getEscapedValue() { |
143 | return this.escapedValue; |
144 | } |
145 | |
146 | /** |
147 | * Sets the string value of this literal node to the given string literal |
148 | * token. The token is the sequence of characters that would appear in the |
149 | * source program, including enclosing double quotes and embedded escapes. |
150 | * For example, |
151 | * <ul> |
152 | * <li><code>""</code> <code>setLiteral("\"\"")</code></li> |
153 | * <li><code>"hello world"</code> <code>setLiteral("\"hello world\"")</code></li> |
154 | * <li><code>"boo\nhoo"</code> <code>setLiteral("\"boo\\nhoo\"")</code></li> |
155 | * </ul> |
156 | * |
157 | * @param token the string literal token, including enclosing double |
158 | * quotes and embedded escapes |
159 | * @exception IllegalArgumentException if the argument is incorrect |
160 | */ |
161 | public void setEscapedValue(String token) { |
162 | // update internalSetEscapedValue(String) if this is changed |
163 | if (token == null) { |
164 | throw new IllegalArgumentException("Token cannot be null"); //$NON-NLS-1$ |
165 | } |
166 | Scanner scanner = this.ast.scanner; |
167 | char[] source = token.toCharArray(); |
168 | scanner.setSource(source); |
169 | scanner.resetTo(0, source.length); |
170 | try { |
171 | int tokenType = scanner.getNextToken(); |
172 | switch(tokenType) { |
173 | case TerminalTokens.TokenNameStringLiteral: |
174 | break; |
175 | default: |
176 | throw new IllegalArgumentException("Invalid string literal : >" + token + "<"); //$NON-NLS-1$//$NON-NLS-2$ |
177 | } |
178 | } catch(InvalidInputException e) { |
179 | throw new IllegalArgumentException("Invalid string literal : >" + token + "<");//$NON-NLS-1$//$NON-NLS-2$ |
180 | } |
181 | preValueChange(ESCAPED_VALUE_PROPERTY); |
182 | this.escapedValue = token; |
183 | postValueChange(ESCAPED_VALUE_PROPERTY); |
184 | } |
185 | |
186 | /* (omit javadoc for this method) |
187 | * This method is a copy of setEscapedValue(String) that doesn't do any validation. |
188 | */ |
189 | void internalSetEscapedValue(String token) { |
190 | preValueChange(ESCAPED_VALUE_PROPERTY); |
191 | this.escapedValue = token; |
192 | postValueChange(ESCAPED_VALUE_PROPERTY); |
193 | } |
194 | |
195 | /** |
196 | * Returns the value of this literal node. |
197 | * <p> |
198 | * For example, |
199 | * <pre> |
200 | * StringLiteral s; |
201 | * s.setEscapedValue("\"hello\\nworld\""); |
202 | * assert s.getLiteralValue().equals("hello\nworld"); |
203 | * </pre> |
204 | * <p> |
205 | * Note that this is a convenience method that converts from the stored |
206 | * string literal token returned by <code>getEscapedLiteral</code>. |
207 | * </p> |
208 | * |
209 | * @return the string value without enclosing double quotes and embedded |
210 | * escapes |
211 | * @exception IllegalArgumentException if the literal value cannot be converted |
212 | */ |
213 | public String getLiteralValue() { |
214 | String s = getEscapedValue(); |
215 | int len = s.length(); |
216 | if (len < 2 || s.charAt(0) != '\"' || s.charAt(len-1) != '\"' ) { |
217 | throw new IllegalArgumentException(); |
218 | } |
219 | |
220 | Scanner scanner = this.ast.scanner; |
221 | char[] source = s.toCharArray(); |
222 | scanner.setSource(source); |
223 | scanner.resetTo(0, source.length); |
224 | try { |
225 | int tokenType = scanner.getNextToken(); |
226 | switch(tokenType) { |
227 | case TerminalTokens.TokenNameStringLiteral: |
228 | return scanner.getCurrentStringLiteral(); |
229 | default: |
230 | throw new IllegalArgumentException(); |
231 | } |
232 | } catch(InvalidInputException e) { |
233 | throw new IllegalArgumentException(); |
234 | } |
235 | } |
236 | |
237 | /** |
238 | * Sets the value of this literal node. |
239 | * <p> |
240 | * For example, |
241 | * <pre> |
242 | * StringLiteral s; |
243 | * s.setLiteralValue("hello\nworld"); |
244 | * assert s.getEscapedValue().equals("\"hello\\nworld\""); |
245 | * assert s.getLiteralValue().equals("hello\nworld"); |
246 | * </pre> |
247 | * <p> |
248 | * Note that this is a convenience method that converts to the stored |
249 | * string literal token acceptable to <code>setEscapedLiteral</code>. |
250 | * </p> |
251 | * |
252 | * @param value the string value without enclosing double quotes and |
253 | * embedded escapes |
254 | * @exception IllegalArgumentException if the argument is incorrect |
255 | */ |
256 | public void setLiteralValue(String value) { |
257 | if (value == null) { |
258 | throw new IllegalArgumentException(); |
259 | } |
260 | int len = value.length(); |
261 | StringBuffer b = new StringBuffer(len + 2); |
262 | |
263 | b.append("\""); // opening delimiter //$NON-NLS-1$ |
264 | for (int i = 0; i < len; i++) { |
265 | char c = value.charAt(i); |
266 | Util.appendEscapedChar(b, c, true); |
267 | } |
268 | b.append("\""); // closing delimiter //$NON-NLS-1$ |
269 | setEscapedValue(b.toString()); |
270 | } |
271 | |
272 | @Override |
273 | int memSize() { |
274 | int size = BASE_NODE_SIZE + 1 * 4 + stringSize(this.escapedValue); |
275 | return size; |
276 | } |
277 | |
278 | @Override |
279 | int treeSize() { |
280 | return memSize(); |
281 | } |
282 | } |
283 |
Members