You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by il...@apache.org on 2014/03/11 13:53:11 UTC
[09/11] [OLINGO-200] V3 (de)serializers + unit tests merged
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractEntry.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractEntry.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractEntry.java
new file mode 100644
index 0000000..fc65cb7
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractEntry.java
@@ -0,0 +1,179 @@
+/*
+ * 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.client.core.data;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.data.Entry;
+import org.apache.olingo.client.api.data.Link;
+import org.apache.olingo.client.api.data.Operation;
+import org.w3c.dom.Element;
+
+/**
+ * Abstract base for classes implementing an OData entry in Atom and JSON.
+ */
+public abstract class AbstractEntry extends AbstractPayloadObject implements Entry {
+
+ private static final long serialVersionUID = 2127764552600969783L;
+
+ private String eTag;
+
+ private String type;
+
+ private String id;
+
+ private Link readLink;
+
+ private Link editLink;
+
+ private final List<Link> associationLinks = new ArrayList<Link>();
+
+ private final List<Link> navigationLinks = new ArrayList<Link>();
+
+ private final List<Link> mediaEditLinks = new ArrayList<Link>();
+
+ private final List<Operation> operations = new ArrayList<Operation>();
+
+ private Element content;
+
+ private Element mediaEntryProperties;
+
+ private String mediaContentSource;
+
+ private String mediaContentType;
+
+ @Override
+ public String getETag() {
+ return eTag;
+ }
+
+ @Override
+ public void setETag(final String eTag) {
+ this.eTag = eTag;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(final String id) {
+ this.id = id;
+ }
+
+ @Override
+ public Link getSelfLink() {
+ return readLink;
+ }
+
+ @Override
+ public void setSelfLink(final Link readLink) {
+ this.readLink = readLink;
+ }
+
+ @Override
+ public Link getEditLink() {
+ return editLink;
+ }
+
+ @Override
+ public void setEditLink(final Link editLink) {
+ this.editLink = editLink;
+ }
+
+ @Override
+ public List<Link> getAssociationLinks() {
+ return associationLinks;
+ }
+
+ @Override
+ public List<Link> getNavigationLinks() {
+ return navigationLinks;
+ }
+
+ @Override
+ public List<Link> getMediaEditLinks() {
+ return mediaEditLinks;
+ }
+
+ @Override
+ public List<Operation> getOperations() {
+ return operations;
+ }
+
+ @Override
+ public Element getContent() {
+ return content;
+ }
+
+ @Override
+ public void setContent(final Element content) {
+ this.content = content;
+ }
+
+ /**
+ * {@inheritDoc }
+ */
+ @Override
+ public Element getMediaEntryProperties() {
+ return mediaEntryProperties;
+ }
+
+ @Override
+ public void setMediaEntryProperties(final Element mediaEntryProperties) {
+ this.mediaEntryProperties = mediaEntryProperties;
+ }
+
+ @Override
+ public String getMediaContentType() {
+ return this.mediaContentType;
+ }
+
+ @Override
+ public void setMediaContentType(final String mediaContentType) {
+ this.mediaContentType = mediaContentType;
+ }
+
+ @Override
+ public String getMediaContentSource() {
+ return this.mediaContentSource;
+ }
+
+ @Override
+ public void setMediaContentSource(final String mediaContentSource) {
+ this.mediaContentSource = mediaContentSource;
+ }
+
+ @Override
+ public boolean isMediaEntry() {
+ return getMediaEntryProperties() != null || StringUtils.isNotBlank(this.mediaContentSource);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractPayloadObject.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractPayloadObject.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractPayloadObject.java
new file mode 100644
index 0000000..b707c49
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractPayloadObject.java
@@ -0,0 +1,48 @@
+/*
+ * 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.client.core.data;
+
+import java.io.Serializable;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * Abstract representation of a payload (Atom, JSON) object.
+ */
+public abstract class AbstractPayloadObject implements Serializable {
+
+ private static final long serialVersionUID = 1634654241914156675L;
+
+ @Override
+ public boolean equals(final Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomDeserializer.java
new file mode 100644
index 0000000..db18bfa
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomDeserializer.java
@@ -0,0 +1,217 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
+import java.net.URI;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+public class AtomDeserializer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AtomDeserializer.class);
+
+ private static final ISO8601DateFormat ISO_DATEFORMAT = new ISO8601DateFormat();
+
+ private final ODataClient client;
+
+ public AtomDeserializer(final ODataClient client) {
+ this.client = client;
+ }
+
+ private void common(final Element input, final AtomObject object) {
+ if (StringUtils.isNotBlank(input.getAttribute(ODataConstants.ATTR_XMLBASE))) {
+ object.setBaseURI(input.getAttribute(ODataConstants.ATTR_XMLBASE));
+ }
+
+ final List<Element> ids = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_ID);
+ if (!ids.isEmpty()) {
+ object.setId(ids.get(0).getTextContent());
+ }
+
+ final List<Element> titles = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_TITLE);
+ if (!titles.isEmpty()) {
+ object.setTitle(titles.get(0).getTextContent());
+ }
+
+ final List<Element> summaries = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_SUMMARY);
+ if (!summaries.isEmpty()) {
+ object.setSummary(summaries.get(0).getTextContent());
+ }
+
+ final List<Element> updateds = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_UPDATED);
+ if (!updateds.isEmpty()) {
+ try {
+ object.setUpdated(ISO_DATEFORMAT.parse(updateds.get(0).getTextContent()));
+ } catch (Exception e) {
+ LOG.error("Could not parse date {}", updateds.get(0).getTextContent(), e);
+ }
+ }
+ }
+
+ public AtomEntryImpl entry(final Element input) {
+ if (!ODataConstants.ATOM_ELEM_ENTRY.equals(input.getNodeName())) {
+ return null;
+ }
+
+ final AtomEntryImpl entry = new AtomEntryImpl();
+
+ common(input, entry);
+
+ final String etag = input.getAttribute(ODataConstants.ATOM_ATTR_ETAG);
+ if (StringUtils.isNotBlank(etag)) {
+ entry.setETag(etag);
+ }
+
+ final List<Element> categories = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_CATEGORY);
+ if (!categories.isEmpty()) {
+ entry.setType(categories.get(0).getAttribute(ODataConstants.ATOM_ATTR_TERM));
+ }
+
+ final List<Element> links = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_LINK);
+ for (Element linkElem : links) {
+ final LinkImpl link = new LinkImpl();
+ link.setRel(linkElem.getAttribute(ODataConstants.ATTR_REL));
+ link.setTitle(linkElem.getAttribute(ODataConstants.ATTR_TITLE));
+ link.setHref(linkElem.getAttribute(ODataConstants.ATTR_HREF));
+
+ if (ODataConstants.SELF_LINK_REL.equals(link.getRel())) {
+ entry.setSelfLink(link);
+ } else if (ODataConstants.EDIT_LINK_REL.equals(link.getRel())) {
+ entry.setEditLink(link);
+ } else if (link.getRel().startsWith(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL))) {
+
+ link.setType(linkElem.getAttribute(ODataConstants.ATTR_TYPE));
+ entry.getNavigationLinks().add(link);
+
+ final List<Element> inlines = XMLUtils.getChildElements(linkElem, ODataConstants.ATOM_ELEM_INLINE);
+ if (!inlines.isEmpty()) {
+ final List<Element> entries =
+ XMLUtils.getChildElements(inlines.get(0), ODataConstants.ATOM_ELEM_ENTRY);
+ if (!entries.isEmpty()) {
+ link.setInlineEntry(entry(entries.get(0)));
+ }
+
+ final List<Element> feeds =
+ XMLUtils.getChildElements(inlines.get(0), ODataConstants.ATOM_ELEM_FEED);
+ if (!feeds.isEmpty()) {
+ link.setInlineFeed(feed(feeds.get(0)));
+ }
+ }
+ } else if (link.getRel().startsWith(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL))) {
+
+ entry.getAssociationLinks().add(link);
+ } else if (link.getRel().startsWith(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.MEDIA_EDIT_LINK_REL))) {
+
+ entry.getMediaEditLinks().add(link);
+ }
+ }
+
+ final List<Element> authors = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_AUTHOR);
+ if (!authors.isEmpty()) {
+ final AtomEntryImpl.Author author = new AtomEntryImpl.Author();
+ for (Node child : XMLUtils.getChildNodes(input, Node.ELEMENT_NODE)) {
+ if (ODataConstants.ATOM_ELEM_AUTHOR_NAME.equals(XMLUtils.getSimpleName(child))) {
+ author.setName(child.getTextContent());
+ } else if (ODataConstants.ATOM_ELEM_AUTHOR_URI.equals(XMLUtils.getSimpleName(child))) {
+ author.setUri(child.getTextContent());
+ } else if (ODataConstants.ATOM_ELEM_AUTHOR_EMAIL.equals(XMLUtils.getSimpleName(child))) {
+ author.setEmail(child.getTextContent());
+ }
+ }
+ if (!author.isEmpty()) {
+ entry.setAuthor(author);
+ }
+ }
+
+ final List<Element> actions = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_ACTION);
+ for (Element action : actions) {
+ final OperationImpl operation = new OperationImpl();
+ operation.setMetadataAnchor(action.getAttribute(ODataConstants.ATTR_METADATA));
+ operation.setTitle(action.getAttribute(ODataConstants.ATTR_TITLE));
+ operation.setTarget(URI.create(action.getAttribute(ODataConstants.ATTR_TARGET)));
+
+ entry.getOperations().add(operation);
+ }
+
+ final List<Element> contents = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_CONTENT);
+ if (!contents.isEmpty()) {
+ final Element content = contents.get(0);
+
+ List<Element> props = XMLUtils.getChildElements(content, ODataConstants.ELEM_PROPERTIES);
+ if (props.isEmpty()) {
+ entry.setMediaContentSource(content.getAttribute(ODataConstants.ATOM_ATTR_SRC));
+ entry.setMediaContentType(content.getAttribute(ODataConstants.ATTR_TYPE));
+
+ props = XMLUtils.getChildElements(input, ODataConstants.ELEM_PROPERTIES);
+ if (!props.isEmpty()) {
+ entry.setMediaEntryProperties(props.get(0));
+ }
+ } else {
+ entry.setContent(props.get(0));
+ }
+ }
+
+ return entry;
+ }
+
+ public AtomFeedImpl feed(final Element input) {
+ if (!ODataConstants.ATOM_ELEM_FEED.equals(input.getNodeName())) {
+ return null;
+ }
+
+ final AtomFeedImpl feed = new AtomFeedImpl();
+
+ common(input, feed);
+
+ final List<Element> entries = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_ENTRY);
+ for (Element entry : entries) {
+ feed.getEntries().add(entry(entry));
+ }
+
+ final List<Element> links = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_LINK);
+ for (Element link : links) {
+ if (ODataConstants.NEXT_LINK_REL.equals(link.getAttribute(ODataConstants.ATTR_REL))) {
+ feed.setNext(URI.create(link.getAttribute(ODataConstants.ATTR_HREF)));
+ }
+ }
+
+ final List<Element> counts = XMLUtils.getChildElements(input, ODataConstants.ATOM_ATTR_COUNT);
+ if (!counts.isEmpty()) {
+ try {
+ feed.setCount(Integer.parseInt(counts.get(0).getTextContent()));
+ } catch (Exception e) {
+ LOG.error("Could not parse $inlinecount {}", counts.get(0).getTextContent(), e);
+ }
+ }
+
+ return feed;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomEntryImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomEntryImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomEntryImpl.java
new file mode 100644
index 0000000..5ddb182
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomEntryImpl.java
@@ -0,0 +1,120 @@
+/*
+ * 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.client.core.data;
+
+import java.net.URI;
+import java.util.Date;
+import org.apache.commons.lang3.StringUtils;
+
+public class AtomEntryImpl extends AbstractEntry implements AtomObject {
+
+ private static final long serialVersionUID = 6973729343868293279L;
+
+ public static class Author {
+
+ private String name;
+
+ private String uri;
+
+ private String email;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public void setUri(final String uri) {
+ this.uri = uri;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(final String email) {
+ this.email = email;
+ }
+
+ public boolean isEmpty() {
+ return StringUtils.isBlank(name) && StringUtils.isBlank(uri) && StringUtils.isBlank(email);
+ }
+ }
+
+ private URI baseURI;
+
+ private String title;
+
+ private String summary;
+
+ private Date updated;
+
+ private Author author;
+
+ @Override
+ public void setBaseURI(final String baseURI) {
+ this.baseURI = URI.create(baseURI);
+ }
+
+ @Override
+ public URI getBaseURI() {
+ return baseURI;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ public void setTitle(final String title) {
+ this.title = title;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ @Override
+ public void setSummary(final String summary) {
+ this.summary = summary;
+ }
+
+ public Date getUpdated() {
+ return new Date(updated.getTime());
+ }
+
+ @Override
+ public void setUpdated(final Date updated) {
+ this.updated = new Date(updated.getTime());
+ }
+
+ public Author getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(final Author author) {
+ this.author = author;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomFeedImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomFeedImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomFeedImpl.java
new file mode 100644
index 0000000..08a85db
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomFeedImpl.java
@@ -0,0 +1,122 @@
+/*
+ * 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.client.core.data;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import org.apache.olingo.client.api.data.Entry;
+import org.apache.olingo.client.api.data.Feed;
+
+/**
+ * List of entries, represented via Atom.
+ *
+ * @see AtomEntry
+ */
+public class AtomFeedImpl extends AbstractPayloadObject implements AtomObject, Feed {
+
+ private static final long serialVersionUID = 5466590540021319153L;
+
+ private URI baseURI;
+
+ private String id;
+
+ private String title;
+
+ private String summary;
+
+ private Date updated;
+
+ private Integer count;
+
+ private final List<Entry> entries = new ArrayList<Entry>();
+
+ private URI next;
+
+ @Override
+ public URI getBaseURI() {
+ return baseURI;
+ }
+
+ @Override
+ public void setBaseURI(final String baseURI) {
+ this.baseURI = URI.create(baseURI);
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(final String id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ public void setTitle(final String title) {
+ this.title = title;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ @Override
+ public void setSummary(final String summary) {
+ this.summary = summary;
+ }
+
+ public Date getUpdated() {
+ return new Date(updated.getTime());
+ }
+
+ @Override
+ public void setUpdated(final Date updated) {
+ this.updated = new Date(updated.getTime());
+ }
+
+ public void setCount(final Integer count) {
+ this.count = count;
+ }
+
+ @Override
+ public Integer getCount() {
+ return count;
+ }
+
+ @Override
+ public List<Entry> getEntries() {
+ return entries;
+ }
+
+ @Override
+ public void setNext(final URI next) {
+ this.next = next;
+ }
+
+ @Override
+ public URI getNext() {
+ return next;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomObject.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomObject.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomObject.java
new file mode 100644
index 0000000..b0ecab9
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomObject.java
@@ -0,0 +1,41 @@
+/*
+ * 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.client.core.data;
+
+import java.util.Date;
+
+/**
+ * Common methods for <tt>AtomEntryImpl</tt> and <tt>AtomFeedImpl</tt>.
+ *
+ * @see AtomEntryImpl
+ * @see AtomFeedImpl
+ */
+public interface AtomObject {
+
+ void setBaseURI(String baseURI);
+
+ void setId(String id);
+
+ void setTitle(String title);
+
+ void setSummary(String summary);
+
+ void setUpdated(Date updated);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomSerializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomSerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomSerializer.java
new file mode 100644
index 0000000..73def31
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomSerializer.java
@@ -0,0 +1,184 @@
+/*
+ * 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.client.core.data;
+
+import java.util.List;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.entity.ContentType;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.data.Entry;
+import org.apache.olingo.client.api.data.Link;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class AtomSerializer {
+
+ private final ODataClient client;
+
+ public AtomSerializer(final ODataClient client) {
+ this.client = client;
+ }
+
+ public <T extends AbstractPayloadObject> Element serialize(final T obj) throws ParserConfigurationException {
+ if (obj instanceof AtomEntryImpl) {
+ return entry((AtomEntryImpl) obj);
+ } else if (obj instanceof AtomFeedImpl) {
+ return feed((AtomFeedImpl) obj);
+ } else {
+ throw new IllegalArgumentException("Unsupported Atom object for standalone serialization: " + obj);
+ }
+ }
+
+ private void setLinks(final Element entry, final List<Link> links) throws ParserConfigurationException {
+ for (Link link : links) {
+ final Element linkElem = entry.getOwnerDocument().createElement(ODataConstants.ATOM_ELEM_LINK);
+
+ linkElem.setAttribute(ODataConstants.ATTR_REL, link.getRel());
+ linkElem.setAttribute(ODataConstants.ATTR_TITLE, link.getTitle());
+ linkElem.setAttribute(ODataConstants.ATTR_HREF, link.getHref());
+
+ if (StringUtils.isNotBlank(link.getType())) {
+ linkElem.setAttribute(ODataConstants.ATTR_TYPE, link.getType());
+ }
+
+ if (link.getInlineEntry() != null || link.getInlineFeed() != null) {
+ final Element inline = entry.getOwnerDocument().createElement(ODataConstants.ATOM_ELEM_INLINE);
+ linkElem.appendChild(inline);
+
+ if (link.getInlineEntry() != null) {
+ inline.appendChild(entry.getOwnerDocument().importNode(
+ entry((AtomEntryImpl) link.getInlineEntry()), true));
+ }
+ if (link.getInlineFeed() != null) {
+ inline.appendChild(entry.getOwnerDocument().importNode(
+ feed((AtomFeedImpl) link.getInlineFeed()), true));
+ }
+ }
+
+ entry.appendChild(linkElem);
+ }
+ }
+
+ private Element entry(final AtomEntryImpl entry) throws ParserConfigurationException {
+ final DocumentBuilder builder = XMLUtils.DOC_BUILDER_FACTORY.newDocumentBuilder();
+ final Document doc = builder.newDocument();
+
+ final Element entryElem = doc.createElement(ODataConstants.ATOM_ELEM_ENTRY);
+ entryElem.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, ODataConstants.NS_ATOM);
+ entryElem.setAttribute(ODataConstants.XMLNS_METADATA,
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
+ entryElem.setAttribute(ODataConstants.XMLNS_DATASERVICES,
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+ entryElem.setAttribute(ODataConstants.XMLNS_GML, ODataConstants.NS_GML);
+ entryElem.setAttribute(ODataConstants.XMLNS_GEORSS, ODataConstants.NS_GEORSS);
+ if (entry.getBaseURI() != null) {
+ entryElem.setAttribute(ODataConstants.ATTR_XMLBASE, entry.getBaseURI().toASCIIString());
+ }
+ doc.appendChild(entryElem);
+
+ final Element category = doc.createElement(ODataConstants.ATOM_ELEM_CATEGORY);
+ category.setAttribute(ODataConstants.ATOM_ATTR_TERM, entry.getType());
+ category.setAttribute(ODataConstants.ATOM_ATTR_SCHEME,
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_SCHEME));
+ entryElem.appendChild(category);
+
+ if (StringUtils.isNotBlank(entry.getTitle())) {
+ final Element title = doc.createElement(ODataConstants.ATOM_ELEM_TITLE);
+ title.appendChild(doc.createTextNode(entry.getTitle()));
+ entryElem.appendChild(title);
+ }
+
+ if (StringUtils.isNotBlank(entry.getSummary())) {
+ final Element summary = doc.createElement(ODataConstants.ATOM_ELEM_SUMMARY);
+ summary.appendChild(doc.createTextNode(entry.getSummary()));
+ entryElem.appendChild(summary);
+ }
+
+ setLinks(entryElem, entry.getAssociationLinks());
+ setLinks(entryElem, entry.getNavigationLinks());
+ setLinks(entryElem, entry.getMediaEditLinks());
+
+ final Element content = doc.createElement(ODataConstants.ATOM_ELEM_CONTENT);
+ if (entry.isMediaEntry()) {
+ if (StringUtils.isNotBlank(entry.getMediaContentType())) {
+ content.setAttribute(ODataConstants.ATTR_TYPE, entry.getMediaContentType());
+ }
+ if (StringUtils.isNotBlank(entry.getMediaContentSource())) {
+ content.setAttribute(ODataConstants.ATOM_ATTR_SRC, entry.getMediaContentSource());
+ }
+ if (content.getAttributes().getLength() > 0) {
+ entryElem.appendChild(content);
+ }
+
+ if (entry.getMediaEntryProperties() != null) {
+ entryElem.appendChild(doc.importNode(entry.getMediaEntryProperties(), true));
+ }
+ } else {
+ content.setAttribute(ODataConstants.ATTR_TYPE, ContentType.APPLICATION_XML.getMimeType());
+ if (entry.getContent() != null) {
+ content.appendChild(doc.importNode(entry.getContent(), true));
+ }
+ entryElem.appendChild(content);
+ }
+
+ return entryElem;
+ }
+
+ private Element feed(final AtomFeedImpl feed) throws ParserConfigurationException {
+ final DocumentBuilder builder = XMLUtils.DOC_BUILDER_FACTORY.newDocumentBuilder();
+ final Document doc = builder.newDocument();
+
+ final Element feedElem = doc.createElement(ODataConstants.ATOM_ELEM_FEED);
+ feedElem.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, ODataConstants.NS_ATOM);
+ feedElem.setAttribute(ODataConstants.XMLNS_METADATA,
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
+ feedElem.setAttribute(ODataConstants.XMLNS_DATASERVICES,
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+ feedElem.setAttribute(ODataConstants.XMLNS_GML, ODataConstants.NS_GML);
+ feedElem.setAttribute(ODataConstants.XMLNS_GEORSS, ODataConstants.NS_GEORSS);
+ if (feed.getBaseURI() != null) {
+ feedElem.setAttribute(ODataConstants.ATTR_XMLBASE, feed.getBaseURI().toASCIIString());
+ }
+ doc.appendChild(feedElem);
+
+ if (StringUtils.isNotBlank(feed.getTitle())) {
+ final Element title = doc.createElement(ODataConstants.ATOM_ELEM_TITLE);
+ title.appendChild(doc.createTextNode(feed.getTitle()));
+ feedElem.appendChild(title);
+ }
+
+ if (StringUtils.isNotBlank(feed.getSummary())) {
+ final Element summary = doc.createElement(ODataConstants.ATOM_ELEM_SUMMARY);
+ summary.appendChild(doc.createTextNode(feed.getSummary()));
+ feedElem.appendChild(summary);
+ }
+
+ for (Entry entry : feed.getEntries()) {
+ feedElem.appendChild(doc.importNode(entry((AtomEntryImpl) entry), true));
+ }
+
+ return feedElem;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/GeospatialJSONHandler.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/GeospatialJSONHandler.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/GeospatialJSONHandler.java
new file mode 100644
index 0000000..610f1a2
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/GeospatialJSONHandler.java
@@ -0,0 +1,412 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.domain.ODataJClientEdmPrimitiveType;
+import org.apache.olingo.client.api.domain.geospatial.Geospatial;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+final class GeospatialJSONHandler {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(GeospatialJSONHandler.class);
+
+ private GeospatialJSONHandler() {
+ // Empty private constructor for static utility classes
+ }
+
+ private static Element deserializePoint(final Document document, final Iterator<JsonNode> itor) {
+ final Element point = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POINT);
+
+ final Element ppos = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POS);
+ point.appendChild(ppos);
+ if (itor.hasNext()) {
+ ppos.appendChild(document.createTextNode(itor.next().asText() + " " + itor.next().asText()));
+ }
+
+ return point;
+ }
+
+ private static void appendPoses(final Element parent, final Document document, final Iterator<JsonNode> itor) {
+ while (itor.hasNext()) {
+ final Iterator<JsonNode> lineItor = itor.next().elements();
+ final Element pos = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POS);
+ parent.appendChild(pos);
+ pos.appendChild(document.createTextNode(lineItor.next().asText() + " " + lineItor.next().asText()));
+ }
+ }
+
+ private static Element deserializeLineString(final Document document, final Iterator<JsonNode> itor) {
+ final Element lineString = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_LINESTRING);
+ if (!itor.hasNext()) {
+ lineString.appendChild(document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POSLIST));
+ }
+
+ appendPoses(lineString, document, itor);
+
+ return lineString;
+ }
+
+ private static Element deserializePolygon(final Document document, final Iterator<JsonNode> itor) {
+ final Element polygon = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON);
+
+ if (itor.hasNext()) {
+ final Iterator<JsonNode> extItor = itor.next().elements();
+ final Element exterior = document.createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON_EXTERIOR);
+ polygon.appendChild(exterior);
+ final Element extLR = document.createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON_LINEARRING);
+ exterior.appendChild(extLR);
+
+ appendPoses(extLR, document, extItor);
+ }
+
+ if (itor.hasNext()) {
+ final Iterator<JsonNode> intItor = itor.next().elements();
+ final Element interior = document.createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON_INTERIOR);
+ polygon.appendChild(interior);
+ final Element intLR = document.createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON_LINEARRING);
+ interior.appendChild(intLR);
+
+ appendPoses(intLR, document, intItor);
+ }
+
+ return polygon;
+ }
+
+ public static void deserialize(final JsonNode node, final Element parent, final String type) {
+ final Iterator<JsonNode> cooItor = node.has(ODataConstants.JSON_COORDINATES)
+ ? node.get(ODataConstants.JSON_COORDINATES).elements()
+ : Collections.<JsonNode>emptyList().iterator();
+
+ Element root = null;
+ final ODataJClientEdmPrimitiveType edmSimpleType = ODataJClientEdmPrimitiveType.fromValue(type);
+ switch (edmSimpleType) {
+ case GeographyPoint:
+ case GeometryPoint:
+ root = deserializePoint(parent.getOwnerDocument(), cooItor);
+ break;
+
+ case GeographyMultiPoint:
+ case GeometryMultiPoint:
+ root = parent.getOwnerDocument().createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_MULTIPOINT);
+ if (cooItor.hasNext()) {
+ final Element pointMembers = parent.getOwnerDocument().createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_POINTMEMBERS);
+ root.appendChild(pointMembers);
+ while (cooItor.hasNext()) {
+ final Iterator<JsonNode> mpItor = cooItor.next().elements();
+ pointMembers.appendChild(deserializePoint(parent.getOwnerDocument(), mpItor));
+ }
+ }
+ break;
+
+ case GeographyLineString:
+ case GeometryLineString:
+ root = deserializeLineString(parent.getOwnerDocument(), cooItor);
+ break;
+
+ case GeographyMultiLineString:
+ case GeometryMultiLineString:
+ root = parent.getOwnerDocument().createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_MULTILINESTRING);
+ if (cooItor.hasNext()) {
+ final Element lineStringMembers = parent.getOwnerDocument().createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_LINESTRINGMEMBERS);
+ root.appendChild(lineStringMembers);
+ while (cooItor.hasNext()) {
+ final Iterator<JsonNode> mlsItor = cooItor.next().elements();
+ lineStringMembers.appendChild(deserializeLineString(parent.getOwnerDocument(), mlsItor));
+ }
+ }
+ break;
+
+ case GeographyPolygon:
+ case GeometryPolygon:
+ root = deserializePolygon(parent.getOwnerDocument(), cooItor);
+ break;
+
+ case GeographyMultiPolygon:
+ case GeometryMultiPolygon:
+ root = parent.getOwnerDocument().createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_MULTIPOLYGON);
+ if (cooItor.hasNext()) {
+ final Element surfaceMembers = parent.getOwnerDocument().createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_SURFACEMEMBERS);
+ root.appendChild(surfaceMembers);
+ while (cooItor.hasNext()) {
+ final Iterator<JsonNode> mpItor = cooItor.next().elements();
+ surfaceMembers.appendChild(deserializePolygon(parent.getOwnerDocument(), mpItor));
+ }
+ }
+ break;
+
+ case GeographyCollection:
+ case GeometryCollection:
+ root = parent.getOwnerDocument().createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_GEOCOLLECTION);
+ if (node.has(ODataConstants.JSON_GEOMETRIES)) {
+ final Iterator<JsonNode> geoItor = node.get(ODataConstants.JSON_GEOMETRIES).elements();
+ if (geoItor.hasNext()) {
+ final Element geometryMembers = parent.getOwnerDocument().createElementNS(
+ ODataConstants.NS_GML, ODataConstants.ELEM_GEOMEMBERS);
+ root.appendChild(geometryMembers);
+
+ while (geoItor.hasNext()) {
+ final JsonNode geo = geoItor.next();
+ final String collItemType = geo.get(ODataConstants.ATTR_TYPE).asText();
+ final String callAsType;
+ if (ODataJClientEdmPrimitiveType.GeographyCollection.name().equals(collItemType)
+ || ODataJClientEdmPrimitiveType.GeometryCollection.name().equals(collItemType)) {
+
+ callAsType = collItemType;
+ } else {
+ callAsType =
+ (edmSimpleType == ODataJClientEdmPrimitiveType.GeographyCollection ? "Geography" : "Geometry")
+ + collItemType;
+ }
+
+ deserialize(geo, geometryMembers, ODataJClientEdmPrimitiveType.namespace() + "." + callAsType);
+ }
+ }
+ }
+ break;
+
+ default:
+ }
+
+ if (root != null) {
+ parent.appendChild(root);
+ if (node.has(ODataConstants.JSON_CRS)) {
+ root.setAttribute(ODataConstants.ATTR_SRSNAME,
+ ODataConstants.JSON_GIS_URLPREFIX
+ + node.get(ODataConstants.JSON_CRS).get(ODataConstants.PROPERTIES).get(ODataConstants.NAME).
+ asText().split(":")[1]);
+ }
+ }
+ }
+
+ private static void serializeCrs(final JsonGenerator jgen, final Node node) throws IOException {
+ if (node.getAttributes().getNamedItem(ODataConstants.ATTR_SRSNAME) != null) {
+ final String srsName = node.getAttributes().getNamedItem(ODataConstants.ATTR_SRSNAME).getTextContent();
+ final int prefIdx = srsName.indexOf(ODataConstants.JSON_GIS_URLPREFIX);
+ final String crsValue = srsName.substring(prefIdx + ODataConstants.JSON_GIS_URLPREFIX.length());
+
+ jgen.writeObjectFieldStart(ODataConstants.JSON_CRS);
+ jgen.writeStringField(ODataConstants.ATTR_TYPE, ODataConstants.NAME);
+ jgen.writeObjectFieldStart(ODataConstants.PROPERTIES);
+ jgen.writeStringField(ODataConstants.NAME, "EPSG:" + crsValue);
+ jgen.writeEndObject();
+ jgen.writeEndObject();
+ }
+ }
+
+ private static void serializePoint(final JsonGenerator jgen, final Node node) throws IOException {
+ for (String coord : node.getTextContent().split(" ")) {
+ jgen.writeNumber(coord);
+ }
+ }
+
+ private static void serializeLineString(final JsonGenerator jgen, final Element node) throws IOException {
+ for (Element element : XMLUtils.getChildElements(node, ODataConstants.ELEM_POS)) {
+ jgen.writeStartArray();
+ serializePoint(jgen, element);
+ jgen.writeEndArray();
+ }
+ }
+
+ private static void serializePolygon(final JsonGenerator jgen, final Element node) throws IOException {
+ for (Element exterior : XMLUtils.getChildElements(node, ODataConstants.ELEM_POLYGON_EXTERIOR)) {
+ jgen.writeStartArray();
+ serializeLineString(jgen,
+ XMLUtils.getChildElements(exterior, ODataConstants.ELEM_POLYGON_LINEARRING).get(0));
+ jgen.writeEndArray();
+
+ }
+ for (Element interior : XMLUtils.getChildElements(node, ODataConstants.ELEM_POLYGON_INTERIOR)) {
+ jgen.writeStartArray();
+ serializeLineString(jgen,
+ XMLUtils.getChildElements(interior, ODataConstants.ELEM_POLYGON_LINEARRING).get(0));
+ jgen.writeEndArray();
+
+ }
+ }
+
+ public static void serialize(final ODataClient client,
+ final JsonGenerator jgen, final Element node, final String type) throws IOException {
+
+ final ODataJClientEdmPrimitiveType edmSimpleType = ODataJClientEdmPrimitiveType.fromValue(type);
+
+ if (edmSimpleType.equals(ODataJClientEdmPrimitiveType.GeographyCollection)
+ || edmSimpleType.equals(ODataJClientEdmPrimitiveType.GeometryCollection)) {
+
+ jgen.writeStringField(ODataConstants.ATTR_TYPE, ODataJClientEdmPrimitiveType.GeometryCollection.name());
+ } else {
+ final int yIdx = edmSimpleType.name().indexOf('y');
+ final String itemType = edmSimpleType.name().substring(yIdx + 1);
+ jgen.writeStringField(ODataConstants.ATTR_TYPE, itemType);
+ }
+
+ Element root = null;
+ switch (edmSimpleType) {
+ case GeographyPoint:
+ case GeometryPoint:
+ root = XMLUtils.getChildElements(node, ODataConstants.ELEM_POINT).get(0);
+
+ jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+ serializePoint(jgen, XMLUtils.getChildElements(root, ODataConstants.ELEM_POS).get(0));
+ jgen.writeEndArray();
+ break;
+
+ case GeographyMultiPoint:
+ case GeometryMultiPoint:
+ root = XMLUtils.getChildElements(node, ODataConstants.ELEM_MULTIPOINT).get(0);
+
+ jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+
+ final List<Element> pMembs = XMLUtils.getChildElements(root, ODataConstants.ELEM_POINTMEMBERS);
+ if (pMembs != null && !pMembs.isEmpty()) {
+ for (Element point : XMLUtils.getChildElements(pMembs.get(0), ODataConstants.ELEM_POINT)) {
+ jgen.writeStartArray();
+ serializePoint(jgen, XMLUtils.getChildElements(point, ODataConstants.ELEM_POS).get(0));
+ jgen.writeEndArray();
+ }
+ }
+
+ jgen.writeEndArray();
+ break;
+
+ case GeographyLineString:
+ case GeometryLineString:
+ root = XMLUtils.getChildElements(node, ODataConstants.ELEM_LINESTRING).get(0);
+
+ jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+ serializeLineString(jgen, root);
+ jgen.writeEndArray();
+ break;
+
+ case GeographyMultiLineString:
+ case GeometryMultiLineString:
+ root = XMLUtils.getChildElements(node, ODataConstants.ELEM_MULTILINESTRING).get(0);
+
+ jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+
+ final List<Element> lMembs = XMLUtils.getChildElements(root, ODataConstants.ELEM_LINESTRINGMEMBERS);
+ if (lMembs != null && !lMembs.isEmpty()) {
+ for (Element lineStr : XMLUtils.getChildElements(lMembs.get(0), ODataConstants.ELEM_LINESTRING)) {
+ jgen.writeStartArray();
+ serializeLineString(jgen, lineStr);
+ jgen.writeEndArray();
+ }
+ }
+
+ jgen.writeEndArray();
+ break;
+
+ case GeographyPolygon:
+ case GeometryPolygon:
+ root = XMLUtils.getChildElements(node, ODataConstants.ELEM_POLYGON).get(0);
+
+ jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+ serializePolygon(jgen, root);
+ jgen.writeEndArray();
+ break;
+
+ case GeographyMultiPolygon:
+ case GeometryMultiPolygon:
+ root = XMLUtils.getChildElements(node, ODataConstants.ELEM_MULTIPOLYGON).get(0);
+
+ jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+
+ final List<Element> mpMembs = XMLUtils.getChildElements(root, ODataConstants.ELEM_SURFACEMEMBERS);
+ if (mpMembs != null & !mpMembs.isEmpty()) {
+ for (Element pol : XMLUtils.getChildElements(mpMembs.get(0), ODataConstants.ELEM_POLYGON)) {
+ jgen.writeStartArray();
+ serializePolygon(jgen, pol);
+ jgen.writeEndArray();
+ }
+ }
+
+ jgen.writeEndArray();
+ break;
+
+ case GeographyCollection:
+ case GeometryCollection:
+ root = XMLUtils.getChildElements(node, ODataConstants.ELEM_GEOCOLLECTION).get(0);
+
+ final List<Element> cMembs = XMLUtils.getChildElements(root, ODataConstants.ELEM_GEOMEMBERS);
+ if (cMembs != null && !cMembs.isEmpty()) {
+ jgen.writeArrayFieldStart(ODataConstants.JSON_GEOMETRIES);
+
+ for (Node geom : XMLUtils.getChildNodes(cMembs.get(0), Node.ELEMENT_NODE)) {
+ try {
+ final DocumentBuilder builder = XMLUtils.DOC_BUILDER_FACTORY.newDocumentBuilder();
+ final Document doc = builder.newDocument();
+
+ final Element fakeParent = doc.createElementNS(
+ client.getServiceVersion().getNamespaceMap().
+ get(ODataServiceVersion.NS_DATASERVICES),
+ ODataConstants.PREFIX_DATASERVICES + "fake");
+ fakeParent.appendChild(doc.importNode(geom, true));
+
+ final ODataJClientEdmPrimitiveType callAsType = XMLUtils.simpleTypeForNode(
+ edmSimpleType == ODataJClientEdmPrimitiveType.GeographyCollection
+ ? Geospatial.Dimension.GEOGRAPHY : Geospatial.Dimension.GEOMETRY,
+ geom);
+
+ jgen.writeStartObject();
+ serialize(client, jgen, fakeParent, callAsType.toString());
+ jgen.writeEndObject();
+ } catch (Exception e) {
+ LOG.warn("While serializing {}", XMLUtils.getSimpleName(geom), e);
+ }
+ }
+
+ jgen.writeEndArray();
+ }
+ break;
+
+ default:
+ }
+
+ if (root != null) {
+ serializeCrs(jgen, root);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONDOMTreeUtils.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONDOMTreeUtils.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONDOMTreeUtils.java
new file mode 100644
index 0000000..bfe36ff
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONDOMTreeUtils.java
@@ -0,0 +1,260 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import java.util.Iterator;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.domain.ODataJClientEdmPrimitiveType;
+import org.apache.olingo.client.api.domain.ODataPrimitiveValue;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * DOM tree utilities class for JSON.
+ */
+final class JSONDOMTreeUtils {
+
+ private JSONDOMTreeUtils() {
+ // Empty private constructor for static utility classes
+ }
+
+ /**
+ * Recursively builds DOM content out of JSON subtree rooted at given node.
+ *
+ * @param client OData client.
+ * @param document root of the DOM document being built
+ * @param parent parent of the nodes being generated during this step
+ * @param node JSON node to be used as source for DOM elements
+ */
+ public static void buildSubtree(final ODataClient client, final Element parent, final JsonNode node) {
+ final Iterator<String> fieldNameItor = node.fieldNames();
+ final Iterator<JsonNode> nodeItor = node.elements();
+ while (nodeItor.hasNext()) {
+ final JsonNode child = nodeItor.next();
+ final String name = fieldNameItor.hasNext() ? fieldNameItor.next() : "";
+
+ // no name? array item
+ if (name.isEmpty()) {
+ final Element element = parent.getOwnerDocument().createElementNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES),
+ ODataConstants.PREFIX_DATASERVICES + ODataConstants.ELEM_ELEMENT);
+ parent.appendChild(element);
+
+ if (child.isValueNode()) {
+ if (child.isNull()) {
+ element.setAttributeNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_NULL, Boolean.toString(true));
+ } else {
+ element.appendChild(parent.getOwnerDocument().createTextNode(child.asText()));
+ }
+ }
+
+ if (child.isContainerNode()) {
+ buildSubtree(client, element, child);
+ }
+ } else if (!name.contains("@") && !ODataConstants.JSON_TYPE.equals(name)) {
+ final Element property = parent.getOwnerDocument().createElementNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES),
+ ODataConstants.PREFIX_DATASERVICES + name);
+ parent.appendChild(property);
+
+ boolean typeSet = false;
+ if (node.hasNonNull(name + "@" + ODataConstants.JSON_TYPE)) {
+ property.setAttributeNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_M_TYPE,
+ node.get(name + "@" + ODataConstants.JSON_TYPE).textValue());
+ typeSet = true;
+ }
+
+ if (child.isNull()) {
+ property.setAttributeNS(client.getServiceVersion().getNamespaceMap().
+ get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_NULL, Boolean.toString(true));
+ } else if (child.isValueNode()) {
+ if (!typeSet) {
+ if (child.isInt()) {
+ property.setAttributeNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Int32.toString());
+ }
+ if (child.isLong()) {
+ property.setAttributeNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Int64.toString());
+ }
+ if (child.isBigDecimal()) {
+ property.setAttributeNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Decimal.toString());
+ }
+ if (child.isDouble()) {
+ property.setAttributeNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Double.toString());
+ }
+ if (child.isBoolean()) {
+ property.setAttributeNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Boolean.toString());
+ }
+ if (child.isTextual()) {
+ property.setAttributeNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.String.toString());
+ }
+ }
+
+ property.appendChild(parent.getOwnerDocument().createTextNode(child.asText()));
+ } else if (child.isContainerNode()) {
+ if (!typeSet && child.hasNonNull(ODataConstants.JSON_TYPE)) {
+ property.
+ setAttributeNS(client.getServiceVersion().getNamespaceMap().
+ get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ATTR_M_TYPE,
+ child.get(ODataConstants.JSON_TYPE).textValue());
+ }
+
+ final String type = property.getAttribute(ODataConstants.ATTR_M_TYPE);
+ if (StringUtils.isNotBlank(type) && ODataJClientEdmPrimitiveType.isGeospatial(type)) {
+ if (ODataJClientEdmPrimitiveType.Geography.toString().equals(type)
+ || ODataJClientEdmPrimitiveType.Geometry.toString().equals(type)) {
+
+ final String geoType = child.get(ODataConstants.ATTR_TYPE).textValue();
+ property.setAttributeNS(client.getServiceVersion().getNamespaceMap().get(
+ ODataServiceVersion.NS_METADATA), ODataConstants.ATTR_M_TYPE,
+ geoType.startsWith("Geo")
+ ? ODataJClientEdmPrimitiveType.namespace() + "." + geoType
+ : type + geoType);
+ }
+
+ if (child.has(ODataConstants.JSON_COORDINATES) || child.has(ODataConstants.JSON_GEOMETRIES)) {
+ GeospatialJSONHandler.deserialize(
+ child, property, property.getAttribute(ODataConstants.ATTR_M_TYPE));
+ }
+ } else {
+ buildSubtree(client, property, child);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Serializes DOM content as JSON.
+ *
+ * @param client OData client.
+ * @param jgen JSON generator.
+ * @param content content.
+ * @throws IOException in case of write error.
+ */
+ public static void writeSubtree(final ODataClient client, final JsonGenerator jgen, final Node content)
+ throws IOException {
+
+ writeSubtree(client, jgen, content, false);
+ }
+
+ /**
+ * Serializes DOM content as JSON.
+ *
+ * @param client OData client.
+ * @param jgen JSON generator.
+ * @param content content.
+ * @param propType whether to output type information in the way needed for property values or not.
+ * @throws IOException in case of write error.
+ */
+ public static void writeSubtree(
+ final ODataClient client, final JsonGenerator jgen, final Node content, final boolean propType)
+ throws IOException {
+
+ for (Node child : XMLUtils.getChildNodes(content, Node.ELEMENT_NODE)) {
+ final String childName = XMLUtils.getSimpleName(child);
+
+ final Node typeAttr = child.getAttributes().getNamedItem(ODataConstants.ATTR_M_TYPE);
+ if (typeAttr != null && ODataJClientEdmPrimitiveType.isGeospatial(typeAttr.getTextContent())) {
+ jgen.writeStringField(propType ? ODataConstants.JSON_TYPE : childName + "@" + ODataConstants.JSON_TYPE,
+ typeAttr.getTextContent());
+
+ jgen.writeObjectFieldStart(childName);
+ GeospatialJSONHandler.serialize(client, jgen, (Element) child, typeAttr.getTextContent());
+ jgen.writeEndObject();
+ } else if (XMLUtils.hasOnlyTextChildNodes(child)) {
+ if (child.hasChildNodes()) {
+ final String out;
+ if (typeAttr == null) {
+ out = child.getChildNodes().item(0).getNodeValue();
+ } else {
+ final ODataJClientEdmPrimitiveType type = ODataJClientEdmPrimitiveType.fromValue(typeAttr.getTextContent());
+ final ODataPrimitiveValue value = client.getPrimitiveValueBuilder().setType(type).
+ setText(child.getChildNodes().item(0).getNodeValue()).build();
+ out = value.toString();
+
+ jgen.writeStringField(childName + "@" + ODataConstants.JSON_TYPE, type.toString());
+ }
+ jgen.writeStringField(childName, out);
+ } else {
+ if (child.getAttributes().getNamedItem(ODataConstants.ATTR_NULL) == null) {
+ if (typeAttr != null && ODataJClientEdmPrimitiveType.String.toString().equals(typeAttr.getTextContent())) {
+ jgen.writeStringField(childName + "@" + ODataConstants.JSON_TYPE, typeAttr.getTextContent());
+ jgen.writeStringField(childName, StringUtils.EMPTY);
+ } else {
+ jgen.writeArrayFieldStart(childName);
+ jgen.writeEndArray();
+ }
+ } else {
+ jgen.writeNullField(childName);
+ }
+ }
+ } else {
+ if (XMLUtils.hasElementsChildNode(child)) {
+ jgen.writeArrayFieldStart(childName);
+
+ for (Node nephew : XMLUtils.getChildNodes(child, Node.ELEMENT_NODE)) {
+ if (XMLUtils.hasOnlyTextChildNodes(nephew)) {
+ jgen.writeString(nephew.getChildNodes().item(0).getNodeValue());
+ } else {
+ jgen.writeStartObject();
+ writeSubtree(client, jgen, nephew);
+ jgen.writeEndObject();
+ }
+ }
+
+ jgen.writeEndArray();
+ } else {
+ jgen.writeObjectFieldStart(childName);
+ if (typeAttr != null) {
+ jgen.writeStringField(ODataConstants.JSON_TYPE, typeAttr.getTextContent());
+ }
+
+ writeSubtree(client, jgen, child);
+
+ jgen.writeEndObject();
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryDeserializer.java
new file mode 100644
index 0000000..0cfac43
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryDeserializer.java
@@ -0,0 +1,240 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.data.LinkType;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Reads JSON string into an entry.
+ * <br/>
+ * If metadata information is available, the corresponding entry fields and content will be populated.
+ */
+public class JSONEntryDeserializer extends ODataJacksonDeserializer<JSONEntryImpl> {
+
+ private String getTitle(final Map.Entry<String, JsonNode> entry) {
+ return entry.getKey().substring(0, entry.getKey().indexOf('@'));
+ }
+
+ private String setInline(final String name, final String suffix, final ObjectNode tree,
+ final ObjectCodec codec, final LinkImpl link) throws IOException {
+
+ final String entryNamePrefix = name.substring(0, name.indexOf(suffix));
+ if (tree.has(entryNamePrefix)) {
+ final JsonNode inline = tree.path(entryNamePrefix);
+
+ if (inline instanceof ObjectNode) {
+ final JsonParser inlineParser = inline.traverse();
+ inlineParser.setCodec(codec);
+ link.setInlineEntry(inlineParser.readValuesAs(JSONEntryImpl.class).next());
+ }
+
+ if (inline instanceof ArrayNode) {
+ final JSONFeedImpl feed = new JSONFeedImpl();
+ final Iterator<JsonNode> entries = ((ArrayNode) inline).elements();
+ while (entries.hasNext()) {
+ final JsonParser inlineParser = entries.next().traverse();
+ inlineParser.setCodec(codec);
+ feed.getEntries().add(inlineParser.readValuesAs(JSONEntryImpl.class).next());
+ }
+
+ link.setInlineFeed(feed);
+ }
+ }
+ return entryNamePrefix;
+ }
+
+ @Override
+ protected JSONEntryImpl doDeserialize(final JsonParser parser, final DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ final ObjectNode tree = (ObjectNode) parser.getCodec().readTree(parser);
+
+ if (tree.has(ODataConstants.JSON_VALUE) && tree.get(ODataConstants.JSON_VALUE).isArray()) {
+ throw new JsonParseException("Expected OData Entity, found EntitySet", parser.getCurrentLocation());
+ }
+
+ final boolean isMediaEntry =
+ tree.hasNonNull(ODataConstants.JSON_MEDIAREAD_LINK)
+ && tree.hasNonNull(ODataConstants.JSON_MEDIA_CONTENT_TYPE);
+
+ final JSONEntryImpl entry = new JSONEntryImpl();
+
+ if (tree.hasNonNull(ODataConstants.JSON_METADATA)) {
+ entry.setMetadata(URI.create(tree.get(ODataConstants.JSON_METADATA).textValue()));
+ tree.remove(ODataConstants.JSON_METADATA);
+ }
+
+ if (tree.hasNonNull(ODataConstants.JSON_MEDIA_ETAG)) {
+ entry.setMediaETag(tree.get(ODataConstants.JSON_MEDIA_ETAG).textValue());
+ tree.remove(ODataConstants.JSON_MEDIA_ETAG);
+ }
+
+ if (tree.hasNonNull(ODataConstants.JSON_ETAG)) {
+ entry.setETag(tree.get(ODataConstants.JSON_ETAG).textValue());
+ tree.remove(ODataConstants.JSON_ETAG);
+ }
+
+ if (tree.hasNonNull(ODataConstants.JSON_TYPE)) {
+ entry.setType(tree.get(ODataConstants.JSON_TYPE).textValue());
+ tree.remove(ODataConstants.JSON_TYPE);
+ }
+
+ if (tree.hasNonNull(ODataConstants.JSON_ID)) {
+ entry.setId(tree.get(ODataConstants.JSON_ID).textValue());
+ tree.remove(ODataConstants.JSON_ID);
+ }
+
+ if (tree.hasNonNull(ODataConstants.JSON_READ_LINK)) {
+ final LinkImpl link = new LinkImpl();
+ link.setRel(ODataConstants.SELF_LINK_REL);
+ link.setHref(tree.get(ODataConstants.JSON_READ_LINK).textValue());
+ entry.setSelfLink(link);
+
+ tree.remove(ODataConstants.JSON_READ_LINK);
+ }
+
+ if (tree.hasNonNull(ODataConstants.JSON_EDIT_LINK)) {
+ final LinkImpl link = new LinkImpl();
+ link.setRel(ODataConstants.EDIT_LINK_REL);
+ link.setHref(tree.get(ODataConstants.JSON_EDIT_LINK).textValue());
+ entry.setEditLink(link);
+
+ tree.remove(ODataConstants.JSON_EDIT_LINK);
+ }
+
+ if (tree.hasNonNull(ODataConstants.JSON_MEDIAREAD_LINK)) {
+ entry.setMediaContentSource(tree.get(ODataConstants.JSON_MEDIAREAD_LINK).textValue());
+ tree.remove(ODataConstants.JSON_MEDIAREAD_LINK);
+ }
+ if (tree.hasNonNull(ODataConstants.JSON_MEDIAEDIT_LINK)) {
+ /*final LinkImpl link = new LinkImpl();
+ link.setHref(tree.get(ODataConstants.JSON_MEDIAEDIT_LINK).textValue());
+ entry.getMediaEditLinks().add(link);*/
+
+ tree.remove(ODataConstants.JSON_MEDIAEDIT_LINK);
+ }
+ if (tree.hasNonNull(ODataConstants.JSON_MEDIA_CONTENT_TYPE)) {
+ entry.setMediaContentType(tree.get(ODataConstants.JSON_MEDIA_CONTENT_TYPE).textValue());
+ tree.remove(ODataConstants.JSON_MEDIA_CONTENT_TYPE);
+ }
+
+ final Set<String> toRemove = new HashSet<String>();
+ final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields();
+ while (itor.hasNext()) {
+ final Map.Entry<String, JsonNode> field = itor.next();
+
+ if (field.getKey().endsWith(ODataConstants.JSON_NAVIGATION_LINK_SUFFIX)) {
+ final LinkImpl link = new LinkImpl();
+ link.setTitle(getTitle(field));
+ link.setRel(client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL)
+ + getTitle(field));
+ if (field.getValue().isValueNode()) {
+ link.setHref(field.getValue().textValue());
+ link.setType(LinkType.ENTITY_NAVIGATION.toString());
+ }
+ // NOTE: this should be expected to happen, but it isn't - at least up to OData 4.0
+ /* if (field.getValue().isArray()) {
+ * link.setHref(field.getValue().asText());
+ * link.setType(LinkType.ENTITY_SET_NAVIGATION.toString());
+ * } */
+ entry.getNavigationLinks().add(link);
+
+ toRemove.add(field.getKey());
+ toRemove.add(setInline(field.getKey(),
+ ODataConstants.JSON_NAVIGATION_LINK_SUFFIX, tree, parser.getCodec(), link));
+ } else if (field.getKey().endsWith(ODataConstants.JSON_ASSOCIATION_LINK_SUFFIX)) {
+ final LinkImpl link = new LinkImpl();
+ link.setTitle(getTitle(field));
+ link.setRel(client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL)
+ + getTitle(field));
+ link.setHref(field.getValue().textValue());
+ link.setType(LinkType.ASSOCIATION.toString());
+ entry.getAssociationLinks().add(link);
+
+ toRemove.add(field.getKey());
+ } else if (field.getKey().endsWith(ODataConstants.JSON_MEDIAEDIT_LINK_SUFFIX)) {
+ final LinkImpl link = new LinkImpl();
+ link.setTitle(getTitle(field));
+ link.setRel(client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.MEDIA_EDIT_LINK_REL)
+ + getTitle(field));
+ link.setHref(field.getValue().textValue());
+ link.setType(LinkType.MEDIA_EDIT.toString());
+ entry.getMediaEditLinks().add(link);
+
+ toRemove.add(field.getKey());
+ toRemove.add(setInline(field.getKey(),
+ ODataConstants.JSON_MEDIAEDIT_LINK_SUFFIX, tree, parser.getCodec(), link));
+ } else if (field.getKey().charAt(0) == '#') {
+ final OperationImpl operation = new OperationImpl();
+ operation.setMetadataAnchor(field.getKey());
+
+ final ObjectNode opNode = (ObjectNode) tree.get(field.getKey());
+ operation.setTitle(opNode.get(ODataConstants.ATTR_TITLE).asText());
+ operation.setTarget(URI.create(opNode.get(ODataConstants.ATTR_TARGET).asText()));
+
+ entry.getOperations().add(operation);
+
+ toRemove.add(field.getKey());
+ }
+ }
+ tree.remove(toRemove);
+
+ try {
+ final DocumentBuilder builder = XMLUtils.DOC_BUILDER_FACTORY.newDocumentBuilder();
+ final Document document = builder.newDocument();
+
+ final Element properties = document.createElementNS(
+ client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+ ODataConstants.ELEM_PROPERTIES);
+
+ JSONDOMTreeUtils.buildSubtree(client, properties, tree);
+
+ if (isMediaEntry) {
+ entry.setMediaEntryProperties(properties);
+ } else {
+ entry.setContent(properties);
+ }
+ } catch (ParserConfigurationException e) {
+ throw new JsonParseException("Cannot build entry content", parser.getCurrentLocation(), e);
+ }
+
+ return entry;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryImpl.java
new file mode 100644
index 0000000..3b7aeda
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.net.URI;
+import org.apache.olingo.client.api.uri.SegmentType;
+
+/**
+ * A single entry, represented via JSON.
+ */
+@JsonSerialize(using = JSONEntrySerializer.class)
+@JsonDeserialize(using = JSONEntryDeserializer.class)
+public class JSONEntryImpl extends AbstractEntry {
+
+ private static final long serialVersionUID = -5275365545400797758L;
+
+ private URI metadata;
+
+ private String mediaETag;
+
+ @JsonIgnore
+ @Override
+ public URI getBaseURI() {
+ URI baseURI = null;
+ if (metadata != null) {
+ final String metadataURI = getMetadata().toASCIIString();
+ baseURI = URI.create(metadataURI.substring(0, metadataURI.indexOf(SegmentType.METADATA.getValue())));
+ }
+
+ return baseURI;
+ }
+
+ /**
+ * Gets the metadata URI.
+ *
+ * @return the metadata URI
+ */
+ public URI getMetadata() {
+ return metadata;
+ }
+
+ /**
+ * Sets the metadata URI.
+ *
+ * @param metadata metadata URI.
+ */
+ public void setMetadata(final URI metadata) {
+ this.metadata = metadata;
+ }
+
+ /**
+ * The odata.mediaEtag annotation MAY be included; its value MUST be the ETag of the binary stream represented by this
+ * media entity or named stream property.
+ *
+ * @return odata.mediaEtag annotation value.
+ */
+ public String getMediaETag() {
+ return mediaETag;
+ }
+
+ /**
+ * The odata.mediaEtag annotation MAY be included; its value MUST be the ETag of the binary stream represented by this
+ * media entity or named stream property.
+ *
+ * @param eTag odata.mediaEtag annotation value.
+ */
+ public void setMediaETag(final String eTag) {
+ this.mediaETag = eTag;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntrySerializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntrySerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntrySerializer.java
new file mode 100644
index 0000000..518ff55
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntrySerializer.java
@@ -0,0 +1,128 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.data.Link;
+import org.apache.olingo.client.api.data.LinkType;
+
+/**
+ * Writes out JSON string from an entry.
+ */
+public class JSONEntrySerializer extends ODataJacksonSerializer<JSONEntryImpl> {
+
+ @Override
+ protected void doSerialize(final JSONEntryImpl entry, final JsonGenerator jgen, final SerializerProvider provider)
+ throws IOException, JsonProcessingException {
+
+ jgen.writeStartObject();
+
+ if (entry.getMetadata() != null) {
+ jgen.writeStringField(ODataConstants.JSON_METADATA, entry.getMetadata().toASCIIString());
+ }
+ if (StringUtils.isNotBlank(entry.getType())) {
+ jgen.writeStringField(ODataConstants.JSON_TYPE, entry.getType());
+ }
+ if (entry.getId() != null) {
+ jgen.writeStringField(ODataConstants.JSON_ID, entry.getId());
+ }
+
+ if (entry.getSelfLink() != null) {
+ jgen.writeStringField(ODataConstants.JSON_READ_LINK, entry.getSelfLink().getHref());
+ }
+
+ if (entry.getEditLink() != null) {
+ jgen.writeStringField(ODataConstants.JSON_EDIT_LINK, entry.getEditLink().getHref());
+ }
+
+ if (entry.getMediaContentSource() != null) {
+ jgen.writeStringField(ODataConstants.JSON_MEDIAREAD_LINK, entry.getMediaContentSource());
+ }
+ if (entry.getMediaContentType() != null) {
+ jgen.writeStringField(ODataConstants.JSON_MEDIA_CONTENT_TYPE, entry.getMediaContentType());
+ }
+
+ final Map<String, List<String>> entitySetLinks = new HashMap<String, List<String>>();
+
+ for (Link link : entry.getNavigationLinks()) {
+ if (link.getInlineEntry() != null) {
+ jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
+ } else if (link.getInlineFeed() != null) {
+ jgen.writeObjectField(link.getTitle(), link.getInlineFeed());
+ } else {
+ LinkType type = null;
+ try {
+ type = LinkType.fromString(client, link.getRel(), link.getType());
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+
+ if (type == LinkType.ENTITY_SET_NAVIGATION) {
+ final List<String> uris;
+ if (entitySetLinks.containsKey(link.getTitle())) {
+ uris = entitySetLinks.get(link.getTitle());
+ } else {
+ uris = new ArrayList<String>();
+ entitySetLinks.put(link.getTitle(), uris);
+ }
+ uris.add(link.getHref());
+ } else {
+ jgen.writeStringField(link.getTitle() + ODataConstants.JSON_BIND_LINK_SUFFIX, link.getHref());
+ }
+ }
+ }
+ for (Map.Entry<String, List<String>> entitySetLink : entitySetLinks.entrySet()) {
+ jgen.writeArrayFieldStart(entitySetLink.getKey() + ODataConstants.JSON_BIND_LINK_SUFFIX);
+ for (String uri : entitySetLink.getValue()) {
+ jgen.writeString(uri);
+ }
+ jgen.writeEndArray();
+ }
+
+ for (Link link : entry.getMediaEditLinks()) {
+ if (link.getTitle() == null) {
+ jgen.writeStringField(ODataConstants.JSON_MEDIAEDIT_LINK, link.getHref());
+ }
+
+ if (link.getInlineEntry() != null) {
+ jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
+ }
+ if (link.getInlineFeed() != null) {
+ jgen.writeObjectField(link.getTitle(), link.getInlineFeed());
+ }
+ }
+
+ if (entry.getMediaEntryProperties() == null) {
+ JSONDOMTreeUtils.writeSubtree(client, jgen, entry.getContent());
+ } else {
+ JSONDOMTreeUtils.writeSubtree(client, jgen, entry.getMediaEntryProperties());
+ }
+
+ jgen.writeEndObject();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONErrorBundle.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONErrorBundle.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONErrorBundle.java
new file mode 100644
index 0000000..a73fb05
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONErrorBundle.java
@@ -0,0 +1,50 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * This class represents a bundle for an OData error returned as JSON.
+ */
+public class JSONErrorBundle extends AbstractPayloadObject {
+
+ private static final long serialVersionUID = -4784910226259754450L;
+
+ @JsonProperty("odata.error")
+ private JSONErrorImpl error;
+
+ /**
+ * Gets error.
+ *
+ * @return OData error object.
+ */
+ public JSONErrorImpl getError() {
+ return error;
+ }
+
+ /**
+ * Sets error.
+ *
+ * @param error OData error object.
+ */
+ public void setError(final JSONErrorImpl error) {
+ this.error = error;
+ }
+}