/*   **********************************************************************  **
 **   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.model;

import net.sourceforge.rssowl.controller.GUI;
import net.sourceforge.rssowl.controller.thread.FeedAutoUpdater;
import net.sourceforge.rssowl.util.shop.StringShop;

import org.eclipse.swt.widgets.TreeItem;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Vector;

/**
 * The URL Tree builds itself from the Root-Object of this class. The root
 * object holds categorys with its favorites. Each category may hold other
 * sub-categorys and favorites. Some static methods provide access to the url /
 * title of the favorites.
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class Category {

  /** Vector holding all blogrolls */
  private static Vector blogrolls = new Vector();

  /** Vector holding all favorite-urls */
  private static Vector favLinks = new Vector();

  /** Hashtable holding all favorites */
  private static Hashtable favPool = new Hashtable();

  /** Vector holding all favorite-titles */
  private static Vector favTitles = new Vector();

  /** Hashtable holding all favorites mapping Link => Title */
  private static Hashtable mapLinkToTitle = new Hashtable();

  /** Hashtable holding all favorites mapping Title => Link */
  private static Hashtable mapTitleToLink = new Hashtable();

  /** The root category of all categories in RSSOwl */
  private static Category rootCategory = new Category("root", null, false);

  /** Hashtable holding the favorites of this category */
  private Hashtable favorites;

  /** TRUE if this category is a blogroll */
  private boolean isBlogroll;

  /** TRUE if this category is expanded in the favorite tree */
  private boolean isExpanded;

  /** TRUE if this category is the root element of the favorite tree */
  private boolean isRoot;

  /** Flag indicating an unsynchronized blogroll */
  private boolean isUnSynchronized;

  /** Name of the Category */
  private String name;

  /** Parent category of this category */
  private Category parent;

  /** Path to the blogroll XML */
  private String pathToBlogroll;

  /** TreeSet holding the sorted titles of the favorites in this category */
  private TreeSet sortedLinkTitles;

  /** TreeSet holding the sorted titles of the subcategories in this category */
  private TreeSet sortedSubCatTitles;

  /** Hashtable holding the subcategories of this category */
  private Hashtable subCategories;

  /** TreeItem this Category is shown */
  private TreeItem treeItem;

  /** TRUE if a proxy should be used */
  private boolean useProxy;

  /**
   * Instantiate a new Category
   * 
   * @param name Name of the category
   * @param parent Parent category of this subcategory
   * @param isBlogroll TRUE if category is from a blogroll
   */
  public Category(String name, Category parent, boolean isBlogroll) {
    this.name = name;
    this.parent = parent;
    this.isBlogroll = isBlogroll;
    favorites = new Hashtable();
    subCategories = new Hashtable();
    sortedLinkTitles = new TreeSet(Collator.getInstance());
    sortedSubCatTitles = new TreeSet(Collator.getInstance());
    isExpanded = false;
    isUnSynchronized = false;

    /** This maybe the root category if parent is NULL */
    isRoot = (parent == null);
  }

  /**
   * Check if the blogroll already exists
   * 
   * @param url URL to the blogroll
   * @return TRUE if blogroll exists
   */
  public static boolean blogrollExists(String url) {
    return (url != null && blogrolls.contains(url));
  }

  /**
   * Create a new root category
   */
  public static void createRootCategory() {
    rootCategory = new Category("root", null, false);
  }

  /**
   * Create a unique link
   * 
   * @param link The link to make unique
   * @return The unique link
   */
  public static String createUniqueLink(String link) {

    /** Create a unique link */
    int count = 1;
    String tempLink = link;
    while (Category.linkExists(link))
      link = tempLink + '#' + (count++);
    return link;
  }

  /**
   * Create a unique title
   * 
   * @param title The title to make unique
   * @return The unique title
   */
  public static String createUniqueTitle(String title) {

    /** Create a unique title */
    int count = 1;
    String tempTitle = title;
    while (Category.titleExists(title))
      title = tempTitle + " #" + (count++);
    return title;
  }

  /**
   * Get the category from the given path or the Root Category if not existing.
   * 
   * @param path Path to the category (e.g. "Test > Sub1 > Sub2")
   * @return Category The matching Category or the Root Category if not
   * existing.
   */
  public static Category fromPath(String path) {
    Category category = Category.getRootCategory();

    /** The Path must be given */
    if (StringShop.isset(path)) {
      String[] categories = path.split(StringShop.CAT_TOKENIZER);

      /** Foreach category in the path */
      for (int a = 0; a < categories.length && category != null; a++)
        category = (Category) category.getSubCategories().get(categories[a]);
    }

    return category != null ? category : Category.getRootCategory();
  }

  /**
   * @return Vector holding the URLs of all blogrolls
   */
  public static Vector getBlogrolls() {
    return blogrolls;
  }

  /**
   * Get the Hashtable that stores all favorites
   * 
   * @return Hashtable All favorites
   */
  public static Hashtable getFavPool() {
    return favPool;
  }

  /**
   * Return the Link for the given Title
   * 
   * @param title Title of the favorit
   * @return String Link of the favorit
   */
  public static String getLinkForTitle(String title) {
    return (String) mapTitleToLink.get(title);
  }

  /**
   * Get the root category of all categories used in RSSOwl to store favorites
   * or other cats.
   * 
   * @return Category The root category
   */
  public static Category getRootCategory() {
    return rootCategory;
  }

  /**
   * Return the Title of the given Link
   * 
   * @param link Link of the favorit
   * @return String Title of the favorit
   */
  public static String getTitleForLink(String link) {
    return (String) mapLinkToTitle.get(link);
  }

  /**
   * Check if the link already exists
   * 
   * @param link URL of the link
   * @return TRUE if link exists
   */
  public static boolean linkExists(String link) {
    return mapLinkToTitle.containsKey(link);
  }

  /**
   * Check if the title already exists
   * 
   * @param title Title of the link
   * @return TRUE if Title exists
   */
  public static boolean titleExists(String title) {
    return mapTitleToLink.containsKey(title);
  }

  /**
   * Add a subcategory to this category
   * 
   * @param rssOwlCategory The subcategory
   */
  public void addCategory(Category rssOwlCategory) {
    addCategory(rssOwlCategory, false);
  }

  /**
   * Add a subcategory to this category
   * 
   * @param rssOwlCategory The subcategory
   * @param makeUniqueTitle TRUE if the new category has to get a unique title
   * to avoid duplicates in the subcategories of this category
   */
  public void addCategory(Category rssOwlCategory, boolean makeUniqueTitle) {

    /**
     * Check if there is already a subcategory existing with that categories
     * name
     */
    if (makeUniqueTitle && getSubCategories().containsKey(rssOwlCategory.getName())) {
      String title = rssOwlCategory.getName();
      String oldTitle = title;
      int number = 1;

      /** Create unique name for the category */
      while (getSubCategories().containsKey(title)) {
        title = oldTitle;
        title = title + " #" + number;
        number++;
      }
      rssOwlCategory.setName(title);
    }

    /** Add to category */
    subCategories.put(rssOwlCategory.getName(), rssOwlCategory);
    sortedSubCatTitles.add(rssOwlCategory.getName());
  }

  /**
   * Add a favorit to the category
   * 
   * @param rssOwlFavorite A RSSOwl favorite
   */
  public void addFavorite(Favorite rssOwlFavorite) {

    /** Add to favorites */
    favorites.put(rssOwlFavorite.getUrl(), rssOwlFavorite);
    sortedLinkTitles.add(rssOwlFavorite.getTitle());

    /** Update map tables */
    mapTitleToLink.put(rssOwlFavorite.getTitle(), rssOwlFavorite.getUrl());
    mapLinkToTitle.put(rssOwlFavorite.getUrl(), rssOwlFavorite.getTitle());

    /** Update fav pool */
    favPool.put(rssOwlFavorite.getUrl(), rssOwlFavorite);
    favLinks.add(rssOwlFavorite.getUrl());
    favTitles.add(rssOwlFavorite.getTitle());

    /** Register to update manager if needed */
    if (rssOwlFavorite.getUpdateInterval() > 0)
      FeedAutoUpdater.updateFeedList(rssOwlFavorite);
  }

  /**
   * Check recursivly what state the use proxy setting has
   * 
   * @param rssOwlCategory Current Category
   */
  public void checkUseProxy(Category rssOwlCategory) {

    /** Return if root categorys is reached */
    if (rssOwlCategory.isRoot)
      return;

    rssOwlCategory.useProxy = true;

    /** Check use proxy status of sub-favorites */
    Enumeration favorites = rssOwlCategory.getFavorites().elements();
    while (favorites.hasMoreElements()) {
      Favorite rssOwlFavorite = (Favorite) favorites.nextElement();

      if (!rssOwlFavorite.isUseProxy()) {
        rssOwlCategory.useProxy = false;
        break;
      }
    }

    /** Check use proxy status of sub-categorys */
    if (rssOwlCategory.useProxy) {
      Enumeration subcategories = rssOwlCategory.getSubCategories().elements();
      while (subcategories.hasMoreElements()) {
        Category rssOwlSubCategory = (Category) subcategories.nextElement();

        if (!rssOwlSubCategory.isUseProxy()) {
          rssOwlCategory.useProxy = false;
          break;
        }
      }
    }

    /** Recursivly check parent categorys */
    rssOwlCategory.checkUseProxy(rssOwlCategory.getParent());
  }

  /**
   * Edit a subcategory
   * 
   * @param title Title of the Subcategory
   * @param newTitle New Title of the Subcategory
   * @return TRUE if new title not yet existing
   */
  public boolean editCategory(String title, String newTitle) {

    /** No changes made */
    if (title.equals(newTitle))
      return true;

    /** The entered title exists */
    if (subCategories.containsKey(newTitle))
      return false;

    /** Update Hashtables and TreeSets */
    Category rssOwlSubCategory = (Category) subCategories.get(title);
    rssOwlSubCategory.setName(newTitle);

    /** Remove */
    subCategories.remove(title);
    sortedSubCatTitles.remove(title);

    /** Add */
    sortedSubCatTitles.add(newTitle);
    subCategories.put(newTitle, rssOwlSubCategory);

    return true;
  }

  /**
   * Edit a favorite
   * 
   * @param oldFavorite The edited favorite
   * @param newFavorite The updated favorite
   */
  public void editFavorite(Favorite oldFavorite, Favorite newFavorite) {
    String newTitle = newFavorite.getTitle();
    String newUrl = newFavorite.getUrl();
    String oldTitle = oldFavorite.getTitle();
    String oldUrl = oldFavorite.getUrl();

    /** Take the URL as title if title is empty */
    if (newTitle.equals(""))
      newTitle = newUrl;

    /** Create a unique link */
    int count = 1;
    String tempLink = newUrl;
    while (!oldUrl.equals(newUrl) && Category.linkExists(newUrl))
      newUrl = tempLink + '#' + (count++);

    /** Create a unique title */
    count = 1;
    String tempTitle = newTitle;
    while (!oldTitle.equals(newTitle) && Category.titleExists(newTitle))
      newTitle = tempTitle + " #" + (count++);

    /** Update Hashtables and TreeSets */
    Favorite selectedFavorite = (Favorite) favorites.get(oldUrl);
    selectedFavorite.setTitle(newTitle);
    selectedFavorite.setUrl(newUrl);
    newFavorite.clone(selectedFavorite);

    /** Remove */
    removeFavorite(oldTitle, false, true);

    /** Add */
    addFavorite(selectedFavorite);
  }

  /**
   * Recursivly get all favorite titles also from the subcategorys
   * 
   * @param allFavoriteTitles TreeSet holding titles of all favorites
   * @param rssOwlCategory Current Category
   */
  public void getAllFavoriteTitles(TreeSet allFavoriteTitles, Category rssOwlCategory) {

    /** Favorites (sorted by name) */
    TreeSet favorites = new TreeSet(new Comparator() {
      public int compare(Object obj1, Object obj2) {
        String favTitle1 = (String) obj1;
        String favTitle2 = (String) obj2;

        return favTitle2.compareTo(favTitle1);
      }
    });

    /** Sort out the ones that are synchronizer */
    Enumeration favElements = rssOwlCategory.getFavorites().elements();
    while (favElements.hasMoreElements()) {
      Favorite favorite = (Favorite) favElements.nextElement();
      if (!favorite.isSynchronizer())
        favorites.add(favorite.getTitle());
    }

    /** Add into list of Titles */
    allFavoriteTitles.addAll(favorites);

    /** Subcategories (sorted by name) */
    TreeSet subcategories = new TreeSet(new Comparator() {
      public int compare(Object obj1, Object obj2) {
        Category cat1 = (Category) obj1;
        Category cat2 = (Category) obj2;

        return cat2.getName().compareTo(cat1.getName());
      }
    });

    /** Add into list of Subcategories */
    subcategories.addAll(rssOwlCategory.getSubCategories().values());

    /** Proceed with Subcategories */
    Iterator subcategoriesIterator = subcategories.iterator();
    while (subcategoriesIterator.hasNext())
      getAllFavoriteTitles(allFavoriteTitles, (Category) subcategoriesIterator.next());
  }

  /**
   * Get all favorites of this Category
   * 
   * @return Hashtable All favorites of this category
   */
  public Hashtable getFavorites() {
    return favorites;
  }

  /**
   * Recursivly get all favorites also from the subcategorys that should
   * autoload on startup of RSSOwl
   * 
   * @param loadOnStartupFavorites TreeSet holding titles of all favorites
   * @param rssOwlCategory Current Category
   */
  public void getLoadOnStartupFavorites(TreeSet loadOnStartupFavorites, Category rssOwlCategory) {

    /** Favorites */
    Enumeration favorites = rssOwlCategory.getFavorites().elements();
    while (favorites.hasMoreElements()) {
      Favorite curFav = (Favorite) favorites.nextElement();

      if (curFav.isLoadOnStartup())
        loadOnStartupFavorites.add(curFav.getTitle());
    }

    /** Subcategorys */
    Enumeration subcategories = rssOwlCategory.getSubCategories().elements();
    while (subcategories.hasMoreElements())
      getLoadOnStartupFavorites(loadOnStartupFavorites, (Category) subcategories.nextElement());
  }

  /**
   * @return String Name of the Category
   */
  public String getName() {
    return name;
  }

  /**
   * @return Category The parent category
   */
  public Category getParent() {
    return parent;
  }

  /**
   * @return String The URL / Path to the blogroll
   */
  public String getPathToBlogroll() {
    return pathToBlogroll;
  }

  /**
   * @return TreeSet Sorted favorite titles
   */
  public TreeSet getSortedLinkTitles() {
    return sortedLinkTitles;
  }

  /**
   * @return TreeSet Sorted subcategory titles
   */
  public TreeSet getSortedSubCatTitles() {
    return sortedSubCatTitles;
  }

  /**
   * @return Hashtable All subcategorys of this category
   */
  public Hashtable getSubCategories() {
    return subCategories;
  }

  /**
   * @return Returns the treeItem.
   */
  public TreeItem getTreeItem() {
    return treeItem;
  }

  /**
   * @return TRUE if this category is from a blogroll
   */
  public boolean isBlogroll() {
    return isBlogroll;
  }

  /**
   * @return TRUE if category is expanded in the url tree
   */
  public boolean isExpanded() {
    return isExpanded;
  }

  /**
   * @return TRUE is this category is the root
   */
  public boolean isRoot() {
    return isRoot;
  }

  /**
   * @return TRUE if Blogroll is unsychronized
   */
  public boolean isUnSynchronized() {
    return isUnSynchronized;
  }

  /**
   * @return TRUE The state of the use proxy field
   */
  public boolean isUseProxy() {
    return useProxy;
  }

  /**
   * Remove all Favorites and Sub-Categories from the Category
   */
  public void removeAll() {

    /** Remove each favorite of the category */
    Enumeration favorites = getFavorites().elements();
    while (favorites.hasMoreElements()) {
      Favorite favorite = (Favorite) favorites.nextElement();
      removeFavorite(favorite.getTitle(), false, !favorites.hasMoreElements());
    }

    /** Remove each sub categorys of the category */
    Enumeration removeSubCatsEnum = getSubCategories().keys();
    while (removeSubCatsEnum.hasMoreElements()) {

      /** Recursivly remove all favorites of the subcategorys */
      removeCategory((String) removeSubCatsEnum.nextElement(), this);
    }
  }

  /**
   * Remove a subcategory from this category. Also remove all favorites of the
   * subcategorys
   * 
   * @param name Name of the subcategory
   * @param rssOwlCategory Current Category
   */
  public void removeCategory(String name, Category rssOwlCategory) {
    Category catToDelete = (Category) rssOwlCategory.getSubCategories().get(name);

    /** Remove each favorite of the category */
    Enumeration favorites = catToDelete.getFavorites().elements();
    while (favorites.hasMoreElements()) {
      Favorite favorite = (Favorite) favorites.nextElement();
      catToDelete.removeFavorite(favorite.getTitle(), false, !favorites.hasMoreElements());
    }

    /** Remove Blogroll, if this is a Blogroll */
    if (catToDelete.isBlogroll && StringShop.isset(catToDelete.getPathToBlogroll()))
      blogrolls.remove(catToDelete.getPathToBlogroll());

    /** Remove each sub categorys of the category */
    Enumeration removeSubCatsEnum = catToDelete.getSubCategories().keys();
    while (removeSubCatsEnum.hasMoreElements()) {

      /** Recursivly remove all favorites of the subcategorys */
      removeCategory((String) removeSubCatsEnum.nextElement(), catToDelete);
    }

    /** Remove from tables */
    rssOwlCategory.getSubCategories().remove(name);
    rssOwlCategory.getSortedSubCatTitles().remove(name);
  }

  /**
   * Remove a favorite from the category
   * 
   * @param disposeTreeItem If TRUE, dispose the favorite's TreeItem
   * @param title Title of the favorite
   * @param updateReadStatus If TRUE, update parent's read status
   */
  public void removeFavorite(String title, boolean disposeTreeItem, boolean updateReadStatus) {

    /** Try to retrieve the Favorite from the given Title */
    Favorite rssOwlFavorite;

    /** Retrieve URL of the given Favorite */
    String favUrl = getLinkForTitle(title);

    /** Title mapped to Link */
    if (favUrl != null)
      rssOwlFavorite = (Favorite) favPool.get(favUrl);

    /** Title not mapped to Link, try as Link */
    else
      rssOwlFavorite = (Favorite) favPool.get(title);

    /** Favorite was found */
    if (rssOwlFavorite != null) {

      /** Dispose connected treeitem */
      TreeItem favTreeItem = rssOwlFavorite.getTreeItem();

      /** Update read status for parent categories */
      if (!favTreeItem.isDisposed()) {
        ((TreeItemData) favTreeItem.getData()).setStatusUnread(false);

        /** Update parent's read status */
        if (updateReadStatus)
          GUI.rssOwlGui.getRSSOwlFavoritesTree().updateTreeReadStatus(favTreeItem);
      }

      /** Dispose the treeitem */
      if (!favTreeItem.isDisposed() && disposeTreeItem)
        favTreeItem.dispose();

      /** Sync with auto updater */
      FeedAutoUpdater.removeFavorite(rssOwlFavorite);
    }

    /** Remove from tables */
    if (favUrl != null) {
      favLinks.remove(favUrl);
      favorites.remove(favUrl);
    }

    favTitles.remove(title);
    sortedLinkTitles.remove(title);

    /** Check if there is another reference to that link */
    if (favUrl != null && !favLinks.contains(favUrl)) {

      /** Remove favorite from the hashtables */
      favPool.remove(favUrl);
      mapLinkToTitle.remove(favUrl);
    }

    /** Check if there is another reference to that title */
    if (!favTitles.contains(title))
      mapTitleToLink.remove(title);
  }

  /** Reset this category. This method is usually called from the root category */
  public void reset() {
    favorites.clear();
    subCategories.clear();
    sortedLinkTitles.clear();
    sortedSubCatTitles.clear();

    if (isRoot) {
      blogrolls.clear();
      favPool.clear();
      mapLinkToTitle.clear();
      mapTitleToLink.clear();
    }
  }

  /**
   * Set TRUE if this category is from a blogroll
   * 
   * @param isBlogroll
   */
  public void setBlogroll(boolean isBlogroll) {
    this.isBlogroll = isBlogroll;
  }

  /**
   * Set TRUE if category is expanded in the url tree
   * 
   * @param isExpanded
   */
  public void setExpanded(boolean isExpanded) {
    this.isExpanded = isExpanded;
  }

  /**
   * Set the Name of this category
   * 
   * @param name
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * If the category is moved it is usefull to set the parent explicitly without
   * calling the constructor.
   * 
   * @param parent The new parent category
   */
  public void setParent(Category parent) {
    this.parent = parent;
  }

  /**
   * Set the URL / Path to the blogroll
   * 
   * @param pathToBlogroll
   */
  public void setPathToBlogroll(String pathToBlogroll) {
    this.pathToBlogroll = pathToBlogroll;

    /** Blogroll path could be already existing! */
    if (!blogrollExists(pathToBlogroll))
      blogrolls.add(pathToBlogroll);
  }

  /**
   * @param treeItem The treeItem to set.
   */
  public void setTreeItem(TreeItem treeItem) {
    this.treeItem = treeItem;
  }

  /**
   * Set the Blogroll's synchronization state
   * 
   * @param isUnSynchronized TRUE indicates that this Blogroll has not yet been
   * synchronized with the blogroll.
   */
  public void setUnSynchronized(boolean isUnSynchronized) {
    this.isUnSynchronized = isUnSynchronized;
  }

  /**
   * Set TRUE if proxy should be used in this category's favorites and
   * subcategories favorites
   * 
   * @param useProxy
   */
  public void setUseProxy(boolean useProxy) {
    this.useProxy = useProxy;
    setUseProxy(this, useProxy);
  }

  /**
   * Crate the category path from the category
   * 
   * @return String Category path to the Category
   */
  public String toCatPath() {
    return toCatPath(false);
  }

  /**
   * Crate the category path from the category
   * 
   * @param humanReadable If TRUE, use '>' as separator
   * @return String Category path to the Category
   */
  public String toCatPath(boolean humanReadable) {
    StringBuffer catPath = new StringBuffer();
    String tokenizer = (humanReadable == true) ? " | " : StringShop.CAT_TOKENIZER;

    /** Handle case user is aggregating all categories */
    if (isRoot) {
      Enumeration categories = subCategories.elements();

      /** Handle situation that user has not saved any favorites */
      if (!categories.hasMoreElements())
        return catPath.toString();

      catPath.append(((Category) categories.nextElement()).getName());
      while (categories.hasMoreElements())
        catPath.append(", ").append(((Category) categories.nextElement()).getName());
      return catPath.toString();
    }

    Category tempCat = this;
    ArrayList titles = new ArrayList();

    /** While category is not root */
    while (!tempCat.isRoot()) {
      titles.add(tempCat.getName());
      tempCat = tempCat.getParent();
    }

    /** Build category path */
    for (int a = titles.size() - 1; a >= 0; a--)
      catPath.append(titles.get(a)).append(tokenizer);

    return catPath.substring(0, catPath.length() - StringShop.CAT_TOKENIZER.length());
  }

  /**
   * Recursivly update state of use proxy
   * 
   * @param rssOwlCategory Current Category
   * @param enabled TRUE if proxy should be used
   */
  private void setUseProxy(Category rssOwlCategory, boolean enabled) {
    rssOwlCategory.useProxy = enabled;

    Hashtable favorites = rssOwlCategory.getFavorites();

    Enumeration elements = favorites.elements();
    while (elements.hasMoreElements())
      ((Favorite) elements.nextElement()).setUseProxy(enabled);

    Hashtable subcategories = rssOwlCategory.getSubCategories();

    elements = subcategories.elements();
    while (elements.hasMoreElements())
      setUseProxy((Category) elements.nextElement(), enabled);
  }
}