1f1e1f7ff545ee055aa2ddc471ad6f548827e4d1
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / core / dom / CharacterLiteral.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11
12 package net.sourceforge.phpdt.core.dom;
13
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
18 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
19 import net.sourceforge.phpdt.internal.compiler.parser.ScannerHelper;
20 import net.sourceforge.phpdt.internal.compiler.parser.TerminalTokens;
21
22 /**
23  * Character literal nodes.
24  * 
25  * @since 2.0
26  * @noinstantiate This class is not intended to be instantiated by clients.
27  */
28 public class CharacterLiteral extends Expression {
29
30         /**
31          * The "escapedValue" structural property of this node type.
32          * @since 3.0
33          */
34         public static final SimplePropertyDescriptor ESCAPED_VALUE_PROPERTY = 
35                 new SimplePropertyDescriptor(CharacterLiteral.class, "escapedValue", String.class, MANDATORY); //$NON-NLS-1$
36         
37         /**
38          * A list of property descriptors (element type: 
39          * {@link StructuralPropertyDescriptor}),
40          * or null if uninitialized.
41          */
42         private static final List PROPERTY_DESCRIPTORS;
43         
44         static {
45                 List properyList = new ArrayList(2);
46                 createPropertyList(CharacterLiteral.class, properyList);
47                 addProperty(ESCAPED_VALUE_PROPERTY, properyList);
48                 PROPERTY_DESCRIPTORS = reapPropertyList(properyList);
49         }
50
51         /**
52          * Returns a list of structural property descriptors for this node type.
53          * Clients must not modify the result.
54          * 
55          * @param apiLevel the API level; one of the
56          * <code>AST.JLS*</code> constants
57
58          * @return a list of property descriptors (element type: 
59          * {@link StructuralPropertyDescriptor})
60          * @since 3.0
61          */
62         public static List propertyDescriptors(int apiLevel) {
63                 return PROPERTY_DESCRIPTORS;
64         }
65                         
66         /**
67          * The literal string, including quotes and escapes; defaults to the 
68          * literal for the character 'X'.
69          */
70         private String escapedValue = "\'X\'";//$NON-NLS-1$
71
72         /**
73          * Creates a new unparented character literal node owned by the given AST.
74          * By default, the character literal denotes an unspecified character.
75          * <p>
76          * N.B. This constructor is package-private.
77          * </p>
78          * 
79          * @param ast the AST that is to own this node
80          */
81         CharacterLiteral(AST ast) {
82                 super(ast);
83         }
84
85         /* (omit javadoc for this method)
86          * Method declared on ASTNode.
87          */
88         final List internalStructuralPropertiesForType(int apiLevel) {
89                 return propertyDescriptors(apiLevel);
90         }
91         
92         /* (omit javadoc for this method)
93          * Method declared on ASTNode.
94          */
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         /* (omit javadoc for this method)
109          * Method declared on ASTNode.
110          */
111         final int getNodeType0() {
112                 return CHARACTER_LITERAL;
113         }
114
115         /* (omit javadoc for this method)
116          * Method declared on ASTNode.
117          */
118         ASTNode clone0(AST target) {
119                 CharacterLiteral result = new CharacterLiteral(target);
120                 result.setSourceRange(this.getStartPosition(), this.getLength());
121                 result.setEscapedValue(getEscapedValue());
122                 return result;
123         }
124
125         /* (omit javadoc for this method)
126          * Method declared on ASTNode.
127          */
128         final boolean subtreeMatch0(ASTMatcher matcher, Object other) {
129                 // dispatch to correct overloaded match method
130                 return matcher.match(this, other);
131         }
132
133         /* (omit javadoc for this method)
134          * Method declared on ASTNode.
135          */
136         void accept0(ASTVisitor visitor) {
137                 visitor.visit(this);
138                 visitor.endVisit(this);
139         }
140         
141         /**
142          * Returns the string value of this literal node. The value is the sequence
143          * of characters that would appear in the source program, including
144          * enclosing single quotes and embedded escapes.
145          * 
146          * @return the escaped string value, including enclosing single quotes
147          *    and embedded escapes
148          */ 
149         public String getEscapedValue() {
150                 return this.escapedValue;
151         }
152                 
153         /**
154          * Sets the string value of this literal node. The value is the sequence
155          * of characters that would appear in the source program, including
156          * enclosing single quotes and embedded escapes. For example,
157          * <ul>
158          * <li><code>'a'</code> <code>setEscapedValue("\'a\'")</code></li>
159          * <li><code>'\n'</code> <code>setEscapedValue("\'\\n\'")</code></li>
160          * </ul>
161          * 
162          * @param value the string value, including enclosing single quotes
163          *    and embedded escapes
164          * @exception IllegalArgumentException if the argument is incorrect
165          */ 
166         public void setEscapedValue(String value) {
167                 // check setInternalEscapedValue(String) if this method is changed
168                 if (value == null) {
169                         throw new IllegalArgumentException();
170                 }
171                 Scanner scanner = this.ast.scanner;
172                 char[] source = value.toCharArray();
173                 scanner.setSource(source);
174                 scanner.resetTo(0, source.length);
175                 try {
176                         int tokenType = scanner.getNextToken();
177                         switch(tokenType) {
178                                 case TerminalTokens.TokenNameCharacterLiteral:
179                                         break;
180                                 default:
181                                         throw new IllegalArgumentException();
182                         }
183                 } catch(InvalidInputException e) {
184                         throw new IllegalArgumentException();
185                 }
186                 preValueChange(ESCAPED_VALUE_PROPERTY);
187                 this.escapedValue = value;
188                 postValueChange(ESCAPED_VALUE_PROPERTY);
189         }
190
191
192         /* (omit javadoc for this method)
193          * This method is a copy of setEscapedValue(String) that doesn't do any validation.
194          */
195         void internalSetEscapedValue(String value) {
196                 preValueChange(ESCAPED_VALUE_PROPERTY);
197                 this.escapedValue = value;
198                 postValueChange(ESCAPED_VALUE_PROPERTY);
199         }
200
201         /**
202          * Returns the value of this literal node. 
203          * <p>
204          * For example,
205          * <pre>
206          * CharacterLiteral s;
207          * s.setEscapedValue("\'x\'");
208          * assert s.charValue() == 'x';
209          * </pre>
210          * </p>
211          * 
212          * @return the character value without enclosing quotes and embedded
213          *    escapes
214          * @exception IllegalArgumentException if the literal value cannot be converted
215          */ 
216         public char charValue() {
217                 Scanner scanner = this.ast.scanner;
218                 char[] source = escapedValue.toCharArray();
219                 scanner.setSource(source);
220                 scanner.resetTo(0, source.length);
221                 int firstChar = scanner.getNextChar();
222                 int secondChar = scanner.getNextChar();
223
224                 if (firstChar == -1 || firstChar != '\'') {
225                         throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
226                 }
227                 char value = (char) secondChar;
228                 int nextChar = scanner.getNextChar();
229                 if (secondChar == '\\') {
230                         if (nextChar == -1) {
231                                 throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
232                         }
233                         switch(nextChar) {
234                                 case 'b' :
235                                         value = '\b';
236                                         break;
237                                 case 't' :
238                                         value = '\t';
239                                         break;
240                                 case 'n' :
241                                         value = '\n';
242                                         break;
243                                 case 'f' :
244                                         value = '\f';
245                                         break;
246                                 case 'r' :
247                                         value = '\r';
248                                         break;
249                                 case '\"':
250                                         value = '\"';
251                                         break;
252                                 case '\'':
253                                         value = '\'';
254                                         break;
255                                 case '\\':
256                                         value = '\\';
257                                         break;
258                                 default : //octal (well-formed: ended by a ' )
259                                         try {
260                                                 if (ScannerHelper.isDigit((char) nextChar)) {
261                                                         int number = ScannerHelper.getNumericValue((char) nextChar);
262                                                         nextChar = scanner.getNextChar();
263                                                         if (nextChar == -1) {
264                                                                 throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
265                                                         }
266                                                         if (nextChar != '\'') {
267                                                                 if (!ScannerHelper.isDigit((char) nextChar)) {
268                                                                         throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
269                                                                 }
270                                                                 number = (number * 8) + ScannerHelper.getNumericValue((char) nextChar);
271                                                                 nextChar = scanner.getNextChar();
272                                                                 if (nextChar == -1) {
273                                                                         throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
274                                                                 }
275                                                                 if (nextChar != '\'') {
276                                                                         if (!ScannerHelper.isDigit((char) nextChar)) {
277                                                                                 throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
278                                                                         }
279                                                                         number = (number * 8) + ScannerHelper.getNumericValue((char) nextChar);
280                                                                 }
281                                                         }
282                                                         return (char) number;
283                                                 } else {
284                                                         throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
285                                                 }
286                                         } catch (InvalidInputException e) {
287                                                 throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
288                                         }
289                         }
290                         nextChar = scanner.getNextChar();
291                         if (nextChar == -1) {
292                                 throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
293                         }
294                 }
295                 if (nextChar == -1 || nextChar != '\'') {
296                         throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$
297                 }
298                 return value;
299         }
300         /**
301          * Sets the value of this character literal node to the given character. 
302          * <p>
303          * For example,
304          * <pre>
305          * CharacterLiteral s;
306          * s.setCharValue('x');
307          * assert s.charValue() == 'x';
308          * assert s.getEscapedValue("\'x\'");
309          * </pre>
310          * </p>
311          * 
312          * @param value the character value
313          */
314         public void setCharValue(char value) {
315                 StringBuffer b = new StringBuffer(3);
316                 
317                 b.append('\''); // opening delimiter
318                 switch(value) {
319                         case '\b' :
320                                 b.append("\\b"); //$NON-NLS-1$
321                                 break;
322                         case '\t' :
323                                 b.append("\\t"); //$NON-NLS-1$
324                                 break;
325                         case '\n' :
326                                 b.append("\\n"); //$NON-NLS-1$
327                                 break;
328                         case '\f' :
329                                 b.append("\\f"); //$NON-NLS-1$
330                                 break;
331                         case '\r' :
332                                 b.append("\\r"); //$NON-NLS-1$
333                                 break;
334                         case '\"':
335                                 b.append("\\\""); //$NON-NLS-1$
336                                 break;
337                         case '\'':
338                                 b.append("\\\'"); //$NON-NLS-1$
339                                 break;
340                         case '\\':
341                                 b.append("\\\\"); //$NON-NLS-1$
342                                 break;
343                         case '\0' :
344                                 b.append("\\0"); //$NON-NLS-1$
345                                 break;
346                         case '\1' :
347                                 b.append("\\1"); //$NON-NLS-1$
348                                 break;
349                         case '\2' :
350                                 b.append("\\2"); //$NON-NLS-1$
351                                 break;
352                         case '\3' :
353                                 b.append("\\3"); //$NON-NLS-1$
354                                 break;
355                         case '\4' :
356                                 b.append("\\4"); //$NON-NLS-1$
357                                 break;
358                         case '\5' :
359                                 b.append("\\5"); //$NON-NLS-1$
360                                 break;
361                         case '\6' :
362                                 b.append("\\6"); //$NON-NLS-1$
363                                 break;
364                         case '\7' :
365                                 b.append("\\7"); //$NON-NLS-1$
366                                 break;                  
367                         default:
368                                 b.append(value);
369                 }
370                 b.append('\''); // closing delimiter
371                 setEscapedValue(b.toString());
372         }
373         
374         /* (omit javadoc for this method)
375          * Method declared on ASTNode.
376          */
377         int memSize() {
378                 int size = BASE_NODE_SIZE + 1 * 4 + stringSize(escapedValue);
379                 return size;
380         }
381         
382         /* (omit javadoc for this method)
383          * Method declared on ASTNode.
384          */
385         int treeSize() {
386                 return memSize();
387         }
388 }
389