/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.community;

import java.util.*;
import org.apache.commons.collections.*;
import org.apache.log4j.Logger;
import org.w3c.dom.*;
import com.silverwrist.util.*;
import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.db.NamespaceCache;
import com.silverwrist.dynamo.db.UserManagement;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.module.ModuleOperations;
import com.silverwrist.dynamo.security.SecurityReferenceMonitor;
import com.silverwrist.dynamo.util.*;
import com.silverwrist.venice.CommunitySearchField;
import com.silverwrist.venice.SearchMode;
import com.silverwrist.venice.VeniceNamespaces;
import com.silverwrist.venice.except.*;
import com.silverwrist.venice.iface.*;

public class CommunityManager
    implements NamedObject, ComponentInitialize, ComponentShutdown, CommunityService, CommunityProxyService
{
  /*--------------------------------------------------------------------------------
   * Internal class implementing community proxies
   *--------------------------------------------------------------------------------
   */

  private class MyCommunityProxy extends CommunityProxy
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private VeniceCommunity m_real_community = null;

    /*====================================================================
     * Constructors
     *====================================================================
     */

    MyCommunityProxy(int cid)
    {
      super(cid);

    } // end constructor

    /*====================================================================
     * Abstract implementations from class CommunityProxy
     *====================================================================
     */

    protected synchronized VeniceCommunity getRealCommunity()
    {
      if (m_real_community==null)
      { // need to retrieve the real community...
	try
	{ // get the real community...
	  m_real_community = CommunityManager.this.getCommunity(m_id);

	} // end try
	catch (DatabaseException e)
	{ // wrap it in a runtime exception type
	  throw new ProxyException(e);

	} // end catch

      } // end if

      return m_real_community;

    } // end getRealCommunity

  } // end class MyCommunityProxy

  /*--------------------------------------------------------------------------------
   * Internal property serializer class
   *--------------------------------------------------------------------------------
   */

  private class CommunitySerializer implements PropertySerializer
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    CommunitySerializer()
    { // do nothing
    } // end constructor

    /*====================================================================
     * Implementations from interface PropertySerializer
     *====================================================================
     */

    public String serializeProperty(Object value)
    {
      if (value instanceof VeniceCommunity)
	return "Community:" + String.valueOf(((VeniceCommunity)value).getCID());

      return null;

    } // end serializeProperty

    public Object deserializeProperty(String value)
    {
      try
      { // look for our known prefixes
	if (value.startsWith("Community:"))
	  return getCommunityProxy(Integer.parseInt(value.substring(10)));

	return null;

      } // end try
      catch (NumberFormatException e)
      { // number parse blew up...
	return null;

      } // end catch

    } // end deserializeProperty

  } // end class CommunitySerializer

  /*--------------------------------------------------------------------------------
   * Internal class that initializes the services
   *--------------------------------------------------------------------------------
   */

  private class ServiceInit implements FinalStageInitHook
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    ServiceInit()
    { // do nothing
    } // end constructor

    /*====================================================================
     * Implementations from interface FinalStageInitHook
     *====================================================================
     */

    public void initialize(Application application, ServiceProvider services) throws DynamoException
    {
      logger.info("Initializing community service list");
      List svclist = m_ops.getMasterServiceList();
      ModuleOperations module_ops = (ModuleOperations)(services.queryService(ModuleOperations.class));
      Iterator it = svclist.iterator();
      while (it.hasNext())
      { // get the Map full of data
	Map item = (Map)(it.next());

	// make sure we have the module
	PropertyKey pk = (PropertyKey)(item.get(CommunityManagerOps.KEY_MODNAME));
	Module module = module_ops.findModule(m_ns_cache.namespaceIdToName(pk.getNamespaceID()),pk.getName());
	if (module==null)
	{ // the module is not loaded - we have to load it
	  String filename = (String)(item.get(CommunityManagerOps.KEY_MODFILENAME));
	  module = module_ops.loadModule(filename,true);
	  QualifiedNameKey modid = module.getModuleID();
	  PropertyKey pk2 = new PropertyKey(m_ns_cache.namespaceNameToId(modid.getNamespace()),modid.getName());
	  if (!(pk.equals(pk2)))
	  { // module name does not match what we expect - throw exception
	    logger.error("Loaded module name " + pk2 + " does not match expected module name " + pk);
	    CommunityServiceException cse = new CommunityServiceException(CommunityManager.class,"CommunityMessages",
									  "svc.modname.mismatch");
	    cse.setParameter(0,filename);
	    throw cse;

	  } // end if

	} // end if
    
	// now get the CommunityServiceController from the module
	pk = (PropertyKey)(item.get(CommunityManagerOps.KEY_OBJNAME));
	DynamicObject dobj = module.getProvidedObject(m_ns_cache.namespaceIdToName(pk.getNamespaceID()),pk.getName());
	if (!(dobj instanceof CommunityServiceController))
	{ // invalid controller object - throw exception
	  CommunityServiceException cse = new CommunityServiceException(CommunityManager.class,"CommunityMessages",
									"svc.object.badType");
	  cse.setParameter(0,m_ns_cache.namespaceIdToName(pk.getNamespaceID()));
	  cse.setParameter(1,pk.getName());
	  throw cse;

	} // end if

	CommunityServiceController csc = (CommunityServiceController)dobj;
	
	// preserve the descriptors
	pk = (PropertyKey)(item.get(CommunityManagerOps.KEY_SVCNAME));
	CommunityServiceDescriptor csdesc =
	    new CommunityServiceDescriptor((Integer)(item.get(CommunityManagerOps.KEY_SVCID)),
					   new QualifiedNameKey(m_ns_cache.namespaceIdToName(pk.getNamespaceID()),
								pk.getName()),
					   (String)(item.get(CommunityManagerOps.KEY_SHORTVAR)),csc);
	m_qname_to_service.put(csdesc.getName(),csdesc);
	m_index_to_service.put(item.get(CommunityManagerOps.KEY_SVCID),csdesc);

      } // end while

      if (logger.isDebugEnabled())
	logger.debug("Community service list initialized: " + m_index_to_service.size() + " "
		     + ((m_index_to_service.size()==1) ? "entry" : "entries"));

      // Now register our ServiceShutdown class as a pre-stage shutdown hook.
      FinalStageRegistration fsreg = (FinalStageRegistration)(services.queryService(FinalStageRegistration.class));
      fsreg.registerPreStageShutdown(new ServiceShutdown());

    } // end initialize

  } // end class ServiceInit

  /*--------------------------------------------------------------------------------
   * Internal class that shuts down the services
   *--------------------------------------------------------------------------------
   */

  private class ServiceShutdown implements ComponentShutdown
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    ServiceShutdown()
    { // do nothing
    } // end constructor

    /*====================================================================
     * Implementations from interface ComponentShutdown
     *====================================================================
     */

    public void shutdown()
    {
      LinkedList shut_list = new LinkedList(m_qname_to_service.values());
      m_qname_to_service.clear();
      m_index_to_service.clear();
      if (logger.isDebugEnabled())
	logger.debug("ServiceShutdown: " + shut_list.size() + " item(s) to shut down");
      while (shut_list.size()>0)
      { // shut down this community service
	CommunityServiceController csc = ((CommunityServiceDescriptor)(shut_list.removeFirst())).getController();
	csc.shutdown();

      } // end while
      
    } // end shutdown

  } // end class ServiceShutdown

  /*--------------------------------------------------------------------------------
   * Internal class implementing the installer
   *--------------------------------------------------------------------------------
   */

  private class MyInstaller implements CommunityServiceInstall
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    MyInstaller()
    { // do nothing
    } // end constructor

    /*====================================================================
     * Implementations from interface CommunityServiceInstall
     *====================================================================
     */

    public void installService(String service_namespace, String service_name, Module module, String object_namespace,
			       String object_name, CommunityServiceController controller, String shortvar)
        throws DatabaseException
    {
      // first, add the service to the database
      QualifiedNameKey id = module.getModuleID();
      int ndx = m_ops.addServiceEntry(m_ns_cache.namespaceNameToId(service_namespace),service_name,
				      m_ns_cache.namespaceNameToId(id.getNamespace()),id.getName(),
				      module.getFilename(),m_ns_cache.namespaceNameToId(object_namespace),
				      object_name,shortvar);

      // now create a descriptor and add it to the list of service descriptors
      CommunityServiceDescriptor csd =
	  new CommunityServiceDescriptor(ndx,new QualifiedNameKey(service_namespace,service_name),shortvar,controller);
      m_qname_to_service.put(csd.getName(),csd);
      m_index_to_service.put(new Integer(ndx),csd);

    } // end installService

    public void removeService(String service_namespace, String service_name) throws DatabaseException
    {
      // seek out the CommunityServiceDescriptor by name
      QualifiedNameKey key = new QualifiedNameKey(service_namespace,service_name);
      CommunityServiceDescriptor csd = (CommunityServiceDescriptor)(m_qname_to_service.get(key));
      if (csd==null)
	return;

      // remove the CSD from the internal tables
      m_qname_to_service.remove(key);
      m_index_to_service.remove(new Integer(csd.getIndex()));

      // shut it down
      csd.getController().shutdown();

      // lastly, remove it from the database
      m_ops.removeServiceEntry(m_ns_cache.namespaceNameToId(service_namespace),service_name);

    } // end removeService

  } // end class MyInstaller

  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static Logger logger = Logger.getLogger(CommunityManager.class);

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private String m_name;                       // this object's name
  private CommunityManagerOps m_ops;           // database operations object
  private NamespaceCache m_ns_cache;           // namespace cache object
  private SecurityReferenceMonitor m_srm;      // security reference monitor
  private UserManagement m_users;              // user management object
  private AuthenticatorLookup m_alook;         // authenticator lookup
  private PostDynamicUpdate m_post;            // where we post dynamic updates
  private CategoryService m_cats;              // category service object
  private ReferenceMap m_id_to_comm;           // ReferenceMap of community IDs to communities
  private ReferenceMap m_alias_to_comm;        // ReferenceMap of community aliases to communities
  private Hashtable m_qname_to_service;        // maps service QualifiedNameKeys to descriptord
  private Hashtable m_index_to_service;        // maps service index numbers to descriptors
  private ComponentShutdown m_pszreg;          // property serializer registration
  private ComponentShutdown m_installreg;      // installer interface registration

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  public CommunityManager()
  {
    m_id_to_comm = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT);
    m_alias_to_comm = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT);
    m_qname_to_service = new Hashtable();
    m_index_to_service = new Hashtable();

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  private final List translateCIDArray(int[] array) throws DatabaseException
  {
    if (logger.isDebugEnabled())
      logger.debug("translateCIDArray(): translating array of length " + array.length);
    if (array.length==0)
      return Collections.EMPTY_LIST;
    ArrayList rc = new ArrayList(array.length);
    for (int i=0; i<array.length; i++)
      rc.add(this.getCommunity(array[i]));
    return Collections.unmodifiableList(rc);

  } // end translateCIDArray

  private final boolean canGetMemberCommunityList(DynamoUser caller, DynamoUser user) throws DatabaseException
  {
    if (caller.equals(user))
      return true;   // a user can always get his OWN member community list
    if (m_srm.getAdminUser().equals(caller))
      return true;   // Administrator can do anything
    if (m_srm.getGlobalAcl().testPermission(caller,VeniceNamespaces.COMMUNITY_PERMS_NAMESPACE,
					    "see.member.community.lists"))
      return true;   // this user is permitted to see all member community lists

    // Get the user's privacy flags and see if they're allowing others to see their member community list.
    OptionSet oset = (OptionSet)(user.getObject(VeniceNamespaces.USER_PROFILE_NAMESPACE,"privacy"));
    return oset.get(5);

  } // end canGetMemberCommunityList

  /*--------------------------------------------------------------------------------
   * Implementations from interface NamedObject
   *--------------------------------------------------------------------------------
   */

  public String getName()
  {
    return m_name;

  } // end getName

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentInitialize
   *--------------------------------------------------------------------------------
   */

  /**
   * Initialize the component.
   *
   * @param config_root Pointer to the section of the Dynamo XML configuration file that configures this
   *                    particular component.  This is to be considered "read-only" by the component.
   * @param services An implementation of {@link com.silverwrist.dynamo.iface.ServiceProvider ServiceProvider}
   *                 which provides initialization services to the component.  This will include an implementation
   *                 of {@link com.silverwrist.dynamo.iface.ObjectProvider ObjectProvider} which may be used to
   *                 get information about other objects previously initialized by the application.
   * @exception com.silverwrist.dynamo.except.ConfigException If an error is encountered in the component
   *            configuration.
   */
  public void initialize(Element config_root, ServiceProvider services) throws ConfigException
  {
    logger.info("CommunityManager initializing");

    XMLLoader loader = XMLLoader.get();
    String name_pool = null, name_nscache = null, name_srm = null, name_users = null, name_cats = null;
    try
    { // verify the right node name
      loader.verifyNodeName(config_root,"object");

      // get the object's name
      m_name = loader.getAttribute(config_root,"name");

      // get the name of the database pool and namespace cache
      DOMElementHelper config_root_h = new DOMElementHelper(config_root);
      Element foo = loader.getSubElement(config_root_h,"database");
      name_pool = loader.getAttribute(foo,"connection");
      name_nscache = loader.getAttribute(foo,"namespaces");

      // get the name of the security reference monitor
      foo = loader.getSubElement(config_root_h,"security");
      name_srm = loader.getAttribute(foo,"object");

      // get the name of the user manager
      foo = loader.getSubElement(config_root_h,"user-manager");
      name_users = loader.getAttribute(foo,"object");

      // get the name of the categories object
      foo = loader.getSubElement(config_root_h,"providers");
      name_cats = loader.getAttribute(foo,"categories");

    } // end try
    catch (XMLLoadException e)
    { // error loading XML config data
      logger.fatal("XML loader exception in CommunityManager",e);
      throw new ConfigException(e);

    } // end catch

    // Get the database connection pool.
    DBConnectionPool pool = GetObjectUtils.getDatabaseConnection(services,name_pool);

    // Get the operations object.
    m_ops = CommunityManagerOps.get(pool);

    // Get the namespace cache.
    m_ns_cache = (NamespaceCache)(GetObjectUtils.getDynamoComponent(services,NamespaceCache.class,name_nscache));

    // Get the security reference monitor.
    m_srm = (SecurityReferenceMonitor)(GetObjectUtils.getDynamoComponent(services,SecurityReferenceMonitor.class,
									 name_srm));

    // Get the user management object.
    m_users = (UserManagement)(GetObjectUtils.getDynamoComponent(services,UserManagement.class,name_users));

    // Get the category service object.
    m_cats = (CategoryService)(GetObjectUtils.getDynamoComponent(services,CategoryService.class,name_cats));

    // Get the authenticator lookup service.
    m_alook = (AuthenticatorLookup)(services.queryService(AuthenticatorLookup.class));

    // Get the dynamic update poster.
    m_post = (PostDynamicUpdate)(services.queryService(PostDynamicUpdate.class));

    // Register our property serializer to let Community objects be serialized.
    PropertySerializerRegistration psreg =
        (PropertySerializerRegistration)(services.queryService(PropertySerializerRegistration.class));
    m_pszreg = psreg.registerPropertySerializer(new CommunitySerializer());

    // Register our install service to let modules install community services.
    ModuleOperations module_ops = (ModuleOperations)(services.queryService(ModuleOperations.class));
    SingletonServiceProvider ssp = new SingletonServiceProvider("Community install services",
								CommunityServiceInstall.class,new MyInstaller());
    m_installreg = module_ops.hookInstallServices(ssp);

    // Register our ServiceInit class as a pre-stage init hook.
    FinalStageRegistration fsreg = (FinalStageRegistration)(services.queryService(FinalStageRegistration.class));
    fsreg.registerFinalStageInitHook(new ServiceInit());

  } // end initialize

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentShutdown
   *--------------------------------------------------------------------------------
   */

  public void shutdown()
  {
    m_installreg.shutdown();
    m_installreg = null;
    m_pszreg.shutdown();
    m_pszreg = null;
    m_id_to_comm.clear();
    m_alias_to_comm.clear();
    m_post = null;
    m_alook = null;
    m_cats = null;
    m_users = null;
    m_srm = null;
    m_ns_cache = null;
    m_ops.dispose();
    m_ops = null;

  } // end shutdown

  /*--------------------------------------------------------------------------------
   * Implementations from interface CommunityService
   *--------------------------------------------------------------------------------
   */

  public List getMemberCommunities(DynamoUser caller, DynamoUser user)
      throws DatabaseException, DynamoSecurityException
  {
    if (logger.isDebugEnabled())
      logger.debug("CommunityManager.getMemberCommunities: " + caller.getName() + " getting community list for "
		   + user.getName());
    if (!(canGetMemberCommunityList(caller,user)))
    { // not permitted - bye!
      logger.error("CommunityManager.getMemberCommunities: permission denied for " + caller.getName()
		   + " to get community list for " + user.getName());
      DynamoSecurityException dse = new DynamoSecurityException(CommunityManager.class,"CommunityMessages",
								"auth.getCommList");
      dse.setParameter(0,user.getName());
      throw dse;

    } // end if

    // call down to ops object, then translate the returned CIDs into VeniceCommunity objects
    return translateCIDArray(m_ops.getMemberCommunityIDs(user.getUID()));

  } // end getMemberCommunities

  public synchronized VeniceCommunity getCommunity(int cid) throws DatabaseException
  {
    if (logger.isDebugEnabled())
      logger.debug("CommunityManager.getCommunity(" + cid + ")");
    Integer key = new Integer(cid);
    CommunityImpl rc = (CommunityImpl)(m_id_to_comm.get(key));
    if (rc==null)
    { // look up the community's information in the database
      Map data = m_ops.lookupCommunity(cid);
      if (data==null)
      { // community not found
	DatabaseException de = new DatabaseException(CommunityManager.class,"CommunityMessages","cid.notfound");
	de.setParameter(0,String.valueOf(cid));
	throw de;

      } // end if

      // The object might have been erased from the ID map but not the alias map.  Check there.
      rc = (CommunityImpl)(m_alias_to_comm.get(data.get(CommunityManagerOps.KEY_ALIAS)));
      if (rc==null)
      { // create the CommunityImpl object
	rc = new CommunityImpl(this,m_ops.getCommunityOps(),m_ns_cache,m_srm,m_users,m_alook,m_post,m_cats,data);

	// poke the maps with the new object
	m_id_to_comm.put(key,rc);
	m_alias_to_comm.put(rc.getAlias(),rc);

      } // end if
      else  // put it back into the ID map
	m_id_to_comm.put(key,rc);

    } // end if
    else  // put it into the "alias" map, as it might have been removed from there
      m_alias_to_comm.put(rc.getAlias(),rc);

    return (VeniceCommunity)rc;

  } // end getCommunity

  public VeniceCommunity getCommunity(String alias) throws DatabaseException
  {
    CommunityImpl rc = (CommunityImpl)(m_alias_to_comm.get(alias));
    if (rc==null)
    { // look up the community's information in the database
      Map data = m_ops.lookupCommunity(alias);
      if (data==null)
      { // community not found
	DatabaseException de = new DatabaseException(CommunityManager.class,"CommunityMessages","alias.notfound");
	de.setParameter(0,alias);
	throw de;

      } // end if

      // The object might have been erased from the alias map but not the ID map.  Check there.
      Integer key = (Integer)(data.get(CommunityManagerOps.KEY_CID));
      rc = (CommunityImpl)(m_id_to_comm.get(key));
      if (rc==null)
      { // create the CommunityImpl object
	rc = new CommunityImpl(this,m_ops.getCommunityOps(),m_ns_cache,m_srm,m_users,m_alook,m_post,m_cats,data);

	// poke the maps with the new object
	m_id_to_comm.put(key,rc);
	m_alias_to_comm.put(rc.getAlias(),rc);

      } // end if
      else  // put it back into the alias map
	m_alias_to_comm.put(rc.getAlias(),rc);

    } // end if
    else  // put it into the "ID" map, as it might have been removed from there
      m_id_to_comm.put(new Integer(rc.getCID()),rc);

    return (VeniceCommunity)rc;

  } // end getCommunity

  public List searchForCommunities(DynamoUser caller, CommunitySearchField field, SearchMode mode, String term,
				   int offset, int count) throws DatabaseException
  {
    boolean show_all = m_srm.getGlobalAcl().testPermission(caller,VeniceNamespaces.COMMUNITY_PERMS_NAMESPACE,
							   "community.search.all");
    int[] rc = null;
    if (CommunitySearchField.NAME.equals(field))
      rc = m_ops.searchName(mode,term,show_all,offset,count);
    else if (CommunitySearchField.SYNOPSIS.equals(field))
      rc = m_ops.searchProperty(m_ns_cache.namespaceNameToId(VeniceNamespaces.COMMUNITY_PROFILE_NAMESPACE),"synopsis",
                                mode,term,show_all,offset,count);
    else
      throw new IllegalArgumentException("invalid search field (shouldn't happen)");
    return translateCIDArray(rc);

  } // end searchForCommunities

  public int getSearchCommunityCount(DynamoUser caller, CommunitySearchField field, SearchMode mode, String term)
      throws DatabaseException
  {
    boolean show_all = m_srm.getGlobalAcl().testPermission(caller,VeniceNamespaces.COMMUNITY_PERMS_NAMESPACE,
							   "community.search.all");
    if (CommunitySearchField.NAME.equals(field))
      return m_ops.searchNameCount(mode,term,show_all);
    else if (CommunitySearchField.SYNOPSIS.equals(field))
      return m_ops.searchPropertyCount(m_ns_cache.namespaceNameToId(VeniceNamespaces.COMMUNITY_PROFILE_NAMESPACE),
				       "synopsis",mode,term,show_all);
    else
      throw new IllegalArgumentException("invalid search field (shouldn't happen)");

  } // end getSearchCommunityCount

  public List getCommunitiesInCategory(DynamoUser caller, int catid, int offset, int count) throws DatabaseException
  {
    boolean show_all = m_srm.getGlobalAcl().testPermission(caller,VeniceNamespaces.COMMUNITY_PERMS_NAMESPACE,
							   "community.directory.all");
    return translateCIDArray(m_ops.getCommunityIDsInCategory(catid,show_all,offset,count));

  } // end getCommunitiesInCategory

  public List getCommunitiesInCategory(DynamoUser caller, VeniceCategory cat, int offset, int count)
      throws DatabaseException
  {
    return this.getCommunitiesInCategory(caller,cat.getCategoryID(),offset,count);

  } // end getCommunitiesInCategory

  public int getNumCommunitiesInCategory(DynamoUser caller, int catid) throws DatabaseException
  {
    boolean show_all = m_srm.getGlobalAcl().testPermission(caller,VeniceNamespaces.COMMUNITY_PERMS_NAMESPACE,
							   "community.directory.all");
    return m_ops.getNumCommunityIDsInCategory(catid,show_all);

  } // end getNumCommunitiesInCategory

  public int getNumCommunitiesInCategory(DynamoUser caller, VeniceCategory cat) throws DatabaseException
  {
    return this.getNumCommunitiesInCategory(caller,cat.getCategoryID());

  } // end getNumCommunitiesInCategory

  public List getAvailableServices()
  {
    if (m_qname_to_service.isEmpty())
      return Collections.EMPTY_LIST;
    ArrayList rc = new ArrayList(m_qname_to_service.values());
    Collections.sort(rc,new Comparator()
      {
	public int compare(Object o1, Object o2)
	{
	  String s1 = ((CommunityServiceDescriptor)o1).getDescription();
	  String s2 = ((CommunityServiceDescriptor)o2).getDescription();
	  return s1.compareTo(s2);

	} // end compare

	public boolean equals(Object obj)
	{
	  return false;

	} // end equals

      }); // end Comparator object
    return Collections.unmodifiableList(rc);

  } // end getAvailableServices

  public CommunityServiceDescriptor getServiceForID(int id)
  {
    return (CommunityServiceDescriptor)(m_index_to_service.get(new Integer(id)));

  } // end getServiceForID

  public CommunityServiceDescriptor getServiceForName(String namespace, String name)
  {
    return (CommunityServiceDescriptor)(m_qname_to_service.get(new QualifiedNameKey(namespace,name)));

  } // end getServiceForName

  /*--------------------------------------------------------------------------------
   * Implementations from interface CommunityProxyService
   *--------------------------------------------------------------------------------
   */

  public synchronized VeniceCommunity getCommunityProxy(int cid)
  {
    Integer key = new Integer(cid);
    VeniceCommunity rc = (VeniceCommunity)(m_id_to_comm.get(key));
    if (rc!=null)
      m_alias_to_comm.put(rc.getAlias(),(CommunityImpl)rc);
    else
      rc = new MyCommunityProxy(cid);
    return rc;

  } // end getCommunityProxy

} // end class CommunityManager
