blob: b72753809c61e2183015c695fb3e5aefefbc7a43 [file] [log] [blame]
/*******************************************************************************
* 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.odata2.client.core.ep.serializer;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.olingo.odata2.api.edm.Edm;
import org.apache.olingo.odata2.api.edm.EdmEntitySet;
import org.apache.olingo.odata2.api.edm.EdmEntityType;
import org.apache.olingo.odata2.api.edm.EdmException;
import org.apache.olingo.odata2.api.edm.EdmMapping;
import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
import org.apache.olingo.odata2.api.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.ep.EntityProviderException;
import org.apache.olingo.odata2.client.api.ep.Entity;
import org.apache.olingo.odata2.client.api.ep.EntityCollection;
import org.apache.olingo.odata2.client.api.ep.EntityCollectionSerializerProperties;
import org.apache.olingo.odata2.client.api.ep.EntitySerializerProperties;
import org.apache.olingo.odata2.core.commons.ContentType;
import org.apache.olingo.odata2.core.ep.EntityProviderProducerException;
import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator;
import org.apache.olingo.odata2.core.ep.util.FormatJson;
import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter;
/**
* Producer for writing an entity in JSON, also usable for function imports
* returning a single instance of an entity type.
*
*/
public class JsonEntryEntitySerializer {
private final EntitySerializerProperties properties;
private String location;
private String idlocation;
private JsonStreamWriter jsonStreamWriter;
private static final String VALUE = "/$value";
/**
*
* @param properties
* @throws EntityProviderException
*/
public JsonEntryEntitySerializer(final EntitySerializerProperties properties) {
this.properties = properties == null ? EntitySerializerProperties.serviceRoot(null).build() : properties;
}
/**
* This serializes the json payload entry
* @param writer
* @param entityInfo
* @param data
* @param isRootElement
* @throws EntityProviderException
*/
public void append(final Writer writer, final EntityInfoAggregator entityInfo, final Entity data)
throws EntityProviderException {
if (data == null) {
throw new EntityProviderException(EntityProviderException.NULL_VALUE);
}
final EdmEntityType type = entityInfo.getEntityType();
try {
jsonStreamWriter = new JsonStreamWriter(writer);
jsonStreamWriter.beginObject();
boolean containsMetadata = false;
if (properties.isIncludeMetadata()) {
writeMetadata(entityInfo, data.getProperties(), type);
containsMetadata = true;
}
writeProperties(entityInfo, data.getProperties(), type, containsMetadata);
writeNavigationProperties(writer, entityInfo, data.getNavigations(), type);
jsonStreamWriter.endObject();
writer.flush();
} catch (final IOException e) {
throw new EntityProviderProducerException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass()
.getSimpleName()), e);
} catch (final EdmException e) {
throw new EntityProviderProducerException(e.getMessageReference(), e);
}
}
private void writeNavigationProperties(final Writer writer, final EntityInfoAggregator entityInfo,
final Map<String, Object> data,
final EdmEntityType type) throws EdmException, EntityProviderException, IOException {
for (final String navigationPropertyName : type.getNavigationPropertyNames()) {
if (data.containsKey(navigationPropertyName)) {
if (data.get(navigationPropertyName) == null) {
throw new EntityProviderException(EntityProviderException.NULL_VALUE);
}
if (data.get(navigationPropertyName) instanceof Entity ||
data.get(navigationPropertyName) instanceof EntityCollection) {
jsonStreamWriter.separator();
jsonStreamWriter.name(navigationPropertyName);
writeExpandedNavigationProperty(writer, entityInfo, data, type, navigationPropertyName);
} else if (data.get(navigationPropertyName) instanceof Map<?,?>){
writeNavigationLinks(entityInfo, data);
} else {
throw new EntityProviderException(EntityProviderException.INCORRECT_NAVIGATION_TYPE);
}
}
}
}
private void writeExpandedNavigationProperty(final Writer writer, final EntityInfoAggregator entityInfo,
final Map<String, Object> data,
final EdmEntityType type, final String navigationPropertyName) throws EdmException, EntityProviderException,
IOException {
final EdmNavigationProperty navigationProperty = (EdmNavigationProperty) type.getProperty(navigationPropertyName);
final boolean isFeed = navigationProperty.getMultiplicity() == EdmMultiplicity.MANY;
final EdmEntitySet entitySet = entityInfo.getEntitySet();
final EdmEntitySet inlineEntitySet = entitySet.getRelatedEntitySet(navigationProperty);
if (isFeed) {
EntityCollection inlineData = (EntityCollection) data.get(navigationProperty.getName());
if(inlineData == null){
throw new EntityProviderException(EntityProviderException.NULL_VALUE);
}
final EntityCollectionSerializerProperties inlineProperties = inlineData.getCollectionProperties() == null ?
EntityCollectionSerializerProperties.
serviceRoot(properties.getServiceRoot()).build() :
inlineData.getCollectionProperties();
JsonFeedEntitySerializer jsonFeedEntityProducer = new JsonFeedEntitySerializer(inlineProperties);
final EntityInfoAggregator inlineEntityInfo =
EntityInfoAggregator.create(inlineEntitySet, null);
jsonFeedEntityProducer.appendAsArray(writer, inlineEntityInfo, inlineData);
} else {
Entity inlineData = (Entity) data.get(navigationProperty.getName());
//This statement is used for the client use case. Flag should never be set on server side
if(inlineData == null){
throw new EntityProviderException(EntityProviderException.NULL_VALUE);
}
if (inlineData != null && inlineData.getProperties() != null &&
!inlineData.getProperties().isEmpty()) {
final EntitySerializerProperties inlineProperties = inlineData.getWriteProperties() == null ?
EntitySerializerProperties.
serviceRoot(properties.getServiceRoot()).build() : inlineData.getWriteProperties();
final EntityInfoAggregator inlineEntityInfo =
EntityInfoAggregator.create(inlineEntitySet, null);
new JsonEntryEntitySerializer(inlineProperties).append(writer, inlineEntityInfo, inlineData);
} else {
jsonStreamWriter.beginObject();
jsonStreamWriter.endObject();
}
}
}
private void writeProperties(final EntityInfoAggregator entityInfo, final Map<String, Object> data,
final EdmEntityType type, boolean containsMetadata) throws EdmException, EntityProviderException, IOException {
// if the payload contains metadata we must not omit the first comm as it separates the _metadata object form the
// properties
boolean omitComma = !containsMetadata;
List<String> propertyNames = type.getPropertyNames();
for (final String propertyName : propertyNames) {
if (data.containsKey(propertyName)) {
omitComma = appendPropertyNameValue(entityInfo, data, omitComma, propertyName);
}
}
}
/**
* @param entityInfo
* @param data
* @param omitComma
* @param propertyName
* @return
* @throws IOException
* @throws EdmException
* @throws EntityProviderException
*/
private boolean appendPropertyNameValue(final EntityInfoAggregator entityInfo, final Map<String, Object> data,
boolean omitComma, String propertyName) throws IOException, EdmException, EntityProviderException {
if (omitComma) {
omitComma = false; //NOSONAR
} else {
jsonStreamWriter.separator();
}
jsonStreamWriter.name(propertyName);
JsonPropertyEntitySerializer.appendPropertyValue(jsonStreamWriter,
entityInfo.getPropertyInfo(propertyName),
data.get(propertyName),
properties.isValidatingFacets());
return omitComma;
}
private void writeMetadata(final EntityInfoAggregator entityInfo, final Map<String, Object> data,
final EdmEntityType type) throws IOException, EntityProviderException, EdmException {
if (properties.getServiceRoot() == null) {
location = "";
idlocation = "";
} else {
location = properties.getServiceRoot().toASCIIString() +
AtomEntryEntitySerializer.createSelfLink(entityInfo, data, null, properties.isKeyAutoGenerated(), false);
idlocation = properties.getServiceRoot().toASCIIString() +
AtomEntryEntitySerializer.createSelfLink(entityInfo, data, null, properties.isKeyAutoGenerated(), true);
}
jsonStreamWriter.name(FormatJson.METADATA);
jsonStreamWriter.beginObject();
jsonStreamWriter.namedStringValue(FormatJson.ID, idlocation);
jsonStreamWriter.separator();
jsonStreamWriter.namedStringValue(FormatJson.URI, location);
jsonStreamWriter.separator();
jsonStreamWriter.namedStringValueRaw(FormatJson.TYPE, type.getNamespace() + Edm.DELIMITER + type.getName());
if (type.hasStream()) {
jsonStreamWriter.separator();
EdmMapping entityTypeMapping = entityInfo.getEntityType().getMapping();
String mediaResourceMimeType = null;
String mediaSrc = null;
if (entityTypeMapping != null) {
String mediaResourceSourceKey = entityTypeMapping.getMediaResourceSourceKey();
if (mediaResourceSourceKey != null) {
mediaSrc = (String) data.get(mediaResourceSourceKey);
}
if (mediaSrc == null) {
mediaSrc = location + VALUE;
}
String mediaResourceMimeTypeKey = entityTypeMapping.getMediaResourceMimeTypeKey();
if (mediaResourceMimeTypeKey != null) {
mediaResourceMimeType = (String) data.get(mediaResourceMimeTypeKey);
}
if (mediaResourceMimeType == null) {
mediaResourceMimeType = ContentType.APPLICATION_OCTET_STREAM.toString();
}
} else {
mediaSrc = location + VALUE;
mediaResourceMimeType = ContentType.APPLICATION_OCTET_STREAM.toString();
}
jsonStreamWriter.namedStringValueRaw(FormatJson.CONTENT_TYPE, mediaResourceMimeType);
jsonStreamWriter.separator();
jsonStreamWriter.namedStringValue(FormatJson.MEDIA_SRC, mediaSrc);
jsonStreamWriter.separator();
jsonStreamWriter.namedStringValue(FormatJson.EDIT_MEDIA, location + VALUE);
}
jsonStreamWriter.endObject();
}
private String createCustomTargetLink(final EntityInfoAggregator entityInfo, final String navigationPropertyName,
final Map<String, Object> key) throws EntityProviderException, EdmException {
String target;
final EntityInfoAggregator targetEntityInfo = EntityInfoAggregator.create(
entityInfo.getEntitySet().getRelatedEntitySet(
(EdmNavigationProperty) entityInfo.getEntityType().getProperty(navigationPropertyName)));
target = (properties.getServiceRoot() == null ? "" : properties.getServiceRoot().toASCIIString())
+ AtomEntryEntitySerializer.createSelfLink(targetEntityInfo, key, null, properties.isKeyAutoGenerated(), false);
return target;
}
private void writeNavigationLinks(final EntityInfoAggregator entityInfo,
Map<String, Object> navigationLinks) throws IOException,
EntityProviderException, EdmException {
if (navigationLinks != null && !navigationLinks.isEmpty()) {
for (Entry<String, Object> entry : navigationLinks.entrySet()) {
String target = null;
if (entry.getValue() instanceof HashMap<?, ?>) {
@SuppressWarnings("unchecked")
Map<String, Object> navigationKeyMap = (Map<String, Object>) entry.getValue();
if (navigationKeyMap != null && !navigationKeyMap.isEmpty()) { //NOSONAR
target = createCustomTargetLink(entityInfo, entry.getKey(), navigationKeyMap);
jsonStreamWriter.separator();
jsonStreamWriter.name(entry.getKey());
jsonStreamWriter.beginObject()
.name(FormatJson.DEFERRED);
JsonLinkEntitySerializer.appendUri(jsonStreamWriter, target);
jsonStreamWriter.endObject();
}
}
}
}
}
public String getLocation() {
return location;
}
}