PROTON-2236 Report more meaningful unexpected encoding errors

When the decoder reads a type encoding that does not match the
encoding code of the primitive type expected then report a more
menaingful error including the type expected and the type / code
that was read, or unknown / code if type code read is not valid.
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/codec/DecoderImpl.java b/proton-j/src/main/java/org/apache/qpid/proton/codec/DecoderImpl.java
index a85b0ff..a34bd6f 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/codec/DecoderImpl.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/codec/DecoderImpl.java
@@ -189,7 +189,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected boolean type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Boolean type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -209,7 +209,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected boolean type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Boolean type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -230,7 +230,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected byte type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Byte type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -274,7 +274,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected Short type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Short type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -320,7 +320,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected Integer type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Integer type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -366,7 +366,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected Long type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Long type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -410,7 +410,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected unsigned byte type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected UnsignedByte type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -432,7 +432,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected UnsignedShort type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected UnsignedShort type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -458,7 +458,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected UnsignedInteger type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected UnsignedInteger type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -484,7 +484,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected UnsignedLong type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected UnsignedLong type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -506,7 +506,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected Character type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Character type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -522,7 +522,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new DecodeException("Expected Character type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Character type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -544,7 +544,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new ProtonException("Expected Float type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Float type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -588,7 +588,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new ProtonException("Expected Double type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Double type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -632,7 +632,7 @@
             case EncodingCodes.NULL:
                 return defaultVal;
             default:
-                throw new ProtonException("Expected UUID type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected UUID type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -654,7 +654,7 @@
             case EncodingCodes.NULL:
                 return defaultValue;
             default:
-                throw new ProtonException("Expected Decimal32 type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Decimal32 type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -676,7 +676,7 @@
             case EncodingCodes.NULL:
                 return defaultValue;
             default:
-                throw new ProtonException("Expected Decimal64 type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Decimal64 type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -698,7 +698,7 @@
             case EncodingCodes.NULL:
                 return defaultValue;
             default:
-                throw new ProtonException("Expected Decimal128 type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Decimal128 type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -720,7 +720,7 @@
             case EncodingCodes.NULL:
                 return defaultValue;
             default:
-                throw new ProtonException("Expected Timestamp type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Timestamp type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -744,7 +744,7 @@
             case EncodingCodes.NULL:
                 return defaultValue;
             default:
-                throw new ProtonException("Expected Binary type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Binary type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -768,7 +768,7 @@
             case EncodingCodes.NULL:
                 return defaultValue;
             default:
-                throw new ProtonException("Expected Symbol type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Symbol type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -792,7 +792,7 @@
             case EncodingCodes.NULL:
                 return defaultValue;
             default:
-                throw new ProtonException("Expected String type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected String type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -813,7 +813,7 @@
             case EncodingCodes.NULL:
                 return null;
             default:
-                throw new ProtonException("Expected List type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected List type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
@@ -838,7 +838,7 @@
             case EncodingCodes.NULL:
                 return null;
             default:
-                throw new ProtonException("Expected Map type but found encoding: " + encodingCode);
+                throw new ProtonException("Expected Map type but found encoding: " + EncodingCodes.toString(encodingCode));
         }
     }
 
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/codec/EncodingCodes.java b/proton-j/src/main/java/org/apache/qpid/proton/codec/EncodingCodes.java
index ec55a90..bd08ab9 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/codec/EncodingCodes.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/codec/EncodingCodes.java
@@ -87,4 +87,90 @@
     public static final byte ARRAY8                   = (byte) 0xe0;
     public static final byte ARRAY32                  = (byte) 0xf0;
 
+    static String toString(byte encoding) {
+        switch (encoding) {
+            case DESCRIBED_TYPE_INDICATOR:
+                return "DESCRIBED_TYPE_INDICATOR:0x00";
+            case NULL:
+                return "NULL:0x40";
+            case BOOLEAN:
+                return "BOOLEAN:0x56";
+            case BOOLEAN_TRUE:
+                return "BOOLEAN_TRUE:0x41";
+            case BOOLEAN_FALSE:
+                return "BOOLEAN_FALSE:0x42";
+            case UBYTE:
+                return "UBYTE:0x50";
+            case USHORT:
+                return "USHORT:0x60";
+            case UINT:
+                return "UINT:0x70";
+            case SMALLUINT:
+                return "SMALLUINT:0x52";
+            case UINT0:
+                return "UINT0:0x43";
+            case ULONG:
+                return "ULONG:0x80";
+            case SMALLULONG:
+                return "SMALLULONG:0x53";
+            case ULONG0:
+                return "ULONG0:0x44";
+            case BYTE:
+                return "BYTE:0x51";
+            case SHORT:
+                return "SHORT:0x61";
+            case INT:
+                return "INT:0x71";
+            case SMALLINT:
+                return "SMALLINT:0x54";
+            case LONG:
+                return "LONG:0x81";
+            case SMALLLONG:
+                return "SMALLLONG:0x55";
+            case FLOAT:
+                return "FLOAT:0x72";
+            case DOUBLE:
+                return "DOUBLE:0x82";
+            case DECIMAL32:
+                return "DECIMAL32:0x74";
+            case DECIMAL64:
+                return "DECIMAL64:0x84";
+            case DECIMAL128:
+                return "DECIMAL128:0x94";
+            case CHAR:
+                return "CHAR:0x73";
+            case TIMESTAMP:
+                return "TIMESTAMP:0x83";
+            case UUID:
+                return "UUID:0x98";
+            case VBIN8:
+                return "VBIN8:0xa0";
+            case VBIN32:
+                return "VBIN32:0xb0";
+            case STR8:
+                return "STR8:0xa1";
+            case STR32:
+                return "STR32:0xb1";
+            case SYM8:
+                return "SYM8:0xa3";
+            case SYM32:
+                return "SYM32:0xb3";
+            case LIST0:
+                return "LIST0:0x45";
+            case LIST8:
+                return "LIST8:0xc0";
+            case LIST32:
+                return "LIST32:0xd0";
+            case MAP8:
+                return "MAP8:0xc1";
+            case MAP32:
+                return "MAP32:0xd1";
+            case ARRAY8:
+                return "ARRAY32:0xe0";
+            case ARRAY32:
+                return "ARRAY32:0xf0";
+            default:
+                return "Unknown-Type:" + String.format("0x%02X ", encoding);
+        }
+    }
 }
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/codec/StringTypeTest.java b/proton-j/src/test/java/org/apache/qpid/proton/codec/StringTypeTest.java
index 3efe81d..5f9415e 100644
--- a/proton-j/src/test/java/org/apache/qpid/proton/codec/StringTypeTest.java
+++ b/proton-j/src/test/java/org/apache/qpid/proton/codec/StringTypeTest.java
@@ -33,7 +33,9 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
+import org.apache.qpid.proton.ProtonException;
 import org.apache.qpid.proton.amqp.messaging.AmqpValue;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -209,6 +211,74 @@
         assertEquals("", result);
     }
 
+    @Test
+    public void testDecodeNonStringWhenStringExpectedReportsUsefulError() {
+        final DecoderImpl decoder = new DecoderImpl();
+        final EncoderImpl encoder = new EncoderImpl(decoder);
+
+        AMQPDefinedTypes.registerAllTypes(decoder, encoder);
+
+        final ByteBuffer buffer = ByteBuffer.allocate(64);
+        final UUID encoded = UUID.randomUUID();
+
+        buffer.put(EncodingCodes.UUID);
+        buffer.putLong(encoded.getMostSignificantBits());
+        buffer.putLong(encoded.getLeastSignificantBits());
+        buffer.flip();
+
+        byte[] copy = new byte[buffer.remaining()];
+        buffer.get(copy);
+
+        CompositeReadableBuffer composite = new CompositeReadableBuffer();
+        composite.append(copy);
+
+        decoder.setBuffer(composite);
+
+        TypeConstructor<?> stringType = decoder.peekConstructor();
+        assertEquals(UUID.class, stringType.getTypeClass());
+
+        composite.mark();
+
+        try {
+            decoder.readString();
+        } catch (ProtonException ex) {
+            // Should indicate the type that it found in the error
+            assertTrue(ex.getMessage().contains(EncodingCodes.toString(EncodingCodes.UUID)));
+        }
+
+        composite.reset();
+        UUID actual = decoder.readUUID();
+        assertEquals(encoded, actual);
+    }
+
+    @Test
+    public void testDecodeUnknownTypeWhenStringExpectedReportsUsefulError() {
+        final DecoderImpl decoder = new DecoderImpl();
+        final EncoderImpl encoder = new EncoderImpl(decoder);
+
+        AMQPDefinedTypes.registerAllTypes(decoder, encoder);
+
+        final ByteBuffer buffer = ByteBuffer.allocate(64);
+
+        buffer.put((byte) 0x01);
+        buffer.flip();
+
+        byte[] copy = new byte[buffer.remaining()];
+        buffer.get(copy);
+
+        CompositeReadableBuffer composite = new CompositeReadableBuffer();
+        composite.append(copy);
+
+        decoder.setBuffer(composite);
+
+        try {
+            decoder.readString();
+        } catch (ProtonException ex) {
+            // Should indicate the type that it found in the error
+            assertTrue(ex.getMessage().contains("Unknown-Type:0x01"));
+        }
+    }
+
     // build up some test data with a set of suitable Unicode characters
     private static List<String> generateTestData()
     {