AI2 Component  (Version nb184)
Ev3BinaryParser.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2011-2012 MIT, All rights reserved
3 // Released under the Apache License, Version 2.0
4 // http://www.apache.org/licenses/LICENSE-2.0
5 
6 package com.google.appinventor.components.runtime.util;
7 
8 import java.util.ArrayList;
9 import java.nio.ByteBuffer;
10 import java.nio.ByteOrder;
11 import java.io.UnsupportedEncodingException;
12 
21 public class Ev3BinaryParser {
22  private static byte PRIMPAR_SHORT = (byte) 0x00;
23  private static byte PRIMPAR_LONG = (byte) 0x80;
24 
25  private static byte PRIMPAR_CONST = (byte) 0x00;
26  private static byte PRIMPAR_VARIABEL = (byte) 0x40;
27  private static byte PRIMPAR_LOCAL = (byte) 0x00;
28  private static byte PRIMPAR_GLOBAL = (byte) 0x20;
29  private static byte PRIMPAR_HANDLE = (byte) 0x10;
30  private static byte PRIMPAR_ADDR = (byte) 0x08;
31 
32  private static byte PRIMPAR_INDEX = (byte) 0x1F;
33  private static byte PRIMPAR_CONST_SIGN = (byte) 0x20;
34  private static byte PRIMPAR_VALUE = (byte) 0x3F;
35 
36  private static byte PRIMPAR_BYTES = (byte) 0x07;
37 
38  private static byte PRIMPAR_STRING_OLD = (byte) 0;
39  private static byte PRIMPAR_1_BYTE = (byte) 1;
40  private static byte PRIMPAR_2_BYTES = (byte) 2;
41  private static byte PRIMPAR_4_BYTES = (byte) 3;
42  private static byte PRIMPAR_STRING = (byte) 4;
43 
44  private static class FormatLiteral {
45  public char symbol;
46  public int size;
47 
48  public FormatLiteral(char symbol, int size) {
49  this.symbol = symbol;
50  this.size = size;
51  }
52  }
53 
54  public static byte[] pack(String format, Object... values) throws IllegalArgumentException {
55  // parse format string
56  String[] formatTokens = format.split("(?<=\\D)");
57  FormatLiteral[] literals = new FormatLiteral[formatTokens.length];
58  int index = 0;
59  int bufferCapacity = 0;
60 
61  // calculate buffer size
62  for (int i = 0; i < formatTokens.length; i++) {
63  String token = formatTokens[i];
64  char symbol = token.charAt(token.length() - 1);
65  int size = 1;
66  boolean sizeSpecified = false;
67 
68  if (token.length() != 1)
69  {
70  size = Integer.parseInt(token.substring(0, token.length() - 1));
71  sizeSpecified = true;
72 
73  if (size < 1)
74  throw new IllegalArgumentException("Illegal format string");
75  }
76 
77  switch (symbol) {
78  case 'x':
79  bufferCapacity += size;
80  break;
81 
82  case 'b':
83  bufferCapacity += size;
84  index += size;
85  break;
86 
87  case 'B':
88  bufferCapacity += size;
89  index++;
90  break;
91 
92  case 'h':
93  bufferCapacity += size * 2;
94  index += size;
95  break;
96 
97  case 'H':
98  bufferCapacity += size * 2;
99  index++;
100  break;
101 
102  case 'i':
103  bufferCapacity += size * 4;
104  index += size;
105  break;
106 
107  case 'I':
108  bufferCapacity += size * 4;
109  index++;
110  break;
111 
112  case 'l':
113  bufferCapacity += size * 8;
114  index += size;
115  break;
116 
117  case 'L':
118  bufferCapacity += size * 8;
119  index++;
120  break;
121 
122  case 'f':
123  bufferCapacity += size * 4;
124  index += size;
125  break;
126 
127  case 'F':
128  bufferCapacity += size * 4;
129  index++;
130  break;
131 
132  case 's':
133  if (size != ((String) values[index]).length())
134  throw new IllegalArgumentException("Illegal format string");
135 
136  bufferCapacity += size;
137  index++;
138  break;
139 
140  case 'S':
141  if (sizeSpecified)
142  throw new IllegalArgumentException("Illegal format string");
143 
144  bufferCapacity += ((String) values[index]).length() + 1;
145  index++;
146  break;
147 
148  default:
149  throw new IllegalArgumentException("Illegal format string");
150  }
151 
152  literals[i] = new FormatLiteral(symbol, size);
153  }
154 
155  if (index != values.length)
156  throw new IllegalArgumentException("Illegal format string");
157 
158  // generate byte buffer
159  index = 0;
160  ByteBuffer buffer = ByteBuffer.allocate(bufferCapacity);
161  buffer.order(ByteOrder.LITTLE_ENDIAN);
162 
163  for (FormatLiteral literal: literals) {
164  switch (literal.symbol) {
165  case 'x':
166  for (int i = 0; i < literal.size; i++)
167  buffer.put((byte) 0x00);
168  break;
169 
170  case 'b':
171  for (int i = 0; i < literal.size; i++) {
172  buffer.put((Byte) values[index]);
173  index += 1;
174  }
175  break;
176 
177  case 'B':
178  buffer.put((byte[]) values[index]);
179  index++;
180  break;
181 
182  case 'h':
183  for (int i = 0; i < literal.size; i++) {
184  buffer.putShort((Short) values[index]);
185  index += 1;
186  }
187  break;
188 
189  case 'H':
190  for (int i = 0; i < literal.size; i++) {
191  buffer.putShort(((short[]) values[index])[i]);
192  }
193  index++;
194  break;
195 
196  case 'i':
197  for (int i = 0; i < literal.size; i++) {
198  buffer.putInt((Integer) values[index]);
199  index += 1;
200  }
201  break;
202 
203  case 'I':
204  for (int i = 0; i < literal.size; i++) {
205  buffer.putInt(((int[]) values[index])[i]);
206  }
207  index++;
208  break;
209 
210  case 'l':
211  for (int i = 0; i < literal.size; i++) {
212  buffer.putLong((Long) values[index]);
213  index += 1;
214  }
215  break;
216 
217  case 'L':
218  for (int i = 0; i < literal.size; i++) {
219  buffer.putLong(((long[]) values[index])[i]);
220  }
221  index++;
222  break;
223 
224  case 'f':
225  for (int i = 0; i < literal.size; i++) {
226  buffer.putFloat((Float) values[index]);
227  index += 1;
228  }
229  break;
230 
231  case 'F':
232  for (int i = 0; i < literal.size; i++) {
233  buffer.putFloat(((float[]) values[index])[i]);
234  }
235  index++;
236  break;
237 
238  case 's':
239  try {
240  buffer.put(((String) values[index]).getBytes("US-ASCII"));
241  } catch (UnsupportedEncodingException e) {
242  throw new IllegalArgumentException(); // non-ASCII cases are regarded as wrong argument exception
243  }
244  index++;
245  break;
246 
247  case 'S':
248  try {
249  buffer.put(((String) values[index]).getBytes("US-ASCII"));
250  } catch (UnsupportedEncodingException e) {
251  throw new IllegalArgumentException(); // non-ASCII cases are regarded as wrong argument exception
252  }
253  buffer.put((byte) 0x00);
254  index++;
255  }
256  }
257 
258  return buffer.array();
259  }
260 
261  public static Object[] unpack(String format, byte[] bytes) throws IllegalArgumentException {
262  String[] formatTokens = format.split("(?<=\\D)");
263  ArrayList<Object> decodedObjects = new ArrayList<Object>();
264  ByteBuffer buffer = ByteBuffer.wrap(bytes);
265  buffer.order(ByteOrder.LITTLE_ENDIAN);
266 
267  for (String token: formatTokens) {
268  boolean sizeSpecified = false;
269  int size = 1;
270  char symbol = token.charAt(token.length() - 1);
271 
272  if (token.length() > 1) {
273  sizeSpecified = true;
274  size = Integer.parseInt(token.substring(0, token.length() - 1));
275 
276  if (size < 1)
277  throw new IllegalArgumentException("Illegal format string");
278  }
279 
280  switch (symbol) {
281  case 'x':
282  for (int i = 0; i < size; i++)
283  buffer.get();
284  break;
285 
286  case 'b':
287  for (int i = 0; i < size; i++)
288  decodedObjects.add(buffer.get());
289  break;
290 
291  case 'B':
292  byte[] byteArray = new byte[size];
293  buffer.get(byteArray, 0, size);
294  decodedObjects.add(byteArray);
295  break;
296 
297  case 'h':
298  for (int i = 0; i < size; i++)
299  decodedObjects.add(buffer.getShort());
300  break;
301 
302  case 'H':
303  short[] shorts = new short[size];
304  for (short i = 0; i < size; i++)
305  shorts[i] = buffer.getShort();
306  decodedObjects.add(shorts);
307  break;
308 
309  case 'i':
310  for (int i = 0; i < size; i++)
311  decodedObjects.add(buffer.getInt());
312  break;
313 
314  case 'I':
315  int[] integers = new int[size];
316  for (int i = 0; i < size; i++)
317  integers[i] = buffer.getInt();
318  decodedObjects.add(integers);
319  break;
320 
321  case 'l':
322  for (int i = 0; i < size; i++)
323  decodedObjects.add(buffer.getLong());
324  break;
325 
326  case 'L':
327  long[] longs = new long[size];
328  for (int i = 0; i < size; i++)
329  longs[i] = buffer.getLong();
330  decodedObjects.add(longs);
331  break;
332 
333  case 'f':
334  for (int i = 0; i < size; i++)
335  decodedObjects.add(buffer.getFloat());
336  break;
337 
338  case 'F':
339  float[] floats = new float[size];
340  for (int i = 0; i < size; i++)
341  floats[i] = buffer.getFloat();
342  decodedObjects.add(floats);
343  break;
344 
345  case 's':
346  byte[] byteString = new byte[size];
347  buffer.get(byteString, 0, size);
348  try {
349  decodedObjects.add(new String(byteString, "US-ASCII"));
350  } catch (UnsupportedEncodingException e) {
351  throw new IllegalArgumentException(); // // non-ASCII cases are regarded as wrong argument exception
352  }
353  break;
354 
355  case 'S':
356  if (sizeSpecified)
357  throw new IllegalArgumentException("Illegal format string");
358 
359  StringBuffer stringBuffer = new StringBuffer();
360 
361  while (true) {
362  byte b = buffer.get();
363  if (b != (byte) 0x00)
364  stringBuffer.append((char) b);
365  else
366  break;
367  }
368 
369  decodedObjects.add(stringBuffer.toString());
370  break;
371 
372  case '$':
373  if (sizeSpecified)
374  throw new IllegalArgumentException("Illegal format string");
375 
376  if (buffer.hasRemaining())
377  throw new IllegalArgumentException("Illegal format string");
378 
379  default:
380  throw new IllegalArgumentException("Illegal format string");
381  }
382  }
383 
384  return decodedObjects.toArray();
385  }
386 
387  public static byte[] encodeLC0(byte v) {
388  if (v < -31 || v > 31)
389  throw new IllegalArgumentException("Encoded value must be in range [0, 127]");
390 
391  return new byte[] { (byte) (v & PRIMPAR_VALUE) };
392  }
393 
394  public static byte[] encodeLC1(byte v) {
395  return new byte[] {(byte) ((byte) (PRIMPAR_LONG | PRIMPAR_CONST) | PRIMPAR_1_BYTE),
396  (byte) (v & 0xFF)};
397  }
398 
399  public static byte[] encodeLC2(short v) {
400  return new byte[] {(byte) ((byte) (PRIMPAR_LONG | PRIMPAR_CONST) | PRIMPAR_2_BYTES),
401  (byte) (v & 0xFF),
402  (byte) ((v >>> 8) & 0xFF)};
403  }
404 
405  public static byte[] encodeLC4(int v) {
406  return new byte[] {(byte) ((byte) (PRIMPAR_LONG | PRIMPAR_CONST) | PRIMPAR_4_BYTES),
407  (byte) (v & 0xFF),
408  (byte) ((v >>> 8) & 0xFF),
409  (byte) ((v >>> 16) & 0xFF),
410  (byte) ((v >>> 24) & 0xFF)};
411  }
412 
413 
414  public static byte[] encodeLV0(int i) {
415  return new byte[] {(byte) ((i & PRIMPAR_INDEX) | PRIMPAR_SHORT | PRIMPAR_VARIABEL | PRIMPAR_LOCAL)};
416  }
417 
418  public static byte[] encodeLV1(int i) {
419  return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_1_BYTE),
420  (byte) (i & 0xFF)};
421  }
422 
423  public static byte[] encodeLV2(int i) {
424  return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_2_BYTES),
425  (byte) (i & 0xFF),
426  (byte) ((i >>> 8) & 0xFF)};
427  }
428 
429  public static byte[] encodeLV4(int i) {
430  return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_4_BYTES),
431  (byte) (i & 0xFF),
432  (byte) ((i >>> 8) & 0xFF),
433  (byte) ((i >>> 16) & 0xFF),
434  (byte) ((i >>> 24) & 0xFF)};
435  }
436 
437  public static byte[] encodeGV0(int i) {
438  return new byte[] {(byte)((i & PRIMPAR_INDEX) | PRIMPAR_SHORT | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL)};
439  }
440 
441  public static byte[] encodeGV1(int i) {
442  return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_1_BYTE),
443  (byte) (i & 0xFF)};
444  }
445 
446  public static byte[] encodeGV2(int i) {
447  return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_2_BYTES),
448  (byte) (i & 0xFF),
449  (byte) ((i >>> 8) & 0xFF)};
450  }
451 
452  public static byte[] encodeGV4(int i) {
453  return new byte[] {(byte) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_4_BYTES),
454  (byte) (i & 0xFF),
455  (byte) ((i >>> 8) & 0xFF),
456  (byte) ((i >>> 16) & 0xFF),
457  (byte) ((i >>> 24) & 0xFF)};
458  }
459 
460 
461  public static byte[] encodeSystemCommand(byte command, boolean needReply, Object... parameters) {
462  int bufferCapacity = 2;
463 
464  // calculate buffer size
465  for (Object obj : parameters) {
466  if (obj instanceof Byte)
467  bufferCapacity += 1;
468  else if (obj instanceof Short)
469  bufferCapacity += 2;
470  else if (obj instanceof Integer)
471  bufferCapacity += 4;
472  else if (obj instanceof String)
473  bufferCapacity += ((String) obj).length() + 1;
474  else
475  throw new IllegalArgumentException("Parameters should be one of the class types: Byte, Short, Integer, String");
476  }
477 
478  // generate byte buffer
479  ByteBuffer buffer = ByteBuffer.allocate(bufferCapacity);
480  buffer.order(ByteOrder.LITTLE_ENDIAN);
481  buffer.put(needReply ? Ev3Constants.SystemCommandType.SYSTEM_COMMAND_REPLY :
482  Ev3Constants.SystemCommandType.SYSTEM_COMMAND_NO_REPLY);
483  buffer.put(command);
484 
485  for (Object obj : parameters) {
486  if (obj instanceof Byte)
487  buffer.put((Byte) obj);
488  else if (obj instanceof Short)
489  buffer.putShort((Short) obj);
490  else if (obj instanceof Integer)
491  buffer.putInt((Integer) obj);
492  else if (obj instanceof String) {
493  try {
494  buffer.put(((String) obj).getBytes("US-ASCII"));
495  } catch (UnsupportedEncodingException e) {
496  throw new IllegalArgumentException("Non-ASCII string encoding is not supported"); // non-ASCII cases are regarded as wrong argument exception
497  }
498  buffer.put((byte) 0);
499  } else
500  throw new IllegalArgumentException("Parameters should be one of the class types: Byte, Short, Integer, String");
501  }
502 
503  return buffer.array();
504  }
505 
506  public static byte[] encodeDirectCommand(byte opcode, boolean needReply, int globalAllocation, int localAllocation, String paramFormat, Object... parameters) {
507  if (globalAllocation < 0 || globalAllocation > 0x3ff || localAllocation < 0 || localAllocation > 0x3f || paramFormat.length() != parameters.length)
508  throw new IllegalArgumentException();
509 
510 
511  // encode parameters
512  ArrayList<byte[]> payloads = new ArrayList<byte[]>();
513 
514  for (int i = 0; i < paramFormat.length(); i++) {
515  char letter = paramFormat.charAt(i);
516  Object obj = parameters[i];
517 
518  switch (letter) {
519  case 'c':
520  if (obj instanceof Byte) {
521  if ((((Byte) obj) <= 31) && (((Byte) obj) >= -31))
522  payloads.add(encodeLC0((Byte) obj));
523  else
524  payloads.add(encodeLC1((Byte) obj));
525  }
526  else if (obj instanceof Short)
527  payloads.add(encodeLC2((Short) obj));
528  else if (obj instanceof Integer)
529  payloads.add(encodeLC4((Integer) obj));
530  else
531  throw new IllegalArgumentException();
532  break;
533 
534  case 'l':
535  if (obj instanceof Byte) {
536  if ((((Byte) obj) <= 31) && (((Byte) obj) >= -31))
537  payloads.add(encodeLV0((Byte) obj));
538  else
539  payloads.add(encodeLV1((Byte) obj));
540  }
541  else if (obj instanceof Short)
542  payloads.add(encodeLV2((Short) obj));
543  else if (obj instanceof Integer)
544  payloads.add(encodeLV4((Integer) obj));
545  else
546  throw new IllegalArgumentException();
547  break;
548 
549  case 'g':
550  if (obj instanceof Byte) {
551  if ((((Byte) obj) <= 31) && (((Byte) obj) >= -31))
552  payloads.add(encodeGV0((Byte) obj));
553  else
554  payloads.add(encodeGV1((Byte) obj));
555  }
556  else if (obj instanceof Short)
557  payloads.add(encodeGV2((Short) obj));
558  else if (obj instanceof Integer)
559  payloads.add(encodeGV4((Integer) obj));
560  else
561  throw new IllegalArgumentException();
562  break;
563 
564  case 's':
565  if (!(obj instanceof String))
566  throw new IllegalArgumentException();
567 
568  try {
569  payloads.add((((String) obj) + '\0').getBytes("US-ASCII"));
570  } catch (UnsupportedEncodingException e) {
571  throw new IllegalArgumentException();
572  }
573  break;
574 
575  default:
576  throw new IllegalArgumentException("Illegal format string");
577  }
578  }
579 
580  // calculate buffer size
581  int bufferCapacity = 4;
582  for (byte[] array : payloads)
583  bufferCapacity += array.length;
584 
585  // generate byte buffer
586  ByteBuffer buffer = ByteBuffer.allocate(bufferCapacity);
587  buffer.order(ByteOrder.LITTLE_ENDIAN);
588  buffer.put(needReply ? Ev3Constants.DirectCommandType.DIRECT_COMMAND_REPLY :
589  Ev3Constants.DirectCommandType.DIRECT_COMMAND_NO_REPLY);
590  buffer.put(new byte[] {(byte) (globalAllocation & 0xff),
591  (byte) (((globalAllocation >>> 8) & 0x3) | (localAllocation << 2))});
592  buffer.put(opcode);
593 
594  for (byte[] array : payloads)
595  buffer.put(array);
596 
597  return buffer.array();
598  }
599 }
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeLV2
static byte[] encodeLV2(int i)
Definition: Ev3BinaryParser.java:423
com.google.appinventor.components.runtime.util.Ev3BinaryParser
Definition: Ev3BinaryParser.java:21
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeLC0
static byte[] encodeLC0(byte v)
Definition: Ev3BinaryParser.java:387
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeSystemCommand
static byte[] encodeSystemCommand(byte command, boolean needReply, Object... parameters)
Definition: Ev3BinaryParser.java:461
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeLC4
static byte[] encodeLC4(int v)
Definition: Ev3BinaryParser.java:405
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeLV1
static byte[] encodeLV1(int i)
Definition: Ev3BinaryParser.java:418
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeDirectCommand
static byte[] encodeDirectCommand(byte opcode, boolean needReply, int globalAllocation, int localAllocation, String paramFormat, Object... parameters)
Definition: Ev3BinaryParser.java:506
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeGV4
static byte[] encodeGV4(int i)
Definition: Ev3BinaryParser.java:452
com.google.appinventor.components.runtime.util.Ev3BinaryParser.pack
static byte[] pack(String format, Object... values)
Definition: Ev3BinaryParser.java:54
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeLC1
static byte[] encodeLC1(byte v)
Definition: Ev3BinaryParser.java:394
com.google.appinventor.components.runtime.util.Ev3Constants.SystemCommandType
Definition: Ev3Constants.java:432
com.google.appinventor.components.runtime.util.Ev3Constants.DirectCommandType
Definition: Ev3Constants.java:476
com.google.appinventor.components.runtime.util.Ev3BinaryParser.unpack
static Object[] unpack(String format, byte[] bytes)
Definition: Ev3BinaryParser.java:261
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeGV2
static byte[] encodeGV2(int i)
Definition: Ev3BinaryParser.java:446
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeGV0
static byte[] encodeGV0(int i)
Definition: Ev3BinaryParser.java:437
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeLC2
static byte[] encodeLC2(short v)
Definition: Ev3BinaryParser.java:399
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeLV4
static byte[] encodeLV4(int i)
Definition: Ev3BinaryParser.java:429
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeGV1
static byte[] encodeGV1(int i)
Definition: Ev3BinaryParser.java:441
com.google.appinventor.components.runtime.util.Ev3Constants
Definition: Ev3Constants.java:14
com.google.appinventor.components.runtime.util.Ev3BinaryParser.encodeLV0
static byte[] encodeLV0(int i)
Definition: Ev3BinaryParser.java:414