/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2003-2006 RSSOwl Development Team                                  **
 **   http://www.rssowl.org/                                                 **
 **                                                                          **
 **   All rights reserved                                                    **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the Eclipse Public License 1.0 which accompanies this     **
 **   distribution, and is available at:                                     **
 **   http://www.rssowl.org/legal/epl-v10.html                               **
 **                                                                          **
 **   A copy is found in the file epl-v10.html and important notices to the  **
 **   license from the team is found in the textfile LICENSE.txt distributed **
 **   in this package.                                                       **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Contributors:                                                          **
 **     RSSOwl - initial API and implementation (bpasero@rssowl.org)         **
 **                                                                          **
 **  **********************************************************************  */

package net.sourceforge.rssowl.dao.feedparser;

import net.sourceforge.rssowl.dao.NewsfeedFactoryException;
import net.sourceforge.rssowl.model.Channel;

import org.jdom.Document;
import org.jdom.Namespace;

/**
 * Class to Parse a RSS XML. Supported RSS Versions are 0.91, 0.92, 1.0 (RDF)
 * and 2.0. Also supporting Atom 0.3 and OPML 1.0 feeds.
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class FeedParser extends AbstractFeedParser {

  /** Newsfeed format: Atom 0.3 */
  public static final int FEED_FORMAT_ATOM_0_3 = 4;

  /** Newsfeed format: Atom 1.0 */
  public static final int FEED_FORMAT_ATOM_1_0 = 6;

  /** Newsfeed format: OPML 1.0 */
  public static final int FEED_FORMAT_OPML_1_0 = 5;

  /** Newsfeed format: RDF 1.0 */
  public static final int FEED_FORMAT_RDF_1_0 = 2;

  /** Newsfeed format: RSS 0.91 */
  public static final int FEED_FORMAT_RSS_0_91 = 0;

  /** Newsfeed format: RSS 0.92 */
  public static final int FEED_FORMAT_RSS_0_92 = 1;

  /** Newsfeed format: RSS 2.0 */
  public static final int FEED_FORMAT_RSS_2_0 = 3;

  /** Namespace used by Atom 1.0 */
  private static final String ATOM_1_0_NAMESPACE = "http://www.w3.org/2005/Atom";

  /**
   * Instantiate a new FeedParser
   * 
   * @param document Document holding the rss xml
   * @param url The url of the RSS XML
   * @throws NewsfeedFactoryException If the document is not a valid Newsfeed
   */
  public FeedParser(Document document, String url) throws NewsfeedFactoryException {
    super(document, new Channel(), url);
  }

  /**
   * Get the parsed Channel
   * 
   * @return Channel The parsed Channel from the document
   */
  public Channel getChannel() {
    return rssChannel;
  }

  /**
   * Try to determine the newsfeed format from the document.
   * 
   * @return int One of the supported newsfeed formats
   */
  public int getFeedFormat() {

    /** XML is a RDF */
    if (root.getName().equalsIgnoreCase("rdf")) {
      rssChannel.setFormat("RDF");
      return FEED_FORMAT_RDF_1_0;
    }

    /** XML is a Atom feed */
    else if (root.getName().equalsIgnoreCase("feed")) {

      /** This could be Atom 1.0 */
      if (root.getNamespace() != null && ATOM_1_0_NAMESPACE.equals(root.getNamespace().getURI())) {
        rssChannel.setFormat("Atom 1.0");
        return FEED_FORMAT_ATOM_1_0;
      }

      /** This is Atom 0.3 */
      rssChannel.setFormat("Atom 0.3");
      return FEED_FORMAT_ATOM_0_3;
    }

    /** XML is a OPML feed */
    else if (root.getName().equalsIgnoreCase("opml")) {
      rssChannel.setFormat("OPML");
      return FEED_FORMAT_OPML_1_0;
    }

    /** XML is a RSS */
    else {
      String version = getAttributeValue(root, "version");

      /** Parse in dependance of the RSS version */
      if (version != null) {
        rssChannel.setFormat("RSS " + version);

        /** Parse RSS Version 0.91 */
        if (version.indexOf("0.91") >= 0)
          return FEED_FORMAT_RSS_0_91;

        /** Parse RSS Version 0.92 */
        else if (version.indexOf("0.92") >= 0)
          return FEED_FORMAT_RSS_0_92;

        /** Parse RDF Version 1.0 */
        else if (version.indexOf("1.0") >= 0)
          return FEED_FORMAT_RDF_1_0;

        /** Parse RSS Version 2.0 */
        else if (version.indexOf("2.0") >= 0)
          return FEED_FORMAT_RSS_2_0;
      }
    }

    /** Per default use RSS 0.91 */
    rssChannel.setFormat("RSS 0.91");
    return FEED_FORMAT_RSS_0_91;
  }

  /**
   * @see net.sourceforge.rssowl.dao.feedparser.AbstractFeedParser#parse()
   */
  public void parse() throws NewsfeedFactoryException {

    /** Namespaces */
    Namespace nameSpaces[] = new Namespace[] { content, dc, defNs, rdf, sy };

    /** First check if format is supported */
    if (!checkFeedFormat())
      throw new NewsfeedFactoryException(url, null, null, NewsfeedFactoryException.ERROR_INVALID_NEWSFEED);

    /** Get the feed format */
    int feedFormat = getFeedFormat();

    /** Call a suitable parser for the format */
    switch (feedFormat) {

      /** Newsfeed format is: RSS 0.91 */
      case FEED_FORMAT_RSS_0_91:
        new RSS_0_91_Parser(document, rssChannel, url, nameSpaces).parse();
        break;

      /** Newsfeed format is: RSS 0.92 */
      case FEED_FORMAT_RSS_0_92:
        new RSS_0_92_Parser(document, rssChannel, url, nameSpaces).parse();
        break;

      /** Newsfeed format is: RDF 1.0 */
      case FEED_FORMAT_RDF_1_0:
        new RDF_1_0_Parser(document, rssChannel, url, nameSpaces).parse();
        break;

      /** Newsfeed format is: RSS 2.0 */
      case FEED_FORMAT_RSS_2_0:
        new RSS_2_0_Parser(document, rssChannel, url, nameSpaces).parse();
        break;

      /** Newsfeed format is: Atom Syndication Format 0.3 */
      case FEED_FORMAT_ATOM_0_3:
        new Atom_0_3_Parser(document, rssChannel, url, nameSpaces).parse();
        break;

      /** Newsfeed format is: Atom Syndication Format 1.0 */
      case FEED_FORMAT_ATOM_1_0:
        new Atom_1_0_Parser(document, rssChannel, url, nameSpaces).parse();
        break;

      /** Newsfeed format is: Outline Processor Language 1.0 */
      case FEED_FORMAT_OPML_1_0:
        new OPML_1_0_Parser(document, rssChannel, url, nameSpaces).parse();
        break;
    }
  }

  /**
   * Check if the given document matches RSS, RDF, Atom or OPML format
   * 
   * @return boolean TRUE if the document has a valid feed format
   */
  private boolean checkFeedFormat() {

    /** Document has RDF format set */
    if (document.getRootElement().getName().equalsIgnoreCase("rdf"))
      return true;

    /** Document has RSS format set */
    if (document.getRootElement().getName().equalsIgnoreCase("rss"))
      return true;

    /** Document has Atom format set */
    if (document.getRootElement().getName().equalsIgnoreCase("feed"))
      return true;

    /** Document has OPML format set */
    if (document.getRootElement().getName().equalsIgnoreCase("opml"))
      return true;

    /** Document has not set a supported format */
    return false;
  }
}