Package org.restlet.ext.odata.internal

Source Code of org.restlet.ext.odata.internal.EntryContentHandler

/**
* Copyright 2005-2011 Noelios Technologies.
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL 1.0 (the
* "Licenses"). You can select the license that you prefer but you may not use
* this file except in compliance with one of these Licenses.
*
* You can obtain a copy of the LGPL 3.0 license at
* http://www.opensource.org/licenses/lgpl-3.0.html
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.opensource.org/licenses/lgpl-2.1.php
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.opensource.org/licenses/cddl1.php
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://www.noelios.com/products/restlet-engine
*
* Restlet is a registered trademark of Noelios Technologies.
*/

package org.restlet.ext.odata.internal;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.restlet.Context;
import org.restlet.data.Language;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.ext.atom.Content;
import org.restlet.ext.atom.Entry;
import org.restlet.ext.atom.EntryReader;
import org.restlet.ext.atom.Feed;
import org.restlet.ext.atom.Link;
import org.restlet.ext.atom.Person;
import org.restlet.ext.atom.Relation;
import org.restlet.ext.odata.Service;
import org.restlet.ext.odata.internal.edm.AssociationEnd;
import org.restlet.ext.odata.internal.edm.EntityType;
import org.restlet.ext.odata.internal.edm.Mapping;
import org.restlet.ext.odata.internal.edm.Metadata;
import org.restlet.ext.odata.internal.edm.Property;
import org.restlet.ext.odata.internal.reflect.ReflectUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

/**
* Content handler for Atom Feed that takes care of OData specific needs, such
* as parsing XML content from other namespaces than Atom. It generates an
* entity based on the values discovered in the entry.
*
* @author Thierry Boileau
* @param <T>
*            The type of the parsed entity.
*/
public class EntryContentHandler<T> extends EntryReader {

    private enum State {
        ASSOCIATION, CONTENT, ENTRY, PROPERTIES, PROPERTY
    }

    /** The currently parsed association. */
    private AssociationEnd association;

    /** The currently parsed inline content. */
    private Content inlineContent;

    /** The currently parsed inline entry. */
    private Entry inlineEntry;

    /** The currently parsed inline feed. */
    private Feed inlineFeed;

    /** The currently parsed inline link. */
    private Link inlineLink;

    /** The path of the current XML element relatively to the Entry. */
    List<String> eltPath;

    /** The entity targeted by this entry. */
    private T entity;

    /** The class of the entity targeted by this entry. */
    private Class<?> entityClass;

    /** The OData type of the parsed entity. */
    private EntityType entityType;

    /** Used to parsed Atom link elements that contains entries. */
    EntryContentHandler<T> inlineEntryHandler;

    /** Used to parsed Atom link elements that contains feeds. */
    FeedContentHandler<T> inlineFeedHandler;

    /** Internal logger. */
    private Logger logger;

    /** The currently parsed OData mapping. */
    private Mapping mapping;

    /** The metadata of the WCF service. */
    private Metadata metadata;

    /** Must the current property be set to null? */
    private boolean parsePropertyNull;

    /** Used to handle complex types. */
    private List<String> propertyPath;

    /** Gleans text content. */
    StringBuilder sb = null;

    /** Heap of states. */
    List<State> states;

    /**
     * Constructor.
     *
     * @param entityClass
     *            The class of the parsed entities.
     * @param entityType
     *            The entity type of the parsed entities.
     * @param metadata
     *            The metadata of the remote OData service.
     * @param logger
     *            The logger.
     */
    public EntryContentHandler(Class<?> entityClass, EntityType entityType,
            Metadata metadata, Logger logger) {
        this.entityClass = entityClass;
        this.entityType = entityType;
        this.metadata = metadata;
        this.logger = logger;
    }

    /**
     * Constructor.
     *
     * @param entityClass
     *            The class of the parsed entities.
     * @param metadata
     *            The metadata of the remote OData service.
     * @param logger
     *            The logger.
     */
    public EntryContentHandler(Class<?> entityClass, Metadata metadata,
            Logger logger) {
        super();
        this.entityClass = entityClass;
        this.entityType = metadata.getEntityType(entityClass);
        this.metadata = metadata;
        this.logger = logger;
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        if (State.ASSOCIATION == getState()) {
            // Delegates to the inline content handler
            if (association.isToMany()) {
                inlineFeedHandler.characters(ch, start, length);
            } else {
                inlineEntryHandler.characters(ch, start, length);
            }
        } else if (State.PROPERTY == getState() || mapping != null) {
            sb.append(ch, start, length);
        }
    }

    @Override
    public void endContent(Content content) {
        if (State.ASSOCIATION == getState()) {
            // Delegates to the inline content handler
            if (association.isToMany()) {
                inlineFeedHandler.endContent(content);
            } else {
                inlineEntryHandler.endContent(content);
            }
        } else {
            popState();
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        if (State.ASSOCIATION == getState()) {
            // Delegates to the inline content handler
            if (uri.equalsIgnoreCase(Feed.ATOM_NAMESPACE)) {
                if (localName.equals("feed")) {
                    inlineFeedHandler.endFeed(inlineFeed);
                } else if (localName.equals("link")) {
                    if (association.isToMany()) {
                        inlineFeedHandler.endLink(inlineLink);
                    } else {
                        inlineEntryHandler.endLink(inlineLink);
                    }
                } else if (localName.equalsIgnoreCase("entry")) {
                    if (association.isToMany()) {
                        inlineFeedHandler.endEntry(inlineEntry);
                    } else {
                        inlineEntryHandler.endEntry(inlineEntry);
                    }
                } else if (localName.equalsIgnoreCase("content")) {
                    if (association.isToMany()) {
                        inlineFeedHandler.endContent(inlineContent);
                    } else {
                        inlineEntryHandler.endContent(inlineContent);
                    }
                }
            }

            if (association.isToMany()) {
                inlineFeedHandler.endElement(uri, localName, qName);
            } else {
                inlineEntryHandler.endElement(uri, localName, qName);
            }
        } else if (State.PROPERTY == getState()) {
            if (parsePropertyNull) {
                popState();
                parsePropertyNull = false;
            } else {
                Object obj = entity;
                if (propertyPath.size() > 1) {
                    // Complex property.
                    for (int i = 0; i < propertyPath.size() - 1; i++) {
                        try {
                            Object o = ReflectUtils.invokeGetter(obj,
                                    propertyPath.get(i));
                            if (o == null) {
                                // Try to instantiate it
                                Field[] fields = obj.getClass()
                                        .getDeclaredFields();
                                for (Field field : fields) {
                                    if (field.getName().equalsIgnoreCase(
                                            propertyPath.get(i))) {
                                        o = field.getType().newInstance();
                                        break;
                                    }
                                }
                            }
                            ReflectUtils.invokeSetter(obj, propertyPath.get(i),
                                    o);
                            obj = o;
                        } catch (Exception e) {
                            obj = null;
                        }
                    }
                }
                Property property = metadata.getProperty(obj, localName);
                if (property != null) {
                    try {
                        ReflectUtils.setProperty(obj, property, sb.toString());
                    } catch (Exception e) {
                        getLogger().warning(
                                "Cannot set " + localName + " property on "
                                        + obj + " with value " + sb.toString());
                    }
                }
                if (!propertyPath.isEmpty()) {
                    propertyPath.remove(propertyPath.size() - 1);
                }
                if (propertyPath.isEmpty()) {
                    // There is only one state for parsing complex or simple
                    // properties.
                    popState();
                }
            }
        } else if (State.PROPERTIES == getState()) {
            popState();
        } else if (State.CONTENT == getState()) {
            popState();
        } else if (mapping != null) {
            // A mapping has been discovered
            if (sb != null) {
                try {
                    ReflectUtils.invokeSetter(entity,
                            mapping.getPropertyPath(), sb.toString());
                } catch (Exception e) {
                    getLogger().warning(
                            "Cannot set the mapped property "
                                    + mapping.getPropertyPath() + " on "
                                    + entity + " with value " + sb.toString());
                }
            }
            mapping = null;
        } else if (State.ENTRY == getState()) {
            if (!eltPath.isEmpty()) {
                eltPath.remove(eltPath.size() - 1);
            }
        }
    }

    @Override
    public void endEntry(Entry entry) {
        this.states = new ArrayList<State>();

        // Handle Atom mapped values.
        for (Mapping m : metadata.getMappings()) {
            if (entityType != null && entityType.equals(m.getType())
                    && m.getNsUri() == null && m.getNsPrefix() == null) {
                // mapping atom
                Person author = (entry.getAuthors().isEmpty()) ? null : entry
                        .getAuthors().get(0);
                Person contributor = (entry.getContributors().isEmpty()) ? null
                        : entry.getContributors().get(0);
                Object value = null;
                if ("SyndicationAuthorEmail".equals(m.getValuePath())) {
                    value = (author != null) ? author.getEmail() : null;
                } else if ("SyndicationAuthorName".equals(m.getValuePath())) {
                    value = (author != null) ? author.getName() : null;
                } else if ("SyndicationAuthorUri".equals(m.getValuePath())) {
                    value = (author != null) ? author.getUri().toString()
                            : null;
                } else if ("SyndicationContributorEmail".equals(m
                        .getValuePath())) {
                    value = (contributor != null) ? contributor.getEmail()
                            : null;
                } else if ("SyndicationContributorName"
                        .equals(m.getValuePath())) {
                    value = (contributor != null) ? contributor.getName()
                            : null;
                } else if ("SyndicationContributorUri".equals(m.getValuePath())) {
                    value = (contributor != null) ? contributor.getUri()
                            .toString() : null;
                } else if ("SyndicationPublished".equals(m.getValuePath())) {
                    value = entry.getPublished();
                } else if ("SyndicationRights".equals(m.getValuePath())) {
                    value = (entry.getRights() != null) ? entry.getRights()
                            .getContent() : null;
                } else if ("SyndicationSummary".equals(m.getValuePath())) {
                    value = entry.getSummary();
                } else if ("SyndicationTitle".equals(m.getValuePath())) {
                    value = (entry.getTitle() != null) ? entry.getTitle()
                            .getContent() : null;
                } else if ("SyndicationUpdated".equals(m.getValuePath())) {
                    value = entry.getUpdated();
                }

                try {
                    if (value != null) {
                        ReflectUtils.invokeSetter(entity, m.getPropertyPath(),
                                value);
                    }
                } catch (Exception e) {
                    getLogger().warning(
                            "Cannot set " + m.getPropertyPath()
                                    + " property on " + entity + " with value "
                                    + value);
                }
            }
        }

        // If the entity is a blob, get the edit reference
        if (entityType != null && entityType.isBlob()
                && entityType.getBlobValueEditRefProperty() != null) {
            // Look for en entry with a "edit-media" relation value.
            Link link = entry.getLink(Relation.EDIT_MEDIA);
            String pty = entityType.getBlobValueEditRefProperty().getName();
            if (link != null) {
                try {
                    ReflectUtils.invokeSetter(entity, pty, link.getHref());
                } catch (Exception e) {
                    getLogger().warning(
                            "Cannot set the property " + pty + " on " + entity
                                    + " with value " + link.getHref());
                }
            }
        }
    }

    @Override
    public void endLink(Link link) {
        if (State.ASSOCIATION == getState()) {
            String propertyName = ReflectUtils.normalize(link.getTitle());
            if (association.isToMany()) {
                inlineFeedHandler.endLink(link);
                try {
                    ReflectUtils.setProperty(entity, propertyName, association
                            .isToMany(), inlineFeedHandler.getEntities()
                            .iterator(), ReflectUtils.getSimpleClass(entity,
                            propertyName));
                } catch (Exception e) {
                    getLogger().warning(
                            "Cannot set " + propertyName + " property on "
                                    + entity + " from link");
                }
                inlineFeedHandler = null;
            } else {
                inlineEntryHandler.endLink(link);
                try {
                    ReflectUtils.invokeSetter(entity, propertyName,
                            inlineEntryHandler.getEntity());
                } catch (Exception e) {
                    getLogger().warning(
                            "Cannot set " + propertyName + " property on "
                                    + entity + " from link");
                }
                inlineEntryHandler = null;
            }

            // This works if the inline entries does not contain links as
            // well...
            popState();
            association = null;
        }
    }

    public T getEntity() {
        return entity;
    }

    /**
     * Returns the current logger.
     *
     * @return The current logger.
     */
    private Logger getLogger() {
        if (logger == null) {
            logger = Context.getCurrentLogger();
        }
        return logger;
    }

    /**
     * Returns a media type from an Atom type attribute.
     *
     * @param type
     *            The Atom type attribute.
     * @return The media type.
     */
    private MediaType getMediaType(String type) {
        MediaType result = null;

        if (type == null) {
            // No type defined
        } else if (type.equals("text")) {
            result = MediaType.TEXT_PLAIN;
        } else if (type.equals("html")) {
            result = MediaType.TEXT_HTML;
        } else if (type.equals("xhtml")) {
            result = MediaType.APPLICATION_XHTML;
        } else {
            result = new MediaType(type);
        }

        return result;
    }

    /**
     * Returns the state at the top of the heap.
     *
     * @return The state at the top of the heap.
     */
    private State getState() {
        State result = null;
        if (this.states != null) {
            int size = this.states.size();
            if (size > 0) {
                result = this.states.get(size - 1);
            }
        }
        return result;
    }

    /**
     * Returns the state at the top of the heap and removes it from the heap.
     *
     * @return The state at the top of the heap.
     */
    private State popState() {
        State result = null;
        int size = this.states.size();
        if (size > 0) {
            result = this.states.remove(size - 1);
        }

        return result;
    }

    /**
     * Adds a new state at the top of the heap.
     *
     * @param state
     *            The state to add.
     */
    private void pushState(State state) {
        this.states.add(state);
    }

    @Override
    public void startContent(Content content) {
        if (State.ENTRY == getState()) {
            pushState(State.CONTENT);
            if (entityType != null && entityType.isBlob()
                    && entityType.getBlobValueRefProperty() != null) {
                Reference ref = content.getExternalRef();
                if (ref != null) {
                    try {
                        ReflectUtils.invokeSetter(entity, entityType
                                .getBlobValueRefProperty().getName(), ref);
                    } catch (Exception e) {
                        getLogger().warning(
                                "Cannot set "
                                        + entityType.getBlobValueRefProperty()
                                                .getName() + " property on "
                                        + entity + " with value " + ref);
                    }
                }
            }

        }
    }

    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attrs) throws SAXException {
        if (State.ASSOCIATION == getState()) {
            // Delegates to the inline content handler
            if (uri.equalsIgnoreCase(Feed.ATOM_NAMESPACE)) {
                if (localName.equals("feed")) {
                    Feed feed = new Feed();
                    String attr = attrs.getValue("xml:base");
                    if (attr != null) {
                        feed.setBaseReference(new Reference(attr));
                    }
                    inlineFeed = feed;
                    this.inlineFeedHandler.startFeed(feed);
                } else if (localName.equals("link")) {
                    Link link = new Link();
                    link.setHref(new Reference(attrs.getValue("", "href")));
                    link.setRel(Relation.valueOf(attrs.getValue("", "rel")));
                    String type = attrs.getValue("", "type");
                    if (type != null && type.length() > 0) {
                        link.setType(new MediaType(type));
                    }

                    link.setHrefLang(new Language(attrs
                            .getValue("", "hreflang")));
                    link.setTitle(attrs.getValue("", "title"));
                    String attr = attrs.getValue("", "length");
                    link.setLength((attr == null) ? -1L : Long.parseLong(attr));

                    inlineLink = link;
                    if (association.isToMany()) {
                        inlineFeedHandler.startLink(link);
                    } else {
                        inlineEntryHandler.startLink(link);
                    }
                } else if (localName.equalsIgnoreCase("entry")) {
                    Entry entry = new Entry();
                    if (association.isToMany()) {
                        inlineFeedHandler.startEntry(entry);
                    } else {
                        inlineEntryHandler.startEntry(entry);
                    }
                    inlineEntry = entry;
                } else if (localName.equalsIgnoreCase("content")) {
                    Content content = new Content();
                    MediaType type = getMediaType(attrs.getValue("", "type"));
                    String srcAttr = attrs.getValue("", "src");
                    if (srcAttr != null)
                        // Content available externally
                        content.setExternalRef(new Reference(srcAttr));
                    content.setExternalType(type);
                    if (association.isToMany()) {
                        inlineFeedHandler.startContent(content);
                    } else {
                        inlineEntryHandler.startContent(content);
                    }
                    inlineContent = content;
                }
            }

            if (association.isToMany()) {
                inlineFeedHandler.startElement(uri, localName, qName, attrs);
            } else {
                inlineEntryHandler.startElement(uri, localName, qName, attrs);
            }
        } else if (Service.WCF_DATASERVICES_METADATA_NAMESPACE.equals(uri)
                && "properties".equals(localName)) {
            pushState(State.PROPERTIES);
            propertyPath = new ArrayList<String>();
        } else if (State.PROPERTIES == getState()) {
            pushState(State.PROPERTY);

            if (Boolean.parseBoolean(attrs.getValue(
                    Service.WCF_DATASERVICES_METADATA_NAMESPACE, "null"))) {
                parsePropertyNull = true;
            } else {
                sb = new StringBuilder();
                propertyPath.add(localName);
            }
        } else if (State.PROPERTY == getState()) {
            sb = new StringBuilder();
            propertyPath.add(localName);
        } else if (State.ENTRY == getState()) {
            if (localName.equalsIgnoreCase("link") && association != null) {
                pushState(State.ASSOCIATION);
            } else {
                // Could be mapped value
                eltPath.add(localName);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < eltPath.size(); i++) {
                    if (i > 0) {
                        sb.append("/");
                    }
                    sb.append(eltPath.get(i));
                }
                String str = sb.toString();

                // Check if this path is mapped.
                for (Mapping m : metadata.getMappings()) {
                    if (entityType != null && entityType.equals(m.getType())
                            && m.getNsUri() != null && m.getNsUri().equals(uri)
                            && str.equals(m.getValueNodePath())) {
                        if (m.isAttributeValue()) {
                            String value = attrs.getValue(m
                                    .getValueAttributeName());
                            if (value != null) {
                                try {
                                    ReflectUtils.invokeSetter(entity, m
                                            .getPropertyPath(), value);
                                } catch (Exception e) {
                                    getLogger().warning(
                                            "Cannot set " + m.getPropertyPath()
                                                    + " property on " + entity
                                                    + " with value " + value);
                                }
                            }
                        } else {
                            this.sb = new StringBuilder();
                            mapping = m;
                        }
                        break;
                    }
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void startEntry(Entry entry) {
        this.states = new ArrayList<State>();
        pushState(State.ENTRY);
        eltPath = new ArrayList<String>();
        // Instantiate the entity
        try {
            entity = (T) entityClass.newInstance();
        } catch (Exception e) {
            getLogger().warning(
                    "Error when instantiating  class " + entityClass);
        }
    }

    @Override
    public void startLink(Link link) {
        if (State.ASSOCIATION == getState()) {
            // Delegates to the inline content handler
            if (association.isToMany()) {
                inlineFeedHandler.startLink(link);
            } else {
                inlineEntryHandler.startLink(link);
            }
        } else {
            if (link.getTitle() != null && entityType != null) {
                String propertyName = ReflectUtils.normalize(link.getTitle());
                // Get the associated entity
                association = metadata.getAssociation(entityType, propertyName);
                if (association != null) {
                    if (association.isToMany()) {
                        inlineFeedHandler = new FeedContentHandler<T>(
                                ReflectUtils.getSimpleClass(entity,
                                        propertyName), metadata, getLogger());
                    } else {
                        inlineEntryHandler = new EntryContentHandler<T>(
                                ReflectUtils.getSimpleClass(entity,
                                        propertyName), metadata, getLogger());
                    }
                }
            }
        }
    }
}
TOP

Related Classes of org.restlet.ext.odata.internal.EntryContentHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.