/*
 * Copyright (C) 2009 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.exoplatform.services.jcr.storage;

import org.exoplatform.services.jcr.datamodel.ItemData;
import org.exoplatform.services.jcr.datamodel.ItemType;
import org.exoplatform.services.jcr.datamodel.NodeData;
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.impl.core.itemfilters.QPathEntryFilter;
import org.exoplatform.services.jcr.impl.dataflow.persistent.ACLHolder;
import org.exoplatform.services.jcr.impl.dataflow.persistent.ChangedSizeHandler;

import java.util.List;

import javax.jcr.InvalidItemStateException;
import javax.jcr.RepositoryException;

/**
 * Created by The eXo Platform SAS<br>
 * 
 * Includes methods for direct read (by identifier and qPath), node child traversing, reference
 * access as well as write single item methods (add, update, delete) and batch operations support.
 * Valid (workable) connection state is "opened" (isOpened() == true). Newly created connection
 * should have "opened" state. The connection becomes "closed" (invalid for using) after calling
 * commit() or rollback() methods. In this case methods calling will cause an IllegalStateException
 * 
 * Connection object intends to be as "light" as possible i.e. connection creation SHOULD NOT be
 * expensive operation, so better NOT to open/close potentially EXPENSIVE resources using by
 * Connection (WorkspaceDataContainer should be responsible for that). The Connection IS NOT a
 * thread-safe object and normally SHOULD NOT be pooled/cached.
 * 
 * @author Gennady Azarenkov
 * @version $Id: WorkspaceStorageConnection.java 11907 2008-03-13 15:36:21Z ksm $
 * @LevelAPI Unsupported
 */
public interface WorkspaceStorageConnection
{

   /**
    * Checks if <code>ItemData</code> exists into the storage using item's parent and name relative the parent
    * location of define type.
    * 
    * @param parentData
    *          - the item's parent NodeData
    * @param name
    *          - item's path entry (QName + index)
    * @param itemType
    *             item type         
    * @return - true if ItemData exists and false otherwise
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   boolean hasItemData(NodeData parentData, QPathEntry name, ItemType itemType) throws RepositoryException,
      IllegalStateException;

   /**
    * Reads <code>ItemData</code> from the storage using item's parent and name relative the parent
    * location of a given type.
    * 
    * @param parentData
    *          - the item's parent NodeData
    * @param name
    *          - item's path entry (QName + index)
    * @param itemType
    *             item type         
    * @return - stored ItemData which has exact the same path Entry (name+index) inside the parent; or
    *         null if not such an item data found
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   ItemData getItemData(NodeData parentData, QPathEntry name, ItemType itemType) throws RepositoryException,
      IllegalStateException;

   /**
    * Reads <code>ItemData</code> from the storage by item identifier.
    * 
    * @param identifier
    *          - Item identifier
    * @return stored ItemData or null if no item found with given id. Basically used for
    *         Session.getNodeByUUID but not necessarily refers to jcr:uuid property (In fact, this
    *         identifier should not necessary be equal of referenceable node's UUID if any) thereby
    *         can return NodeData for not referenceable node data or PropertyData.
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   ItemData getItemData(String identifier) throws RepositoryException, IllegalStateException;

   /**
    * Reads <code>List</code> of <code>NodeData</code> from the storage using item's parent location.
    * 
    * @param parent
    *          NodeData
    * @return child nodes data or empty <code>List</code>
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException, IllegalStateException;

   /**
    * Reads <code>List</code> of <code>NodeData</code> from the storage using item's parent location, and name filter.
    * 
    * @param parent
    *          NodeData
    * @param pattern  - list of QPathEntryFilters
    * @return child nodes data or empty <code>List</code>
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   List<NodeData> getChildNodesData(NodeData parent, List<QPathEntryFilter> pattern) throws RepositoryException,
      IllegalStateException;

   /**
    * Reads count of <code>parent</code> child nodes.
    *
    * @param parent NodeData
    * @return long, childs count
    * @throws RepositoryException if error occurs
    */
   int getChildNodesCount(NodeData parent) throws RepositoryException;

   /**
    * Reads order number of last <code>parent</code> child nodes.
    *
    * @param parent NodeData
    * @return long, order number of last parent's child node.
    * @throws RepositoryException if error occurs
    */
   int getLastOrderNumber(NodeData parent) throws RepositoryException;
   
   /**
    * Reads <code>List</code> of <code>PropertyData</code> from the storage using item's parent
    * location.
    * 
    * @param parent
    *          NodeData
    * @return child properties data or empty <code>List</code>
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   List<PropertyData> getChildPropertiesData(NodeData parent) throws RepositoryException, IllegalStateException;

   /**
    * Reads <code>List</code> of <code>PropertyData</code> from the storage using item's parent
    * location.
    * 
    * @param parent
    *          NodeData
    * @param pattern
    *          String[] list of wild card names 
    * @return child properties data or empty <code>List</code>
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   List<PropertyData> getChildPropertiesData(NodeData parent, List<QPathEntryFilter> pattern) throws RepositoryException,
      IllegalStateException;

   /**
    * Reads <code>List</code> of <code>PropertyData</code> with empty <code>ValueData</code> from the
    * storage using item's parent location.
    * 
    * <br>
    * This method specially dedicated for non-content modification operations (e.g. Items delete).
    * 
    * @param parent
    *          NodeData
    * @return child properties data (with empty data) or empty <code>List</code>
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   List<PropertyData> listChildPropertiesData(NodeData parent) throws RepositoryException, IllegalStateException;

   /**
    * Reads <code>List</code> of <code>PropertyData</code> from the storage using item's parent
    * location.
    * 
    * <br>
    * It's REFERENCE type Properties referencing Node with given <code>nodeIdentifier</code>.
    * 
    * See more {@link javax.jcr.Node#getReferences()}
    * 
    * @param nodeIdentifier
    *          of referenceable Node
    * @return list of referenced property data or empty <code>List</code>
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    * @throws UnsupportedOperationException
    *           if operation is not supported
    */
   List<PropertyData> getReferencesData(String nodeIdentifier) throws RepositoryException, IllegalStateException,
      UnsupportedOperationException;

   /**
    * Get child Nodes of the parent node.
    * 
    * @param parent 
    *          the parent data
    * @param fromOrderNum
    *          the returned list of child nodes should not contain the node with order number 
    *          less than <code>fromOrderNum</code>
    * @param offset
    *          the position of the first element to retrieve.
    * @param pageSize
    *          the total amount of element expected per page.
    * @param childs
    *          will contain the resulted child nodes
    * @return true if there are data to retrieve for next request and false in other case 
    */
   boolean getChildNodesDataByPage(NodeData parent, int fromOrderNum, int offset, int pageSize, List<NodeData> childs)
      throws RepositoryException;
   
   /**
    * Adds single <code>NodeData</code>.
    * 
    * @param data
    *          - the new data
    * @throws InvalidItemStateException
    *           if the item already exists
    * @throws UnsupportedOperationException
    *           if operation is not supported (it is container for level 1)
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   void add(NodeData data) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException,
      IllegalStateException;

   /**
    * Adds single <code>PropertyData</code>.
    * 
    * @param data
    *          - the new data
    * @param sizeHandler
    *          accumulate changed size
    * @throws InvalidItemStateException
    *           if the item already exists
    * @throws UnsupportedOperationException
    *           if operation is not supported (it is container for level 1)
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   void add(PropertyData data, ChangedSizeHandler sizeHandler) throws RepositoryException,
      UnsupportedOperationException, InvalidItemStateException, IllegalStateException;

   /**
    * Updates <code>NodeData</code>.
    * 
    * @param data
    *          - the new data
    * @throws InvalidItemStateException
    *           (1)if the data is already updated, i.e. persisted version value of persisted data {@code >=}
    *           of new data's persisted version value (2) if the persisted data is not NodeData (i.e.
    *           it is PropertyData). It means that some other process deleted original data and
    *           replace it with other type of data.
    * @throws UnsupportedOperationException
    *           if operation is not supported (it is container for level 1)
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   void update(NodeData data) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException,
      IllegalStateException;

   /**
    * Updates <code>PropertyData</code>.
    * 
    * @param data
    *          - the new data
    * @param sizeHandler
    *          accumulates changed size
    * @throws InvalidItemStateException
    *           (1)if the data is already updated, i.e. persisted version value of persisted data {@code >=}
    *           of new data's persisted version value (2) if the persisted data is not PropertyData
    *           (i.e. it is NodeData). It means that some other process deleted original data and
    *           replace it with other type of data.
    * @throws UnsupportedOperationException
    *           if operation is not supported (it is container for level 1)
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   void update(PropertyData data, ChangedSizeHandler sizeHandler) throws RepositoryException,
      UnsupportedOperationException, InvalidItemStateException, IllegalStateException;

   /**
    * Renames <code>NodeData</code> using Node identifier and new name and index from the data.
    * 
    * @param data
    *          - NodeData to be renamed
    * @throws InvalidItemStateException
    *           (1)if the data is already updated, i.e. persisted version value of persisted data {@code >=}
    *           of new data's persisted version value (2) if the persisted data is not PropertyData
    *           (i.e. it is NodeData). It means that some other process deleted original data and
    *           replace it with other type of data.
    * @throws UnsupportedOperationException
    *           if operation is not supported (it is container for level 1)
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   void rename(NodeData data) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException,
      IllegalStateException;

   /**
    * Deletes <code>NodeData</code>.
    * 
    * @param data
    *          that identifies data to be deleted
    * 
    * @throws InvalidItemStateException
    *           if the data is already deleted
    * @throws UnsupportedOperationException
    *           if operation is not supported (it is container for level 1)
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   void delete(NodeData data) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException,
      IllegalStateException;

   /**
    * Deletes <code>PropertyData</code>.
    * 
    * @param data
    *          that identifies data to be deleted
    * @param sizeHandler
    *          accumulate changed size
    * @throws InvalidItemStateException
    *           if the data is already deleted
    * @throws UnsupportedOperationException
    *           if operation is not supported (it is container for level 1)
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    */
   void delete(PropertyData data, ChangedSizeHandler sizeHandler) throws RepositoryException,
      UnsupportedOperationException, InvalidItemStateException, IllegalStateException;

   /**
    * Prepare the commit phase.
    * 
    * @throws IllegalStateException
    *           if connection is already closed
    * @throws RepositoryException
    *           if some exception occurred
    */
   void prepare() throws IllegalStateException, RepositoryException;
   
   /**
    * Persist changes and closes connection. It can be database transaction commit for instance etc.
    * 
    * @throws IllegalStateException
    *           if connection is already closed
    * @throws RepositoryException
    *           if some exception occurred
    */
   void commit() throws IllegalStateException, RepositoryException;

   /**
    * Refuses persistent changes and closes connection. It can be database transaction rollback for
    * instance etc.
    * 
    * @throws IllegalStateException
    *           if connection is already closed
    * @throws RepositoryException
    *           if some exception occurred
    */
   void rollback() throws IllegalStateException, RepositoryException;

   /**
    * Close connection.
    * 
    * @throws IllegalStateException
    *           if connection is already closed
    * @throws RepositoryException
    *           if some exception occurred
    */
   void close() throws IllegalStateException, RepositoryException;

   /**
    * Returns true if connection can be used.
    * 
    * @return boolean, true if connection is open and ready, false - otherwise
    */
   boolean isOpened();

   /**
    * Returns all the nodes that hold some ACL info like owner or permissions
    * 
    * @return a list of all the ACL holders for this workspace
    * @throws RepositoryException
    *           if some exception occurred
    * @throws IllegalStateException
    *           if connection is closed
    * @throws UnsupportedOperationException
    *           if operation is not supported
    */
   List<ACLHolder> getACLHolders() throws RepositoryException, IllegalStateException, UnsupportedOperationException;

   /**
    * Reads count of nodes in workspace.
    * 
    * @return
    *          nodes count 
    * @throws RepositoryException
    *           if a database access error occurs
    */
   public long getNodesCount() throws RepositoryException;

   /**
    * Calculate workspace data size.
    *
    * @throws RepositoryException
    */
   public long getWorkspaceDataSize() throws RepositoryException;

   /**
    * Calculate node data size.
    *
    * @param nodeIdentifier
    *          node identifier which size need to calculate
    * @throws RepositoryException
    */
   public long getNodeDataSize(String nodeIdentifier) throws RepositoryException;

}
