/******************************************************************************* * 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.core.ep.producer; import java.util.List; import java.util.Map; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.apache.olingo.odata2.api.edm.Edm; import org.apache.olingo.odata2.api.edm.EdmCustomizableFeedMappings; 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.EdmSimpleType; import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException; import org.apache.olingo.odata2.api.ep.EntityProviderException; import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties; import org.apache.olingo.odata2.core.ep.EntityProviderProducerException; import org.apache.olingo.odata2.core.ep.aggregator.EntityComplexPropertyInfo; import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; import org.apache.olingo.odata2.core.ep.util.FormatXml; /** * Internal EntityProvider for simple and complex EDM properties which are pre-analyzed as {@link EntityPropertyInfo}. * */ public class XmlPropertyEntityProducer { private final boolean includeSimplePropertyType; private final boolean validateFacets; private boolean isDataBasedPropertySerialization = false; public XmlPropertyEntityProducer(final EntityProviderWriteProperties writeProperties) { this(writeProperties.isIncludeSimplePropertyType(), writeProperties.isValidatingFacets()); isDataBasedPropertySerialization = writeProperties.isDataBasedPropertySerialization(); } public XmlPropertyEntityProducer(final boolean includeSimplePropertyType, final boolean validateFacets) { this.includeSimplePropertyType = includeSimplePropertyType; this.validateFacets = validateFacets; } /** * Append {@link Object} <code>value</code> based on {@link EntityPropertyInfo} to {@link XMLStreamWriter} in an * already existing XML structure inside the d namespace. * * @param writer * @param name Name of the outer XML tag * @param propertyInfo * @param value * @throws EntityProviderException */ public void append(final XMLStreamWriter writer, final String name, final EntityPropertyInfo propertyInfo, final Object value) throws EntityProviderException { try { writer.writeStartElement(Edm.NAMESPACE_D_2007_08, name); if (propertyInfo.isComplex()) { appendProperty(writer, (EntityComplexPropertyInfo) propertyInfo, value); } else { appendProperty(writer, propertyInfo, value); } writer.writeEndElement(); } catch (XMLStreamException e) { throw new EntityProviderProducerException(EntityProviderException.COMMON, e); } catch (EdmException e) { throw new EntityProviderProducerException(e.getMessageReference(), e); } } public void appendCustomProperty(final XMLStreamWriter writer, final String name, final EntityPropertyInfo propertyInfo, final Object value) throws EntityProviderException { try { if (!propertyInfo.isComplex()) { writeStartElementWithCustomNamespace(writer, propertyInfo, name); appendProperty(writer, propertyInfo, value); writer.writeEndElement(); } } catch (XMLStreamException e) { throw new EntityProviderProducerException(EntityProviderException.COMMON, e); } catch (EdmException e) { throw new EntityProviderProducerException(e.getMessageReference(), e); } } /** * Append {@link Object} <code>value</code> based on {@link EntityPropertyInfo} to {@link XMLStreamWriter} as a * stand-alone XML structure, including writing of default namespace declarations. * The name of the outermost XML element comes from the {@link EntityPropertyInfo}. * * @param writer * @param propertyInfo * @param value * @throws EntityProviderException */ public void append(final XMLStreamWriter writer, final EntityPropertyInfo propertyInfo, final Object value) throws EntityProviderException { try { writer.writeStartElement(propertyInfo.getName()); writer.writeDefaultNamespace(Edm.NAMESPACE_D_2007_08); writer.writeNamespace(Edm.PREFIX_M, Edm.NAMESPACE_M_2007_08); if (propertyInfo.isComplex()) { appendProperty(writer, (EntityComplexPropertyInfo) propertyInfo, value); } else { appendProperty(writer, propertyInfo, value); } writer.writeEndElement(); } catch (XMLStreamException e) { throw new EntityProviderProducerException(EntityProviderException.COMMON, e); } catch (EdmException e) { throw new EntityProviderProducerException(e.getMessageReference(), e); } } /** * * @param writer * @param propertyInfo * @param value * @throws XMLStreamException * @throws EdmException * @throws EntityProviderException */ private void appendProperty(final XMLStreamWriter writer, final EntityComplexPropertyInfo propertyInfo, final Object value) throws XMLStreamException, EdmException, EntityProviderException { if (value == null) { writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.ATOM_NULL, FormatXml.ATOM_VALUE_TRUE); } else { writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.ATOM_TYPE, getFqnTypeName(propertyInfo)); List<EntityPropertyInfo> propertyInfos = propertyInfo.getPropertyInfos(); for (EntityPropertyInfo childPropertyInfo : propertyInfos) { if (isDataBasedPropertySerialization && !((Map<?,?>)value).containsKey(childPropertyInfo.getName())) { continue; } Object childValue = extractChildValue(value, childPropertyInfo.getName()); append(writer, childPropertyInfo.getName(), childPropertyInfo, childValue); } } } /** * Returns full qualified name of a type of a given PropertyInfo. * @return Full qualified name */ private String getFqnTypeName(final EntityComplexPropertyInfo propertyInfo) throws EdmException { return propertyInfo.getType().getNamespace() + Edm.DELIMITER + propertyInfo.getType().getName(); } /** * If <code>value</code> is a {@link Map} the element with given <code>name</code> as key is returned. * If <code>value</code> is NOT a {@link Map} its {@link String#valueOf(Object)} result is returned. * * @param value * @param name * @return name or result (see above) */ private Object extractChildValue(final Object value, final String name) { if (value instanceof Map) { Map<?, ?> map = (Map<?, ?>) value; return map.get(name); } return String.valueOf(value); } /** * Appends a simple-property value to the XML stream. * @param writer the XML stream writer * @param prop property informations * @param value the value of the property * @throws XMLStreamException * @throws EdmException * @throws EntityProviderProducerException */ private void appendProperty(final XMLStreamWriter writer, final EntityPropertyInfo prop, final Object value) throws XMLStreamException, EdmException, EntityProviderProducerException { Object contentValue = value; String mimeType = null; if (prop.getMimeType() != null) { mimeType = prop.getMimeType(); } else if (prop.getMapping() != null && prop.getMapping().getMediaResourceMimeTypeKey() != null) { mimeType = (String) extractChildValue(value, prop.getMapping().getMediaResourceMimeTypeKey()); contentValue = extractChildValue(value, prop.getName()); } if (mimeType != null) { writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.M_MIME_TYPE, mimeType); } final EdmSimpleType type = (EdmSimpleType) prop.getType(); if (includeSimplePropertyType) { String fqnTypeName = type.getNamespace() + Edm.DELIMITER + type.getName(); writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.ATOM_TYPE, fqnTypeName); } final EdmFacets facets = validateFacets ? prop.getFacets() : null; String valueAsString = null; try { valueAsString = type.valueToString(contentValue, EdmLiteralKind.DEFAULT, facets); } catch (EdmSimpleTypeException e) { throw new EntityProviderProducerException(EdmSimpleTypeException.getMessageReference( e.getMessageReference()).updateContent( e.getMessageReference().getContent(), prop.getName()), e); } if (valueAsString == null) { writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.ATOM_NULL, FormatXml.ATOM_VALUE_TRUE); } else { writer.writeCharacters(valueAsString); } } /** * * @param writer * @param prop * @param name * @throws XMLStreamException * @throws EntityProviderException */ private void writeStartElementWithCustomNamespace(final XMLStreamWriter writer, final EntityPropertyInfo prop, final String name) throws XMLStreamException, EntityProviderException { EdmCustomizableFeedMappings mapping = prop.getCustomMapping(); String nsPrefix = mapping.getFcNsPrefix(); String nsUri = mapping.getFcNsUri(); if (nsUri == null || nsPrefix == null) { throw new EntityProviderProducerException(EntityProviderException.INVALID_NAMESPACE.addContent(name)); } writer.writeStartElement(nsPrefix, name, nsUri); writer.writeNamespace(nsPrefix, nsUri); } }