| /******************************************************************************* |
| * 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.consumer; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.xml.namespace.NamespaceContext; |
| import javax.xml.stream.XMLStreamConstants; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamReader; |
| |
| 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.EdmFacets; |
| 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.EdmSimpleType; |
| import org.apache.olingo.odata2.api.ep.EntityProviderException; |
| import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties; |
| import org.apache.olingo.odata2.api.ep.callback.OnReadInlineContent; |
| import org.apache.olingo.odata2.api.ep.callback.ReadEntryResult; |
| import org.apache.olingo.odata2.api.ep.callback.ReadFeedResult; |
| 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.api.uri.ExpandSelectTreeNode; |
| import org.apache.olingo.odata2.core.commons.ContentType; |
| 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.EntityTypeMapping; |
| 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.FeedMetadataImpl; |
| import org.apache.olingo.odata2.core.ep.feed.ODataDeltaFeedImpl; |
| import org.apache.olingo.odata2.core.ep.util.FormatXml; |
| import org.apache.olingo.odata2.core.uri.ExpandSelectTreeNodeImpl; |
| |
| /** |
| * Atom/XML format reader/consumer for entries. |
| * |
| * {@link XmlEntryConsumer} instance can be reused for several |
| * {@link #readEntry(XMLStreamReader, EntityInfoAggregator, EntityProviderReadProperties)} calls |
| * but be aware that the instance and their <code>readEntry*</code> methods are <b>NOT THREAD SAFE</b>. |
| * |
| */ |
| public class XmlEntryConsumer { |
| |
| private ODataEntryImpl readEntryResult; |
| private Map<String, Object> properties; |
| private MediaMetadataImpl mediaMetadata; |
| private EntryMetadataImpl entryMetadata; |
| private ExpandSelectTreeNodeImpl expandSelectTree; |
| private EntityTypeMapping typeMappings; |
| private String currentHandledStartTagName; |
| |
| public ODataEntry readEntry(final XMLStreamReader reader, final EntityInfoAggregator eia, |
| final EntityProviderReadProperties readProperties, final boolean isInline) throws EntityProviderException { |
| try { |
| initialize(readProperties); |
| |
| if (isInline) { |
| setETag(reader); |
| } |
| |
| while (reader.hasNext() && !isEntryEndTag(reader)) { |
| reader.nextTag(); |
| if (reader.isStartElement()) { |
| handleStartedTag(reader, eia, readProperties); |
| } |
| } |
| |
| return readEntryResult; |
| } catch (XMLStreamException 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); |
| } |
| } |
| |
| private boolean isEntryEndTag(final XMLStreamReader reader) { |
| return reader.isEndElement() |
| && Edm.NAMESPACE_ATOM_2005.equals(reader.getNamespaceURI()) |
| && FormatXml.ATOM_ENTRY.equals(reader.getLocalName()); |
| } |
| |
| /** |
| * Initializes the {@link XmlEntryConsumer} to be ready for reading an entry. |
| * @param readProperties |
| * @throws EntityProviderException |
| */ |
| private void initialize(final EntityProviderReadProperties readProperties) throws EntityProviderException { |
| properties = new HashMap<String, Object>(); |
| mediaMetadata = new MediaMetadataImpl(); |
| entryMetadata = new EntryMetadataImpl(); |
| expandSelectTree = new ExpandSelectTreeNodeImpl(); |
| |
| readEntryResult = new ODataEntryImpl(properties, mediaMetadata, entryMetadata, expandSelectTree); |
| typeMappings = EntityTypeMapping.create(readProperties.getTypeMappings()); |
| } |
| |
| private void handleStartedTag(final XMLStreamReader reader, final EntityInfoAggregator eia, |
| final EntityProviderReadProperties readProperties) |
| throws EntityProviderException, XMLStreamException, EdmException { |
| |
| currentHandledStartTagName = reader.getLocalName(); |
| |
| if (FormatXml.ATOM_ID.equals(currentHandledStartTagName)) { |
| readId(reader); |
| } else if (FormatXml.ATOM_ENTRY.equals(currentHandledStartTagName)) { |
| readEntry(reader); |
| } else if (FormatXml.ATOM_LINK.equals(currentHandledStartTagName)) { |
| readLink(reader, eia, readProperties); |
| } else if (FormatXml.ATOM_CONTENT.equals(currentHandledStartTagName)) { |
| readContent(reader, eia, readProperties); |
| } else if (FormatXml.M_PROPERTIES.equals(currentHandledStartTagName)) { |
| readProperties(reader, eia, readProperties); |
| } else if (!readProperties.getMergeSemantic()) { |
| readCustomElement(reader, currentHandledStartTagName, eia, readProperties); |
| } else { |
| skipStartedTag(reader); |
| } |
| } |
| |
| private void readCustomElement(final XMLStreamReader reader, final String tagName, final EntityInfoAggregator eia, |
| final EntityProviderReadProperties readProperties) |
| throws EdmException, EntityProviderException, XMLStreamException { |
| EntityPropertyInfo targetPathInfo = eia.getTargetPathInfo(tagName); |
| NamespaceContext nsctx = reader.getNamespaceContext(); |
| |
| boolean skipTag = true; |
| if (!Edm.NAMESPACE_ATOM_2005.equals(reader.getName().getNamespaceURI())) { |
| |
| if (targetPathInfo != null) { |
| final String customPrefix = targetPathInfo.getCustomMapping().getFcNsPrefix(); |
| final String customNamespaceURI = targetPathInfo.getCustomMapping().getFcNsUri(); |
| |
| if (customPrefix != null && customNamespaceURI != null) { |
| String xmlPrefix = nsctx.getPrefix(customNamespaceURI); |
| String xmlNamespaceUri = reader.getNamespaceURI(customPrefix); |
| |
| if (customNamespaceURI.equals(xmlNamespaceUri) && customPrefix.equals(xmlPrefix)) { |
| skipTag = false; |
| reader.require(XMLStreamConstants.START_ELEMENT, customNamespaceURI, tagName); |
| final String text = reader.getElementText(); |
| reader.require(XMLStreamConstants.END_ELEMENT, customNamespaceURI, tagName); |
| |
| final EntityPropertyInfo propertyInfo = getValidatedPropertyInfo(eia, tagName); |
| final Class<?> typeMapping = typeMappings.getMappingClass(propertyInfo.getName()); |
| final EdmSimpleType type = (EdmSimpleType) propertyInfo.getType(); |
| final Class<?> typeMappingClass = typeMapping == null ? type.getDefaultType() : (Class<?>) typeMapping; |
| final EdmFacets facets = readProperties == null || readProperties.isValidatingFacets() ? |
| propertyInfo.getFacets() : null; |
| final Object value = type.valueOfString(text, EdmLiteralKind.DEFAULT, |
| facets, typeMappingClass); |
| properties.put(tagName, value); |
| } |
| } |
| } else { |
| throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(tagName)); |
| } |
| } |
| |
| if (skipTag) { |
| skipStartedTag(reader); |
| } |
| } |
| |
| /** |
| * Skip the tag to which the {@link XMLStreamReader} currently points. |
| * Therefore it is read until an end element tag with current local name is found. |
| * |
| * @param reader |
| * @throws XMLStreamException |
| */ |
| private void skipStartedTag(final XMLStreamReader reader) throws XMLStreamException { |
| final String name = reader.getLocalName(); |
| int read = 1; |
| while (read > 0 && reader.hasNext()) { |
| reader.next(); |
| if (reader.hasName() && name.equals(reader.getLocalName())) { |
| if (reader.isEndElement()) { |
| read--; |
| } else if (reader.isStartElement()) { |
| read++; |
| } |
| } |
| } |
| } |
| |
| private void readEntry(final XMLStreamReader reader) throws EntityProviderException, XMLStreamException { |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_ENTRY); |
| setETag(reader); |
| } |
| |
| private void setETag(final XMLStreamReader reader) { |
| final String etag = reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, FormatXml.M_ETAG); |
| entryMetadata.setEtag(etag); |
| } |
| |
| /** |
| * |
| * @param reader |
| * @param eia |
| * @param readProperties |
| * @throws EntityProviderException |
| * @throws XMLStreamException |
| * @throws EdmException |
| */ |
| private void readLink(final XMLStreamReader reader, final EntityInfoAggregator eia, |
| final EntityProviderReadProperties readProperties) throws EntityProviderException, XMLStreamException, |
| EdmException { |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_LINK); |
| |
| final String rel = reader.getAttributeValue(null, FormatXml.ATOM_REL); |
| final String uri = reader.getAttributeValue(null, FormatXml.ATOM_HREF); |
| final String type = reader.getAttributeValue(null, FormatXml.ATOM_TYPE); |
| final String etag = reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, FormatXml.M_ETAG); |
| |
| // read to next tag to check if <link> contains any further tags |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_LINK); |
| reader.nextTag(); |
| |
| if (rel == null || uri == null) { |
| throw new EntityProviderException(EntityProviderException.MISSING_ATTRIBUTE.addContent( |
| FormatXml.ATOM_HREF + "' and/or '" + FormatXml.ATOM_REL).addContent(FormatXml.ATOM_LINK)); |
| } else if (rel.startsWith(Edm.NAMESPACE_REL_2007_08)) { |
| final String navigationPropertyName = rel.substring(Edm.NAMESPACE_REL_2007_08.length()); |
| entryMetadata.putAssociationUri(navigationPropertyName, uri); |
| } else if (rel.equals(Edm.LINK_REL_EDIT_MEDIA)) { |
| mediaMetadata.setEditLink(uri); |
| mediaMetadata.setEtag(etag); |
| } |
| |
| if (!reader.isEndElement() && rel != null && rel.startsWith(Edm.NAMESPACE_REL_2007_08)) { |
| readInlineContent(reader, eia, readProperties, type, rel); |
| } |
| } |
| |
| /** |
| * Inline content was found and {@link XMLStreamReader} already points to <m:inline> tag. |
| * |
| * @param reader |
| * @param eia |
| * @param readProperties |
| * @param atomLinkType the atom <code>type</code> attribute value of the <code>link</code> tag |
| * @param atomLinkRel the atom <code>rel</code> attribute value of the <code>link</code> tag |
| * @throws XMLStreamException |
| * @throws EntityProviderException |
| * @throws EdmException |
| */ |
| private void readInlineContent(final XMLStreamReader reader, final EntityInfoAggregator eia, |
| final EntityProviderReadProperties readProperties, |
| final String atomLinkType, final String atomLinkRel) |
| throws XMLStreamException, EntityProviderException, EdmException { |
| |
| // |
| String navigationPropertyName = atomLinkRel.substring(Edm.NAMESPACE_REL_2007_08.length()); |
| |
| EdmNavigationProperty navigationProperty = |
| (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName); |
| EdmEntitySet entitySet = eia.getEntitySet().getRelatedEntitySet(navigationProperty); |
| EntityInfoAggregator inlineEia = EntityInfoAggregator.create(entitySet); |
| |
| final EntityProviderReadProperties inlineProperties = createInlineProperties(readProperties, navigationProperty); |
| |
| // validations |
| boolean isFeed = isInlineFeedValidated(reader, eia, atomLinkType, navigationProperty); |
| |
| List<ODataEntry> inlineEntries = new ArrayList<ODataEntry>(); |
| |
| while (!(reader.isEndElement() && Edm.NAMESPACE_M_2007_08.equals(reader.getNamespaceURI()) && FormatXml.M_INLINE |
| .equals(reader.getLocalName()))) { |
| |
| if (reader.isStartElement() && Edm.NAMESPACE_ATOM_2005.equals(reader.getNamespaceURI()) |
| && FormatXml.ATOM_ENTRY.equals(reader.getLocalName())) { |
| XmlEntryConsumer xec = new XmlEntryConsumer(); |
| ODataEntry inlineEntry = xec.readEntry(reader, inlineEia, inlineProperties, true); |
| inlineEntries.add(inlineEntry); |
| } |
| // next tag |
| reader.next(); |
| } |
| |
| updateExpandSelectTree(navigationPropertyName, inlineEntries); |
| updateReadProperties(readProperties, navigationPropertyName, navigationProperty, isFeed, inlineEntries); |
| |
| reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE); |
| } |
| |
| /** |
| * Updates the read properties ({@link #properties}) for this {@link ReadEntryResult} ({@link #readEntryResult}). |
| * |
| * @param readProperties |
| * @param navigationPropertyName |
| * @param navigationProperty |
| * @param isFeed |
| * @param inlineEntries |
| * @throws EntityProviderException |
| */ |
| private void updateReadProperties(final EntityProviderReadProperties readProperties, |
| final String navigationPropertyName, |
| final EdmNavigationProperty navigationProperty, final boolean isFeed, final List<ODataEntry> inlineEntries) |
| throws EntityProviderException { |
| Object entry = extractODataEntity(isFeed, inlineEntries); |
| OnReadInlineContent callback = readProperties.getCallback(); |
| if (callback == null) { |
| readEntryResult.setContainsInlineEntry(true); |
| properties.put(navigationPropertyName, entry); |
| } else { |
| doCallback(readProperties, navigationProperty, callback, isFeed, entry); |
| } |
| } |
| |
| /** |
| * Updates the expand select tree ({@link #expandSelectTree}) for this {@link ReadEntryResult} ( |
| * {@link #readEntryResult}). |
| * |
| * @param navigationPropertyName |
| * @param inlineEntries |
| * @throws EntityProviderException |
| */ |
| private void updateExpandSelectTree(final String navigationPropertyName, final List<ODataEntry> inlineEntries) |
| throws EntityProviderException { |
| expandSelectTree.setExpanded(); |
| ExpandSelectTreeNodeImpl subNode = getExpandSelectTreeNode(inlineEntries); |
| expandSelectTree.putLink(navigationPropertyName, subNode); |
| for(ODataEntry entry : inlineEntries){ |
| ExpandSelectTreeNodeImpl newExpandedSelectedTree = new ExpandSelectTreeNodeImpl(); |
| newExpandedSelectedTree.setExpanded(); |
| newExpandedSelectedTree.setExplicitlySelected(); |
| newExpandedSelectedTree.putLink(navigationPropertyName, |
| (ExpandSelectTreeNodeImpl) entry.getExpandSelectTree()); |
| expandSelectTree.getExpandedList().add(newExpandedSelectedTree); |
| } |
| |
| } |
| |
| /** |
| * Get the {@link ExpandSelectTreeNodeImpl} from the <code>inlineEntries</code> or if none exists create a new |
| * {@link ExpandSelectTreeNodeImpl}. |
| * |
| * @param inlineEntries entries which are checked for existing {@link ExpandSelectTreeNodeImpl} |
| * @return {@link ExpandSelectTreeNodeImpl} from the <code>inlineEntries</code> or if none exists create a new |
| * {@link ExpandSelectTreeNodeImpl}. |
| * @throws EntityProviderException if an unsupported {@link ExpandSelectTreeNode} implementation was found. |
| */ |
| private ExpandSelectTreeNodeImpl getExpandSelectTreeNode(final List<ODataEntry> inlineEntries) |
| throws EntityProviderException { |
| if (inlineEntries.isEmpty()) { |
| return new ExpandSelectTreeNodeImpl(); |
| } else { |
| ExpandSelectTreeNode inlinedEntryEstNode = inlineEntries.get(0).getExpandSelectTree(); |
| if (inlinedEntryEstNode instanceof ExpandSelectTreeNodeImpl) { |
| return (ExpandSelectTreeNodeImpl) inlinedEntryEstNode; |
| } else { |
| throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT |
| .addContent("Unsupported implementation for " + ExpandSelectTreeNode.class + " found.")); |
| } |
| } |
| } |
| |
| /** |
| * Get a list of {@link ODataEntry}, an empty list, a single {@link ODataEntry} or <code>NULL</code> based on |
| * <code>isFeed</code> value and <code>inlineEntries</code> content. |
| * |
| * @param isFeed |
| * @param inlineEntries |
| * @return |
| */ |
| private Object extractODataEntity(final boolean isFeed, final List<ODataEntry> inlineEntries) { |
| if (isFeed) { |
| // TODO: fill metadata correctly with inline count and inline next link. Both are currently ignored. |
| return new ODataDeltaFeedImpl(inlineEntries, new FeedMetadataImpl()); |
| } else if (!inlineEntries.isEmpty()) { |
| return inlineEntries.get(0); |
| } |
| return null; |
| } |
| |
| /** |
| * Do the callback based on given parameters. |
| * |
| * @param readProperties |
| * @param navigationProperty |
| * @param callback |
| * @param isFeed |
| * @param entry |
| * @throws EntityProviderException |
| */ |
| private void doCallback(final EntityProviderReadProperties readProperties, |
| final EdmNavigationProperty navigationProperty, |
| final OnReadInlineContent callback, final boolean isFeed, final Object content) throws EntityProviderException { |
| |
| try { |
| if (isFeed) { |
| ReadFeedResult callbackInfo = new ReadFeedResult(readProperties, navigationProperty, |
| (ODataFeed) content, entryMetadata.getId()); |
| callback.handleReadFeed(callbackInfo); |
| } else { |
| ReadEntryResult callbackInfo = new ReadEntryResult(readProperties, navigationProperty, |
| (ODataEntry) content, entryMetadata.getId()); |
| callback.handleReadEntry(callbackInfo); |
| } |
| } catch (ODataApplicationException e) { |
| throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() |
| .getSimpleName()), e); |
| } |
| } |
| |
| /** |
| * Create {@link EntityProviderReadProperties} which can be used for reading of inline properties/entrys of navigation |
| * links within |
| * this current read entry. |
| * |
| * @param readProperties |
| * @param navigationProperty |
| * @return |
| * @throws EntityProviderException |
| */ |
| private EntityProviderReadProperties createInlineProperties(final EntityProviderReadProperties readProperties, |
| final EdmNavigationProperty navigationProperty) throws EntityProviderException { |
| final OnReadInlineContent callback = readProperties.getCallback(); |
| |
| EntityProviderReadProperties currentReadProperties = EntityProviderReadProperties.initFrom(readProperties).build(); |
| if (callback == null) { |
| return currentReadProperties; |
| } else { |
| try { |
| return callback.receiveReadProperties(currentReadProperties, navigationProperty); |
| } catch (ODataApplicationException e) { |
| throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() |
| .getSimpleName()), e); |
| } |
| } |
| } |
| |
| /** |
| * <p> |
| * Inline content was found and {@link XMLStreamReader} already points to <code><m:inline> tag</code>. |
| * <br/> |
| * <b>ATTENTION</b>: If {@link XMLStreamReader} does not point to the <code><m:inline> tag</code> an exception is |
| * thrown. |
| * </p> |
| * <p> |
| * Check whether it is an inline <code>Feed</code> or <code>Entry</code> and validate that... |
| * <ul> |
| * <li>...{@link FormatXml#M_INLINE} tag is correctly set.</li> |
| * <li>...based on {@link EdmMultiplicity} of {@link EdmNavigationProperty} all tags are correctly set.</li> |
| * <li>...{@link FormatXml#ATOM_TYPE} tag is correctly set and according to {@link FormatXml#ATOM_ENTRY} or |
| * {@link FormatXml#ATOM_FEED} to following tags are available.</li> |
| * </ul> |
| * |
| * For the case that one of above validations fail an {@link EntityProviderException} is thrown. |
| * If validation was successful <code>true</code> is returned for <code>Feed</code> and <code>false</code> for |
| * <code>Entry</code> |
| * multiplicity. |
| * </p> |
| * |
| * @param reader xml content reader which already points to <code><m:inline> tag</code> |
| * @param eia all necessary information about the entity |
| * @param type the atom type attribute value of the <code>link</code> tag |
| * @param navigationProperty the navigation property name of the entity |
| * @return <code>true</code> for <code>Feed</code> and <code>false</code> for <code>Entry</code> |
| * @throws EntityProviderException is thrown if at least one validation fails. |
| * @throws EdmException if edm access fails |
| */ |
| private boolean isInlineFeedValidated(final XMLStreamReader reader, final EntityInfoAggregator eia, |
| final String type, final EdmNavigationProperty navigationProperty) throws EntityProviderException, EdmException { |
| boolean isFeed = false; |
| try { |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE); |
| // |
| ContentType cType = ContentType.parse(type); |
| if (cType == null) { |
| throw new EntityProviderException(EntityProviderException.INVALID_INLINE_CONTENT.addContent("xml data")); |
| } |
| EdmMultiplicity navigationMultiplicity = navigationProperty.getMultiplicity(); |
| |
| switch (navigationMultiplicity) { |
| case MANY: |
| validateFeedTags(reader, cType); |
| isFeed = true; |
| break; |
| case ONE: |
| case ZERO_TO_ONE: |
| validateEntryTags(reader, cType); |
| break; |
| } |
| } catch (XMLStreamException e) { |
| throw new EntityProviderException(EntityProviderException.INVALID_INLINE_CONTENT.addContent("xml data"), e); |
| } |
| return isFeed; |
| } |
| |
| private void validateEntryTags(final XMLStreamReader reader, final ContentType cType) throws XMLStreamException, |
| EntityProviderException { |
| if (FormatXml.ATOM_ENTRY.equals(cType.getParameters().get(FormatXml.ATOM_TYPE))) { |
| int next = reader.nextTag(); |
| if (XMLStreamConstants.START_ELEMENT == next) { |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_ENTRY); |
| } else { |
| reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE); |
| } |
| } else { |
| throw new EntityProviderException(EntityProviderException.INVALID_INLINE_CONTENT.addContent("entry")); |
| } |
| } |
| |
| private void validateFeedTags(final XMLStreamReader reader, final ContentType cType) throws XMLStreamException, |
| EntityProviderException { |
| if (FormatXml.ATOM_FEED.equals(cType.getParameters().get(FormatXml.ATOM_TYPE))) { |
| int next = reader.nextTag(); |
| if (XMLStreamConstants.START_ELEMENT == next) { |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_FEED); |
| } else { |
| reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE); |
| } |
| } else { |
| throw new EntityProviderException(EntityProviderException.INVALID_INLINE_CONTENT.addContent("feed")); |
| } |
| } |
| |
| private void readContent(final XMLStreamReader reader, final EntityInfoAggregator eia, |
| final EntityProviderReadProperties readProperties) |
| throws EntityProviderException, XMLStreamException, EdmException { |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_CONTENT); |
| |
| final String contentType = reader.getAttributeValue(null, FormatXml.ATOM_TYPE); |
| final String sourceLink = reader.getAttributeValue(null, FormatXml.ATOM_SRC); |
| |
| reader.nextTag(); |
| |
| if (reader.isStartElement() && reader.getLocalName().equals(FormatXml.M_PROPERTIES)) { |
| readProperties(reader, eia, readProperties); |
| } else if (reader.isEndElement()) { |
| reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_CONTENT); |
| } else { |
| throw new EntityProviderException(EntityProviderException.INVALID_STATE |
| .addContent("Expected closing 'content' or starting 'properties' but found '" |
| + reader.getLocalName() + "'.")); |
| } |
| |
| mediaMetadata.setContentType(contentType); |
| mediaMetadata.setSourceLink(sourceLink); |
| } |
| |
| private void readId(final XMLStreamReader reader) throws EntityProviderException, XMLStreamException { |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_ID); |
| entryMetadata.setId(reader.getElementText()); |
| reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_ID); |
| } |
| |
| private void readProperties(final XMLStreamReader reader, final EntityInfoAggregator entitySet, |
| final EntityProviderReadProperties readProperties) |
| throws XMLStreamException, EdmException, EntityProviderException { |
| // validate namespace |
| reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_PROPERTIES); |
| if (entitySet.getEntityType().hasStream()) { |
| // external properties |
| checkCurrentHandledStartTag(FormatXml.M_PROPERTIES); |
| } else { |
| // inline properties |
| checkCurrentHandledStartTag(FormatXml.ATOM_CONTENT); |
| } |
| |
| EntityPropertyInfo property; |
| XmlPropertyConsumer xpc = new XmlPropertyConsumer(); |
| |
| String closeTag = null; |
| boolean run = true; |
| reader.next(); |
| |
| while (run) { |
| if (reader.isStartElement() && closeTag == null) { |
| closeTag = reader.getLocalName(); |
| if (isEdmNamespaceProperty(reader)) { |
| if (properties.containsKey(closeTag)) { |
| throw new EntityProviderException(EntityProviderException.DOUBLE_PROPERTY.addContent(closeTag)); |
| } |
| property = getValidatedPropertyInfo(entitySet, closeTag); |
| final Object value = xpc.readStartedElement(reader, closeTag, property, typeMappings, readProperties); |
| properties.put(closeTag, value); |
| closeTag = null; |
| } |
| } else if (reader.isEndElement()) { |
| if (reader.getLocalName().equals(closeTag)) { |
| closeTag = null; |
| } else if (Edm.NAMESPACE_M_2007_08.equals(reader.getNamespaceURI()) |
| && FormatXml.M_PROPERTIES.equals(reader.getLocalName())) { |
| run = false; |
| } |
| } |
| reader.next(); |
| } |
| } |
| |
| /** |
| * Check if the {@link #currentHandledStartTagName} is the same as the <code>expectedTagName</code>. |
| * If tag name is not as expected or if {@link #currentHandledStartTagName} is not set an |
| * {@link EntityProviderException} is thrown. |
| * |
| * @param expectedTagName expected name for {@link #currentHandledStartTagName} |
| * @throws EntityProviderException if tag name is not as expected or if {@link #currentHandledStartTagName} is |
| * <code>NULL</code>. |
| */ |
| private void checkCurrentHandledStartTag(final String expectedTagName) throws EntityProviderException { |
| if (currentHandledStartTagName == null) { |
| throw new EntityProviderException(EntityProviderException.INVALID_STATE |
| .addContent("No current handled start tag name set.")); |
| } else if (!currentHandledStartTagName.equals(expectedTagName)) { |
| throw new EntityProviderException(EntityProviderException.INVALID_PARENT_TAG.addContent(expectedTagName) |
| .addContent(currentHandledStartTagName)); |
| } |
| } |
| |
| /** |
| * Checks if property of currently read tag in {@link XMLStreamReader} is defined in |
| * <code>edm properties namespace</code> {@value Edm#NAMESPACE_D_2007_08}. |
| * |
| * If no namespace uri definition is found for namespace prefix of property (<code>tag</code>) an exception is thrown. |
| * |
| * @param reader {@link XMLStreamReader} with position at to checked tag |
| * @return <code>true</code> if property is in <code>edm properties namespace</code>, otherwise <code>false</code>. |
| * @throws EntityProviderException If no namespace uri definition is found for namespace prefix of property |
| * (<code>tag</code>). |
| */ |
| private boolean isEdmNamespaceProperty(final XMLStreamReader reader) throws EntityProviderException { |
| final String nsUri = reader.getNamespaceURI(); |
| if (nsUri == null) { |
| throw new EntityProviderException(EntityProviderException.INVALID_NAMESPACE.addContent(reader.getLocalName())); |
| } else { |
| return Edm.NAMESPACE_D_2007_08.equals(nsUri); |
| } |
| } |
| |
| /** |
| * Get validated {@link EntityPropertyInfo} for property with given <code>name</code>. |
| * If validation fails an {@link EntityProviderException} is thrown. |
| * |
| * Currently this is the case if no {@link EntityPropertyInfo} if found for given <code>name</code>. |
| * |
| * @param entitySet |
| * @param name |
| * @return valid {@link EntityPropertyInfo} (which is never <code>NULL</code>). |
| * @throws EntityProviderException |
| */ |
| private EntityPropertyInfo getValidatedPropertyInfo(final EntityInfoAggregator entitySet, final String name) |
| throws EntityProviderException { |
| EntityPropertyInfo info = entitySet.getPropertyInfo(name); |
| if (info == null) { |
| throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(name)); |
| } |
| return info; |
| } |
| } |