[OLINGO-1505]Support instance annotation for Stream property
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
index babc5a0..4e09c7a 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
@@ -115,6 +115,9 @@
   private static final String ODATA_ANNOTATION_MARKER = "@";
   private static final String ODATA_CONTROL_INFORMATION_PREFIX = "@odata.";
   private static final String REASON = "reason";
+  private static final String ODATA_STREAM_PROPERTY_MEDIA_READ_LINK = "mediaReadLink";
+  private static final String ODATA_STREAM_PROPERTY_MEDIA_EDIT_LINK = "mediaEditLink";
+  private static final String ODATA_STREAM_PROPERTY_MEDIA_MIME_TYPE = "mediaMimeType";
 
   private final boolean isIEEE754Compatible;
   private ServiceMetadata serviceMetadata;
@@ -479,6 +482,9 @@
     		  entity.getAnnotations().add(annotation);
     	  }
     	  toRemove.add(field.getKey());
+      } else if (isStreamPropertyNode(field.getKey())) {
+        consumeStreamPropertyNode(entity, edmEntityType, field);
+        toRemove.add(field.getKey());
       }
     }
     // remove here to avoid iterator issues.
@@ -487,6 +493,53 @@
     removeAnnotations(node);
   }
 
+  /**
+   * Process stream property instance annotation,
+   * include
+   * <ul>
+   * <li>odata.mediaReadLink for 4.0 or mediaReadLink for 4.01</li>
+   * <li>odata.mediaEditLink for 4.0 or mediaEditLink for 4.01</li>
+   * <li>odata.mediaMimeType for 4.0 or mediaMimeType for 4.01</li>
+   * </ul>
+   *
+   * @return true if jsonNodeKey present stream property annotation, false for otherwise
+   */
+  private boolean isStreamPropertyNode(String jsonNodeKey) {
+    return jsonNodeKey.endsWith(ODATA_STREAM_PROPERTY_MEDIA_READ_LINK)
+      || jsonNodeKey.endsWith(ODATA_STREAM_PROPERTY_MEDIA_EDIT_LINK)
+      || jsonNodeKey.endsWith(ODATA_STREAM_PROPERTY_MEDIA_MIME_TYPE);
+  }
+
+  /**
+   * Construct a empty {@code Property} and fill stream property annotation data into it
+   *
+   * @param entity entity instance which is filled
+   * @param edmEntityType edm entity type which for which the json node is consumed
+   * @param field Json field entry which current consuming
+   *
+   * @throws DeserializerException thrown by {@code instanceAnnotDeserializer} if consume
+   * instance annotation failed
+   */
+  private void consumeStreamPropertyNode(final Entity entity,
+                                         final EdmEntityType edmEntityType,
+                                         final Entry<String, JsonNode> field) throws DeserializerException {
+    String[] keySplit = field.getKey().split(ODATA_ANNOTATION_MARKER);
+    String termName = keySplit[1];
+    Annotation annotation = instanceAnnotDeserializer.consumeInstanceAnnotation(termName, field.getValue());
+    String propertyName = keySplit[0];
+    if(edmEntityType.getProperty(propertyName) == null) {
+      return;
+    }
+
+    Property property = entity.getProperty(propertyName);
+    if(property == null) {
+      property = new Property();
+      property.setName(propertyName);
+      entity.addProperty(property);
+    }
+    property.getAnnotations().add(annotation);
+  }
+
   private void consumeEntityProperties(final EdmEntityType edmEntityType, final ObjectNode node,
       final Entity entity) throws DeserializerException {
     List<String> propertyNames = edmEntityType.getPropertyNames();
@@ -929,7 +982,7 @@
   /**
    * Returns the primitive type's default class or the manually mapped class if present.
    * @param mapping
-   * @param edmPrimitiveType
+   * @param type
    * @return the java class to be used during deserialization
    */
   private Class<?> getJavaClassForPrimitiveType(final EdmMapping mapping, final EdmPrimitiveType type) {
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonInstanceAnnotationDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonInstanceAnnotationDeserializer.java
index 0ac5251..d00f641 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonInstanceAnnotationDeserializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonInstanceAnnotationDeserializer.java
@@ -108,7 +108,7 @@
 

 		case PRIMITIVE:

 			if (valuable.getType() == null && typeInfo != null) {

-				valuable.setType(typeInfo.getFullQualifiedName().toString());

+				valuable.setType(typeInfo.getPrimitiveTypeKind().name());

 			}

 			final Object primitiveValue = fromPrimitive(node, typeInfo);

 			valuable.setValue(primitiveValue instanceof Geospatial ? 

diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerWithInstanceAnnotationsTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerWithInstanceAnnotationsTest.java
index ab4b2eb..283be48 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerWithInstanceAnnotationsTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerWithInstanceAnnotationsTest.java
@@ -231,7 +231,43 @@
     assertEquals(ValueType.COLLECTION_COMPLEX, complxProperties.get(1).getValueType());
     assertEquals(2, complxProperties.get(1).asCollection().size());
   }
-  
+
+	@Test
+	public void instanceAnnotationForStreamProperty() throws DeserializerException {
+		final String entityString = "{"
+			+	"\"@odata.context\":\"$metadata#ETWithStream/$entity\","
+			+	"\"PropertyInt16@odata.type\":\"#Int16\","
+			+	"\"PropertyInt16\":32767,"
+			+	"\"PropertyStream@odata.mediaReadLink\":\"http://mockReadLink1\","
+			+ "\"PropertyStream@odata.mediaEditLink\":\"http://mockEditLink1\","
+			+ "\"PropertyStream@odata.mediaMimeType\":\"image/png\","
+			+	"\"PropertyStream@mediaReadLink\":\"http://mockReadLink2\","
+			+ "\"PropertyStream@mediaEditLink\":\"http://mockEditLink2\","
+			+ "\"PropertyStream@mediaMimeType\":\"image/jpeg\""
+			+"}";
+		final Entity entity = deserialize(entityString, "ETWithStream");
+		assertNotNull(entity);
+		Property propertyStream = entity.getProperty("PropertyStream");
+		assertNotNull(propertyStream);
+		List<Annotation> annotations = propertyStream.getAnnotations();
+		assertEquals(6, annotations.size());
+		for(Annotation annotation : annotations) {
+			if("odata.mediaReadLink".equals(annotation.getTerm())) {
+				assertEquals("http://mockReadLink1", annotation.getValue().toString());
+			} else if("odata.mediaEditLink".equals(annotation.getTerm())) {
+				assertEquals("http://mockEditLink1", annotation.getValue().toString());
+			} else if("odata.mediaMimeType".equals(annotation.getTerm())) {
+				assertEquals("image/png", annotation.getValue().toString());
+			} else if("mediaReadLink".equals(annotation.getTerm())) {
+				assertEquals("http://mockReadLink2", annotation.getValue().toString());
+			} else if("mediaEditLink".equals(annotation.getTerm())) {
+				assertEquals("http://mockEditLink2", annotation.getValue().toString());
+			} else if("mediaMimeType".equals(annotation.getTerm())) {
+				assertEquals("image/jpeg", annotation.getValue().toString());
+			}
+		}
+	}
+
   protected static DeserializerResult deserializeWithResultWithConstantV401(final InputStream stream, 
 		  final String entityTypeName, final ContentType contentType) 
 				  throws DeserializerException {