+ private static String decodeExtended(String encoded) throws ReaderException {
+ int length = encoded.length();
+ StringBuffer decoded = new StringBuffer(length);
+ for (int i = 0; i < length; i++) {
+ char c = encoded.charAt(i);
+ if (c == '+' || c == '$' || c == '%' || c == '/') {
+ char next = encoded.charAt(i + 1);
+ char decodedChar = '\0';
+ switch (c) {
+ case '+':
+ // +A to +Z map to a to z
+ if (next >= 'A' && next <= 'Z') {
+ decodedChar = (char) (next + 32);
+ } else {
+ throw new ReaderException("Invalid extended code 39 sequence: " + c + next);
+ }
+ break;
+ case '$':
+ // $A to $Z map to control codes SH to SB
+ if (next >= 'A' && next <= 'Z') {
+ decodedChar = (char) (next - 64);
+ } else {
+ throw new ReaderException("Invalid extended code 39 sequence: " + c + next);
+ }
+ break;
+ case '%':
+ // %A to %E map to control codes ESC to US
+ if (next >= 'A' && next <= 'E') {
+ decodedChar = (char) (next - 38);
+ } else if (next >= 'F' && next <= 'W') {
+ decodedChar = (char) (next - 11);
+ } else {
+ throw new ReaderException("Invalid extended code 39 sequence: " + c + next);
+ }
+ break;
+ case '/':
+ // /A to /O map to ! to , and /Z maps to :
+ if (next >= 'A' && next <= 'O') {
+ decodedChar = (char) (next - 32);
+ } else if (next == 'Z') {
+ decodedChar = ':';
+ } else {
+ throw new ReaderException("Invalid extended sequence: " + c + next);
+ }
+ break;
+ }
+ decoded.append(decodedChar);
+ // bump up i again since we read two characters
+ i++;
+ } else {
+ decoded.append(c);
+ }
+ }
+ return decoded.toString();
+ }
+
+}