Package com.google.gdata.model.atom

Source Code of com.google.gdata.model.atom.Feed$FeedState

/* Copyright (c) 2008 Google Inc.
*
* Licensed 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 com.google.gdata.model.atom;

import com.google.gdata.client.CoreErrorDomain;
import com.google.gdata.client.Query;
import com.google.gdata.client.Service;
import com.google.gdata.data.IFeed;
import com.google.gdata.model.AttributeKey;
import com.google.gdata.model.Element;
import com.google.gdata.model.ElementCreator;
import com.google.gdata.model.ElementKey;
import com.google.gdata.model.ElementMetadata;
import com.google.gdata.model.MetadataRegistry;
import com.google.gdata.model.QName;
import com.google.gdata.model.ValidationContext;
import com.google.gdata.model.batch.BatchOperation;
import com.google.gdata.util.Namespaces;
import com.google.gdata.util.ServiceException;
import com.google.gdata.wireformats.ContentCreationException;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* The Feed class is a base class that represents a generic GData feed object,
* based primarily on the data model for an {@code <atom:feed>} element.
* It is extended to represent OpenSearch RSS channel elements and other gdata
* standard elements.
*
* <p>The Feed Class contains all the necessary parsing and generation code for
* feed data, but can be subclassed to create subtypes that contain convenience
* APIs for accessing additional elements and entries.
*
* <p>An instance can be initialized by directly initializing its component
* elements.
*
* <p>Here is the Relax-NG schema that represents an Atom 1.0 feed:
*
* <pre>
* AtomFeed =
*  element atom:feed {
*    atomCommonAttributes,
*    (atomAuthor*
*     atomCategory*
*     atomContributor*
*     atomGenerator?
*     atomIcon?
*     atomId
*     atomLink*
*     atomLogo?
*     atomRights?
*     atomSubtitle?
*     atomTitle
*     atomUpdated
*     extensionElement*),
*     atomEntry*
*   }
* </pre>
*
* <p>Because the Feed schema differs from the Source schema only by the
* presence of the entries, the Feed class derives its base property model
* from the {@link Source} class.
*/
public class Feed extends Source implements IFeed {

  /**
   * The key for this element.
   */
  @SuppressWarnings("hiding")
  public static final ElementKey<Void, Feed> KEY = ElementKey.of(
      new QName(Namespaces.atomNs, "feed"), Feed.class);

  /**
   * The gd:etag attribute.
   *
   * See RFC 2616, Section 3.11.
   */
  public static final AttributeKey<String> ETAG = AttributeKey.of(
      new QName(Namespaces.gNs, "etag"));

  /**
   * The gd:kind attribute.
   */
  public static final AttributeKey<String> GD_KIND = AttributeKey.of(
      new QName(Namespaces.gNs, "kind"));

  /**
   * The xml:base attribute.
   */
  public static final AttributeKey<URI> XML_BASE = AttributeKey.of(
      new QName(Namespaces.xmlNs, "base"), URI.class);
 
  /**
   * The opensearch:itemsPerPage element.
   */
  public static final ElementKey<Integer, Element> ITEMS_PER_PAGE =
      ElementKey.of(new QName(Namespaces.openSearch1_1Ns, "itemsPerPage"),
          Integer.class, Element.class);

  /**
   * The opensearch:startIndex element.
   */
    public static final ElementKey<Integer, Element> START_INDEX =
      ElementKey.of(new QName(Namespaces.openSearch1_1Ns, "startIndex"),
          Integer.class, Element.class);

  /**
   * The opensearch:totalResults element.
   */
    public static final ElementKey<Integer, Element> TOTAL_RESULTS =
      ElementKey.of(new QName(Namespaces.openSearch1_1Ns, "totalResults"),
          Integer.class, Element.class);

  /**
   * Registers the metadata for this element.
   */
  public static void registerMetadata(MetadataRegistry registry) {
    if (registry.isRegistered(KEY)) {
      return;
    }

    // Register superclass metadata.
    Source.registerMetadata(registry);

    // The builder for this element
    ElementCreator builder = registry.build(KEY);

    // Local properties
    builder.addAttribute(ETAG);
    builder.addAttribute(GD_KIND);
    builder.addAttribute(XML_BASE);
    builder.addElement(TOTAL_RESULTS);
    builder.addElement(START_INDEX);
    builder.addElement(ITEMS_PER_PAGE);
    builder.addElement(BatchOperation.KEY);
    builder.addUndeclaredElementMarker();
    builder.addElement(Entry.KEY);
  }

  /**
   * The FeedState class provides a simple structure that encapsulates the
   * attributes of an Atom feed that should be shared with a shallow copy if the
   * feed is adapted to a more specific Feed subtypes.
   *
   * <p><b>Note: Feed entries are not part of feed shared state, because the
   * entry lists will need to be typed differently for adapted instances.</b>
   * This means that entries that are created, updated, or deleted in an adapted
   * feed will not be reflected in the base feed used to construct it. The
   * reverse is also true: changes made to a base feed will not be reflected in
   * any adapted instances of the feed.
   */
  protected static class FeedState {

    /** Service associated with the feed. */
    public Service service;

    /** Specifies whether the feed can be posted to. */
    public boolean canPost = true;

    /**
     * Version ID. This is a unique number representing this particular
     * entry. Every update changes the version ID (unless the update
     * doesn't modify anything, in which case it's permissible for
     * version ID to stay the same). Services are free to interpret this
     * string in the most convenient way. Some services may choose to use
     * a monotonically increasing sequence of version IDs. Other services
     * may compute a hash of entry properties and use that.
     *
     * <p>This property is only used for services to communicate the current
     * version ID back to the servlet. It is NOT set when entries are
     * parsed (either from requests or from arbitrary XML).
     */
    public String versionId;
  }

  /**
   * Basic state for this feed. May be shared across multiple adapted instances
   * associated with the same logical feed.
   */
  protected final FeedState feedState;

  /**
   * Constructs a new Feed instance, using default metadata.
   */
  public Feed() {
    this(KEY);
  }

  /**
   * Creates a new feed instance using the specified metadata.
   *
   * @param key the feed key.
   */
  protected Feed(ElementKey<?, ? extends Feed> key) {
    super(key);
    feedState = new FeedState();
  }

  /**
   * Copy constructor that initializes a new Feed instance to have identical
   * contents to another instance, using a shared reference to the same
   * {@link FeedState}. Subclasses of {@code Feed} can use this constructor to
   * create adaptor instances of a feed that share state with the original but
   * use a different set of metadata.
   */
  protected Feed(ElementKey<?, ? extends Feed> key, Feed source) {
    super(key, source);
    feedState = source.feedState;
  }

  /**
   * Returns that GData {@link Service} instance tassociated with this feed.
   */
  public Service getService() {
    return feedState.service;
  }

  /**
   * Sets that GData {@link Service} instance associated with this feed.
   */
  public void setService(Service v) {
    feedState.service = v;

    // Propagate service information to nested entries
    for (Entry entry : getEntries()) {
      entry.setService(v);
    }
  }

  /**
   * Gets the property that indicates if it is possible to post new entries to
   * the feed.
   */
  public boolean getCanPost() {
    return feedState.canPost;
  }

  /**
   * Sets the property that indicates if it is possible to post new entries to
   * the feed.
   */
  public void setCanPost(boolean v) {
    feedState.canPost = v;
  }

  /**
   * Returns the resource version id for this feed. This will be
   * used to generate an etag value on output. This is never set
   * when the Feed has been parsed.
   */
  public String getVersionId() {
    return feedState.versionId;
  }

  /**
   * Set the resource version id for this feed. This will be
   * used to generate an etag value on output. If {@code null},
   * the updated time will be used instead to generate an etag.
   */
  public void setVersionId(String v) {
    feedState.versionId = v;
  }

  /**
   * Returns the current entity tag value for this feed. A value of {@code null}
   * indicates the value is unknown.
   */
  public String getEtag() {
    return getAttributeValue(ETAG);
  }

  /**
   * Sets the current entity tag value (for this feed. A value of {@code null}
   * indicates the value is unknown.
   */
  public void setEtag(String v) {
    setAttributeValue(ETAG, v);
  }

  /**
   * Returns the current gd:kind attribute for this feed.  The kind attribute
   * may be null if this feed does not have a kind.
   */
  public String getKind() {
    return getAttributeValue(GD_KIND);
  }

  /**
   * Sets current gd:kind attribute for this feed.  The kind may be set to null
   * to remove the attribute value.
   */
  public void setKind(String v) {
    setAttributeValue(GD_KIND, v);
  }

  /**
   * Returns the current xml:base attribute for this feed.  The base attribute
   * may be {@code null} if this feed does not have an xml:base.
   */
  public URI getXmlBase() {
    return getAttributeValue(XML_BASE);
  }
 
  /**
   * Sets the current xml:base attribute for this feed.  The base may be set to
   * {@code null} to remove the attribute value.
   */
  public void setXmlBase(URI v) {
    setAttributeValue(XML_BASE, v);
  }
 
  /**
   * Gets the total number of results associated with this feed. The value may
   * be larger than the number of contained entries for paged feeds. A value of
   * {@link Query#UNDEFINED} indicates the total size is undefined.
   */
  public int getTotalResults() {
    Integer v = getElementValue(TOTAL_RESULTS);
    if (v == null) {
      return Query.UNDEFINED;
    }
    return v;
  }

  /**
   * Sets the total number of results associated with this feed. The value may
   * be larger than the number of contained entries for paged feeds. A value of
   * {@link Query#UNDEFINED} indicates the total size is undefined.
   */
  public void setTotalResults(int v) {
    setElement(TOTAL_RESULTS, new Element(TOTAL_RESULTS).setTextValue(v));
  }

  /**
   * Gets the starting index of the contained entries for paged feeds. A value
   * of {@link Query#UNDEFINED} indicates the start index is undefined.
   */
  public int getStartIndex() {
    Integer v = getElementValue(START_INDEX);
    if (v == null) {
      return Query.UNDEFINED;
    }
    return v;
  }

  /**
   * Sets the starting index of the contained entries for paged feeds. A value
   * of {@link Query#UNDEFINED} indicates the start index is undefined.
   */
  public void setStartIndex(int v) {
    setElement(START_INDEX, new Element(START_INDEX).setTextValue(v));
  }

  /**
   * Gets the number of items that will be returned per page for paged feeds. A
   * value of {@link Query#UNDEFINED} indicates the page item count is
   * undefined.
   */
  public int getItemsPerPage() {
    Integer v = getElementValue(ITEMS_PER_PAGE);
    if (v == null) {
      return Query.UNDEFINED;
    }
    return v;
  }

  /**
   * Sets the number of items that will be returned per page for paged feeds. A
   * value of {@link Query#UNDEFINED} indicates the page item count is
   * undefined.
   */
  public void setItemsPerPage(int v) {
    setElement(ITEMS_PER_PAGE, new Element(ITEMS_PER_PAGE).setTextValue(v));
  }

  /** Returns the list of entries in this feed */
  public List<? extends Entry> getEntries() {
    return getElements(Entry.KEY);
  }

  /** Returns a list of entries matching the given entry key. */
  protected <T extends Entry> List<T> getEntries(ElementKey<?, T> key) {
    return getElements(key);
  }

  /** Sets the entries in this feed to the given entries. */
  public void setEntries(Collection<? extends Entry> entries) {
    clearEntries();
    for (Entry entry : entries) {
      addElement(Entry.KEY, entry);
    }
  }

  /** Clears the list of entries on this feed. */
  public void clearEntries() {
    removeElement(Entry.KEY);
  }

  /** Adds an entry to this feed. */
  public void addEntry(Entry entry) {
    addElement(entry);
  }

  /** Removes a single entry from this feed. */
  public boolean removeEntry(Entry entry) {
    return removeElement(Entry.KEY, entry);
  }

  /**
   * Creates a new entry for the feed.
   */
  public Entry createEntry() {
    return createEntry(Entry.KEY);
  }

  /**
   * Creates a new entry for the feed.
   */
  public <E extends Entry> E createEntry(ElementKey<?, E> entryKey) {
    E entry;
    try {
      entry = Element.createElement(entryKey);
    } catch (ContentCreationException cce) {
      throw new IllegalStateException(cce);
    }

    // Propagate the associated service (if any)
    if (feedState.service != null) {
      entry.setService(feedState.service);
    }

    return entry;
  }

  /** Returns the entry post link for the feed. */
  public Link getEntryPostLink() {
    Link postLink = getLink(Link.Rel.ENTRY_POST, Link.Type.ATOM);
    return postLink;
  }

  /** Returns the self link for the feed. */
  public Link getSelfLink() {
    Link postLink = getLink(Link.Rel.SELF, Link.Type.ATOM);
    return postLink;
  }

  /**
   * Returns the link that provides the URI of next page in a paged feed.
   *
   * @return Link that provides the URI of next page in a paged feed or {@code
   *         null} for none.
   */
  public Link getNextLink() {
    return getLink(Link.Rel.NEXT, Link.Type.ATOM);
  }

  /**
   * Returns the link that provides the URI of previous page in a paged feed.
   *
   * @return Link that provides the URI of previous page in a paged feed or
   *         {@code null} for none.
   */
  public Link getPreviousLink() {
    return getLink(Link.Rel.PREVIOUS, Link.Type.ATOM);
  }

  /**
   * Returns the link that provides the URI that can be used to batch operations
   * to query, insert, update and delete entries on this feed.
   *
   * @return Link that provides the URI that can be used to batch operations to
   *         query, insert, update and delete entries on this feed or
   *         {@code null} for none.
   */
  public Link getFeedBatchLink() {
    return getLink(Link.Rel.FEED_BATCH, Link.Type.ATOM);
  }

  /**
   * Returns the current representation of the feed by requesting it from the
   * associated service using the feed's self link.
   *
   * @return the current state of the feed.
   */
  public Feed getSelf() throws IOException, ServiceException {
    if (feedState.service == null) {
      throw new ServiceException(
          CoreErrorDomain.ERR.feedNotAssociated);
    }
    Link selfLink = getSelfLink();
    if (selfLink == null) {
      throw new UnsupportedOperationException("Feed cannot be retrieved");
    }
    URL feedUrl = new URL(selfLink.getHref());
    throw new UnsupportedOperationException("Not supported yet");
    // try {
    // // Use Etag if available to conditionalize the retrieval, otherwise use
    // // the updated value.
    // String etag = getEtag();
    // if (etag != null) {
    // return (F) feedState.service.getFeed(feedUrl, this.getClass(), etag);
    // } else {
    // return (F) feedState.service.getFeed(feedUrl, this.getClass(),
    // getUpdated());
    // }
    // } catch (NotModifiedException e) {
    // return (F) this;
    // }
  }

  /**
   * Removes all links.
   */
  public void removeLinks() {
    removeElement(Link.KEY);
  }

  /**
   * Inserts a new Entry into the feed, if the feed is currently associated with
   * a Service.
   *
   * @return the inserted Entry returned by the Service.
   *
   * @throws ServiceException If there is no associated GData service or the
   *         service is unable to perform the insertion.
   *
   * @throws UnsupportedOperationException If insert is not supported for the
   *         target feed.
   *
   * @throws IOException If there is an error communicating with the GData
   *         service.
   */
  public <T extends Entry> T insert(T newEntry) throws ServiceException,
      IOException {
    if (feedState.service == null) {
      throw new ServiceException(
          CoreErrorDomain.ERR.entryNotAssociated);
    }
    Link postLink = getEntryPostLink();
    if (postLink == null) {
      throw new UnsupportedOperationException("Media cannot be inserted");
    }
    URL postUrl = new URL(postLink.getHref());
    throw new UnsupportedOperationException("Not supported yet");
    // return feedState.service.insert(postUrl, newEntry);
  }

  /**
   * Narrows this feed using categories with an appropriate kind value.
   * This will loop through the categories, checking if they represent kinds,
   * and adapting the feed to that kind if an appropriate adaptation was
   * found. This will return the most specific subtype of the narrowed type.
   */
  @Override
  protected Element narrow(ElementMetadata<?,?> meta, ValidationContext vc) {
    Element narrowed = this;
    for (Category category : getCategories()) {
      if (Namespaces.gKind.equals(category.getScheme())) {
        narrowed = adapt(narrowed, meta, category.getTerm());
      }
    }

    if (narrowed == this) {
      narrowed = super.narrow(meta, vc);
    }
    return narrowed;
  }

  @Override
  public Element resolve(ElementMetadata<?, ?> metadata, ValidationContext vc) {

    // Fix "setCanPost" based on the existence of an entry post link.
    feedState.canPost = getEntryPostLink() != null;

    // Continue parent resolution.
    return super.resolve(metadata, vc);
  }

  /**
   * Gets a list of entries of a particular kind.  Will only return entries
   * that match the expected return class.
   */
  public <T extends Entry> List<T> getEntries(Class<T> returnClass) {
    List<T> result = new ArrayList<T>();

    for (Entry entry : getEntries()) {
      if (returnClass.isInstance(entry)) {
        result.add(returnClass.cast(entry));
      }
    }

    return result;
  }
}
TOP

Related Classes of com.google.gdata.model.atom.Feed$FeedState

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.