View Javadoc

1   package cz.cuni.amis.pogamut.shady;
2   
3   /**
4    * Interface that unified various arguments used when shade is calling
5    * the primitive. 
6    * @author Honza
7    */
8   public interface IArgument<T> {
9   
10      T getValue();
11  }
12  
13  /**
14   * TODO:Stub
15   * @author Honza
16   */
17  class Arg<T> implements IArgument<T> {
18  
19      private final T value;
20  
21      public Arg(T value) {
22          this.value = value;
23      }
24  
25      @Override
26      public T getValue() {
27          return value;
28      }
29  
30      @Override
31      public boolean equals(Object obj) {
32          if (obj == null) {
33              return false;
34          }
35          if (obj == this) {
36              return true;
37          }
38          if (!(obj instanceof IArgument)) {
39              return false;
40          }
41          IArgument arg = (IArgument) obj;
42          return this.value == null ? arg.getValue() == null : value.equals(arg.getValue());
43      }
44  }
45  
46  class ArgInt extends Arg<Integer> {
47  
48      public ArgInt(int number) {
49          super(number);
50      }
51  }
52  
53  class ArgFloat extends Arg<Double> {
54  
55      public ArgFloat(double number) {
56          super(number);
57      }
58  }
59  
60  class ArgChar extends Arg<Character> {
61  
62      public ArgChar(char character) {
63          super(character);
64      }
65  
66      /**
67       * The the escaped char (e.g. '\n' but without quotes) and return its actual
68       * value.
69       * <p/>
70       * There is slight difference in valid strings ("'", "\'" and "\"") vs valid 
71       * chars ('"', '\"' and '\''), but not all are interchangeable.
72       * 
73       */
74      public static char unescape(String escapedChar) throws ParseException {
75          if (escapedChar.length() == 0) {
76              throw new ParseException("The string representing the escaped character must have legth > 0.");
77          }
78          StringBuilder sb = new StringBuilder(escapedChar);
79          char result = parseCharacter(sb);
80          if (sb.length() > 0)
81              throw new ParseException("There still are some unparsed characters remaining(" + sb.length() + "):" + sb.toString());
82          return result;
83      }
84  
85      /**
86       * Take string starting with a single escaped character (e.g. 'ahoj', 
87       * '\nWorld'..) try to parse the first character and return it. Remove the 
88       * characters representing the escaped character form the sb.
89       * @param sb string that should start with escaped character
90       * @return parsed character
91       * @throws ParseException
92       */
93      protected static char parseCharacter(StringBuilder sb) throws ParseException {
94          char result = sb.charAt(0);
95          sb.deleteCharAt(0);
96          if (result == '\\') {
97              Character parsed = ArgChar.parseEscapeSequence(sb);
98              if (parsed == null) {
99                  throw new ParseException("Unable to unescape sequence:" + sb.toString());
100             }
101             result = parsed;
102         }
103         return result;
104     }
105 
106     
107     /**
108      * The the escaped character literal (e.g.  'a', '\n', '\0' or '\u3067' 
109      * including quotes) and return the character that is represented by this literal.
110      */
111     public static char parseCharacterListeral(String charLiteral) throws ParseException {
112         StringBuilder sb = new StringBuilder(charLiteral);
113         if (sb.charAt(0) != '\'') {
114             throw new ParseException("Expecting \' at the start of " + charLiteral.toString());
115         }
116         sb.deleteCharAt(0);
117 
118         char result = parseCharacter(sb);
119 
120         if (sb.length() != 1) {
121             throw new ParseException("Expecting exactly one character (single quote), but " + sb.length() + " characters remain:" + sb.toString());
122         }
123         if (sb.charAt(0) != '\'') {
124             throw new ParseException("Expecting ending double quote, but got: " + sb.charAt(0));
125         }
126         return result;
127     }
128 
129     /**
130      * Try to parse single escape sequence (we assume that backslash has already 
131      * been eaten):
132      * <ul>
133      *  <li>[0-3][0-7][0-7] - into character with specified octal number</li>
134      *  <li>[0-7][0-7]</li>
135      *  <li>[0-7]</li>
136      *  <li>b,t,n,r,f,",',\ - into backspace, tab, LF, CR, FF, ", ' and \</li>
137      *  <li>(u)+\p{XDigit}\p{XDigit}\p{XDigit}\p{XDigit} - into character with specified hexa number</li>
138      * </ul>
139      * If there is no escape sequence, don't touch sb and return null.
140      * @param sb the escaped char should be at the beginning of sb.
141      * @return character represented by escape sequence or null
142      */
143     protected static Character parseEscapeSequence(StringBuilder sb) {
144         // take care of octal first, it uses no prefix
145         Character octal = parseOctal(sb);
146         if (octal != null) {
147             return octal;
148         }
149         // take care of single char escape sequence
150         Character simpleEscape = parseSingleEscape(sb);
151         if (simpleEscape != null) {
152             return simpleEscape;
153         }
154         Character unicode = parseUnicode(sb);
155         if (unicode != null) {
156             return unicode;
157         }
158         return null;
159     }
160 
161     /**
162      * Try to parse allowed octal number (0-255 ~o0-o377) at the start of 
163      * the passed string.
164      * @param sb string buffer that may have octal number at the start. If octal 
165      *        number is found, remove it from the start.
166      * @return parsed number or null if not found.
167      */
168     protected static Character parseOctal(StringBuilder sb) {
169         if (sb.length() >= 3) {
170             String octal3 = sb.substring(0, 3);
171             if (octal3.matches("[0-3][0-7][0-7]")) {
172                 sb.delete(0, 3);
173                 return Character.toChars(Integer.parseInt(octal3, 8))[0];
174             }
175         }
176         if (sb.length() >= 2) {
177             String octal2 = sb.substring(0, 2);
178             if (octal2.matches("[0-7][0-7]")) {
179                 sb.delete(0, 2);
180                 return Character.toChars(Integer.parseInt(octal2, 8))[0];
181             }
182         }
183         String octal1 = sb.substring(0, 1);
184         if (octal1.matches("[0-7]")) {
185             sb.delete(0, 1);
186             return Character.toChars(Integer.parseInt(octal1, 8))[0];
187         }
188         return null;
189     }
190 
191     /**
192      * Check if there is a single char escape sequence (we assume that backslash
193      * has already been deleted) and if it is, remove it from sb and return 
194      * the escaped character.
195      * Example: sequence 'nAndy said.' could would return character '\n' and sb 
196      * would delete first character, thus being 'Andy said.'
197      * @param sb
198      * @return escaped character or null if there is something else (e.g. m)
199      */
200     protected static Character parseSingleEscape(StringBuilder sb) {
201         char ch = sb.charAt(0);
202         char res;
203         switch (ch) {
204             case 'b':
205                 res = '\b'; // backspace BS
206                 break;
207             case 't':
208                 res = '\t'; // horizontal tab
209                 break;
210             case 'n':
211                 res = '\n'; // linefeed LF
212                 break;
213             case 'f':
214                 res = '\f'; // form feed FF
215                 break;
216             case 'r':
217                 res = '\r'; // carriage return CR
218                 break;
219             case '"':
220                 res = '\"'; // double quote
221                 break;
222             case '\'':
223                 res = '\''; // single quote
224                 break;
225             case '\\':
226                 res = '\\'; // backslash
227                 break;
228             default:
229                 return null;
230         }
231         sb.deleteCharAt(0);
232         return res;
233     }
234 
235     /**
236      * Check if there is unicode escape at the start of sb (without backslash)
237      * and if it is, parse it, remove it from the sb and return parsed unicode
238      * character. 
239      * Example: 'u306A\u306b \u30673059 \u304B?' ('nani desu ka?') would return
240      * character '\u306A' (na) and in sb would be '\u306b \u30673059 \u304B?'
241      * @param sb sb, unescaped character will be removed
242      * @return unescaped character or null
243      */
244     protected static Character parseUnicode(StringBuilder sb) {
245         String regexp = "u+\\p{XDigit}\\p{XDigit}\\p{XDigit}\\p{XDigit}.*";
246         if (sb.toString().matches(regexp)) {
247             while (sb.charAt(0) == 'u') {
248                 sb.deleteCharAt(0);
249             }
250             String hexaString = sb.substring(0, 4);
251             sb.delete(0, 4);
252             return Character.toChars(Integer.parseInt(hexaString, 16))[0];
253         }
254         return null;
255     }
256 }