package com.dbxml.db.client.xmldb;

/*
 * dbXML - Native XML Database
 * Copyright (C) 1999-2004  The dbXML Group, L.L.C.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: CollectionImpl.java,v 1.3 2004/02/12 00:17:55 bradford Exp $
 */

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import com.dbxml.db.client.CollectionClient;
import com.dbxml.util.dbXMLException;

import org.xmldb.api.base.*;

/**
 * CollectionImpl
 */

public final class CollectionImpl implements Collection, Configurable {
   private static final Service[] EmptyServices = new Service[0];

   private Properties props = new Properties();
   private List services = new ArrayList();
   private CollectionClient client;
   private boolean opened = true;

   private String name;
   private int type = -1;

   public CollectionImpl(CollectionClient client) {
      this.client = client;
      addService(new XPathQueryServiceImpl(this));
      addService(new XUpdateQueryServiceImpl(this));
      addService(new XSLTransformServiceImpl(this));
   }

   private int getContentType() throws dbXMLException {
      if ( type == -1 )
         type = client.getCollectionType();
      return type;
   }

   private void checkOpened() throws XMLDBException {
      if ( !opened )
         throw new XMLDBException(ErrorCodes.COLLECTION_CLOSED, "Collection is closed");
   }

   public CollectionClient getCollectionClient() {
      return client;
   }

   public String getName() throws XMLDBException {
      checkOpened();
      try {
         if ( name == null )
            name = client.getName();
         return name;
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public String getProperty(String name) throws XMLDBException {
      return props.getProperty(name);
   }

   public void setProperty(String name, String value) throws XMLDBException {
      props.setProperty(name, value);
   }

   private void addService(Service service) {
      services.add(service);
   }

   public Service[] getServices() throws XMLDBException {
      return (Service[])services.toArray(EmptyServices);
   }

   public Service getService(String name, String version) throws XMLDBException {
      checkOpened();

      Iterator iter = services.iterator();
      while ( iter.hasNext() ) {
         Service service = (Service)iter.next();
         if ( service.getName().equals(name) && service.getVersion().equals(version) )
            return service;
      }

      return null;
   }

   public Collection getParentCollection() throws XMLDBException {
      checkOpened();
      try {
         CollectionClient parent = client.getParentCollection();
         if ( parent != null )
            return new CollectionImpl(parent);
         else
            throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION, "No Parent Collection");
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public int getChildCollectionCount() throws XMLDBException {
      checkOpened();
      try {
         return client.listCollections().length;
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public String[] listChildCollections() throws XMLDBException {
      checkOpened();
      try {
         return client.listCollections();
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public Collection getChildCollection(String name) throws XMLDBException {
      try {
         CollectionClient child = client.getCollection(name);
         if ( child != null )
            return new CollectionImpl(child);
         else
            throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION, "No Child Collection named '" + name + "'");
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION, "No Child Collection named '" + name + "'");
      }
   }

   public int getResourceCount() throws XMLDBException {
      checkOpened();
      try {
         return (int)client.getKeyCount();
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public String[] listResources() throws XMLDBException {
      checkOpened();
      try {
         return client.listKeys();
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public Resource createResource(String id, String type) throws XMLDBException {
      try {
         switch ( getContentType() ) {
            case CollectionClient.TYPE_DOCUMENTS:
               if ( type.equals(XMLResourceImpl.TYPE) )
                  return new XMLResourceImpl(this, id, id, null);
               break;

            case CollectionClient.TYPE_VALUES:
               if ( type.equals(BinaryResourceImpl.TYPE) )
                  return new BinaryResourceImpl(this, id, null);
               break;
         }

         throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "Invalid Resource type '" + type + "'");
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public void removeResource(Resource res) throws XMLDBException {
      checkOpened();
      try {
         String id = res.getId();
         if ( id != null && id.length() > 0 )
            client.remove(id);
         else
            throw new XMLDBException(ErrorCodes.NO_SUCH_RESOURCE, "Invalid Resource ID '" + id + "'");
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public void storeResource(Resource res) throws XMLDBException {
      try {
         switch ( getContentType() ) {
            case CollectionClient.TYPE_DOCUMENTS:
               if ( res instanceof XMLResourceImpl )
                  client.setDocumentAsText(res.getId(), (String)res.getContent());
               else
                  throw new dbXMLException("Collection can only store document content");
               break;

            case CollectionClient.TYPE_VALUES:
               if ( res instanceof BinaryResourceImpl )
                  client.setValue(res.getId(), (byte[])res.getContent());
               else
                  throw new dbXMLException("Collection can only store binary content");
               break;

            default:
               throw new dbXMLException("Unknown Content Type");
         }
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public Resource getResource(String id) throws XMLDBException {
      try {
         switch ( getContentType() ) {
            case CollectionClient.TYPE_DOCUMENTS:
               return new XMLResourceImpl(this, id, id, client.getDocumentAsText(id));

            case CollectionClient.TYPE_VALUES:
               return new BinaryResourceImpl(this, id, client.getValue(id));

            default:
               throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "Unknown Content Type");
         }
      }
      catch ( dbXMLException e ) {
         if ( e.getMessage().endsWith("not found") )
            return null;
         else
            throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public String createId() throws XMLDBException {
      try {
         return client.createKey();
      }
      catch ( dbXMLException e ) {
         throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage());
      }
   }

   public boolean isOpen() throws XMLDBException {
      return opened;
   }

   public void close() throws XMLDBException {
      opened = false;
      client = null;
   }
}
