http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/4261deb7/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/JsonSerializerDeserializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/JsonSerializerDeserializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/JsonSerializerDeserializer.java new file mode 100644 index 0000000..aaba634 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/JsonSerializerDeserializer.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * 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; + +import java.io.BufferedWriter; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.util.List; + +import org.apache.olingo.odata2.api.batch.BatchException; +import org.apache.olingo.odata2.api.batch.BatchResponsePart; +import org.apache.olingo.odata2.api.client.batch.BatchPart; +import org.apache.olingo.odata2.api.edm.EdmEntitySet; +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.edm.EdmFunctionImport; +import org.apache.olingo.odata2.api.edm.EdmMultiplicity; +import org.apache.olingo.odata2.api.edm.EdmTypeKind; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.api.ep.entry.ODataEntry; +import org.apache.olingo.odata2.api.ep.feed.ODataFeed; +import org.apache.olingo.odata2.api.processor.ODataErrorContext; +import org.apache.olingo.odata2.api.processor.ODataResponse; +import org.apache.olingo.odata2.client.api.ep.ContentTypeBasedDeserializer; +import org.apache.olingo.odata2.client.api.ep.ContentTypeBasedSerializer; +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.client.api.ep.EntityStream; +import org.apache.olingo.odata2.client.core.ep.deserializer.JsonEntityDeserializer; +import org.apache.olingo.odata2.client.core.ep.serializer.JsonEntryEntitySerializer; +import org.apache.olingo.odata2.client.core.ep.serializer.JsonFeedEntitySerializer; +import org.apache.olingo.odata2.core.batch.BatchRequestWriter; +import org.apache.olingo.odata2.core.batch.BatchResponseWriter; +import org.apache.olingo.odata2.core.ep.EntityProviderProducerException; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; +import org.apache.olingo.odata2.core.ep.consumer.JsonErrorDocumentConsumer; +import org.apache.olingo.odata2.core.ep.util.CircleStreamBuffer; + +/** + * This class includes methods to serialize deserialize JSON Content type + */ +public class JsonSerializerDeserializer implements ContentTypeBasedSerializer, ContentTypeBasedDeserializer { + + private static final String DEFAULT_CHARSET = "UTF-8"; + + @Override + public ODataResponse writeEntry(EdmEntitySet entitySet, Entity data) + throws EntityProviderException { + + final EntitySerializerProperties properties = data == null ? + EntitySerializerProperties.serviceRoot(null).build() : data.getWriteProperties() == null ? + EntitySerializerProperties.serviceRoot(null).build() : data.getWriteProperties(); + final EntityInfoAggregator entityInfo = EntityInfoAggregator.create(entitySet, null); + CircleStreamBuffer buffer = new CircleStreamBuffer(); + + try { + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET)); + JsonEntryEntitySerializer producer = new JsonEntryEntitySerializer(properties); + producer.append(writer, entityInfo, data); + writer.flush(); + buffer.closeWrite(); + + return ODataResponse.entity(buffer.getInputStream()) + .idLiteral(producer.getLocation()) + .build(); + } catch (EntityProviderException e) { + buffer.close(); + throw e; + } catch (Exception e) { + buffer.close(); + throw new EntityProviderProducerException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + + } + + @Override + public ODataFeed readFeed(EdmEntitySet entitySet, EntityStream content) + throws EntityProviderException { + return new JsonEntityDeserializer().readFeed(entitySet, content); + } + + @Override + public ODataEntry readEntry(EdmEntitySet entitySet, EntityStream content) + throws EntityProviderException { + return new JsonEntityDeserializer().readEntry(entitySet, content); + + } + + @Override + public ODataErrorContext readErrorDocument(InputStream errorDocument) throws EntityProviderException { + return new JsonErrorDocumentConsumer().readError(errorDocument); + } + + @Override + public ODataResponse writeFeed(EdmEntitySet entitySet, EntityCollection data) throws EntityProviderException { + final EntityCollectionSerializerProperties properties = data == null ? + EntityCollectionSerializerProperties.serviceRoot(null).build() : data.getCollectionProperties() == null ? + EntityCollectionSerializerProperties.serviceRoot(null).build() : data.getCollectionProperties(); + final EntityInfoAggregator entityInfo = EntityInfoAggregator.create(entitySet, null); + CircleStreamBuffer buffer = new CircleStreamBuffer(); + + try { + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(buffer.getOutputStream(), DEFAULT_CHARSET)); + new JsonFeedEntitySerializer(properties).appendAsObject(writer, entityInfo, data); + writer.flush(); + buffer.closeWrite(); + + return ODataResponse.entity(buffer.getInputStream()).build(); + } catch (EntityProviderException e) { + buffer.close(); + throw e; + } catch (Exception e) { + buffer.close(); + throw new EntityProviderProducerException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + + @Override + public ODataResponse writeBatchResponse(List<BatchResponsePart> batchResponseParts) throws BatchException { + BatchResponseWriter batchWriter = new BatchResponseWriter(); + return batchWriter.writeResponse(batchResponseParts); + } + + @Override + public InputStream readBatchRequest(List<BatchPart> batchParts, + String boundary) { + BatchRequestWriter batchWriter = new BatchRequestWriter(); + return batchWriter.writeBatchRequest(batchParts, boundary); + } + + @Override + public Object readFunctionImport(EdmFunctionImport functionImport, EntityStream content) + throws EntityProviderException { + try { + if (functionImport.getReturnType().getType().getKind() == EdmTypeKind.ENTITY) { + return functionImport.getReturnType().getMultiplicity() == EdmMultiplicity.MANY + ? new JsonEntityDeserializer().readFeed(functionImport.getEntitySet(), content) + : new JsonEntityDeserializer().readEntry(functionImport.getEntitySet(), content); + } else { + final EntityPropertyInfo info = EntityInfoAggregator.create(functionImport); + return functionImport.getReturnType().getMultiplicity() == EdmMultiplicity.MANY ? + new JsonEntityDeserializer().readCollection(info, content) : + new JsonEntityDeserializer().readProperty(info, content).get(info.getName()); + } + } catch (final EdmException e) { + throw new EntityProviderException(e.getMessageReference(), e); + } + } +}
http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/4261deb7/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonEntityDeserializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonEntityDeserializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonEntityDeserializer.java new file mode 100644 index 0000000..49c61d7 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonEntityDeserializer.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * 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.deserializer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Map; + +import org.apache.olingo.odata2.api.edm.EdmEntitySet; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.api.ep.entry.ODataEntry; +import org.apache.olingo.odata2.api.ep.feed.ODataDeltaFeed; +import org.apache.olingo.odata2.api.ep.feed.ODataFeed; +import org.apache.olingo.odata2.client.api.ep.EntityStream; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; + +import com.google.gson.stream.JsonReader; + +/** + * This class includes method for deserialization of feed and entry data + */ +public class JsonEntityDeserializer { + + /** Default used charset for reader */ + private static final String DEFAULT_CHARSET = "UTF-8"; + + /** + * Returns an ODataEntry deserializing EntityStream + * @param entitySet + * @param entityStream + * @return ODataEntry + * @throws EntityProviderException + */ + public ODataEntry readEntry(final EdmEntitySet entitySet, final EntityStream entityStream) + throws EntityProviderException { + JsonReader reader = null; + EntityProviderException cachedException = null; + + try { + EntityInfoAggregator eia = EntityInfoAggregator.create(entitySet); + reader = createJsonReader(entityStream.getContent()); + + return new JsonEntryDeserializer(reader, eia, entityStream.getReadProperties()).readSingleEntry(); + } catch (UnsupportedEncodingException e) { + cachedException = + new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + throw cachedException; + } finally {// NOPMD (suppress DoNotThrowExceptionInFinally) + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { //NOPMD - suppressed + if (cachedException != null) { //NOSONAR + throw cachedException; //NOSONAR + } else { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.//NOSONAR + addContent(e.getClass() + .getSimpleName()), e); + } + } + } + } + } + + /** + * Returns an ODataFeed deserializing EntityStream + * @param entitySet + * @param entityStream + * @return ODataFeed + * @throws EntityProviderException + */ + public ODataFeed readFeed(final EdmEntitySet entitySet, final EntityStream entityStream) + throws EntityProviderException { + return readDeltaFeed(entitySet, entityStream); + } + + /** + * Returns an ODataDeltaFeed deserializing EntityStream + * @param entitySet + * @param entityStream + * @return ODataDeltaFeed + * @throws EntityProviderException + */ + public ODataDeltaFeed readDeltaFeed(final EdmEntitySet entitySet, final EntityStream entityStream) + throws EntityProviderException { + + JsonReader reader = null; + EntityProviderException cachedException = null; + + try { + EntityInfoAggregator eia = EntityInfoAggregator.create(entitySet); + reader = createJsonReader(entityStream.getContent()); + + JsonFeedDeserializer jfc = new JsonFeedDeserializer(reader, eia, entityStream.getReadProperties()); + return jfc.readFeedStandalone(); + } catch (UnsupportedEncodingException e) { + cachedException = + new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + throw cachedException; + } finally {// NOPMD (suppress DoNotThrowExceptionInFinally) + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { //NOPMD - suppressed + if (cachedException != null) { //NOSONAR + throw cachedException; //NOSONAR + } else { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.//NOSONAR + addContent(e.getClass() + .getSimpleName()), e); + } + } + } + } + } + +/** + * + * @param content + * @return JsonReader + * @throws EntityProviderException + * @throws UnsupportedEncodingException + */ + private JsonReader createJsonReader(final Object content) throws EntityProviderException, + UnsupportedEncodingException { + + if (content == null) { + throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT + .addContent("Got not supported NULL object as content to de-serialize.")); + } + + if (content instanceof InputStream) { + return new JsonReader(new InputStreamReader((InputStream) content, DEFAULT_CHARSET)); + } + throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT + .addContent("Found not supported content of class '" + content.getClass() + "' to de-serialize.")); + } + + public List<?> readCollection(final EntityPropertyInfo info, final EntityStream entityStream) + throws EntityProviderException { + JsonReader reader = null; + EntityProviderException cachedException = null; + + try { + reader = createJsonReader(entityStream.getContent()); + return new JsonPropertyDeserializer().readCollection(reader, info, entityStream.getReadProperties()); + } catch (final UnsupportedEncodingException e) { + cachedException = new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED + .addContent(e.getClass().getSimpleName()), e); + throw cachedException; + } finally {// NOPMD (suppress DoNotThrowExceptionInFinally) + if (reader != null) { + try { + reader.close(); + } catch (final IOException e) { + if (cachedException != null) { + throw cachedException; + } else { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED + .addContent(e.getClass().getSimpleName()), e); + } + } + } + } + } + + public Map<String, Object> readProperty(final EntityPropertyInfo propertyInfo, final EntityStream entityStream) + throws EntityProviderException { + JsonReader reader = null; + EntityProviderException cachedException = null; + + try { + reader = createJsonReader(entityStream.getContent()); + return new JsonPropertyDeserializer().readPropertyStandalone(reader, propertyInfo, + entityStream.getReadProperties()); + } catch (final UnsupportedEncodingException e) { + cachedException = + new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + throw cachedException; + } finally {// NOPMD (suppress DoNotThrowExceptionInFinally) + if (reader != null) { + try { + reader.close(); + } catch (final IOException e) { + if (cachedException != null) { + throw cachedException; + } else { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/4261deb7/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonEntryDeserializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonEntryDeserializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonEntryDeserializer.java new file mode 100644 index 0000000..f9fb915 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonEntryDeserializer.java @@ -0,0 +1,451 @@ +/******************************************************************************* + * 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.deserializer; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.apache.olingo.odata2.api.edm.Edm; +import org.apache.olingo.odata2.api.edm.EdmEntitySet; +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.edm.EdmLiteralKind; +import org.apache.olingo.odata2.api.edm.EdmMultiplicity; +import org.apache.olingo.odata2.api.edm.EdmNavigationProperty; +import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.api.ep.entry.ODataEntry; +import org.apache.olingo.odata2.api.ep.feed.ODataFeed; +import org.apache.olingo.odata2.api.exception.ODataApplicationException; +import org.apache.olingo.odata2.client.api.ep.DeserializerProperties; +import org.apache.olingo.odata2.client.api.ep.callback.OnDeserializeInlineContent; +import org.apache.olingo.odata2.core.edm.EdmDateTimeOffset; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; +import org.apache.olingo.odata2.core.ep.aggregator.NavigationPropertyInfo; +import org.apache.olingo.odata2.core.ep.entry.DeletedEntryMetadataImpl; +import org.apache.olingo.odata2.core.ep.entry.EntryMetadataImpl; +import org.apache.olingo.odata2.core.ep.entry.MediaMetadataImpl; +import org.apache.olingo.odata2.core.ep.entry.ODataEntryImpl; +import org.apache.olingo.odata2.core.ep.feed.JsonFeedEntry; +import org.apache.olingo.odata2.core.ep.util.FormatJson; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; + +/** + * This class Deserializes JsonEntry payloads + */ +public class JsonEntryDeserializer { + + private final EntityInfoAggregator eia; + private final JsonReader reader; + private final DeserializerProperties readProperties; + + private ODataEntryImpl resultEntry; + private Map<String, Object> properties; + private MediaMetadataImpl mediaMetadata; + private EntryMetadataImpl entryMetadata; + + private DeletedEntryMetadataImpl resultDeletedEntry; + + /** + * + * @param reader + * @param eia + * @param readProperties + */ + public JsonEntryDeserializer(final JsonReader reader, final EntityInfoAggregator eia, + final DeserializerProperties readProperties) { + this.eia = eia; + this.readProperties = readProperties; + this.reader = reader; + } + + /** + * Returns ODataEntry deserializing a single entry + * @return ODataEntry + * @throws EntityProviderException + */ + 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 resultEntry; + } + + /** + * Returns Feed deserializing feed entry + * @return JsonFeedEntry + * @throws EdmException + * @throws EntityProviderException + * @throws IOException + */ + public JsonFeedEntry readFeedEntry() throws EdmException, EntityProviderException, IOException {//NOSONAR + reader.beginObject(); + readEntryContent(); + reader.endObject(); + + if (resultDeletedEntry == null) { + return new JsonFeedEntry(resultEntry); + } else { + return new JsonFeedEntry(resultDeletedEntry); + } + } + + /** + * + * @throws IOException + * @throws EdmException + * @throws EntityProviderException + */ + private void readEntryContent() throws IOException, EdmException, EntityProviderException { + while (reader.hasNext()) { + final String name = reader.nextName(); + handleName(name); + } + } + + /** + * Ensure that instance field {@link #resultEntry} exists. + * If it not already exists create an instance (as well as all other necessary objects like: {@link #properties}, + * {@link #mediaMetadata}, {@link #entryMetadata}, {@link #expandSelectTree}). + */ + private void ensureODataEntryExists() { + if (resultEntry == null) { + properties = new HashMap<String, Object>(); + mediaMetadata = new MediaMetadataImpl(); + entryMetadata = new EntryMetadataImpl(); + + resultEntry = new ODataEntryImpl(properties, mediaMetadata, entryMetadata, null); + } + } + + /** + * Ensure that instance field {@link #resultDeletedEntry} exists. + * If it not already exists create an instance. + */ + private void ensureDeletedEntryMetadataExists() { + if (resultDeletedEntry == null) { + resultDeletedEntry = new DeletedEntryMetadataImpl(); + } + } + + /** + * + * @param name + * @throws IOException + * @throws EdmException + * @throws EntityProviderException + */ + private void handleName(final String name) throws IOException, EdmException, EntityProviderException { + if (FormatJson.METADATA.equals(name)) { + ensureODataEntryExists(); + readMetadata(); + validateMetadata(); + } else if (FormatJson.ODATA_CONTEXT.equals(name)) { + readODataContext(); + } else { + ensureODataEntryExists(); + EntityPropertyInfo propertyInfo = eia.getPropertyInfo(name); + if (propertyInfo != null) { + //TODO: put Type mapping instead of null + Object propertyValue = new JsonPropertyDeserializer() + .readPropertyValue(reader, propertyInfo, null, readProperties); + if (properties.containsKey(name)) { + throw new EntityProviderException(EntityProviderException.DOUBLE_PROPERTY.addContent(name)); + } + properties.put(name, propertyValue); + } else { + readNavigationProperty(name); + } + } + } + /** + * + * @throws IOException + * @throws EntityProviderException + */ + private void readODataContext() throws IOException, EntityProviderException { + String contextValue = reader.nextString(); + if (contextValue == null) { + throw new EntityProviderException(EntityProviderException.MISSING_ATTRIBUTE.addContent(FormatJson.ODATA_CONTEXT) + .addContent(FormatJson.RESULTS)); + } + + if (contextValue.startsWith(FormatJson.DELTA_CONTEXT_PREFIX) + && contextValue.endsWith(FormatJson.DELTA_CONTEXT_POSTFIX)) { + while (reader.hasNext()) { + ensureDeletedEntryMetadataExists(); + String name = reader.nextName(); + String value = reader.nextString(); + if (FormatJson.ID.equals(name)) { + resultDeletedEntry.setUri(value); + } else if (FormatJson.DELTA_WHEN.equals(name)) { + Date when = parseWhen(value); + resultDeletedEntry.setWhen(when); + } + } + } + } + + /** + * + * @param value + * @return Date + * @throws EntityProviderException + */ + private Date parseWhen(final String value) throws EntityProviderException { + try { + return EdmDateTimeOffset.getInstance().valueOfString(value, EdmLiteralKind.JSON, null, Date.class); + } catch (EdmSimpleTypeException e) { + throw new EntityProviderException(EntityProviderException.INVALID_DELETED_ENTRY_METADATA + .addContent("Unparsable format for when field value."), e); + } + } + + /** + * + * @throws IOException + * @throws EdmException + * @throws EntityProviderException + */ + private void readMetadata() throws IOException, EdmException, EntityProviderException {//NOSONAR + 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(); + } + + /** + * + * @throws EdmException + * @throws EntityProviderException + */ + 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)); + } + } else { + if (mediaMetadata.getContentType() != null || mediaMetadata.getEditLink() != null + || mediaMetadata.getEtag() != null || mediaMetadata.getSourceLink() != null) { + throw new EntityProviderException(EntityProviderException.MEDIA_DATA_NOT_INITIAL); + } + } + } + + /** + * + * @param navigationPropertyName + * @throws IOException + * @throws EntityProviderException + * @throws EdmException + */ + 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)); + } + + JsonToken peek = reader.peek(); + if (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 { + handleInlineEntries(navigationPropertyName, name); + } + reader.endObject(); + } else if (peek == JsonToken.NULL) { + reader.nextNull(); + } else { + handleArray(navigationPropertyName); + } + } + + /** + * @param navigationPropertyName + * @throws EdmException + * @throws EntityProviderException + * @throws IOException + */ + private void handleArray(final String navigationPropertyName) throws EdmException, EntityProviderException, + IOException { + final EdmNavigationProperty navigationProperty = + (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName); + final EdmEntitySet inlineEntitySet = eia.getEntitySet().getRelatedEntitySet(navigationProperty); + final EntityInfoAggregator inlineInfo = EntityInfoAggregator.create(inlineEntitySet); + OnDeserializeInlineContent callback = readProperties.getCallback(); + DeserializerProperties inlineReadProperties; + try { + if (callback == null) { + inlineReadProperties = + DeserializerProperties.init() + .isValidatingFacets(readProperties.isValidatingFacets()) + .build(); + + } else { + inlineReadProperties = callback.receiveReadProperties(readProperties, navigationProperty); + } + } catch (ODataApplicationException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + + ODataFeed feed = new JsonFeedDeserializer(reader, inlineInfo, inlineReadProperties).readInlineFeedStandalone(); + properties.put(navigationPropertyName, feed); + resultEntry.setContainsInlineEntry(true); + } + + /** + * @param navigationPropertyName + * @param name + * @throws EdmException + * @throws EntityProviderException + * @throws IOException + */ + private void handleInlineEntries(final String navigationPropertyName, String name) throws EdmException, + EntityProviderException, IOException { + EdmNavigationProperty navigationProperty = + (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName); + EdmEntitySet inlineEntitySet = eia.getEntitySet().getRelatedEntitySet(navigationProperty); + EntityInfoAggregator inlineEia = EntityInfoAggregator.create(inlineEntitySet); + OnDeserializeInlineContent callback = readProperties.getCallback(); + final DeserializerProperties inlineReadProperties; + try { + if (callback == null) { + inlineReadProperties = + DeserializerProperties.init() + .isValidatingFacets(readProperties.isValidatingFacets()) + .build(); + + } else { + inlineReadProperties = callback.receiveReadProperties(readProperties, navigationProperty); + } + } catch (ODataApplicationException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + if (navigationProperty.getMultiplicity() == EdmMultiplicity.MANY) { + JsonFeedDeserializer inlineConsumer = new JsonFeedDeserializer(reader, inlineEia, inlineReadProperties); + ODataFeed feed = inlineConsumer.readStartedInlineFeed(name); + properties.put(navigationPropertyName, feed); + resultEntry.setContainsInlineEntry(true); + } else { + JsonEntryDeserializer inlineConsumer = new JsonEntryDeserializer(reader, inlineEia, inlineReadProperties); + ODataEntry entry = inlineConsumer.readInlineEntry(name); + properties.put(navigationPropertyName, entry); + resultEntry.setContainsInlineEntry(true); + } + } + + /** + * + * @param name + * @return ODataEntry + * @throws EdmException + * @throws EntityProviderException + * @throws IOException + */ + 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 resultEntry; + } + +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/4261deb7/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonErrorDocumentDeserializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonErrorDocumentDeserializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonErrorDocumentDeserializer.java new file mode 100644 index 0000000..8b5cc45 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonErrorDocumentDeserializer.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * 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.deserializer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.api.processor.ODataErrorContext; +import org.apache.olingo.odata2.core.commons.ContentType; +import org.apache.olingo.odata2.core.ep.util.FormatJson; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; + +/** + * Consuming (read / deserialization) for OData error document in JSON format. + */ +public class JsonErrorDocumentDeserializer { + /** Default used charset for reader */ + private static final String DEFAULT_CHARSET = "UTF-8"; + private static final String FOUND = "' found."; + /** + * Map containing language code (language - country) to Locale mapping + * based on Locale.getAvailableLocales() + * */ + private static final Map<String, Locale> AVAILABLE_LOCALES = new HashMap<String, Locale>(); + static { + Locale[] locales = Locale.getAvailableLocales(); + for (Locale l : locales) { + AVAILABLE_LOCALES.put(l.getLanguage() + "-" + l.getCountry(), l); + } + } + + /** + * Deserialize / read OData error document in ODataErrorContext. + * + * @param errorDocument OData error document in JSON format + * @return created ODataErrorContext based on input stream content. + * @throws EntityProviderException if an exception during read / deserialization occurs. + */ + public ODataErrorContext readError(final InputStream errorDocument) throws EntityProviderException { + JsonReader reader = createJsonReader(errorDocument); + try { + return parseJson(reader); + } catch (IOException e) { + throw new EntityProviderException( + EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getMessage()), e); + } + } + + /** + * + * @param reader + * @return ODataErrorContext + * @throws IOException + * @throws EntityProviderException + */ + private ODataErrorContext parseJson(final JsonReader reader) throws IOException, EntityProviderException { + ODataErrorContext errorContext; + + if (reader.hasNext()) { + reader.beginObject(); + String currentName = reader.nextName(); + if (FormatJson.ERROR.equals(currentName)) { + errorContext = parseError(reader); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent( + "Invalid object with name '" + currentName + FOUND)); + } + } else { + throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent( + "No content to parse found.")); + } + + errorContext.setContentType(ContentType.APPLICATION_JSON.toContentTypeString()); + return errorContext; + } + + /** + * + * @param reader + * @return ODataErrorContext + * @throws IOException + * @throws EntityProviderException + */ + private ODataErrorContext parseError(final JsonReader reader) throws IOException, EntityProviderException { + ODataErrorContext errorContext = new ODataErrorContext(); + String currentName; + reader.beginObject(); + boolean messageFound = false; + boolean codeFound = false; + + while (reader.hasNext()) { + currentName = reader.nextName(); + if (FormatJson.CODE.equals(currentName)) { + codeFound = true; + errorContext.setErrorCode(getValue(reader)); + } else if (FormatJson.MESSAGE.equals(currentName)) { + messageFound = true; + parseMessage(reader, errorContext); + } else if (FormatJson.INNER_ERROR.equals(currentName)) { + parseInnerError(reader, errorContext); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent( + "Invalid name '" + currentName + FOUND)); + } + } + + if (!codeFound) { + throw new EntityProviderException( + EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'code' property not found.'")); + } + if (!messageFound) { + throw new EntityProviderException( + EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'message' property not found.'")); + } + + reader.endObject(); + return errorContext; + } + + /** + * + * @param reader + * @param errorContext + * @throws IOException + */ + private void parseInnerError(final JsonReader reader, final ODataErrorContext errorContext) throws IOException { + JsonToken token = reader.peek(); + if (token == JsonToken.STRING) { + // implementation for parse content as provided by JsonErrorDocumentProducer + String innerError = reader.nextString(); + errorContext.setInnerError(innerError); + } else if (token == JsonToken.BEGIN_OBJECT) { + // implementation for OData v2 Section 2.2.8.1.2 JSON Error Response + // (RFC4627 Section 2.2 -> https://www.ietf.org/rfc/rfc4627.txt)) + // currently partial provided + errorContext.setInnerError(readJson(reader)); + } + } + + /** + * + * @param reader + * @return String + * @throws IOException + */ + private String readJson(final JsonReader reader) throws IOException { + StringBuilder sb = new StringBuilder(); + + while (reader.hasNext()) { + JsonToken token = reader.peek(); + if (token == JsonToken.NAME) { + if (sb.length() > 0) { + sb.append(","); + } + String name = reader.nextName(); + sb.append("\"").append(name).append("\"").append(":"); + } else if (token == JsonToken.BEGIN_OBJECT) { + reader.beginObject(); + sb.append("{") + .append(readJson(reader)) + .append("}"); + reader.endObject(); + } else if (token == JsonToken.BEGIN_ARRAY) { + reader.beginArray(); + sb.append("[") + .append(readJson(reader)) + .append("]"); + reader.endArray(); + } else { + sb.append("\"") + .append(reader.nextString()) + .append("\""); + } + } + + return sb.toString(); + } + + /** + * + * @param reader + * @param errorContext + * @throws IOException + * @throws EntityProviderException + */ + private void parseMessage(final JsonReader reader, final ODataErrorContext errorContext) + throws IOException, EntityProviderException { + + reader.beginObject(); + boolean valueFound = false; + boolean langFound = false; + String currentName; + + while (reader.hasNext()) { + currentName = reader.nextName(); + if (FormatJson.LANG.equals(currentName)) { + langFound = true; + String langValue = getValue(reader); + if (langValue != null) { + errorContext.setLocale(getLocale(langValue)); + } + } else if (FormatJson.VALUE.equals(currentName)) { + valueFound = true; + errorContext.setMessage(getValue(reader)); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent("Invalid name '" + + currentName + FOUND)); + } + } + + if (!langFound) { + throw new EntityProviderException( + EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'lang' property not found.'")); + } + if (!valueFound) { + throw new EntityProviderException( + EntityProviderException.MISSING_PROPERTY.addContent("Mandatory 'value' property not found.'")); + } + reader.endObject(); + } + + private Locale getLocale(final String langValue) { + return AVAILABLE_LOCALES.get(langValue); + } + + /** + * Read the string value from the JsonReader or 'null' if no value is available. + * + * @param reader to read from + * @return the string value or 'null' + * @throws IOException if an exception occurs + */ + private String getValue(final JsonReader reader) throws IOException { + JsonToken token = reader.peek(); + if (JsonToken.NULL == token) { + reader.skipValue(); + return null; + } + return reader.nextString(); + } + + private JsonReader createJsonReader(final InputStream in) throws EntityProviderException { + if (in == null) { + throw new EntityProviderException(EntityProviderException.INVALID_STATE + .addContent(("Got not supported NULL object as content to de-serialize."))); + } + try { + return new JsonReader(new InputStreamReader(in, DEFAULT_CHARSET)); + } catch (final UnsupportedEncodingException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/4261deb7/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonFeedDeserializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonFeedDeserializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonFeedDeserializer.java new file mode 100644 index 0000000..dd79c3a --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonFeedDeserializer.java @@ -0,0 +1,255 @@ +/******************************************************************************* + * 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.deserializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.api.ep.entry.DeletedEntryMetadata; +import org.apache.olingo.odata2.api.ep.entry.ODataEntry; +import org.apache.olingo.odata2.api.ep.feed.ODataDeltaFeed; +import org.apache.olingo.odata2.api.ep.feed.ODataFeed; +import org.apache.olingo.odata2.client.api.ep.DeserializerProperties; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.feed.FeedMetadataImpl; +import org.apache.olingo.odata2.core.ep.feed.JsonFeedEntry; +import org.apache.olingo.odata2.core.ep.feed.ODataDeltaFeedImpl; +import org.apache.olingo.odata2.core.ep.util.FormatJson; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; + +/** + * This class Deserializes JsonFeed payloads + */ +public class JsonFeedDeserializer { + + private JsonReader reader; + private EntityInfoAggregator eia; + private DeserializerProperties readProperties; + private List<DeletedEntryMetadata> deletedEntries = new ArrayList<DeletedEntryMetadata>(); + private List<ODataEntry> entries = new ArrayList<ODataEntry>(); + private FeedMetadataImpl feedMetadata = new FeedMetadataImpl(); + private boolean resultsArrayPresent = false; + private static final String JSONFEED = "JsonFeed"; + + /** + * + * @param reader + * @param eia + * @param readProperties + */ + public JsonFeedDeserializer(final JsonReader reader, final EntityInfoAggregator eia, + final DeserializerProperties readProperties) { + this.reader = reader; + this.eia = eia; + this.readProperties = readProperties; + } + + /** + * + * @return ODataDeltaFeed + * @throws EntityProviderException + */ + public ODataDeltaFeed readFeedStandalone() throws EntityProviderException { + try { + readFeed(); + + 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 new ODataDeltaFeedImpl(entries, feedMetadata, deletedEntries); + } + + /** + * + * @throws IOException + * @throws EdmException + * @throws EntityProviderException + */ + private void readFeed() throws IOException, EdmException, EntityProviderException { + JsonToken peek = reader.peek(); + if (peek == JsonToken.BEGIN_ARRAY) { + readArrayContent(); + } else { + reader.beginObject(); + final String nextName = reader.nextName(); + if (FormatJson.D.equals(nextName)) { + if (reader.peek() == JsonToken.BEGIN_ARRAY) { + readArrayContent(); + } else { + reader.beginObject(); + readFeedContent(); + reader.endObject(); + } + } else { + handleName(nextName); + readFeedContent(); + } + + reader.endObject(); + } + } + + /** + * + * @throws IOException + * @throws EdmException + * @throws EntityProviderException + */ + private void readFeedContent() throws IOException, EdmException, EntityProviderException { + while (reader.hasNext()) { + final String nextName = reader.nextName(); + handleName(nextName); + } + + if (!resultsArrayPresent) { + throw new EntityProviderException(EntityProviderException.MISSING_RESULTS_ARRAY); + } + } + + /** + * + * @param nextName + * @throws IOException + * @throws EdmException + * @throws EntityProviderException + */ + private void handleName(final String nextName) throws IOException, EdmException, EntityProviderException { + if (FormatJson.RESULTS.equals(nextName)) { + resultsArrayPresent = true; + readArrayContent(); + + } else if (FormatJson.COUNT.equals(nextName)) { + readInlineCount(reader, feedMetadata); + + } else if (FormatJson.NEXT.equals(nextName)) { + if (reader.peek() == JsonToken.STRING && feedMetadata.getNextLink() == null) { + String nextLink = reader.nextString(); + feedMetadata.setNextLink(nextLink); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_CONTENT.addContent(nextName).addContent( + JSONFEED)); + } + + } else if (FormatJson.DELTA.equals(nextName)) { + if (reader.peek() == JsonToken.STRING && feedMetadata.getDeltaLink() == null) { + String deltaLink = reader.nextString(); + feedMetadata.setDeltaLink(deltaLink); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_CONTENT.addContent(nextName).addContent( + JSONFEED)); + } + } else { + throw new EntityProviderException(EntityProviderException.INVALID_CONTENT.addContent(nextName).addContent( + JSONFEED)); + } + } + + /** + * + * @throws IOException + * @throws EdmException + * @throws EntityProviderException + */ + private void readArrayContent() throws IOException, EdmException, EntityProviderException { + reader.beginArray(); + while (reader.hasNext()) { + final JsonFeedEntry entry = new JsonEntryDeserializer(reader, eia, readProperties).readFeedEntry(); + if (entry.isODataEntry()) { + entries.add(entry.getODataEntry()); + } else { + deletedEntries.add(entry.getDeletedEntryMetadata()); + } + } + reader.endArray(); + } + + /** + * + * @param reader + * @param feedMetadata + * @throws IOException + * @throws EntityProviderException + */ + protected static void readInlineCount(final JsonReader reader, final FeedMetadataImpl feedMetadata) + throws IOException, EntityProviderException { + if (reader.peek() == JsonToken.STRING && feedMetadata.getInlineCount() == null) { + int inlineCount; + try { + inlineCount = reader.nextInt(); + } catch (final NumberFormatException e) { + throw new EntityProviderException(EntityProviderException.INLINECOUNT_INVALID.addContent(""), e); + } + if (inlineCount >= 0) { + feedMetadata.setInlineCount(inlineCount); + } else { + throw new EntityProviderException(EntityProviderException.INLINECOUNT_INVALID.addContent(inlineCount)); + } + } else { + throw new EntityProviderException(EntityProviderException.INLINECOUNT_INVALID.addContent(reader.peek())); + } + } + + /** + * + * @param name + * @return ODataFeed + * @throws EdmException + * @throws EntityProviderException + * @throws IOException + */ + protected ODataFeed readStartedInlineFeed(final String name) throws EdmException, EntityProviderException, + IOException { + // consume the already started content + handleName(name); + // consume the rest of the entry content + readFeedContent(); + return new ODataDeltaFeedImpl(entries, feedMetadata); + } + + /** + * + * @return ODataFeed + * @throws EdmException + * @throws EntityProviderException + * @throws IOException + */ + protected ODataFeed readInlineFeedStandalone() throws EdmException, EntityProviderException, IOException { + readFeed(); + return new ODataDeltaFeedImpl(entries, feedMetadata); + } + +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/4261deb7/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonPropertyDeserializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonPropertyDeserializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonPropertyDeserializer.java new file mode 100644 index 0000000..44d8d07 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/JsonPropertyDeserializer.java @@ -0,0 +1,326 @@ +/******************************************************************************* + * 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.deserializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.olingo.odata2.api.edm.Edm; +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.edm.EdmFacets; +import org.apache.olingo.odata2.api.edm.EdmLiteralKind; +import org.apache.olingo.odata2.api.edm.EdmProperty; +import org.apache.olingo.odata2.api.edm.EdmSimpleType; +import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.client.api.ep.DeserializerProperties; +import org.apache.olingo.odata2.core.ep.aggregator.EntityComplexPropertyInfo; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; +import org.apache.olingo.odata2.core.ep.util.FormatJson; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; + +/** + * JSON property consumer. + */ +public class JsonPropertyDeserializer { + + /** + * Deserializes Property + * @param reader + * @param edmProperty + * @param readProperties + * @return Map<String, Object> + * @throws EntityProviderException + */ + public Map<String, Object> readPropertyStandalone(JsonReader reader, final EdmProperty edmProperty, + final DeserializerProperties readProperties) throws EntityProviderException { + return readPropertyStandalone(reader, EntityInfoAggregator.create(edmProperty), readProperties); + } + + /** + * Deserializes Property + * @param reader + * @param propertyInfo + * @param readProperties + * @return Map<String, Object> + * @throws EntityProviderException + */ + public Map<String, Object> readPropertyStandalone(final JsonReader reader, final EntityPropertyInfo propertyInfo, + final DeserializerProperties readProperties) throws EntityProviderException { + Map<String, Object> typeMappings = null ; + Map<String, Object> result = new HashMap<String, Object>(); + + try { + reader.beginObject(); + String nextName = reader.nextName(); + if (FormatJson.D.equals(nextName)) { + reader.beginObject(); + nextName = reader.nextName(); + handleName(reader, typeMappings, propertyInfo, readProperties, result, nextName); + reader.endObject(); + } else { + handleName(reader, typeMappings, propertyInfo, readProperties, result, nextName); + } + reader.endObject(); + + if (reader.peek() != JsonToken.END_DOCUMENT) { + throw new EntityProviderException(EntityProviderException.END_DOCUMENT_EXPECTED.addContent(reader.peek() + .toString())); + } + + return result; + } catch (final IOException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } catch (final IllegalStateException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + + /** + * This method reads through a collection of entities and deserializes each entry + * @param reader + * @param propertyInfo + * @param readProperties + * @return List<?> + * @throws EntityProviderException + */ + public List<?> readCollection(JsonReader reader, final EntityPropertyInfo propertyInfo, + final DeserializerProperties readProperties) throws EntityProviderException { + final Object typeMapping = null ; + List<Object> result = new ArrayList<Object>(); + String name = null; + boolean wrapped = false; + boolean version2 = false; + + try { + if (reader.peek() == JsonToken.BEGIN_OBJECT) { + reader.beginObject(); + name = reader.nextName(); + if (FormatJson.D.equals(name)) { + wrapped = true; + if (reader.peek() == JsonToken.BEGIN_OBJECT) { + reader.beginObject(); + name = reader.nextName(); + } else { + name = null; + } + } + } + if (name != null) { + version2 = true; + if (FormatJson.METADATA.equals(name)) { + readAndCheckTypeInfo(reader, + "Collection(" + propertyInfo.getType().getNamespace() + Edm.DELIMITER + + propertyInfo.getType().getName() + ")"); + name = reader.nextName(); + } + if (!FormatJson.RESULTS.equals(name)) { + throw new EntityProviderException(EntityProviderException.INVALID_PARENT_TAG + .addContent(FormatJson.RESULTS, name)); + } + } + reader.beginArray(); + while (reader.hasNext()) { + result.add(readPropertyValue(reader, propertyInfo, typeMapping, readProperties)); + } + reader.endArray(); + if (version2) { + reader.endObject(); + } + if (wrapped) { + reader.endObject(); + } + + if (reader.peek() != JsonToken.END_DOCUMENT) { + throw new EntityProviderException(EntityProviderException.END_DOCUMENT_EXPECTED + .addContent(reader.peek().toString())); + } + + return result; + } catch (final EdmException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED + .addContent(e.getClass().getSimpleName()), e); + } catch (final IOException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED + .addContent(e.getClass().getSimpleName()), e); + } catch (final IllegalStateException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED + .addContent(e.getClass().getSimpleName()), e); + } + } + + private void handleName(final JsonReader reader, final Map<String, Object> typeMappings, + final EntityPropertyInfo entityPropertyInfo, final DeserializerProperties readProperties, + final Map<String, Object> result, final String nextName) throws EntityProviderException { + if (!entityPropertyInfo.getName().equals(nextName)) { + throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT.addContent(nextName)); + } + final Object mapping = typeMappings == null ? null : typeMappings.get(nextName); + final Object propertyValue = readPropertyValue(reader, entityPropertyInfo, mapping, readProperties); + result.put(nextName, propertyValue); + } + + protected Object readPropertyValue(final JsonReader reader, final EntityPropertyInfo entityPropertyInfo, + final Object typeMapping, final DeserializerProperties readProperties) + throws EntityProviderException { + try { + return entityPropertyInfo.isComplex() ? + readComplexProperty(reader, (EntityComplexPropertyInfo) entityPropertyInfo, typeMapping, readProperties) : + readSimpleProperty(reader, entityPropertyInfo, typeMapping, readProperties); + } catch (final EdmException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } catch (final IOException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + + private Object readSimpleProperty(final JsonReader reader, final EntityPropertyInfo entityPropertyInfo, + final Object typeMapping, final DeserializerProperties readProperties) + throws EdmException, EntityProviderException, IOException { + final EdmSimpleType type = (EdmSimpleType) entityPropertyInfo.getType(); + Object value = null; + final JsonToken tokenType = reader.peek(); + if (tokenType == JsonToken.NULL) { + reader.nextNull(); + } else { + switch (EdmSimpleTypeKind.valueOf(type.getName())) { + case Boolean: + if (tokenType == JsonToken.BOOLEAN) { + value = reader.nextBoolean(); + value = value.toString(); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY_VALUE + .addContent(entityPropertyInfo.getName())); + } + break; + case Byte: + case SByte: + case Int16: + case Int32: + if (tokenType == JsonToken.NUMBER) { + value = reader.nextInt(); + value = value.toString(); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY_VALUE + .addContent(entityPropertyInfo.getName())); + } + break; + case Single: + case Double: + if (tokenType == JsonToken.STRING) { + value = reader.nextString(); + } else if (tokenType == JsonToken.NUMBER) { + value = reader.nextDouble(); + value = value.toString(); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY_VALUE + .addContent(entityPropertyInfo.getName())); + } + break; + default: + if (tokenType == JsonToken.STRING) { + value = reader.nextString(); + } else { + throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY_VALUE + .addContent(entityPropertyInfo.getName())); + } + break; + } + } + + final Class<?> typeMappingClass = typeMapping == null ? type.getDefaultType() : (Class<?>) typeMapping; + final EdmFacets facets = readProperties == null || readProperties.isValidatingFacets() ? + entityPropertyInfo.getFacets() : null; + return type.valueOfString((String) value, EdmLiteralKind.JSON, facets, typeMappingClass); + } + + @SuppressWarnings("unchecked") + private Object readComplexProperty(final JsonReader reader, final EntityComplexPropertyInfo complexPropertyInfo, + final Object typeMapping, final DeserializerProperties readProperties) + throws EdmException, EntityProviderException, IOException { + if (reader.peek().equals(JsonToken.NULL)) { + reader.nextNull(); + if ((readProperties == null || readProperties.isValidatingFacets()) && complexPropertyInfo.isMandatory()) { + throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY_VALUE.addContent(complexPropertyInfo + .getName())); + } + return null; + } + + reader.beginObject(); + Map<String, Object> data = new HashMap<String, Object>(); + + Map<String, Object> mapping; + if (typeMapping != null) { + if (typeMapping instanceof Map) { + mapping = (Map<String, Object>) typeMapping; + } else { + throw new EntityProviderException(EntityProviderException.INVALID_MAPPING.addContent(complexPropertyInfo + .getName())); + } + } else { + mapping = new HashMap<String, Object>(); + } + + while (reader.hasNext()) { + final String childName = reader.nextName(); + if (FormatJson.METADATA.equals(childName)) { + readAndCheckTypeInfo(reader, + complexPropertyInfo.getType().getNamespace() + Edm.DELIMITER + complexPropertyInfo.getType().getName()); + } else { + EntityPropertyInfo childPropertyInfo = complexPropertyInfo.getPropertyInfo(childName); + if (childPropertyInfo == null) { + throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(childName)); + } + Object childData = readPropertyValue(reader, childPropertyInfo, mapping.get(childName), readProperties); + if (data.containsKey(childName)) { + throw new EntityProviderException(EntityProviderException.DOUBLE_PROPERTY.addContent(childName)); + } + data.put(childName, childData); + } + } + reader.endObject(); + return data; + } + + protected void readAndCheckTypeInfo(final JsonReader reader, String expectedTypeName) + throws IOException, EntityProviderException { + reader.beginObject(); + if (!FormatJson.TYPE.equals(reader.nextName())) { + throw new EntityProviderException(EntityProviderException.MISSING_ATTRIBUTE.addContent(FormatJson.TYPE) + .addContent(FormatJson.METADATA)); + } + final String actualTypeName = reader.nextString(); + if (!expectedTypeName.equals(actualTypeName)) { + throw new EntityProviderException(EntityProviderException.INVALID_ENTITYTYPE.addContent(expectedTypeName) + .addContent(actualTypeName)); + } + reader.endObject(); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/4261deb7/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/XmlEntityDeserializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/XmlEntityDeserializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/XmlEntityDeserializer.java new file mode 100644 index 0000000..ea3393c --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/XmlEntityDeserializer.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * 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.deserializer; + +import java.util.Map; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.olingo.odata2.api.edm.EdmEntitySet; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.api.ep.entry.ODataEntry; +import org.apache.olingo.odata2.api.ep.feed.ODataDeltaFeed; +import org.apache.olingo.odata2.client.api.ep.EntityStream; +import org.apache.olingo.odata2.core.commons.XmlHelper; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; + +/** + * Xml entity (content type dependent) consumer for reading input (from <code>content</code>). + * + * + */ +public class XmlEntityDeserializer { + + /** + * + * @throws EntityProviderException + */ + public XmlEntityDeserializer() throws EntityProviderException { + super(); + } + + /** + * Returns an ODataDeltaFeed deserializing EntityStream + * @param entitySet + * @param entity + * @return ODataDeltaFeed + * @throws EntityProviderException + */ + public ODataDeltaFeed readFeed(final EdmEntitySet entitySet, final EntityStream entity) + throws EntityProviderException { + XMLStreamReader reader = null; + EntityProviderException cachedException = null; + + try { + reader = XmlHelper.createStreamReader(entity.getContent()); + + EntityInfoAggregator eia = EntityInfoAggregator.create(entitySet); + XmlFeedDeserializer xfc = new XmlFeedDeserializer(); + return xfc.readFeed(reader, eia, entity.getReadProperties()); + } catch (EntityProviderException e) { + cachedException = e; + throw cachedException; + } finally { //NOPMD - suppressed + if (reader != null) { + try { + reader.close(); + } catch (XMLStreamException e) { + if (cachedException != null) { + throw cachedException; //NOSONAR + } else { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.//NOSONAR + addContent(e.getClass() + .getSimpleName()), e); + } + } + } + } + } + + /** + * Returns an ODataEntry deserializing EntityStream + * @param entitySet + * @param entity + * @return ODataEntry + * @throws EntityProviderException + */ + public ODataEntry readEntry(final EdmEntitySet entitySet, final EntityStream entity) + throws EntityProviderException { + XMLStreamReader reader = null; + EntityProviderException cachedException = null; + + try { + reader = XmlHelper.createStreamReader(entity.getContent()); + EntityInfoAggregator eia = EntityInfoAggregator.create(entitySet); + + return new XmlEntryDeserializer().readEntry(reader, eia, entity.getReadProperties(), false); + } catch (EntityProviderException e) { + cachedException = e; + throw cachedException; + } finally {//NOPMD - suppressed + if (reader != null) { + try { + reader.close(); + } catch (XMLStreamException e) { + if (cachedException != null) { + throw cachedException; //NOSONAR + } else { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.//NOSONAR + addContent(e.getClass() + .getSimpleName()), e); + } + } + } + } + } + + /** + * + * @param info + * @param entityStream + * @return + * @throws EntityProviderException + */ + public Object readCollection(final EntityPropertyInfo info, EntityStream entityStream) + throws EntityProviderException { + XMLStreamReader reader = null; + EntityProviderException cachedException = null; + try { + reader = XmlHelper.createStreamReader(entityStream.getContent()); + return new XmlPropertyDeserializer().readCollection(reader, info, entityStream.getReadProperties()); + } catch (final EntityProviderException e) { + cachedException = e; + throw cachedException; + } finally {// NOPMD (suppress DoNotThrowExceptionInFinally) + if (reader != null) { + try { + reader.close(); + } catch (final XMLStreamException e) { + if (cachedException != null) { + throw cachedException; + } else { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED + .addContent(e.getClass().getSimpleName()), e); + } + } + } + } + } + + /** + * + * @param propertyInfo + * @param entityStream + * @return + * @throws EntityProviderException + */ + public Map<String, Object> readProperty(final EntityPropertyInfo propertyInfo, final EntityStream entityStream) + throws EntityProviderException { + XMLStreamReader reader = null; + EntityProviderException cachedException = null; + try { + reader = XmlHelper.createStreamReader(entityStream.getContent()); + return new XmlPropertyDeserializer().readProperty(reader, propertyInfo, entityStream.getReadProperties()); + } catch (EntityProviderException e) { + cachedException = e; + throw cachedException; + } finally {// NOPMD (suppress DoNotThrowExceptionInFinally) + if (reader != null) { + try { + reader.close(); + } catch (XMLStreamException e) { + if (cachedException != null) { + throw cachedException; + } else { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + } + } + } +}
