diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java
index 65d85e2d9..39e7a1e09 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java
@@ -166,6 +166,7 @@ public class JSONTest extends AbstractTest {
entity("Advertisements_f89dee73-af9f-4cd4-b330-db93c25ff3c7", getODataPubFormat());
entity("entityReference", getODataPubFormat());
entity("entity.withcomplexnavigation", getODataPubFormat());
+ entity("annotated", getODataPubFormat());
}
protected void property(final String filename, final ODataFormat format) throws Exception {
diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v3/PersonDetails_0_Person.json b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v3/PersonDetails_0_Person.json
index 61f020869..2d66e2324 100644
--- a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v3/PersonDetails_0_Person.json
+++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v3/PersonDetails_0_Person.json
@@ -1 +1 @@
-{"odata.metadata":"http://services.odata.org/V3/OData/OData.svc/$metadata#PersonDetails/@Element","odata.type":"ODataDemo.PersonDetail","odata.id":"http://services.odata.org/V3/OData/OData.svc/PersonDetails(0)","odata.editLink":"PersonDetails(0)","Person@odata.navigationLinkUrl":"PersonDetails(0)/Person","Person@odata.associationLinkUrl":"PersonDetails(0)/$links/Person","Person":{"odata.type":"ODataDemo.Person","odata.id":"http://services.odata.org/V3/OData/OData.svc/Persons(0)","odata.editLink":"Persons(0)","PersonDetail@odata.navigationLinkUrl":"Persons(0)/PersonDetail","PersonDetail@odata.associationLinkUrl":"Persons(0)/$links/PersonDetail","ID":0,"Name":"Paula Wilson"},"PersonID":0,"Age@odata.type":"Edm.Byte","Age":21,"Gender":false,"Phone":"(505) 555-5939","Address":{"odata.type":"ODataDemo.Address","Street":"2817 Milton Dr.","City":"Albuquerque","State":"NM","ZipCode":"87110","Country":"USA"},"Photo@odata.mediaEditLink":"PersonDetails(0)/Photo","Photo@odata.mediaETag":"\"nCP1Tf4Uax96eYIWjvoC/6ZflG8=\""}
+{"odata.metadata":"http://services.odata.org/V3/OData/OData.svc/$metadata#PersonDetails/@Element","odata.type":"ODataDemo.PersonDetail","odata.id":"http://services.odata.org/V3/OData/OData.svc/PersonDetails(0)","odata.editLink":"PersonDetails(0)","Person@odata.navigationLinkUrl":"PersonDetails(0)/Person","Person@odata.associationLinkUrl":"PersonDetails(0)/$links/Person","Person":{"odata.type":"ODataDemo.Person","odata.id":"http://services.odata.org/V3/OData/OData.svc/Persons(0)","odata.editLink":"Persons(0)","PersonDetail@odata.navigationLinkUrl":"Persons(0)/PersonDetail","PersonDetail@odata.associationLinkUrl":"Persons(0)/$links/PersonDetail","ID":0,"Name":"Paula Wilson"},"PersonID":0,"Age@odata.type":"Edm.Byte","Age":21,"Gender":false,"Phone":"(505) 555-5939","Address":{"odata.type":"ODataDemo.Address","Street":"2817 Milton Dr.","City":"Albuquerque","State":"NM","ZipCode":"87110","Country":"USA"},"Photo@odata.mediaEditLink":"PersonDetails(0)/Photo","Photo@odata.mediaEtag":"\"nCP1Tf4Uax96eYIWjvoC/6ZflG8=\""}
diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.json b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.json
new file mode 100644
index 000000000..a69884b04
--- /dev/null
+++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.json
@@ -0,0 +1,62 @@
+{
+ "@odata.context": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/$metadata#Customers/$entity",
+ "@odata.type": "#Microsoft.Test.OData.Services.ODataWCFService.Customer",
+ "@odata.id": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)",
+ "@odata.editLink": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)",
+ "@com.contoso.display.highlight": true,
+ "@com.contoso.PersonalInfo.PhoneNumbers": ["(203)555-1718", "(203)555-1719"],
+ "PersonID": 1,
+ "FirstName": "Bob",
+ "LastName@com.contoso.display.style": {
+ "@odata.type": "#com.contoso.display.styleType",
+ "title": true,
+ "order": 1
+ },
+ "LastName": "Cat",
+ "MiddleName": null,
+ "HomeAddress": {
+ "@odata.type": "#Microsoft.Test.OData.Services.ODataWCFService.HomeAddress",
+ "Street": "1 Microsoft Way",
+ "City": "London",
+ "PostalCode": "98052",
+ "FamilyName": "Cats"
+ },
+ "Home@odata.type": "#GeographyPoint",
+ "Home": {
+ "type": "Point",
+ "coordinates": [23.1, 32.1],
+ "crs": {
+ "type": "name",
+ "properties": {
+ "name": "EPSG:4326"
+ }
+ }
+ },
+ "Numbers@odata.type": "#Collection(String)",
+ "Numbers": ["111-111-1111", "0-12", "3-10", "bca", "ayz"],
+ "Emails@odata.type": "#Collection(String)",
+ "Emails": ["abc@abc.com"],
+ "City": "London",
+ "Birthday@odata.type": "#DateTimeOffset",
+ "Birthday": "1957-04-03T00:00:00Z",
+ "TimeBetweenLastTwoOrders@odata.type": "#Duration",
+ "TimeBetweenLastTwoOrders": "PT0.0000001S",
+ "Parent@odata.associationLink": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Parent/$ref",
+ "Parent@odata.navigationLink": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Parent",
+ "Orders@com.contoso.display.style": {
+ "@odata.type": "#com.contoso.display.styleType",
+ "order": 2
+ },
+ "Orders@odata.associationLink": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Orders/$ref",
+ "Orders@odata.navigationLink": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Orders",
+ "Company@odata.associationLink": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Company/$ref",
+ "Company@odata.navigationLink": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Company",
+ "#Microsoft.Test.OData.Services.ODataWCFService.ResetAddress": {
+ "title": "Microsoft.Test.OData.Services.ODataWCFService.ResetAddress",
+ "target": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Microsoft.Test.OData.Services.ODataWCFService.ResetAddress"
+ },
+ "#Microsoft.Test.OData.Services.ODataWCFService.GetHomeAddress": {
+ "title": "Microsoft.Test.OData.Services.ODataWCFService.GetHomeAddress",
+ "target": "http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Microsoft.Test.OData.Services.ODataWCFService.GetHomeAddress"
+ }
+}
\ No newline at end of file
diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.xml b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.xml
new file mode 100644
index 000000000..851f96088
--- /dev/null
+++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.xml
@@ -0,0 +1,76 @@
+
+
+
+ http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)
+
+
+
+
+
+ 2
+
+
+
+
+ 2014-03-31T09:35:14Z
+
+
+
+
+
+ 1
+ Bob
+ Cat
+
+ true
+ 1
+
+
+
+ 1 Microsoft Way
+ London
+ 98052
+ Cats
+
+
+
+ 32.1 23.1
+
+
+
+ 111-111-1111
+
+
+ abc@abc.com
+
+ London
+ 1957-04-03T00:00:00Z
+ PT0.0000001S
+
+
+ true
+
+ (203)555-1718
+ (203)555-1719
+
+
+
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
index 17a65eb16..c0612af15 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
@@ -152,6 +152,8 @@ public interface Constants {
public static final String ATTR_RELATIONSHIP = "relationship";
+ public static final String ANNOTATION = "annotation";
+
// JSON stuff
public final static String JSON_METADATA = "odata.metadata";
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Annotation.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Annotation.java
index 96db64634..33f393f9b 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Annotation.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Annotation.java
@@ -21,17 +21,9 @@ package org.apache.olingo.commons.api.data;
/**
* Represents an instance annotation.
*/
-public interface Annotation {
+public interface Annotation extends Valuable {
String getTerm();
void setTerm(String term);
-
- String getType();
-
- void setType(String type);
-
- Value getValue();
-
- void setValue(Value value);
}
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java
index 3f1ca03dd..36c6b74ed 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java
@@ -18,17 +18,9 @@
*/
package org.apache.olingo.commons.api.data;
-public interface Property extends Annotatable {
+public interface Property extends Valuable, Annotatable {
String getName();
void setName(String name);
-
- String getType();
-
- void setType(String type);
-
- Value getValue();
-
- void setValue(Value value);
}
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Valuable.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Valuable.java
new file mode 100644
index 000000000..11a4bf031
--- /dev/null
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Valuable.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.commons.api.data;
+
+public interface Valuable {
+
+ String getType();
+
+ void setType(String type);
+
+ Value getValue();
+
+ void setValue(Value value);
+}
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractAtomDealer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractAtomDealer.java
index 2f3ebb3ae..9c3fec247 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractAtomDealer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractAtomDealer.java
@@ -54,6 +54,8 @@ abstract class AbstractAtomDealer {
protected final QName nextQName;
+ protected final QName annotationQName;
+
protected final QName contextQName;
protected final QName entryRefQName;
@@ -98,6 +100,8 @@ abstract class AbstractAtomDealer {
new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES), Constants.ELEM_URI);
this.nextQName =
new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES), Constants.NEXT_LINK_REL);
+ this.annotationQName =
+ new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.ANNOTATION);
this.contextQName =
new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.CONTEXT);
this.entryRefQName =
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java
index 82f8ae05c..f94ff6821 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractJsonDeserializer.java
@@ -27,14 +27,21 @@ import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotatable;
+import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.ComplexValue;
-import org.apache.olingo.commons.api.data.ResWrap;
import org.apache.olingo.commons.api.data.Linked;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.ResWrap;
+import org.apache.olingo.commons.api.data.Valuable;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.domain.ODataLinkType;
import org.apache.olingo.commons.api.domain.ODataPropertyType;
@@ -44,6 +51,8 @@ import org.apache.olingo.commons.core.edm.EdmTypeInfo;
abstract class AbstractJsonDeserializer extends ODataJacksonDeserializer> {
+ protected final Pattern CUSTOM_ANNOTATION = Pattern.compile("(.+)@(.+)\\.(.+)");
+
private JSONGeoValueDeserializer geoDeserializer;
private JSONGeoValueDeserializer getGeoDeserializer() {
@@ -203,6 +212,48 @@ abstract class AbstractJsonDeserializer extends ODataJacksonDeserializer(type, typeInfo);
}
+ protected void populate(final Annotatable annotatable, final List properties,
+ final ObjectNode tree, final ObjectCodec codec) throws IOException {
+
+ String type = null;
+ Annotation annotation = null;
+ for (final Iterator> itor = tree.fields(); itor.hasNext();) {
+ final Map.Entry field = itor.next();
+ final Matcher customAnnotation = CUSTOM_ANNOTATION.matcher(field.getKey());
+
+ if (field.getKey().charAt(0) == '@') {
+ final Annotation entityAnnot = new AnnotationImpl();
+ entityAnnot.setTerm(field.getKey().substring(1));
+
+ value(entityAnnot, field.getValue(), codec);
+ if (annotatable != null) {
+ annotatable.getAnnotations().add(entityAnnot);
+ }
+ } else if (type == null && field.getKey().endsWith(getJSONAnnotation(jsonType))) {
+ type = field.getValue().asText();
+ } else if (annotation == null && customAnnotation.matches() && !"odata".equals(customAnnotation.group(2))) {
+ annotation = new AnnotationImpl();
+ annotation.setTerm(customAnnotation.group(2) + "." + customAnnotation.group(3));
+ value(annotation, field.getValue(), codec);
+ } else {
+ final JSONPropertyImpl property = new JSONPropertyImpl();
+ property.setName(field.getKey());
+ property.setType(type == null
+ ? null
+ : new EdmTypeInfo.Builder().setTypeExpression(type).build().internal());
+ type = null;
+
+ value(property, field.getValue(), codec);
+ properties.add(property);
+
+ if (annotation != null) {
+ property.getAnnotations().add(annotation);
+ annotation = null;
+ }
+ }
+ }
+ }
+
private Value fromPrimitive(final JsonNode node, final EdmTypeInfo typeInfo) {
final Value value;
@@ -234,22 +285,7 @@ abstract class AbstractJsonDeserializer extends ODataJacksonDeserializer> itor = node.fields(); itor.hasNext();) {
- final Map.Entry field = itor.next();
-
- if (type == null && field.getKey().endsWith(getJSONAnnotation(jsonType))) {
- type = field.getValue().asText();
- } else {
- final JSONPropertyImpl property = new JSONPropertyImpl();
- property.setName(field.getKey());
- property.setType(type);
- type = null;
-
- value(property, field.getValue(), codec);
- value.get().add(property);
- }
- }
+ populate(value.asLinkedComplex(), value.get(), node, codec);
return value;
}
@@ -283,12 +319,12 @@ abstract class AbstractJsonDeserializer extends ODataJacksonDeserializer guessed = guessPropertyType(node);
if (typeInfo == null) {
@@ -307,31 +343,31 @@ abstract class AbstractJsonDeserializer extends ODataJacksonDeserializer extends ODataJacksonSerializer {
protected void clientLinks(final Linked linked, final JsonGenerator jgen) throws IOException {
final Map> entitySetLinks = new HashMap>();
for (Link link : linked.getNavigationLinks()) {
+ for (Annotation annotation : link.getAnnotations()) {
+ valuable(jgen, annotation, link.getTitle() + "@" + annotation.getTerm());
+ }
+
ODataLinkType type = null;
try {
type = ODataLinkType.fromString(version, link.getRel(), link.getType());
@@ -185,7 +192,7 @@ abstract class AbstractJsonSerializer extends ODataJacksonSerializer {
} else if (value.isComplex()) {
jgen.writeStartObject();
for (Property property : value.asComplex().get()) {
- property(jgen, property, property.getName());
+ valuable(jgen, property, property.getName());
}
if (value.isLinkedComplex()) {
links(value.asLinkedComplex(), jgen);
@@ -194,12 +201,10 @@ abstract class AbstractJsonSerializer extends ODataJacksonSerializer {
}
}
- protected void property(final JsonGenerator jgen, final Property property, final String name) throws IOException {
+ protected void valuable(final JsonGenerator jgen, final Valuable valuable, final String name) throws IOException {
if (serverMode && !Constants.VALUE.equals(name)) {
- String type = property.getType();
- if (StringUtils.isBlank(type)
- && property.getValue().isPrimitive() || property.getValue().isNull()) {
-
+ String type = valuable.getType();
+ if (StringUtils.isBlank(type) && valuable.getValue().isPrimitive() || valuable.getValue().isNull()) {
type = EdmPrimitiveTypeKind.String.getFullQualifiedName().toString();
}
if (StringUtils.isNotBlank(type)) {
@@ -209,7 +214,13 @@ abstract class AbstractJsonSerializer extends ODataJacksonSerializer {
}
}
+ if (valuable instanceof Annotatable) {
+ for (Annotation annotation : ((Annotatable) valuable).getAnnotations()) {
+ valuable(jgen, annotation, name + "@" + annotation.getTerm());
+ }
+ }
+
jgen.writeFieldName(name);
- value(jgen, property.getType(), property.getValue());
+ value(jgen, valuable.getType(), valuable.getValue());
}
}
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractProperty.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractProperty.java
index 3f8da74ef..2363c0b3e 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractProperty.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AbstractProperty.java
@@ -23,6 +23,8 @@ import org.apache.olingo.commons.api.data.Value;
public abstract class AbstractProperty extends AbstractAnnotatedObject implements Property {
+ private static final long serialVersionUID = -7175704800169997060L;
+
private String name;
private String type;
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AnnotationImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AnnotationImpl.java
new file mode 100644
index 000000000..e6a277eb4
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AnnotationImpl.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.commons.core.data;
+
+import org.apache.olingo.commons.api.data.Annotation;
+import org.apache.olingo.commons.api.data.Value;
+
+public class AnnotationImpl extends AbstractAnnotatedObject implements Annotation {
+
+ private static final long serialVersionUID = -2532246000091187020L;
+
+ private String term;
+
+ private String type;
+
+ private Value value;
+
+ @Override
+ public String getTerm() {
+ return term;
+ }
+
+ @Override
+ public void setTerm(final String term) {
+ this.term = term;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ @Override
+ public Value getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(final Value value) {
+ this.value = value;
+ }
+}
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomDeserializer.java
index a5171bad8..27379da86 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomDeserializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomDeserializer.java
@@ -23,6 +23,10 @@ import org.apache.olingo.commons.core.data.v4.AtomDeltaImpl;
import java.io.InputStream;
import java.net.URI;
import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
@@ -32,10 +36,13 @@ import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.DeletedEntity.Reason;
import org.apache.olingo.commons.api.data.EntitySet;
+import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.ResWrap;
+import org.apache.olingo.commons.api.data.Valuable;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.domain.ODataOperation;
import org.apache.olingo.commons.api.domain.ODataPropertyType;
@@ -242,6 +249,14 @@ public class AtomDeserializer extends AbstractAtomDealer {
property.setName(start.getName().getLocalPart());
}
+ valuable(property, reader, start);
+
+ return property;
+ }
+
+ private void valuable(final Valuable valuable, final XMLEventReader reader, final StartElement start)
+ throws XMLStreamException {
+
final Attribute nullAttr = start.getAttributeByName(this.nullQName);
Value value;
@@ -254,7 +269,7 @@ public class AtomDeserializer extends AbstractAtomDealer {
: new EdmTypeInfo.Builder().setTypeExpression(typeAttrValue).build();
if (typeInfo != null) {
- property.setType(typeInfo.internal());
+ valuable.setType(typeInfo.internal());
}
final ODataPropertyType propType = typeInfo == null
@@ -277,7 +292,7 @@ public class AtomDeserializer extends AbstractAtomDealer {
case PRIMITIVE:
// No type specified? Defaults to Edm.String
if (typeInfo == null) {
- property.setType(EdmPrimitiveTypeKind.String.getFullQualifiedName().toString());
+ valuable.setType(EdmPrimitiveTypeKind.String.getFullQualifiedName().toString());
}
value = fromPrimitive(reader, start, typeInfo);
break;
@@ -290,9 +305,7 @@ public class AtomDeserializer extends AbstractAtomDealer {
value = new NullValueImpl();
}
- property.setValue(value);
-
- return property;
+ valuable.setValue(value);
}
private ResWrap property(final InputStream input) throws XMLStreamException {
@@ -344,23 +357,27 @@ public class AtomDeserializer extends AbstractAtomDealer {
while (reader.hasNext() && !foundEndElement) {
final XMLEvent event = reader.nextEvent();
- if (event.isStartElement() && inlineQName.equals(event.asStartElement().getName())) {
- StartElement inline = null;
- while (reader.hasNext() && inline == null) {
- final XMLEvent innerEvent = reader.peek();
- if (innerEvent.isCharacters() && innerEvent.asCharacters().isWhiteSpace()) {
- reader.nextEvent();
- } else if (innerEvent.isStartElement()) {
- inline = innerEvent.asStartElement();
+ if (event.isStartElement()) {
+ if (inlineQName.equals(event.asStartElement().getName())) {
+ StartElement inline = null;
+ while (reader.hasNext() && inline == null) {
+ final XMLEvent innerEvent = reader.peek();
+ if (innerEvent.isCharacters() && innerEvent.asCharacters().isWhiteSpace()) {
+ reader.nextEvent();
+ } else if (innerEvent.isStartElement()) {
+ inline = innerEvent.asStartElement();
+ }
}
- }
- if (inline != null) {
- if (Constants.QNAME_ATOM_ELEM_ENTRY.equals(inline.getName())) {
- link.setInlineEntity(entity(reader, inline));
- }
- if (Constants.QNAME_ATOM_ELEM_FEED.equals(inline.getName())) {
- link.setInlineEntitySet(entitySet(reader, inline));
+ if (inline != null) {
+ if (Constants.QNAME_ATOM_ELEM_ENTRY.equals(inline.getName())) {
+ link.setInlineEntity(entity(reader, inline));
+ }
+ if (Constants.QNAME_ATOM_ELEM_FEED.equals(inline.getName())) {
+ link.setInlineEntitySet(entitySet(reader, inline));
+ }
}
+ } else if (annotationQName.equals(event.asStartElement().getName())) {
+ link.getAnnotations().add(annotation(reader, event.asStartElement()));
}
}
@@ -502,18 +519,47 @@ public class AtomDeserializer extends AbstractAtomDealer {
private void properties(final XMLEventReader reader, final StartElement start, final AtomEntityImpl entity)
throws XMLStreamException {
+
+ final Map> annotations = new HashMap>();
+
boolean foundEndProperties = false;
while (reader.hasNext() && !foundEndProperties) {
final XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
- entity.getProperties().add(property(reader, event.asStartElement()));
+ if (annotationQName.equals(event.asStartElement().getName())) {
+ final String target = event.asStartElement().
+ getAttributeByName(QName.valueOf(Constants.ATTR_TARGET)).getValue();
+ if (!annotations.containsKey(target)) {
+ annotations.put(target, new ArrayList());
+ }
+ annotations.get(target).add(annotation(reader, event.asStartElement()));
+ } else {
+ entity.getProperties().add(property(reader, event.asStartElement()));
+ }
}
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
foundEndProperties = true;
}
}
+
+ for (Property property : entity.getProperties()) {
+ if (annotations.containsKey(property.getName())) {
+ property.getAnnotations().addAll(annotations.get(property.getName()));
+ }
+ }
+ }
+
+ private Annotation annotation(final XMLEventReader reader, final StartElement start)
+ throws XMLStreamException {
+
+ final Annotation annotation = new AnnotationImpl();
+
+ annotation.setTerm(start.getAttributeByName(QName.valueOf(Constants.ATOM_ATTR_TERM)).getValue());
+ valuable(annotation, reader, start);
+
+ return annotation;
}
private AtomEntityImpl entityRef(final StartElement start) throws XMLStreamException {
@@ -636,6 +682,8 @@ public class AtomDeserializer extends AbstractAtomDealer {
}
} else if (propertiesQName.equals(event.asStartElement().getName())) {
properties(reader, event.asStartElement(), entity);
+ } else if (annotationQName.equals(event.asStartElement().getName())) {
+ entity.getAnnotations().add(annotation(reader, event.asStartElement()));
}
}
@@ -643,8 +691,6 @@ public class AtomDeserializer extends AbstractAtomDealer {
foundEndEntry = true;
}
}
-
- return entity;
} else {
entity = null;
}
@@ -719,6 +765,8 @@ public class AtomDeserializer extends AbstractAtomDealer {
entitySet.getEntities().add(entity(reader, event.asStartElement()));
} else if (entryRefQName.equals(event.asStartElement().getName())) {
entitySet.getEntities().add(entityRef(event.asStartElement()));
+ } else if (annotationQName.equals(event.asStartElement().getName())) {
+ entitySet.getAnnotations().add(annotation(reader, event.asStartElement()));
}
}
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java
index 0fdab4230..0f9750af7 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java
@@ -28,6 +28,7 @@ import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.ResWrap;
import org.apache.olingo.commons.api.data.Entity;
@@ -124,6 +125,10 @@ public class AtomSerializer extends AbstractAtomDealer {
}
writer.writeEndElement();
+
+ for (Annotation annotation : property.getAnnotations()) {
+ annotation(writer, annotation, property.getName());
+ }
}
private void property(final XMLStreamWriter writer, final Property property) throws XMLStreamException {
@@ -185,6 +190,10 @@ public class AtomSerializer extends AbstractAtomDealer {
writer.writeEndElement();
}
+ for (Annotation annotation : link.getAnnotations()) {
+ annotation(writer, annotation, null);
+ }
+
writer.writeEndElement();
}
}
@@ -211,6 +220,36 @@ public class AtomSerializer extends AbstractAtomDealer {
}
}
+ private void annotation(final XMLStreamWriter writer, final Annotation annotation, final String target)
+ throws XMLStreamException {
+
+ writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ANNOTATION,
+ version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
+
+ writer.writeAttribute(Constants.ATOM_ATTR_TERM, annotation.getTerm());
+
+ if (target != null) {
+ writer.writeAttribute(Constants.ATTR_TARGET, target);
+ }
+
+ if (StringUtils.isNotBlank(annotation.getType())) {
+ final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(annotation.getType()).build();
+ if (!EdmPrimitiveTypeKind.String.getFullQualifiedName().toString().equals(typeInfo.internal())) {
+ writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ Constants.ATTR_TYPE, typeInfo.external(version));
+ }
+ }
+
+ if (annotation.getValue().isNull()) {
+ writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ Constants.ATTR_NULL, Boolean.TRUE.toString());
+ } else {
+ value(writer, annotation.getValue());
+ }
+
+ writer.writeEndElement();
+ }
+
private void entity(final XMLStreamWriter writer, final Entity entity) throws XMLStreamException {
if (entity.getBaseURI() != null) {
writer.writeAttribute(XMLConstants.XML_NS_URI, Constants.ATTR_XML_BASE, entity.getBaseURI().toASCIIString());
@@ -284,6 +323,10 @@ public class AtomSerializer extends AbstractAtomDealer {
writer.writeEndElement();
}
writer.writeEndElement();
+
+ for (Annotation annotation : entity.getAnnotations()) {
+ annotation(writer, annotation, null);
+ }
}
private void entityRef(final XMLStreamWriter writer, final Entity entity) throws XMLStreamException {
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntityDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntityDeserializer.java
index 4fd7ace9e..567fadbcd 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntityDeserializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntityDeserializer.java
@@ -26,12 +26,17 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Matcher;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.ResWrap;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.domain.ODataLinkType;
@@ -106,7 +111,6 @@ public class JSONEntityDeserializer extends AbstractJsonDeserializer toRemove = new HashSet();
+
+ final Map> annotations = new HashMap>();
for (final Iterator> itor = tree.fields(); itor.hasNext();) {
final Map.Entry field = itor.next();
+ final Matcher customAnnotation = CUSTOM_ANNOTATION.matcher(field.getKey());
links(field, entity, toRemove, tree, parser.getCodec());
if (field.getKey().endsWith(getJSONAnnotation(jsonMediaEditLink))) {
@@ -172,29 +179,39 @@ public class JSONEntityDeserializer extends AbstractJsonDeserializer());
+ }
+ annotations.get(customAnnotation.group(1)).add(annotation);
}
}
+
+ for (Link link : entity.getNavigationLinks()) {
+ if (annotations.containsKey(link.getTitle())) {
+ link.getAnnotations().addAll(annotations.get(link.getTitle()));
+ for (Annotation annotation : annotations.get(link.getTitle())) {
+ toRemove.add(link.getTitle() + "@" + annotation.getTerm());
+ }
+ }
+ }
+ for (Link link : entity.getMediaEditLinks()) {
+ if (annotations.containsKey(link.getTitle())) {
+ link.getAnnotations().addAll(annotations.get(link.getTitle()));
+ for (Annotation annotation : annotations.get(link.getTitle())) {
+ toRemove.add(link.getTitle() + "@" + annotation.getTerm());
+ }
+ }
+ }
+
tree.remove(toRemove);
- String type = null;
- for (final Iterator> itor = tree.fields(); itor.hasNext();) {
- final Map.Entry field = itor.next();
+ populate(entity, entity.getProperties(), tree, parser.getCodec());
- if (type == null && field.getKey().endsWith(getJSONAnnotation(jsonType))) {
- type = field.getValue().asText();
- } else {
- final JSONPropertyImpl property = new JSONPropertyImpl();
- property.setName(field.getKey());
- property.setType(type == null
- ? null
- : new EdmTypeInfo.Builder().setTypeExpression(type).build().internal());
- type = null;
-
- value(property, field.getValue(), parser.getCodec());
- entity.getProperties().add(property);
- }
- }
-
return new ResWrap(contextURL, metadataETag, entity);
}
}
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySerializer.java
index 043978344..cb0280d70 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySerializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySerializer.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.net.URI;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.ResWrap;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.Link;
@@ -78,8 +79,12 @@ public class JSONEntitySerializer extends AbstractJsonSerializer
jgen.writeStringField(version.getJSONMap().get(ODataServiceVersion.JSON_ID), entity.getId());
}
+ for (Annotation annotation : entity.getAnnotations()) {
+ valuable(jgen, annotation, "@" + annotation.getTerm());
+ }
+
for (Property property : entity.getProperties()) {
- property(jgen, property, property.getName());
+ valuable(jgen, property, property.getName());
}
if (serverMode && entity.getEditLink() != null && StringUtils.isNotBlank(entity.getEditLink().getHref())) {
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySetDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySetDeserializer.java
index df5d40a80..241dcbc43 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySetDeserializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySetDeserializer.java
@@ -27,8 +27,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.net.URI;
import java.util.Iterator;
+import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.ResWrap;
/**
@@ -74,12 +76,15 @@ public class JSONEntitySetDeserializer extends AbstractJsonDeserializer() {
}).getPayload());
}
+ tree.remove(Constants.VALUE);
+ }
+
+ // any remaining entry is supposed to be an annotation or is ignored
+ for (final Iterator> itor = tree.fields(); itor.hasNext();) {
+ final Map.Entry field = itor.next();
+ if (field.getKey().charAt(0) == '@') {
+ final Annotation annotation = new AnnotationImpl();
+ annotation.setTerm(field.getKey().substring(1));
+
+ value(annotation, field.getValue(), parser.getCodec());
+ entitySet.getAnnotations().add(annotation);
+ }
}
return new ResWrap(contextURL, metadataETag, entitySet);
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySetSerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySetSerializer.java
index f3195abc5..206e385c6 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySetSerializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntitySetSerializer.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.net.URI;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.ResWrap;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
@@ -78,6 +79,10 @@ public class JSONEntitySetSerializer extends AbstractJsonSerializer> itor = tree.fields(); itor.hasNext();) {
+ final Map.Entry field = itor.next();
+ if (field.getKey().charAt(0) == '@') {
+ final Annotation annotation = new AnnotationImpl();
+ annotation.setTerm(field.getKey().substring(1));
+
+ value(annotation, field.getValue(), parser.getCodec());
+ property.getAnnotations().add(annotation);
+ }
}
return new ResWrap(contextURL, metadataETag, property);
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertySerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertySerializer.java
index 74e8cf008..1a7b9085a 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertySerializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertySerializer.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.net.URI;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
import org.apache.olingo.commons.api.data.ResWrap;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
@@ -40,7 +41,7 @@ public class JSONPropertySerializer extends AbstractJsonSerializer((URI) null, null, property), jgen, provider);
}
@@ -64,6 +65,10 @@ public class JSONPropertySerializer extends AbstractJsonSerializer