package org.dbxml.core.objects;

/*
 * dbXML License, Version 1.0
 *
 *
 * Copyright (c) 1999-2001 The dbXML Group, L.L.C.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by
 *        The dbXML Group (http://www.dbxml.com/)."
 *    Alternately, this acknowledgment may appear in the software
 *    itself, if and wherever such third-party acknowledgments normally
 *    appear.
 *
 * 4. The names "dbXML" and "The dbXML Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    info@dbxml.com.
 *
 * 5. Products derived from this software may not be called "dbXML",
 *    nor may "dbXML" appear in their name, without prior written
 *    permission of The dbXML Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE DBXML GROUP OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: XMLObjectManager.java,v 1.31 2001/07/24 09:34:42 kstaken Exp $
 */

import org.dbxml.core.*;
import org.dbxml.core.Collection;
import org.dbxml.server.*;

import java.util.*;

/**
 * XMLObjectManager manages XMLObject instances.
 */

public final class XMLObjectManager extends SimpleConfigurable {
   private static final String XMLOBJECTS = "xmlobjects";
   private static final String XMLOBJECT = "xmlobject";
   private static final String NAME = "name";
   private static final String CLASS = "class";

   private static final String[] EmptyStrings = new String[0];

   private Map objects = new HashMap(); // String to XMLObjectInfo
   private Collection collection;

   public XMLObjectManager(Collection collection) {
      this.collection = collection;
   }

   public void setConfig(Configuration config) throws dbXMLException {
      super.setConfig(config);
      config.processChildren(XMLOBJECT,
         new ConfigurationCallback() {
            public void process(Configuration cfg) {
               String className = cfg.getAttribute(CLASS);
               try {
                  register(Class.forName(className), cfg);
               }
               catch ( Exception e ) {
                  org.dbxml.Debug.printStackTrace(e);
               }
            }
      });
   }

   /**
    * list returns a list of the XMLObjects that this XMLObjectManager has
    * registered.
    *
    * @return An array containing the XMLObject names
    */
   public String[] list() {
      return (String[])objects.keySet().toArray(EmptyStrings);
   }

   /**
    * drop physically removes the specified XMLObject and any
    * associated system resources that the XMLObject uses.
    *
    * @param name The XMLObject to drop
    * @return Whether or not the XMLObject was dropped
    */
   public boolean drop(final String name) {
      unregister(name);
      config.processChildren(XMLOBJECT,
         new ConfigurationCallback() {
            public void process(Configuration cfg) {
               try {
                  if ( cfg.getAttribute(NAME).equals(name) )
                     cfg.delete();
               }
               catch ( Exception e ) {
                  org.dbxml.Debug.printStackTrace(e);
               }
            }
      });
      return true;
   }

   /**
    * create creates a new XMLObject object and any associated
    * system resources that the XMLObject will need.
    *
    * @param cfg The XMLObject's configuration
    * @return The XMLObject that was created
    */
   public XMLObject create(Configuration cfg) throws DBException {
      String className = cfg.getAttribute(CLASS);
      try {
         // Check for duplicates
         String name = cfg.getAttribute(NAME);
         Configuration[] cfgs = config.getChildren();
         for ( int i = 0; i < cfgs.length; i++ )
            if ( cfgs[i].getAttribute(NAME).equals(name) )
               throw new DuplicateObjectException("Duplicate XMLObject '"+name+"'");

         Class c = Class.forName(className);
         XMLObject obj = register(c, cfg);

         config.add(cfg);
         
         return obj;
      }
      catch ( DBException d ) {
         throw d;
      }
      catch ( Exception e ) {         
         throw new CannotCreateException("Cannot create XMLObject");
      }
   }

   public XMLObject register(Class c, Configuration cfg) throws DBException {
      String name = null;
      try {
         XMLObject obj = (XMLObject)c.newInstance();
         initialize(obj, cfg);
         name = obj.getName();
         
         if ( name == null || name.trim().equals("") )
            throw new CannotCreateException("No name specified");
         
         int policy = ThreadPolicy.SHARED_INSTANCE;

         if ( obj instanceof ThreadPolicy )
            policy = ((ThreadPolicy)obj).getThreadPolicy();

         objects.put(name, new XMLObjectInfo(name, obj, cfg, policy));
         
         return obj;
      }
      catch ( DBException d ) {
         throw d;
      }
      catch ( Exception e ) {
         throw new InvalidContextException("'"+name+"' is not a valid XMLObject");
      }
   }

   public void unregister(String name) {
      objects.remove(name);
   }

   private void initialize(XMLObject obj, Configuration cfg) throws dbXMLException {
      obj.setConfig(cfg);
      obj.setCollection(collection);
   }

   /**
    * get retrieves an XMLObject by name, and will also do the work of
    * instantiation and pool-retrieval.
    *
    * @name The XMLObject name
    * @return The XMLObject
    */
   public XMLObject get(String name) {
      XMLObject obj = null;
      try {
         XMLObjectInfo info = (XMLObjectInfo)objects.get(name);

         switch ( info.policy ) {

            case ThreadPolicy.NEW_INSTANCE:
               obj = (XMLObject)info.c.newInstance();
               initialize(obj, info.cfg);
               break;

            case ThreadPolicy.POOLED_INSTANCE:
               obj = (XMLObject)info.getObject();
               break;

            case ThreadPolicy.SHARED_INSTANCE:
               obj = info.instance;
               break;

         }
      }
      catch ( Exception e ) {
      }
      return obj;
   }

   public String getCanonicalName(String name) {
      return collection.getCanonicalName()+"/"+name;
   }

   private class XMLObjectInfo extends ObjectPool {
      public String name;
      public Class c;
      public int policy;
      public XMLObject instance;
      public Configuration cfg;

      public XMLObjectInfo(String name, XMLObject instance, Configuration cfg, int policy) {
         this.name = name;
         this.policy = policy;
         this.cfg = cfg;
         switch ( policy ) {

            case ThreadPolicy.POOLED_INSTANCE:
               if ( ! (instance instanceof Poolable) )
                   policy = ThreadPolicy.NEW_INSTANCE;

            case ThreadPolicy.NEW_INSTANCE:
               c = instance.getClass();
               break;

            case ThreadPolicy.SHARED_INSTANCE:
               this.instance = instance;
               break;
         }
      }

      public Poolable createObject() {
         try {
            Poolable obj = (Poolable)c.newInstance();
            XMLObject xobj = (XMLObject)obj;
            initialize(xobj, cfg);
            return obj;
         }
         catch ( Exception e ) {
            org.dbxml.Debug.printStackTrace(e);
         }
         return null;
      }
   }
}


