/*******************************************************************************
* Copyright 2013 SAP AG
*
* Licensed 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 com.sap.core.odata.core.ep.consumer;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.sap.core.odata.api.edm.Edm;
import com.sap.core.odata.api.edm.EdmEntitySet;
import com.sap.core.odata.api.edm.EdmException;
import com.sap.core.odata.api.edm.EdmMultiplicity;
import com.sap.core.odata.api.edm.EdmNavigationProperty;
import com.sap.core.odata.api.ep.EntityProviderException;
import com.sap.core.odata.api.ep.EntityProviderReadProperties;
import com.sap.core.odata.api.ep.callback.OnReadInlineContent;
import com.sap.core.odata.api.ep.callback.ReadEntryResult;
import com.sap.core.odata.api.ep.callback.ReadFeedResult;
import com.sap.core.odata.api.ep.entry.ODataEntry;
import com.sap.core.odata.api.ep.feed.ODataFeed;
import com.sap.core.odata.api.exception.ODataApplicationException;
import com.sap.core.odata.core.ep.aggregator.EntityInfoAggregator;
import com.sap.core.odata.core.ep.aggregator.EntityPropertyInfo;
import com.sap.core.odata.core.ep.aggregator.NavigationPropertyInfo;
import com.sap.core.odata.core.ep.entry.EntryMetadataImpl;
import com.sap.core.odata.core.ep.entry.MediaMetadataImpl;
import com.sap.core.odata.core.ep.entry.ODataEntryImpl;
import com.sap.core.odata.core.ep.util.FormatJson;
import com.sap.core.odata.core.uri.ExpandSelectTreeNodeImpl;
/**
* @author SAP AG
*/
public class JsonEntryConsumer {
private final Map<String, Object> properties = new HashMap<String, Object>();
private final MediaMetadataImpl mediaMetadata = new MediaMetadataImpl();
private final EntryMetadataImpl entryMetadata = new EntryMetadataImpl();
private final ExpandSelectTreeNodeImpl expandSelectTree = new ExpandSelectTreeNodeImpl();
private final Map<String, Object> typeMappings;
private final EntityInfoAggregator eia;
private final JsonReader reader;
private final EntityProviderReadProperties readProperties;
private final ODataEntryImpl entryResult;
public JsonEntryConsumer(final JsonReader reader, final EntityInfoAggregator eia, final EntityProviderReadProperties readProperties) {
typeMappings = readProperties.getTypeMappings();
this.eia = eia;
this.readProperties = readProperties;
this.reader = reader;
entryResult = new ODataEntryImpl(properties, mediaMetadata, entryMetadata, expandSelectTree);
}
public ODataEntry readSingleEntry() throws EntityProviderException {
try {
reader.beginObject();
String nextName = reader.nextName();
if (FormatJson.D.equals(nextName)) {
reader.beginObject();
readEntryContent();
reader.endObject();
} else {
handleName(nextName);
readEntryContent();
}
reader.endObject();
if (reader.peek() != JsonToken.END_DOCUMENT) {
throw new EntityProviderException(EntityProviderException.END_DOCUMENT_EXPECTED.addContent(reader.peek().toString()));
}
} catch (IOException e) {
throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
} catch (EdmException e) {
throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
} catch (IllegalStateException e) {
throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
}
return entryResult;
}
public ODataEntry readFeedEntry() throws EdmException, EntityProviderException, IOException {
reader.beginObject();
readEntryContent();
reader.endObject();
return entryResult;
}
private void readEntryContent() throws IOException, EdmException, EntityProviderException {
while (reader.hasNext()) {
final String name = reader.nextName();
handleName(name);
}
}
private void handleName(final String name) throws IOException, EdmException, EntityProviderException {
if (FormatJson.METADATA.equals(name)) {
readMetadata();
validateMetadata();
} else {
EntityPropertyInfo propertyInfo = eia.getPropertyInfo(name);
if (propertyInfo != null) {
JsonPropertyConsumer jpc = new JsonPropertyConsumer();
Object propertyValue = jpc.readPropertyValue(reader, propertyInfo, typeMappings.get(name));
if (properties.containsKey(name)) {
throw new EntityProviderException(EntityProviderException.DOUBLE_PROPERTY.addContent(name));
}
properties.put(name, propertyValue);
} else {
readNavigationProperty(name);
}
}
}
private void readMetadata() throws IOException, EdmException, EntityProviderException {
String name = null;
String value = null;
reader.beginObject();
while (reader.hasNext()) {
name = reader.nextName();
if (FormatJson.PROPERTIES.equals(name)) {
reader.skipValue();
continue;
}
value = reader.nextString();
if (FormatJson.ID.equals(name)) {
entryMetadata.setId(value);
} else if (FormatJson.URI.equals(name)) {
entryMetadata.setUri(value);
} else if (FormatJson.TYPE.equals(name)) {
String fullQualifiedName = eia.getEntityType().getNamespace() + Edm.DELIMITER + eia.getEntityType().getName();
if (!fullQualifiedName.equals(value)) {
throw new EntityProviderException(EntityProviderException.INVALID_ENTITYTYPE.addContent(fullQualifiedName).addContent(value));
}
} else if (FormatJson.ETAG.equals(name)) {
entryMetadata.setEtag(value);
} else if (FormatJson.EDIT_MEDIA.equals(name)) {
mediaMetadata.setEditLink(value);
} else if (FormatJson.MEDIA_SRC.equals(name)) {
mediaMetadata.setSourceLink(value);
} else if (FormatJson.MEDIA_ETAG.equals(name)) {
mediaMetadata.setEtag(value);
} else if (FormatJson.CONTENT_TYPE.equals(name)) {
mediaMetadata.setContentType(value);
} else {
throw new EntityProviderException(EntityProviderException.INVALID_CONTENT.addContent(name).addContent(FormatJson.METADATA));
}
}
reader.endObject();
}
private void validateMetadata() throws EdmException, EntityProviderException {
if (eia.getEntityType().hasStream()) {
if (mediaMetadata.getSourceLink() == null) {
throw new EntityProviderException(EntityProviderException.MISSING_ATTRIBUTE.addContent(FormatJson.MEDIA_SRC).addContent(FormatJson.METADATA));
}
if (mediaMetadata.getContentType() == null) {
throw new EntityProviderException(EntityProviderException.MISSING_ATTRIBUTE.addContent(FormatJson.CONTENT_TYPE).addContent(FormatJson.METADATA));
}
//TODO Mime Type Mapping
} else {
if (mediaMetadata.getContentType() != null || mediaMetadata.getEditLink() != null
|| mediaMetadata.getEtag() != null || mediaMetadata.getSourceLink() != null) {
throw new EntityProviderException(EntityProviderException.MEDIA_DATA_NOT_INITIAL);
}
}
}
private void readNavigationProperty(final String navigationPropertyName) throws IOException, EntityProviderException, EdmException {
NavigationPropertyInfo navigationPropertyInfo = eia.getNavigationPropertyInfo(navigationPropertyName);
if (navigationPropertyInfo == null) {
throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT.addContent(navigationPropertyName));
}
if (reader.peek() == JsonToken.BEGIN_OBJECT) {
reader.beginObject();
String name = reader.nextName();
if (FormatJson.DEFERRED.equals(name)) {
reader.beginObject();
String uri = reader.nextName();
if (FormatJson.URI.equals(uri)) {
String value = reader.nextString();
entryMetadata.putAssociationUri(navigationPropertyInfo.getName(), value);
} else {
throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT.addContent(uri));
}
reader.endObject();
} else {
EdmNavigationProperty navigationProperty = (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName);
EdmEntitySet inlineEntitySet = eia.getEntitySet().getRelatedEntitySet(navigationProperty);
EntityInfoAggregator inlineEia = EntityInfoAggregator.create(inlineEntitySet);
EntityProviderReadProperties inlineReadProperties;
OnReadInlineContent callback = readProperties.getCallback();
try {
if (callback == null) {
inlineReadProperties = EntityProviderReadProperties.init().mergeSemantic(readProperties.getMergeSemantic()).build();
} else {
inlineReadProperties = callback.receiveReadProperties(readProperties, navigationProperty);
}
if (navigationProperty.getMultiplicity() == EdmMultiplicity.MANY) {
JsonFeedConsumer inlineConsumer = new JsonFeedConsumer(reader, inlineEia, inlineReadProperties);
ODataFeed feed = inlineConsumer.readStartedInlineFeed(name);
updateExpandSelectTree(navigationPropertyName, feed);
if (callback == null) {
properties.put(navigationPropertyName, feed);
entryResult.setContainsInlineEntry(true);
} else {
ReadFeedResult result = new ReadFeedResult(inlineReadProperties, navigationProperty, feed);
callback.handleReadFeed(result);
}
} else {
JsonEntryConsumer inlineConsumer = new JsonEntryConsumer(reader, inlineEia, inlineReadProperties);
ODataEntry entry = inlineConsumer.readInlineEntry(name);
updateExpandSelectTree(navigationPropertyName, entry);
if (callback == null) {
properties.put(navigationPropertyName, entry);
entryResult.setContainsInlineEntry(true);
} else {
ReadEntryResult result = new ReadEntryResult(inlineReadProperties, navigationProperty, entry);
callback.handleReadEntry(result);
}
}
} catch (ODataApplicationException e) {
throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
}
}
reader.endObject();
} else {
final EdmNavigationProperty navigationProperty = (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName);
final EdmEntitySet inlineEntitySet = eia.getEntitySet().getRelatedEntitySet(navigationProperty);
final EntityInfoAggregator inlineInfo = EntityInfoAggregator.create(inlineEntitySet);
OnReadInlineContent callback = readProperties.getCallback();
EntityProviderReadProperties inlineReadProperties;
if (callback == null) {
inlineReadProperties = EntityProviderReadProperties.init().mergeSemantic(readProperties.getMergeSemantic()).build();
} else {
try {
inlineReadProperties = callback.receiveReadProperties(readProperties, navigationProperty);
} catch (final ODataApplicationException e) {
throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
}
}
ODataFeed feed = new JsonFeedConsumer(reader, inlineInfo, inlineReadProperties).readInlineFeedStandalone();
updateExpandSelectTree(navigationPropertyName, feed);
if (callback == null) {
properties.put(navigationPropertyName, feed);
entryResult.setContainsInlineEntry(true);
} else {
ReadFeedResult result = new ReadFeedResult(inlineReadProperties, navigationProperty, feed);
try {
callback.handleReadFeed(result);
} catch (final ODataApplicationException e) {
throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
}
}
}
}
private void updateExpandSelectTree(final String navigationPropertyName, final ODataFeed feed) {
List<ODataEntry> entries = feed.getEntries();
if (entries.size() > 0) {
updateExpandSelectTree(navigationPropertyName, entries.get(0));
} else {
expandSelectTree.setExpanded();
expandSelectTree.setExplicitlySelected();
expandSelectTree.putLink(navigationPropertyName, new ExpandSelectTreeNodeImpl());
}
}
private void updateExpandSelectTree(final String navigationPropertyName, final ODataEntry entry) {
expandSelectTree.setExpanded();
expandSelectTree.setExplicitlySelected();
expandSelectTree.putLink(navigationPropertyName, (ExpandSelectTreeNodeImpl) entry.getExpandSelectTree());
}
private ODataEntry readInlineEntry(final String name) throws EdmException, EntityProviderException, IOException {
//consume the already started content
handleName(name);
//consume the rest of the entry content
readEntryContent();
return entryResult;
}
}